Conversation
ByteParser kept #info.compressed set after delivering a permessage-deflate message. The guard that rejects a continuation frame arriving with no message in progress is skipped when that flag is set, so a stray continuation frame following a compressed message was decompressed and delivered as an extra message instead of failing the connection with 1002. Clear the flag in consumeFragments(), where the message it describes is completed.
…pressed messages Adds four cases around the compressed-flag reset: a stray continuation separated from the completed message by a control frame, a stray continuation that carries a decompressible payload, two consecutive compressed messages, and a control frame interleaved into a fragmented compressed message.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5827 +/- ##
=======================================
Coverage 93.53% 93.53%
=======================================
Files 110 110
Lines 39773 39778 +5
=======================================
+ Hits 37200 37207 +7
+ Misses 2573 2571 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ByteParsernever cleared#info.compressedafter delivering a permessage-deflate message, so the flag described the previous message while the next frame header was parsed.lib/web/websocket/receiver.js:171that rejects a continuation frame with no message in progress is conditioned on!this.#info.compressed, so it was skipped after any compressed message.consumeFragments(), the one place where the message the flag describes is completed (bothwebsocketMessageReceivedcall sites go through it).test/websocket/receiver-unit.js(plus 1 pre-existing test in the file, 9 total): 4 discriminate on this bug and 4 are declared controls.Both arms below were measured at head
998d2a463b8c348f1c3029ba728a818e3bb423c7, with all 9 tests present in the file and onlylib/web/websocket/receiver.jsswapped between arms.Decisions
consumeFragments()is the correct place to clear#info.compressed: it is called from exactly the twowebsocketMessageReceivedsites (receiver.js:258uncompressed,receiver.js:290compressed), i.e. every point where a message completes, and it already owns the rest of the per-message state reset (#fragments,#fragmentsBytes). Clearing the flag there keeps the reset with the state it belongs to and costs one assignment per message.Alternatives rejected:
websocketMessageReceivedcall sites. Same effect, duplicated in two places, and a future third completion path would miss it.!this.#info.compressedfrom the guard at line 171. Reverts the fix from permessage-deflate decompression support in websocket #3263 and breaks compressed fragmented messages, which drain#fragmentswhile still in progress — the...still assembles a fragmented compressed message (control)test is the control that would catch that.Not run: the
borp-basedtest:websocketscript (the full websocket suite, needsnode_modules) and the Autobahn conformance suite —node --teston the touched file was used instead, covering the same code path unmodified.AI assistance: this bug was found and the fix and tests were drafted with AI tooling in my workflow; the tests and checks above were executed as pasted. I'm responsible for the change and will handle review feedback.