Skip to content

fix(http): prefer brotli for equal compression q-values#35011

Merged
littledivy merged 2 commits into
mainfrom
orch/divybot-531
Jun 10, 2026
Merged

fix(http): prefer brotli for equal compression q-values#35011
littledivy merged 2 commits into
mainfrom
orch/divybot-531

Conversation

@divybot

@divybot divybot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary:

  • choose Deno-supported response compression by q-value instead of first parsed token
  • prefer Brotli over gzip when both are equally acceptable
  • cover Chrome-style Accept-Encoding headers that include zstd

Tests:

  • cargo test -p deno_http compression_
  • cargo build --bin deno

Closes #23050
Closes denoland/divybot#531

@bartlomieju

Copy link
Copy Markdown
Member

Review

The core fix is right: fly_accept_encoding::preferred early-returns on the first encoding with qval ≈ 1.0 (ext/http/fly_accept_encoding.rs:65-66), so Chrome's gzip, deflate, br, zstd (all implicit q=1.0) makes gzip win because it's listed first. Tie-breaking toward Brotli in the new helper correctly addresses that.

A few things before merge:

🔴 Regression: unknown encoding tokens now disable compression

The old code filtered the iterator before selecting:

.filter(|r| matches!(r, Ok((Some(Identity|Gzip|Brotli), _))))

which silently dropped parse errors. The new helper instead propagates them via ?:

let (encoding, qval) = encoding?;   // propagates Err(UnknownEncoding)

Encoding::parse returns Err(EncodingError::UnknownEncoding) for any token outside {gzip, deflate, br, zstd, identity, *}. So a header like Accept-Encoding: br, compress or gzip, x-gzip now returns Err.unwrap_or(Compression::None)no compression at all, even though br/gzip are present and acceptable. Previously those compressed fine. This is a fail-closed change for legacy/registered codings (compress, x-gzip) and any future encoding.

Suggested fix — ignore unparseable tokens instead of propagating:

for encoding in encodings {
    let Ok((encoding, qval)) = encoding else { continue; };
    ...
}

If you do this, the function no longer needs to return Result — return Compression directly and drop the .unwrap_or(...) at both call sites (and the .unwrap() in the test helper). Please also add a regression test, e.g. compression_for("br, compress") should yield Brotli (the current tests only use known tokens, so they don't catch this).

🟡 Same bug remains in the legacy serveHttp path

ext/http/lib.rs:806-816 uses the identical fly_accept_encoding::preferred(...) pattern and has the same first-q=1.0-wins behavior, so the brotli-vs-gzip issue persists there. Since #23050 is filed against Deno.serve generally and this PR introduces a reusable helper, consider routing that site through the same helper, or note in the description why the deprecated path is intentionally left alone.

🟡 No end-to-end coverage

The unit tests for the helper are good. It'd be worth adding a spec test under tests/specs/ that hits a server with Chrome's exact Accept-Encoding and asserts content-encoding: br, to directly validate the closed issue.

✅ Looks correct

  • q=0 handling (br;q=0 stays at 0.0, gated out by > 0.0).
  • Identity preference (identity;q=1.0, gzip;q=0.9, br;q=0.9None).
  • Good de-duplication of the previously copy-pasted match logic.

@littledivy
littledivy force-pushed the orch/divybot-531 branch 2 times, most recently from 64a158d to 68a37eb Compare June 8, 2026 06:12
@littledivy
littledivy merged commit 1e9e951 into main Jun 10, 2026
136 checks passed
@littledivy
littledivy deleted the orch/divybot-531 branch June 10, 2026 02:20
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.

Bug: Deno.serve prefers gzip over br compression for Chrome 123

3 participants