Releases: GetPageSpeed/zstd-nginx-module
Release list
0.2.1
0.2.0
First release from the GetPageSpeed continuation of this module. The upstream it
derives from has had no release since 2023; this one carries correctness fixes, a
test suite, and static-analysis CI.
Compression defaults are unchanged. Existing configurations compress the same
content at the same level as before, with one addition noted below.
Fixes
Out-of-bounds read on an invalid zstd_static value. The directive's enum
table had no terminating entry, and nginx's ngx_conf_set_enum_slot walks such a
table until it finds a zero-length name. Any value other than off/on/always
— a plain typo — read past the end of the array. Under AddressSanitizer this
aborts nginx at startup with a global-buffer-overflow; without it, the error was
usually reported by luck.
zstd_static silently disabled gzip. The handler decided whether the client
accepts zstd before it knew whether a .zst file existed, and that decision
marked the request as not-gzip-eligible. For every URI without a precompressed
sibling the handler then declined with the flag already set, and the response went
out uncompressed even though the client offered gzip and gzip was enabled. Only
clients offering both encodings were affected.
Accept-Encoding is now parsed per RFC 9110 §12.5.3 instead of by substring
search. Previously:
zstd;q=0compressed anyway — q-values were never parsed, so an explicit
refusal read as an acceptance*never matched; the wildcard was not implementedzstd-foomatched, via a four-byte prefix comparison- a coding name inside a quoted parameter value could be mistaken for a real one
- an unbounded run of fraction digits was consumed without validation
Coding names are now matched as whole tokens, parameter values are skipped
quote-aware, and an explicit entry for a coding takes precedence over * in both
directions. q-values are parsed into thousandths with the fraction capped at three
digits, so an over-long value is rejected rather than accumulating into something
that could wrap.
A leaked dictionary on every reload. The dictionary built from
zstd_dict_file is allocated by libzstd and was never released when the
configuration pool went away, so each reload leaked one for the lifetime of the
master process. It now has a pool cleanup handler.
Stale control flags on recycled output buffers. A buffer returning from the
free list kept the flags it was last sent with, so a last_buf could be
re-emitted on an unrelated buffer and mark the response complete early.
ZSTD_freeCStream() reported the wrong error. The result was checked in one
variable and formatted from another, which was not a libzstd error code at all, so
the log line was meaningless in exactly the case it existed to explain.
zstd_min_length accepted any number of arguments and quietly ignored all but
the first. It now takes exactly one.
Added
application/wasm and text/wgsl are compressed by default. Both are text-like
formats served under a non-text media type, so they compress well but are missed
by configurations that never listed them. Setting zstd_types explicitly still
replaces the defaults.
Quality
A Test::Nginx suite of 275 tests covering the filter's eligibility rules, the
Accept-Encoding grammar, the zstd_static fallback, and buffer recycling. CI
runs the suite, the suite again under AddressSanitizer on a static build, cppcheck
in exhaustive mode, and CodeQL.