Summary
In the default WebSocket++ permessage-deflate client configuration, generate_offer() advertises client_no_context_takeover. If the server accepts permessage-deflate but omits client_no_context_takeover in the response, the client still reuses compressor context across messages.
That makes the client's outbound wire behavior depend on whether the server echoed the parameter back, even though the client had already advertised that it would not retain compression context.
According to RFC 7692 7.1.1.2:
By including this extension parameter in an extension negotiation offer,
a client informs the peer server of a hint that even if the server doesn't
include the "client_no_context_takeover" extension parameter in the
corresponding extension negotiation response to the offer, the client
is not going to use context takeover.
Therefore, a partially compliant server that is configured for no context takeover but omits the response parameter can fail to decompress messages sent by the client beyond the first.
Details
The issue shows up on the client send path. A client that offered client_no_context_takeover should emit each compressed message independently. In the current implementation, client-side no-context-takeover behavior is only enabled when client_no_context_takeover appears in the negotiated response attributes.
As a result, the client offer and the runtime behavior can diverge:
- the opening handshake advertises
client_no_context_takeover
- later compressed messages may still depend on previous compression history
Reproduction (repo-local test)
This can be reproduced with an extension-level test in test/extension/permessage_deflate.cpp:
- Generate a client offer and confirm it contains
client_no_context_takeover
- Accept
permessage-deflate with an empty response attribute list
- Initialize the client extension
- Compress the same deterministic 4096-byte message twice
- Decompress the first output with one server-side extension instance
- Decompress the second output with a fresh server-side extension instance
Current behavior
- the first message decompresses correctly
- the second message assumes prior compressor history and does not decode correctly with a fresh receiver
- identical inputs do not yield independently decodable outputs under the advertised no-context-takeover behavior
Expected behavior
If the client offers client_no_context_takeover and the extension is negotiated, each outbound compressed message should be independent of previous messages, even when the server omits client_no_context_takeover from the response.
In the repro above, both compressed outputs should decompress correctly when handled by separate fresh server-side extension instances.
Actual behavior
The client only switches to no-context-takeover behavior when the server response explicitly includes client_no_context_takeover. If the server accepts permessage-deflate without echoing that parameter, the client still reuses compression context across messages.
Summary
In the default WebSocket++
permessage-deflateclient configuration,generate_offer()advertisesclient_no_context_takeover. If the server acceptspermessage-deflatebut omitsclient_no_context_takeoverin the response, the client still reuses compressor context across messages.That makes the client's outbound wire behavior depend on whether the server echoed the parameter back, even though the client had already advertised that it would not retain compression context.
According to RFC 7692 7.1.1.2:
Therefore, a partially compliant server that is configured for no context takeover but omits the response parameter can fail to decompress messages sent by the client beyond the first.
Details
The issue shows up on the client send path. A client that offered
client_no_context_takeovershould emit each compressed message independently. In the current implementation, client-side no-context-takeover behavior is only enabled whenclient_no_context_takeoverappears in the negotiated response attributes.As a result, the client offer and the runtime behavior can diverge:
client_no_context_takeoverReproduction (repo-local test)
This can be reproduced with an extension-level test in
test/extension/permessage_deflate.cpp:client_no_context_takeoverpermessage-deflatewith an empty response attribute listCurrent behavior
Expected behavior
If the client offers
client_no_context_takeoverand the extension is negotiated, each outbound compressed message should be independent of previous messages, even when the server omitsclient_no_context_takeoverfrom the response.In the repro above, both compressed outputs should decompress correctly when handled by separate fresh server-side extension instances.
Actual behavior
The client only switches to no-context-takeover behavior when the server response explicitly includes
client_no_context_takeover. If the server acceptspermessage-deflatewithout echoing that parameter, the client still reuses compression context across messages.