Visitar URL original
fix(websocket): enforce deflate remaining payload budget by mcollina · Pull Request #5435 · nodejs/undici · GitHub
Skip to content

fix(websocket): enforce deflate remaining payload budget - #5435

Open
mcollina wants to merge 1 commit into
mainfrom
fix/websocket-deflate-remaining-budget
Open

mcollina wants to merge 1 commit into
mainfrom
fix/websocket-deflate-remaining-budget

Conversation

@mcollina

Copy link
Copy Markdown
Member

This relates to...

WebSocket maxPayloadSize enforcement for fragmented permessage-deflate messages.

Rationale

The receiver passed the current decompressed fragment total to PerMessageDeflate.decompress(), but the inflater ignored that argument. As a result, compressed fragmented messages were rejected only after the inflated chunk was appended, allowing inflate work beyond the remaining message budget.

Changes

Features

N/A

Bug Fixes

  • Teach PerMessageDeflate.decompress() to account for the current decompressed message size while inflating.
  • Reject once currentPayloadSize + current inflate output exceeds maxPayloadSize.
  • Add regression coverage for enforcing maxPayloadSize against existing decompressed fragments.

Breaking Changes and Deprecations

N/A

Status

Validation:

  • node --test test/websocket/fragments.js test/websocket/permessage-deflate-limit.js test/websocket/permessage-deflate-config.js test/websocket/permessage-deflate-windowbits.js test/websocket/stream/too-many-fragments.js
  • npm run lint

Signed-off-by: Matteo Collina <hello@matteocollina.com>
@codecov-commenter

codecov-commenter commented Jun 17, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.33%. Comparing base (f4045b9) to head (e522b51).
⚠️ Report is 186 commits behind head on main.

Files with missing lines Patch % Lines
lib/web/websocket/permessage-deflate.js 70.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5435      +/-   ##
==========================================
- Coverage   93.36%   93.33%   -0.03%     
==========================================
  Files         110      110              
  Lines       36993    37019      +26     
==========================================
+ Hits        34537    34552      +15     
- Misses       2456     2467      +11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pacocartones

Copy link
Copy Markdown
Contributor

Independent verification of e522b51 — I am not the author; I checked out the PR locally and tried to prove it wrong. Offered in case it saves a reviewer some time.

The new test discriminates. Applied to the base commit f4045b9 (the PR head's parent) without the lib/web/websocket/permessage-deflate.js change, the new spec fails exactly at the new test:

$ git checkout f4045b9d && git checkout e522b517 -- test/websocket/permessage-deflate-limit.js
$ node --test test/websocket/permessage-deflate-limit.js
✖ PerMessageDeflate enforces maxPayloadSize against existing decompressed fragments
  AssertionError [ERR_ASSERTION]: t.assert.ok(second.error instanceof MessageSizeExceededError)
ℹ pass 13  ℹ fail 1   [exit code 1]

On the PR head the same file passes 14/14, three consecutive runs, identical exit code 0. The rationale also checks out against the code: receiver.js already passed this.#fragmentsBytes as the 4th argument to decompress() (receiver.js:296) and the inflater ignored it; the fix makes the limit check currentPayloadSize + inflate output > maxPayloadSize, which is what the PR description claims.

Also run, on the PR head (Windows 11, Node v24.14.1):

  • The PR's own validation set (fragments.js, permessage-deflate-limit.js, permessage-deflate-config.js, permessage-deflate-windowbits.js, stream/too-many-fragments.js): 39/39 pass.
  • Every other file in test/websocket/ (incl. stream/), one process per file: all pass except three anomalies that reproduce identically on the base commit, so they are pre-existing environment issues, not caused by this PR: diagnostics-channel-handshake-response-h2.js (fails) and issue-4989.js / opening-handshake.js (hang; all three are h2/bundled-undici related).

On the 9 uncovered lines codecov flags (patch coverage 70%): locally they are the reworked inflate 'error' handler (permessage-deflate.js:87-94) and the two new defensive if (!this.#inflate) return guards (98-100, 104-106). The core of the fix — the budget check and the MessageSizeExceededError rejection path (69-85) — is covered by the new test. The uncovered parts look low-risk (the error path is behavior-equivalent to before but now attributes the error to the latest decompress() call; the guards protect against the inflater being nulled synchronously mid-call), but no test in the suite feeds corrupt deflate data to an active inflater, so a small unit test doing that would close the gap.

Not verified here: benchmarks, the Autobahn suite, WPT, other Node versions/OSes (CI covers those and is green), the sender/compress path, and anything outside test/websocket/.

Reproduction details and output hashes
base (must fail): git checkout -q f4045b9d && git checkout -q e522b517 -- test/websocket/permessage-deflate-limit.js
                  node --test test/websocket/permessage-deflate-limit.js  -> exit 1
head (must pass): git checkout -q e522b517
                  node --test test/websocket/permessage-deflate-limit.js  -> exit 0 (x3, deterministic)
so: Windows 11 (AMD64), node v24.14.1
head verified: e522b517420050de0ee02610ba14b652b490272d
sha256(base run 1) = 5fe4673636d6798d…  exit 1
sha256(head run 1) = abe862a5c8193032…  exit 0
sha256(head run 2) = 6722078445294689…  exit 0
sha256(head run 3) = 5bb9f7d1e5c84cb6…  exit 0
acta: 6ca2ecd2d3232bda

This branch has not been deployed

No deployments
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.

3 participants