Skip to content

fix(v3/otel): avoid stream replacement for static responses#1902

Open
edgarsilva wants to merge 2 commits into
gofiber:mainfrom
edgarsilva:fix/otel-stream-lifecycle-1734
Open

fix(v3/otel): avoid stream replacement for static responses#1902
edgarsilva wants to merge 2 commits into
gofiber:mainfrom
edgarsilva:fix/otel-stream-lifecycle-1734

Conversation

@edgarsilva
Copy link
Copy Markdown

@edgarsilva edgarsilva commented May 7, 2026

Summary

  • stop wrapping request/response body streams in v3/otel middleware, which avoids SetBodyStream -> ResetBody side effects that can close fasthttp static readers and stall/panic requests
  • preserve request/response size metrics without mutating streams by recording known sizes from Content-Length, *io.LimitedReader, or Len() when available
  • add regression tests covering repeated static asset requests and repeated /.well-known/... not-found requests to verify middleware does not hang responses

Validation

  • run go test ./... in v3/otel

Fixes #1734.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Resolved potential hangs in streaming middleware during static asset requests and 404 responses.
  • Improvements

    • Improved HTTP request/response body size measurement accuracy in telemetry collection.

Stop wrapping request/response body streams for size metrics so middleware no longer triggers fasthttp stream reset/close side effects on static file readers. Add regression coverage for repeated static and .well-known requests while preserving size metrics when stream length is known.
@edgarsilva edgarsilva requested a review from a team as a code owner May 7, 2026 00:01
@edgarsilva edgarsilva requested review from ReneWerner87, efectn, gaby and sixcolors and removed request for a team May 7, 2026 00:01
@ReneWerner87 ReneWerner87 added the ☢️ Bug Something isn't working label May 7, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the OpenTelemetry middleware to improve how request and response body sizes are determined. The previous implementation, which wrapped body streams to count bytes during transit, has been replaced with a more direct approach that leverages the 'Content-Length' header or the stream's internal length where available. This change simplifies the logic and addresses potential hanging issues, as verified by the newly added tests for static assets and 404 responses. Feedback was provided to use a case-insensitive comparison when identifying Server-Sent Events (SSE) to ensure the middleware correctly skips size calculations for those streams.

Comment thread v3/otel/fiber.go Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Warning

Rate limit exceeded

@edgarsilva has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 1 second before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 536387a7-513d-4d62-b1ae-97ced4dfe046

📥 Commits

Reviewing files that changed from the base of the PR and between 636838e and 2134901.

📒 Files selected for processing (1)
  • v3/otel/fiber.go

Walkthrough

This PR fixes a critical bug in the OTEL Fiber middleware that caused nil pointer dereferences and request hangs when tracing static assets. It replaces the unsafe bodyStreamSizeReader wrapper approach with a new bodyStreamSize() helper that detects body size without wrapping streams, and guards metric recording with requestSizeKnown and responseSizeKnown flags.

Changes

Body Size Tracking and Metrics Recording Refactor

Layer / File(s) Summary
Size Detection Helper
v3/otel/fiber.go (lines 22–42)
New bodyStreamSize(io.Reader) (int64, bool) helper infers body size from *io.LimitedReader or readers exposing Len(), returning size only when determinable without wrapping.
Request Size Computation
v3/otel/fiber.go (lines 71–161)
Replaces bodyStreamSizeReader wrapper with direct Content-Length checks; falls back to bodyStreamSize() for streaming bodies. Tracks knowledge via requestSizeKnown flag.
Response Size Computation
v3/otel/fiber.go (lines 204–220)
Replaces response stream wrapping with Content-Length and bodyStreamSize() fallback; maintains SSE stream skipping. Tracks knowledge via responseSizeKnown flag.
Metric Recording & Span Attributes
v3/otel/fiber.go (lines 221–240)
Records httpServerRequestBodySize and httpServerResponseBodySize metrics only when size is known. Removes EOF-driven callbacks and records response size directly from computed value. Span attributes set conditionally based on size knowledge.
Import Cleanup
v3/otel/fiber.go (lines 44–69)
Removes unused imports tied to prior streaming reader tracking logic.
Test Coverage
v3/otel/fiber_stream_test.go
Adds TestMiddleware_StaticAssetsDoNotHang to verify static asset requests complete without hanging, and TestMiddleware_NotFoundPathDoesNotHang to verify repeated 404 responses don't stall. Both tests read/close response bodies to catch improper stream handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • gofiber/contrib#1120: Modifies OTEL Fiber middleware's response-size measurement to skip Server-Sent-Events and adjust body size computation.

Suggested labels

🧹 Updates, 🔧 fixes

