Compression Extensions for WebSocket
Extracted elements (26)
Context takeover (reusing the LZ77 sliding window across messages) can improve compression ratio by exploiting redundancy between successive messages, but requires the decompressor to retain state. The no_context_takeover parameters allow resource-limited devices to trade compression efficiency for lower memory usage.
Parameters in negotiation offers are classified as 'requests' (binding constraints the server must honor if it accepts the offer) or 'hints' (advisory information the server may ignore). This distinction is why a client-side hint like 'client_no_context_takeover' in an offer does not bind the client unless echoed back in the server's agreed parameters.
The RSV1 bit is used as a per-message (not per-frame) flag so that endpoints can choose not to compress messages with incompressible contents without additional framing overhead. Compression is applied to the concatenated payload of all frames forming a message, not frame by frame.
The trailing 4-octet sequence 0x00 0x00 0xff 0xff is removed from the compressed output and re-appended during decompression to align compressed data to an octet boundary, following the algorithm from RFC 1979 (PPP Deflate). This avoids transmitting a known constant suffix on every message.
A PMCE MUST NOT be accepted together with another extension if the PMCE would be applied to the output of that extension and the extension either requires frame-boundary preservation or uses Extension data / reserved header bits as per-frame attributes. Violating this causes the client to Fail the WebSocket Connection.
On platforms that only expose compressed data with a zlib header and Adler-32 checksum (rather than raw DEFLATE), developers must write stub code to strip and restore the zlib wrapper and checksum, since permessage-deflate uses raw DEFLATE (RFC 1951) without zlib framing.
A client MUST support the 'client_no_context_takeover' extension parameter in an extension negotiation response. A server MUST decline an offer containing unrecognized parameters, invalid parameter values, duplicate parameter names, or unsupported configurations.
A server MUST NOT accept a PMCE extension negotiation offer together with another extension if the PMCE will conflict with that extension on their use of the RSV1 bit. A client receiving a response accepting such a conflicting combination MUST Fail the WebSocket Connection.
An endpoint MUST NOT set the 'Per-Message Compressed' bit on control frames or on non-first fragments of a data message. An endpoint receiving such a frame MUST Fail the WebSocket Connection.
An endpoint MUST NOT use an LZ77 sliding window longer than 32,768 bytes to compress messages. If the agreed parameters contain server_max_window_bits or client_max_window_bits with a value of w, the respective compressor MUST NOT use an LZ77 sliding window longer than 2^w bytes.
Each PMCE referring to this framework MUST define: the extension name and applicable extension parameters for inclusion in Sec-WebSocket-Extensions during negotiation, how to interpret the exchanged extension parameters, and how to transform (compress and decompress) the message payload.
If a received extension negotiation offer does not include the 'client_max_window_bits' parameter, the corresponding extension negotiation response MUST NOT include the 'client_max_window_bits' parameter.
If a server gives an invalid response — such as accepting a PMCE the client did not offer — the client MUST Fail the WebSocket Connection. A server MUST NOT accept a PMCE together with an extension that requires frame-boundary preservation or uses the Extension data field or reserved bits as per-frame attributes.
If a server responds with a valid PMCE element in Sec-WebSocket-Extensions and the WebSocket Connection is established, both endpoints MUST use the compression/decompression algorithm described in Section 6, configured with the 'agreed parameters' returned by the server.
If the agreed parameters contain 'client_no_context_takeover', the client MUST start compressing each new message with an empty LZ77 sliding window. If the agreed parameters contain 'server_no_context_takeover', the server MUST do the same.
To compress a message with permessage-deflate: (1) compress payload using DEFLATE, (2) append an empty DEFLATE block with no compression (BTYPE=00) if not already present, (3) remove the trailing 4 octets 0x00 0x00 0xff 0xff. For non-final fragments, removal of the 4 octets MUST NOT be done.
To decompress a permessage-deflate message: (1) append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the payload, (2) decompress the resulting data using DEFLATE.
The 'permessage-deflate' extension compresses WebSocket message payloads using the DEFLATE algorithm (RFC 1951) with byte boundary alignment from RFC 1979. It defines four extension parameters: server_no_context_takeover, client_no_context_takeover, server_max_window_bits, and client_max_window_bits.
The RSV1 bit of the WebSocket frame header is allocated for PMCEs and designated the 'Per-Message Compressed' bit. When set on the first fragment of a data message, it indicates the message payload is compressed; the bit MUST NOT be set on control frames or non-first fragments.
The 'server_max_window_bits' and 'client_max_window_bits' parameters carry a decimal integer value (8–15, no leading zeros) representing the base-2 logarithm of the LZ77 sliding window size limit. A response value for server_max_window_bits must be equal to or smaller than the offered value.
The 'server_no_context_takeover' parameter (no value) prevents the server from reusing the LZ77 sliding window across messages; the 'client_no_context_takeover' parameter prevents the client from doing so. Both can appear in offers and responses to limit decompressor memory requirements.
The WebSocket Per-Message Compression Extension (PMCE) framework defines how compression can be applied to WebSocket data message payloads on a per-message basis. PMCEs use the RSV1 bit of the WebSocket frame header to indicate whether a message is compressed, enabling endpoints to skip compression for incompressible content.
IANA registered the 'permessage-deflate' extension name in the WebSocket Extension Name Registry (defined in RFC 6455). The extension identifier is 'permessage-deflate', common name 'WebSocket Per-Message Deflate', with no known incompatible extensions.
IANA registered the RSV1 bit as the 'Per-Message Compressed' framing header bit in the WebSocket Framing Header Bits Registry (defined in RFC 6455). RSV1 is set for compressed messages and unset for uncompressed messages on the first fragment of data frames.
There is a known exploit (CRIME) when history-based compression is combined with a secure transport. Implementors must consider this vulnerability when integrating the permessage-deflate extension with other extensions or protocols, particularly when compressing data that includes secret values such as authentication tokens over TLS.
A compressed WebSocket frame has RSV1=1 on its first fragment. The payload data portion contains DEFLATE-compressed data with the trailing 4 octets (0x00 0x00 0xff 0xff) removed. For example, 'Hello' compresses to 7 bytes: 0xf2 0x48 0xcd 0xc9 0xc9 0x07 0x00.