You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RFC 9842 — Compression Dictionary Transport — defines how HTTP clients/servers negotiate and use external dictionaries with Brotli and Zstandard.
It has two distinct layers:
Wire format — dcz (Dictionary-Compressed Zstandard): a 40-byte fixed header, followed by a standard Zstandard frame compressed against that dictionary. Same idea for Brotli (dcb), out of scope here.
HTTP semantics — three new headers (Use-As-Dictionary, Available-Dictionary, Dictionary-ID), URL pattern matching (WHATWG URL Pattern) to decide which dictionary applies to which resource, dictionary storage/cache management (treated like cookies), HTTPS-only operation, and cross-origin/CORS protections. Tracked separately in Framework-agnostic model layer for Compression Dictionary Transport headers #92.
dcz header, exact layout (RFC 9842 §5)
Magic: 8 fixed bytes — 0x5e 0x2a 0x4d 0x18 0x20 0x00 0x00 0x00. This is a Zstandard skippable frame magic + little-endian size field, so a dcz-unaware zstd decoder can skip it per the standard skippable-frame mechanism and still land on a valid frame.
Hash: 32 bytes — full SHA-256 digest of the dictionary content, no truncation.
Total: 40 bytes, fixed, once per compressed message (not per chunk).
Why the hash lives in the frame, not just the headers
RFC 9842 also carries a dictionary hash/id in the Available-Dictionary (request) and Dictionary-ID (response) headers (see #92). The in-frame hash isn't redundant with those — it does a different job:
Negotiation vs. decoding. The headers are for negotiating whether a dictionary can be used before any bytes are sent. The in-frame hash is for decoding: once compressed bytes exist, the decompressor must self-verify/select the dictionary without needing the original HTTP transaction's headers.
Caching/CDN replay. A cached response can be served later over a different connection than the one that negotiated it. The stored bytes must carry their own "which dictionary" fact, since the original headers may not be available or re-checked at replay time.
Layering. The zstd decompressor itself shouldn't need any HTTP-header awareness — it's handed Content-Encoding: dcz bytes and must pick/verify the dictionary from the stream alone, same principle as any self-describing compression frame.
Fail-fast correctness. Decompressing with the wrong dictionary produces garbage or errors; checking the embedded hash first is a cheap mandatory sanity check that doesn't depend on trusting a header an intermediary could have dropped or altered.
Practical upshot for this repo: layer (1) and layer (2) are genuinely independent, not just conceptually — the dcz codec here can be implemented and tested with zero knowledge of the header/URL-matching model in #92.
Proposal
Support layer (1) only, in a framework-agnostic way:
A small dcz frame codec: wrap/unwrap the 40-byte header (magic + SHA-256 hash) around compressed bytes, reusing the existing dictionary compress/decompress APIs.
On encode: compute SHA-256 of the dictionary, prepend magic + hash, then zstd-compress with the dictionary.
On decode: parse the header, validate the hash against the dictionary in hand, then decompress with it.
No HTTP dependency — pure byte-in/byte-out, consistent with the segment-first zero-copy API style already used elsewhere in the library.
Dictionary cache/storage lifecycle ("treat like a cookie")
Cross-origin/CORS checks, HTTPS-only enforcement
These are HTTP-client/server concerns and belong in whatever HTTP stack (Jetty, OkHttp, etc.) consumes zstd-java's dictionary compression as a primitive — not in this library.
Open question
Worth a lightweight sketch of the dcz codec API before committing to it, to confirm it fits naturally alongside the existing Zstd*Dictionary classes.
Background
RFC 9842 — Compression Dictionary Transport — defines how HTTP clients/servers negotiate and use external dictionaries with Brotli and Zstandard.
It has two distinct layers:
dcz(Dictionary-Compressed Zstandard): a 40-byte fixed header, followed by a standard Zstandard frame compressed against that dictionary. Same idea for Brotli (dcb), out of scope here.Use-As-Dictionary,Available-Dictionary,Dictionary-ID), URL pattern matching (WHATWG URL Pattern) to decide which dictionary applies to which resource, dictionary storage/cache management (treated like cookies), HTTPS-only operation, and cross-origin/CORS protections. Tracked separately in Framework-agnostic model layer for Compression Dictionary Transport headers #92.dczheader, exact layout (RFC 9842 §5)0x5e 0x2a 0x4d 0x18 0x20 0x00 0x00 0x00. This is a Zstandard skippable frame magic + little-endian size field, so adcz-unaware zstd decoder can skip it per the standard skippable-frame mechanism and still land on a valid frame.Why the hash lives in the frame, not just the headers
RFC 9842 also carries a dictionary hash/id in the
Available-Dictionary(request) andDictionary-ID(response) headers (see #92). The in-frame hash isn't redundant with those — it does a different job:Content-Encoding: dczbytes and must pick/verify the dictionary from the stream alone, same principle as any self-describing compression frame.Practical upshot for this repo: layer (1) and layer (2) are genuinely independent, not just conceptually — the
dczcodec here can be implemented and tested with zero knowledge of the header/URL-matching model in #92.Proposal
Support layer (1) only, in a framework-agnostic way:
dczframe codec: wrap/unwrap the 40-byte header (magic + SHA-256 hash) around compressed bytes, reusing the existing dictionary compress/decompress APIs.Explicitly out of scope
Use-As-Dictionary/Available-Dictionary/Dictionary-ID) — see Framework-agnostic model layer for Compression Dictionary Transport headers #92These are HTTP-client/server concerns and belong in whatever HTTP stack (Jetty, OkHttp, etc.) consumes zstd-java's dictionary compression as a primitive — not in this library.
Open question
Worth a lightweight sketch of the
dczcodec API before committing to it, to confirm it fits naturally alongside the existingZstd*Dictionaryclasses.