Suggested reviewers

  • gaby
  • sixcolors
  • ReneWerner87
  • efectn

Poem

🐰 Streams that wrapped now fly so free,
No nil-dereference tragedy!
Static assets serve with grace,
No hangs to slow the browser's pace.
Tests hop-check each asset load—
Safe and sound on the road! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(v3/otel): avoid stream replacement for static responses' directly describes the main change—refactoring body measurement to avoid stream wrapping that caused hangs/panics with static asset requests.
Linked Issues check ✅ Passed Changes address all core coding requirements from issue #1734: eliminate stream wrapping that causes fasthttp static reader panics, preserve size metrics via Content-Length/Len() introspection, and add regression tests for static assets and 404 responses.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #1734: refactor fiber.go to remove bodyStreamSizeReader, add streaming test coverage, and update imports—no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
v3/otel/fiber_stream_test.go (1)

36-48: 💤 Low value

Optional: use Go 1.22+ for range N to drop unused loop variables.

The module targets Go 1.25.0, supporting the for range N syntax. Variable i is unused in both nested loops at lines 36–48 and the loop at line 57. Simplify to for range 25 for clarity.

♻️ Suggested changes
-	for i := 0; i < 25; i++ {
+	for range 25 {
 		for _, tc := range testCases {

Apply the same to line 57 in TestMiddleware_NotFoundPathDoesNotHang.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/otel/fiber_stream_test.go` around lines 36 - 48, Replace the unused index
loops with Go 1.22+ "for range N" form: in fiber_stream_test.go change the outer
loop in the test that iterates 25 times (currently written as "for i := 0; i <
25; i++") to "for range 25" to drop the unused variable, and do the same for the
similar repetition loop in TestMiddleware_NotFoundPathDoesNotHang; update the
loop surrounding the inner table-driven loop over testCases (and any other
identical repetition loops) so the inner variable names (e.g., tc) remain
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@v3/otel/fiber_stream_test.go`:
- Around line 36-48: Replace the unused index loops with Go 1.22+ "for range N"
form: in fiber_stream_test.go change the outer loop in the test that iterates 25
times (currently written as "for i := 0; i < 25; i++") to "for range 25" to drop
the unused variable, and do the same for the similar repetition loop in
TestMiddleware_NotFoundPathDoesNotHang; update the loop surrounding the inner
table-driven loop over testCases (and any other identical repetition loops) so
the inner variable names (e.g., tc) remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bff155af-b5a5-4ac8-b992-a54c2751500a

📥 Commits

Reviewing files that changed from the base of the PR and between 7dc3912 and 636838e.

📒 Files selected for processing (2)
  • v3/otel/fiber.go
  • v3/otel/fiber_stream_test.go
📜 Review details
🔇 Additional comments (5)
v3/otel/fiber.go (4)

22-42: Helper looks correct and minimal.

The non-wrapping approach via type assertions on *io.LimitedReader and the locally-declared lenReader interface cleanly addresses the root cause from #1734 (no SetBodyStream mutation, so fasthttp's static reader cleanup is preserved). The >= 0 guards correctly exclude fasthttp's sentinel Content-Length values (-1 for chunked, -2 for identity) when this helper is used downstream.


147-161: Request size capture order is correct.

Capturing requestSize before c.Next() (line 188) ensures Len()/LimitedReader.N reflect the full body, not a partially-consumed remainder. No issue.


221-243: Metric/attribute gating is consistent.

Both the deferred histogram recording and the span attribute set use responseSizeKnown (and requestSizeKnown) consistently, so unknown sizes are no longer recorded as 0. This is a small correctness improvement on top of the hang fix.


201-219: ⚡ Quick win

The fix correctly avoids stream-wrapping side effects by only inspecting the stream, not replacing it.

The original concern was well-founded given the prior SetBodyStream issues, but the implementation is safe. The bodyStreamSize() function performs only read-only inspections (accessing .Len() property or type-asserting to *io.LimitedReader) without consuming, closing, or modifying the stream. Regression tests confirm repeated static file and 404 requests work correctly across 25 iterations without hangs or body corruption—directly validating that calling response.BodyStream() here is side-effect-free.

v3/otel/fiber_stream_test.go (1)

51-66: Targeted regression test for the /.well-known/... 404 hang case.

Coverage matches the symptom path called out in the linked issue. app.Test will surface a real hang via its default deadline, and io.ReadAll + Body.Close() ensures the response is fully drained per iteration.

Treat response Content-Type as media type plus optional parameters and match SSE values case-insensitively so event streams are consistently excluded from body-size calculation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

☢️ Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 [Bug]: Fiber v3 OTel middleware can stall browser request/response flow when tracing static assets with default config

2 participants