compression only.Expand description
Application-layer payload compression.
§Honest layer assessment: why this is NOT permessage-deflate
The right layer for WebSocket compression is the handshake extension
(permessage-deflate, RFC 7692): it compresses at the frame boundary,
shares context across frames, and is negotiated by the server echoing
Sec-WebSocket-Extensions during the upgrade.
The underlying stack cannot do that today. As of the versions this
crate builds against — tokio-tungstenite 0.29 (what axum 0.8 pulls)
and tungstenite 0.29 — permessage-deflate is not implemented;
tungstenite merely parses the Sec-WebSocket-Extensions header for
handshake validation and offers no extension hook a library could wire
a negotiation into. Shipping a hand-rolled RFC 7692 extension on top of
a transport that owns the framing would mean re-implementing the
context-takeover state machine against frame internals we do not
control — the wrong layer and a correctness hazard.
So the compression feature provides an application-layer codec
instead, and says so plainly:
- Payloads are DEFLATE-compressed (RFC 1951, raw) via
flate2with the pure-Rustminiz_oxidebackend — the crate’s#![forbid(unsafe_code)]posture is preserved end to end. - Compressed payloads travel inside ordinary binary frames with a
1-byte envelope (see
FrameCompressor). Both endpoints must run ws-kit and opt in — browsers cannot use this: they only speakpermessage-deflate. This is for Rust-to-Rust links (ws-kit server ↔ ws-kit client/proxy/worker). - “Negotiation” is out-of-band configuration (both sides build with the
feature and construct a compressor). For integrators who signal the
capability in a custom handshake header,
extension_acceptedmatches theEXTENSION_NAMEtoken case-insensitively in a comma-separated header value. When a tungstenite/axum handshake-level implementation lands upstream,CompressionConfigis what gets wired through — the config struct is deliberately transport-shaped.
§Security posture
- Decompression-bomb guard:
FrameCompressor::decompressrefuses output larger thanCompressionConfig::max_size(checked during inflation viaRead::take, so a 10 GiB bomb never materializes in memory) and refuses identity payloads above the same cap. - Fail-closed envelopes: unknown flag bits are rejected, corrupt
deflate streams error (
WsError::Compression), and a text frame restored from non-UTF-8 bytes errors rather than lossy-coercing. - Compressed payloads ride
Frame::Binary— the crate-wide rule that binary is never parsed as text/JSON applies to them too.
§Example
use ws_kit::codec::Frame;
use ws_kit::compression::{CompressionConfig, FrameCompressor};
let c = FrameCompressor::new(CompressionConfig::default());
let frame = Frame::from("a very repetitive payload ".repeat(50));
let wire = c.compress_frame(frame.clone()).unwrap();
// Wire is always a Binary frame with the envelope prefix:
assert!(wire.is_binary());
let restored = c.decompress_frame(wire).unwrap();
assert_eq!(restored, frame);Structs§
- Compression
Config - Compression settings.
- Frame
Compressor - Encodes/decodes
Frames into compressed wire form.
Constants§
- EXTENSION_
NAME - Capability token for out-of-band negotiation in a custom handshake
header. Not
permessage-deflate— see the module docs for why this is an application-layer codec.
Functions§
- extension_
accepted - True when a handshake header value (e.g. the integrator’s custom
capability header) offers
EXTENSION_NAME. - offer_
extension - The value a client/server puts in its capability header to offer
compression. Alias of
EXTENSION_NAMEfor call-site readability.