Skip to content

fix: set octet-stream Content-Type on pprof endpoints for scraper compatibility#5071

Open
tcx4c70 wants to merge 1 commit into
maximhq:devfrom
tcx4c70:fix/pprof
Open

fix: set octet-stream Content-Type on pprof endpoints for scraper compatibility#5071
tcx4c70 wants to merge 1 commit into
maximhq:devfrom
tcx4c70:fix/pprof

Conversation

@tcx4c70

@tcx4c70 tcx4c70 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

The custom pprof server wrote profile payloads without setting a Content-Type, so net/http sniffed the gzipped protobuf and labeled responses application/x-gzip. Pyroscope/Grafana Alloy scrapers require application/octet-stream and rejected every profile with "invalid profile data: unexpected Content-Type application/x-gzip".

Changes

Explicitly set Content-Type (and X-Content-Type-Options: nosniff plus Content-Disposition) before the first write on the Lookup, CPU profile, and trace handlers, matching the standard net/http/pprof behavior.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Describe the steps to validate this change. Include commands and expected outcomes.

# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build

If adding new configs or environment variables, document them here.

Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

Breaking changes

  • Yes
  • No

If yes, describe impact and migration instructions.

Related issues

Link related issues and discussions. Example: Closes #123

Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

…patibility

The custom pprof server wrote profile payloads without setting a
Content-Type, so net/http sniffed the gzipped protobuf and labeled
responses application/x-gzip. Pyroscope/Grafana Alloy scrapers require
application/octet-stream and rejected every profile with "invalid
profile data: unexpected Content-Type application/x-gzip".

Explicitly set Content-Type (and X-Content-Type-Options: nosniff plus
Content-Disposition) before the first write on the Lookup, CPU profile,
and trace handlers, matching the standard net/http/pprof behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved profiling endpoints to send consistent response headers before streaming begins.
    • Downloads now include clearer content types and attachment filenames, helping browsers and tools handle profile, CPU, and trace outputs correctly.
    • Disabled MIME sniffing for profiling responses to reduce unexpected handling of streamed data.

Walkthrough

Response headers are added to the pprof profiling HTTP handlers before payload writes. The index handler sets a text Content-Type and nosniff; the named-profile handler sets Content-Type conditionally on the debug parameter plus a Content-Disposition filename; the CPU profile and trace handlers set octet-stream Content-Type, nosniff, and Content-Disposition filenames before streaming.

Changes

Profiling response headers

Layer / File(s) Summary
pprof handler headers
transports/bifrost-http/profiling/profiling.go
Adds X-Content-Type-Options: nosniff and explicit Content-Type headers across the pprof index, named-profile, CPU profile, and trace handlers; named-profile responses choose plain text or octet-stream based on the debug query parameter; octet-stream responses set a Content-Disposition attachment filename via fmt.Sprintf, requiring the new fmt import.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not address the direct requirement in [#123] for Files API support and instead changes pprof response headers. Implement the file upload API described in #123, or retarget/remove the link if this PR is only for pprof header fixes.
Out of Scope Changes check ⚠️ Warning The pprof transport changes are unrelated to the linked Files API support objective in [#123]. Remove the unrelated pprof work from this PR or update the linked issue to match the actual scope.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the pprof header fix and clearly describes the main change.
Description check ✅ Passed The description includes all required sections and clearly explains the pprof header fix and testing intent.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The changed flow looks mergeable after a small cleanup to error-response headers.

  • Successful profile and trace responses now carry the expected binary content type.
  • Early CPU-profile and trace failures can still include attachment metadata on text errors.
  • The issue affects diagnostics and client handling, not the profile payload success path.

transports/bifrost-http/profiling/profiling.go

Important Files Changed

Filename Overview
transports/bifrost-http/profiling/profiling.go Adds explicit pprof response headers; success paths match scraper needs, while early CPU and trace failures should clear attachment metadata.

Comments Outside Diff (2)

  1. transports/bifrost-http/profiling/profiling.go, line 94-98 (link)

    P2 CPU Error Downloads As Profile

    When a second /debug/pprof/profile request arrives while CPU profiling is already active, StartCPUProfile fails before writing and http.Error sends a text error. The new attachment header remains on that error response, so clients can save or parse the failure body as a profile file instead of handling it as an error.

  2. transports/bifrost-http/profiling/profiling.go, line 123-127 (link)

    P2 Trace Error Downloads As Trace

    When /debug/pprof/trace is requested while runtime tracing is already active, trace.Start fails before writing and http.Error sends a text error. The new attachment header remains on that response, so clients can save or parse the error body as a trace artifact.

Reviews (1): Last reviewed commit: "fix: set octet-stream Content-Type on pp..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
transports/bifrost-http/profiling/profiling.go (1)

88-94: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Optional: clear Content-Disposition on error paths.

If pprof.StartCPUProfile or trace.Start fails, http.Error overrides Content-Type to text/plain but does not remove the Content-Disposition: attachment header set just above. The error response would then be downloaded as a file named "profile"/"trace" instead of displayed inline. This is a debug-only endpoint so impact is negligible, but a one-liner w.Header().Del("Content-Disposition") before the http.Error call would make error responses cleaner.

Optional fix for CPU profile handler
 	if err := pprof.StartCPUProfile(w); err != nil {
+		w.Header().Del("Content-Disposition")
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}

Also applies to: 119-123

🤖 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 `@transports/bifrost-http/profiling/profiling.go` around lines 88 - 94, The
profiling handlers in profiling.go leave Content-Disposition set when
StartCPUProfile or trace.Start fails, causing http.Error responses to be
downloaded as files instead of shown inline. In the CPU and trace error paths
inside the profiling handler functions, remove the attachment header before
calling http.Error so error responses are clean; use the existing header setup
around Content-Disposition in the profile/trace response flow as the place to
adjust.
🤖 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 `@transports/bifrost-http/profiling/profiling.go`:
- Around line 88-94: The profiling handlers in profiling.go leave
Content-Disposition set when StartCPUProfile or trace.Start fails, causing
http.Error responses to be downloaded as files instead of shown inline. In the
CPU and trace error paths inside the profiling handler functions, remove the
attachment header before calling http.Error so error responses are clean; use
the existing header setup around Content-Disposition in the profile/trace
response flow as the place to adjust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a9427f2c-13dc-4acc-bd7b-7e048ccc073b

📥 Commits

Reviewing files that changed from the base of the PR and between 2164080 and 09e7ee5.

📒 Files selected for processing (1)
  • transports/bifrost-http/profiling/profiling.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Files API Support

1 participant