Skip to content

fix(decompress): join a repeated content-encoding header - #5664

Open
luantaraschi wants to merge 1 commit into
nodejs:mainfrom
luantaraschi:fix/decompress-repeated-content-encoding
Open

fix(decompress): join a repeated content-encoding header#5664
luantaraschi wants to merge 1 commit into
nodejs:mainfrom
luantaraschi:fix/decompress-repeated-content-encoding

Conversation

@luantaraschi

@luantaraschi luantaraschi commented Aug 7, 2026

Copy link
Copy Markdown

This relates to...

No open issue. Found while reading lib/interceptor/decompress.js.

Rationale

DecompressHandler.onResponseStart reads the header as a string:

const contentEncoding = headers['content-encoding']
...
const decompressors = this.#createDecompressionChain(contentEncoding.toLowerCase())

parseHeaders turns a repeated field into an array (lib/core/util.js, the val = [val] branch), and the JSDoc on this very method already types headers as Record<string, string | string[] | undefined>. An array is truthy, so #shouldSkipDecompression lets it through and .toLowerCase() throws:

TypeError: contentEncoding.toLowerCase is not a function

Reproduced against a raw socket server that writes the field twice:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Encoding: gzip
Content-Encoding: gzip

RFC 9110 section 5.3 lets a recipient combine repeated field lines into one by joining the values with commas, and that comma-joined form is exactly what #createDecompressionChain already splits and handles. So the fix is to join before anything else looks at the value.

The existing chain limit still applies afterwards: six repeated Content-Encoding lines join into six parts and hit the same maxContentEncodings guard as the single-line form, which the suite already covers.

Changes

onResponseStart joins an array-valued content-encoding with commas before the skip check and the chain build.

Test added to test/interceptors/decompress.js: a response with two Content-Encoding: gzip lines and a doubly gzipped body decompresses to the original text.

Features

N/A

Bug Fixes

The decompress interceptor no longer throws a TypeError when a response repeats Content-Encoding across field lines, and decompresses it the same way it decompresses the comma-joined single-line form.

Breaking Changes and Deprecations

None. Responses with a single Content-Encoding line take exactly the same path as before.

Status

parseHeaders turns a repeated field into an array, and this method's own
JSDoc already types the headers record as string | string[] | undefined.
An array is truthy, so the skip check let it through and toLowerCase threw
a TypeError.

RFC 9110 section 5.3 allows joining repeated field lines with commas, which
produces the single-line form the decompression chain already splits and
handles, including the maxContentEncodings guard.
Copilot AI lite review requested due to automatic review settings August 7, 2026 16:54

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants