Skip to content

Fix AutoRouter upstream response encoding#13

Merged
jhaynie merged 2 commits intomainfrom
fix/autorouter-accept-encoding
May 5, 2026
Merged

Fix AutoRouter upstream response encoding#13
jhaynie merged 2 commits intomainfrom
fix/autorouter-accept-encoding

Conversation

@parteeksingh24
Copy link
Copy Markdown
Contributor

@parteeksingh24 parteeksingh24 commented May 5, 2026

Summary

  • Force AutoRouter upstream requests to send Accept-Encoding: identity
  • Share the upstream response-encoding policy across Forward and ForwardStreaming
  • Add regressions for compressed client request headers and existing streaming behavior

Why this matters: normal clients like curl --compressed, Bun fetch, and Node fetch can advertise compressed response support. AutoRouter response extractors expect plain provider JSON, so forwarding that client header upstream can make extraction fail on compressed bytes.

Closes #12

Context

AutoRouter.Forward copied the caller's request headers into the upstream provider request. When the caller sent:

Accept-Encoding: gzip, deflate, br

the upstream provider could return compressed JSON. Provider response extractors read resp.Body directly as JSON, which reproduced the observed failure:

invalid character '\x1f' looking for beginning of value

AutoRouter.ForwardStreaming already forced upstream Accept-Encoding: identity. This PR applies the same policy to non-streaming AutoRouter requests and keeps both paths on one helper.

What Changed

AutoRouter upstream responses stay uncompressed

AutoRouter.Forward now normalizes the upstream request before provider enrichment so response extractors receive plain provider bodies.

The streaming path uses the same helper, preserving the existing behavior while making the shared invariant explicit.

Regression coverage

The new non-streaming regression simulates a compressed-capable client. The test upstream returns gzipped JSON unless llmproxy sends:

Accept-Encoding: identity

Without the fix, the test fails with the same \x1f JSON parse error. With the fix, extraction succeeds.

A streaming regression confirms ForwardStreaming still sends Accept-Encoding: identity upstream.

Verification

  • Reproduced TestAutoRouter_ForwardForcesIdentityAcceptEncoding failing without the fix:
    • Forward() error = invalid character '\x1f' looking for beginning of value
  • git diff --cached --check
  • go vet ./...
  • go test -run 'TestAutoRouter_(ForwardForcesIdentityAcceptEncoding|ForwardStreamingForcesIdentityAcceptEncoding|Forward)$' .
  • go test ./...
  • go test -race ./...
  • /Users/parteek/go/bin/golangci-lint run --new-from-rev=origin/main ./...
  • go run golang.org/x/vuln/cmd/govulncheck@latest -show verbose ./...

Notes

  • make test was not used as the final verification command because local govulncheck is not installed on PATH.
  • The same vulnerability check passed through go run golang.org/x/vuln/cmd/govulncheck@latest.

References

Summary by CodeRabbit

  • New Features
    • Upstream responses are now transmitted uncompressed to ensure consistent handling across request types.
  • Tests
    • Added verification tests for uncompressed response transmission in standard requests.
    • Added verification tests for uncompressed response transmission in streaming requests.

- Request `identity` responses from `AutoRouter` upstreams
- Share the `Accept-Encoding` policy across router paths
- Add regressions for compressed client request headers
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

A helper function is added to disable upstream response compression by setting Accept-Encoding to identity. The helper is integrated into the Forward and ForwardStreaming paths, replacing prior explicit header manipulation. Tests verify correct behavior in both paths.

Changes

Upstream Compression Disabling

Layer / File(s) Summary
Helper Function Definition
autorouter.go
New disableUpstreamResponseCompression(req *http.Request) function sets Accept-Encoding header to "identity" to prevent upstream compression.
Forward Path Integration
autorouter.go
Forward calls disableUpstreamResponseCompression(upstreamReq) after copying upstream request headers.
Streaming Path Integration
autorouter.go
ForwardStreaming calls disableUpstreamResponseCompression(upstreamReq), consolidating prior explicit SSE compression handling.
Test Coverage
autorouter_test.go
Added TestAutoRouter_ForwardForcesIdentityAcceptEncoding and TestAutoRouter_ForwardStreamingForcesIdentityAcceptEncoding with gzip import; both verify that upstream observes "identity" encoding and responses remain correct.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Pull request successfully addresses Issue #12 by forcing Accept-Encoding: identity for non-streaming Forward requests, consolidating the policy into a shared helper, and adding comprehensive regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the AutoRouter Accept-Encoding issue with no unrelated modifications to other functionality or systems.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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.

