fix: keep content-encoding when forwarding undecoded passthrough bodies - #678
Conversation
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-678Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr678.tgz
npm install -g packageThis comment is automatically updated on each push. |
[bot] 🏷 Reviewing PR #678 now — single commit touching |
[bot] 🏷 Review of PR #678 — verified independently against the code and re-ran the full pre-flight locally on branch Diff cleanliness (first gate). Exactly 2 files, +129/−6 ( Request side — verified safe.
Response side — strip sites are exhaustive. There are exactly two client-facing upstream-header-forward sites, both in Both compression modes considered. The change sits at the HTTP wire boundary, orthogonal to plugin/proxy mode: processed requests (both modes) drop the header at decode; passthrough is Other. The old #619 catch-block comment ("the content-encoding header stays intact", Local re-run (not just trusting the PR's claim): No issues found that needed fixing on the branch. Root cause correctly identified (end-to-end header lumped into a direction-shared strip set), fixed at the root rather than per-path, minimal diff. Ready to merge. 中文摘要:修复了 content-encoding(端到端头)被误放进双向共用的 hop-by-hop 剥离集、导致未解码透传请求体(#619 不可解码编码、未知路径)以"无编码声明的二进制字节"发给上游引发 400 的问题;按方向拆分剥离集(请求侧保留、响应侧仍剥离),已逐路径核实安全性并本地复跑 typecheck/1272 测试/build 全绿,可以合并。 |
Problem (#677)
content-encodingis an end-to-end header (RFC 9110 §7.2), not hop-by-hop, but it lived inUPSTREAM_HOP_HEADERS— a single strip set shared by both forwarding directions. That is correct for responses (Node's fetch transparently decodes upstream bodies, so the marker must not reach the client) but wrong for requests on any path where bili forwards bytes it did not decode:src/server.ts, protocol dropped → verbatim relay of still-encoded bytes) — header stripped at forward, upstream receives undeclared binary bytes → 400-class failures;POST /v1/embeddings) where no decode is ever attempted.The comment near the #619 catch block ("the content-encoding header stays intact") also contradicted actual behavior; after this change it is true again.
Fix
Split the strip set by direction:
content-encodingremoved fromUPSTREAM_HOP_HEADERS(request side now forwards it);RESPONSE_ONLY_STRIP_HEADERS = new Set(["content-encoding"])applied at the two response-forward sites (forward(), normal + debug log copy), preserving today's client-facing behavior exactly.Safe because every processed request already drops the header at decode time (
if (decoded.decoded) delete req.headers["content-encoding"]) before forwarding — only byte-identical passthrough bodies carry it to the forward, and there the marker describes exactly the bytes being sent.Tests (
tests/decode-fail-passthrough.test.ts)content-encoding: gzipreaches the upstream;content-encodingon a valid gzip body;content-encodingheader) — pins the direction asymmetry;tests/codex-official.test.tsvalid-gzip decoded path still asserts the header is absent upstream (unchanged behavior).Pre-flight
npm run typecheck✅npm test— 1272/1272 ✅npm run build✅Fixes #677