Skip to content

Harden WebSocket transport and permessage-deflate for RFC 6455/7692 conformance#847

Open
arturobernalg wants to merge 1 commit into
apache:masterfrom
arturobernalg:websocket-rfc6455-conformance
Open

Harden WebSocket transport and permessage-deflate for RFC 6455/7692 conformance#847
arturobernalg wants to merge 1 commit into
apache:masterfrom
arturobernalg:websocket-rfc6455-conformance

Conversation

@arturobernalg

Copy link
Copy Markdown
Member

Fixed permessage-deflate fragmentation and the SYNC_FLUSH drain in both stacks, derived the client masking key from SecureRandom, and failed the connection on RSV1 control frames and on a data frame received mid-fragment.
Bounded the HTTP/2 server inbound window and outbound queue, rejected a 64-bit length with the MSB set, enforced the 125-byte control frame limit, and truncated the CLOSE reason.
Deduplicated extension offers by name, released the Deflater and Inflater on session end, accepted any 2xx Extended CONNECT response, and guarded the H2 transport against a cancelled selection key on shutdown.
Added tests for each; the module suite passes and checkstyle is clean.

@arturobernalg
arturobernalg force-pushed the websocket-rfc6455-conformance branch 3 times, most recently from aa9ecce to 04f46bf Compare July 14, 2026 10:37
@arturobernalg
arturobernalg force-pushed the websocket-rfc6455-conformance branch 3 times, most recently from 727bf32 to f7ab9e8 Compare July 21, 2026 12:00
@arturobernalg
arturobernalg marked this pull request as draft July 21, 2026 12:09
@arturobernalg
arturobernalg force-pushed the websocket-rfc6455-conformance branch 2 times, most recently from 9a733ad to a716347 Compare July 21, 2026 12:38
@arturobernalg
arturobernalg marked this pull request as ready for review July 21, 2026 16:26
@arturobernalg
arturobernalg force-pushed the websocket-rfc6455-conformance branch from a716347 to a1cd7a4 Compare July 21, 2026 16:30
… conformance

Fixed the client permessage-deflate encoder, which stripped the flush trailer on every
fragment and could truncate a SYNC_FLUSH, so a compressed message split across frames now
decodes, and an empty final fragment is encoded as a single 0x00 octet; the frame writer
derives the masking key from SecureRandom. The client no longer offers client_max_window_bits,
since the JDK Deflater cannot honour a reduced window, and rejects a handshake response that
echoes it. The session engine fails the connection on RSV1 set on a control frame, no longer
delivers a truncated message after a 1009 close, and surfaces a selection key cancelled during
shutdown as an I/O error on write so the engine takes its close path instead of retaining the
frame as back-pressure.

Extension negotiation on the server is stricter. Offers are parsed from every
Sec-WebSocket-Extensions field and combined as RFC 6455 requires, so a client's fallback offer
in a later field is still examined; a parameter repeated within an offer drops that offer; and
the permessage-deflate factory accepts only recognised parameters with well-formed values, the
no-context-takeover parameters carrying no value and the window-bits parameters being exactly
15, the only size the JDK Deflater supports. A given extension name is accepted only once, and
an unacceptable offer is declined rather than failing the handshake.

On the HTTP/2 server the handler advertises a bounded inbound flow-control window and bounds
the outbound queue by a byte budget, splitting large writes into chunks and signalling the
reactor after each so a write larger than the budget cannot deadlock; the frame reader rejects
a 64-bit length with the most significant bit set, the frame writer enforces the 125-byte
control frame limit and truncates the CLOSE reason, and the processor fails a data frame
received in the middle of a fragmented message. The HTTP/2 client accepts any 2xx handshake
response.

Both permessage-deflate implementations release their Deflater and Inflater when the session
ends. The extension chains close every codec they hold even if one of them fails, the
negotiation closes each extension exactly once, and the session engine coordinates the encoder
and decoder close with try/finally so a failure in one still releases the other.
@arturobernalg
arturobernalg force-pushed the websocket-rfc6455-conformance branch from a1cd7a4 to 3433ff3 Compare July 25, 2026 16:43
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.

1 participant