WebSocket Compression¶
Autobahn|Python implements WebSocket per-message compression (RFC 7692) with four compression
extensions. Only permessage-deflate is standardized; the other three are
Autobahn|Python specific, and both peers must be Autobahn|Python (or another
implementation of the same extension) to use them.
The compression itself is done by a library for each extension, which is either part of Python or a separate package. This page lists which library, in which version, each extension uses, on CPython and on PyPy.
Extensions and Libraries¶
Extension |
Installed by |
Python package |
Compression library used |
|---|---|---|---|
|
always available |
|
zlib - the zlib the Python interpreter was built with |
|
always available [1] |
|
bzip2 (libbz2) - the libbz2 the Python interpreter was built with |
|
|
CPython: brotli PyPy: brotlicffi |
google/brotli (libbrotli), bundled in the package: brotli 1.2.0, brotlicffi 1.2.0.0 and brotlicffi 1.2.0.2 all bundle libbrotli 1.2.0 |
|
installed separately ( |
python-snappy (tested with 0.7.3), a pure Python package on top of cramjam (Rust, PyO3) |
The Rust snap crate, an independent implementation of the Snappy format, bundled in cramjam (cramjam 2.14.0 bundles snap 1.1.1). Not google/snappy (libsnappy). |
Notes:
brotli>= 1.2.0/brotlicffi>= 1.2.0is required: older versions lackDecompressor.process(output_buffer_limit=...), whichpermessage-brotlineeds to bound decompression (see below).Before version 0.7, python-snappy was a C extension linking google/snappy (libsnappy). Autobahn|Python is tested with python-snappy 0.7.3 only.
python-snappy declares no minimum version for cramjam.
On PyPy, cramjam (a PyO3 extension) runs through PyPy’s emulation of the CPython C API (cpyext), unlike brotlicffi, which uses CFFI.
python-snappy is not part of the
compressextra; install it yourself where you needpermessage-snappy.zlib and libbz2 come with the Python interpreter, so their version depends on how it was built - not on Autobahn|Python. For zlib, check with
import zlib print(zlib.ZLIB_RUNTIME_VERSION)
For example, the PyPy 3.11 Linux binaries that
uvinstalls ship their ownlibz.so.1.
To list the compression extensions available in an environment:
from autobahn.websocket.compress import PERMESSAGE_COMPRESSION_EXTENSION
print(sorted(PERMESSAGE_COMPRESSION_EXTENSION))
In a development checkout, just check-compressors <venv> does the same
and fails unless all four are available; CI runs it on every supported
Python.
Context Takeover¶
With context takeover (the default), a compressor keeps its state from one message to the next, so later messages can refer back to earlier ones.
permessage-deflatetakes over context as negotiated (client_no_context_takeover/server_no_context_takeover).permessage-snappynegotiates the same parameters. The Snappy framing format has no back-references across chunks, so context takeover makes no difference to the compression ratio.permessage-bzip2andpermessage-brotlinever take over context: each message is one complete bzip2 or brotli stream.permessage-brotlistill negotiates the parameters.
Decompression Limits and Invalid Data¶
maxMessagePayloadSize (see WebSocket Programming) bounds the size of a
message after decompression. All four extensions decompress incrementally
and stop once the remaining budget of the message is exceeded, rather than
decompressing a whole frame first:
permessage-deflateandpermessage-bzip2stop one octet over the budget,permessage-brotlistops within one output buffer of the budget,permessage-snappystops within one Snappy chunk (at most 64 KiB) of the budget.
A message over the limit raises autobahn.exception.PayloadExceededError
and fails the connection with close code 1009 (message too big). This is the
same for all four extensions, on both Twisted and asyncio.
Data an extension cannot decompress - corrupt, truncated, or not in the
extension’s format - raises autobahn.exception.PayloadCorruptError and
fails the connection with close code 1007 (invalid frame payload data). This
is the same for all four extensions, on both Twisted and asyncio.
Without a limit (maxMessagePayloadSize = 0, the default), a small
compressed message can decompress to a very large one. Set a limit when
accepting compressed messages from peers you do not trust.
Example¶
A complete example is in the repository: examples/twisted/websocket/echo_compressed.