@parteeksingh24 parteeksingh24 marked this pull request as ready for review May 5, 2026 22:37
Copy link
Copy Markdown

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
autorouter.go (1)

195-197: ⚡ Quick win

disableUpstreamResponseCompression should be called after Enrich to prevent override

Currently called before Enrich in both Forward (line 195) and ForwardStreaming (line 330). A provider enricher that sets Accept-Encoding — even inadvertently — will silently override identity and reintroduce the compression bug this PR fixes. Calling the helper after Enrich makes the guarantee unconditional.

🛠️ Proposed fix for Forward
-	disableUpstreamResponseCompression(upstreamReq)
-
 	if err := provider.RequestEnricher().Enrich(upstreamReq, meta, body); err != nil {
 		return nil, ResponseMetadata{}, err
 	}
+
+	disableUpstreamResponseCompression(upstreamReq)

Apply the same reordering in ForwardStreaming (around lines 330–332).

🤖 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 `@autorouter.go` around lines 195 - 197, The call to
disableUpstreamResponseCompression is currently made before
provider.RequestEnricher().Enrich(upstreamReq, meta, body), which allows an
enricher to set Accept-Encoding and override the enforced identity; move the
call to disableUpstreamResponseCompression(upstreamReq) to immediately after the
Enrich(...) call in both Forward and ForwardStreaming so enrichment cannot
reintroduce compression on upstreamReq.
🤖 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.

Inline comments:
In `@autorouter_test.go`:
- Around line 209-222: The test calls router.Forward(...) and asserts on resp
but never closes resp.Body; add a close to avoid leaking the response body by
deferring resp.Body.Close() (or explicitly closing it after use) immediately
after the nil error check when resp is non-nil so the http response body is
always released; locate the Forward call and ensure resp.Body.Close() is invoked
(e.g., directly after resp, meta, err := router.Forward(...) and err check) to
satisfy bodyclose vet/linter.

---

Nitpick comments:
In `@autorouter.go`:
- Around line 195-197: The call to disableUpstreamResponseCompression is
currently made before provider.RequestEnricher().Enrich(upstreamReq, meta,
body), which allows an enricher to set Accept-Encoding and override the enforced
identity; move the call to disableUpstreamResponseCompression(upstreamReq) to
immediately after the Enrich(...) call in both Forward and ForwardStreaming so
enrichment cannot reintroduce compression on upstreamReq.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 90f8ca09-1c25-4847-ba71-b95a7b043287

📥 Commits

Reviewing files that changed from the base of the PR and between 5dacc0a and 63b8b6e.

📒 Files selected for processing (2)
  • autorouter.go
  • autorouter_test.go
📜 Review details
🧰 Additional context used
🪛 golangci-lint (2.12.1)
autorouter_test.go

[error] 209-209: response body must be closed

(bodyclose)


[error] 205-205: net/http/httptest.NewRequest must not be called. use net/http/httptest.NewRequestWithContext

(noctx)

🔇 Additional comments (2)
autorouter_test.go (1)

690-752: LGTM — streaming regression test is well-structured.

Verifies Accept-Encoding, status code, metadata, and full event-stream body propagation in one test.

autorouter.go (1)

18-22: disableUpstreamResponseCompression helper is correct and well-scoped.

Comment thread autorouter_test.go
- Enforce `Accept-Encoding: identity` after provider enrichment
- Cover enricher overrides in streaming and non-streaming tests
- Close response bodies and satisfy diff-scoped lint
@parteeksingh24 parteeksingh24 requested a review from jhaynie May 5, 2026 23:00
@jhaynie jhaynie merged commit 235565c into main May 5, 2026
2 checks passed
@jhaynie jhaynie deleted the fix/autorouter-accept-encoding branch May 5, 2026 23:10
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.

AutoRouter fails when non-streaming clients request compressed responses

2 participants