v0.2.3
A correctness release. Both bugs were found by pointing shb at public
servers rather than at a local test server, and neither could show up
against the benchmark setup.
Fixed
The Huffman codes for '3' to '9' were off by one. RFC 7541 Appendix B
gives them 011001 through 011111; the table here started at 011010
and invented a seven-bit '9'. A Huffman-coded :status therefore decoded
one digit low wherever a 3-9 appeared — 404 came out as 303 — and a
status containing a 3 failed outright, because 011001 matched nothing.
Both HPACK and QPACK carried the same table, so HTTP/2 and HTTP/3 were
both affected. It never showed against the benchmark server because
:status 200 is a static-table index, so nothing is Huffman-decoded at
all.
TLS stopped part-way through a receive. rustls refuses more ciphertext
once 16 KiB of decrypted plaintext is waiting — one maximum-sized TLS
record — and that limit is not configurable. Feeding a whole receive before
draining any of it therefore failed against any server that sends
full-sized records: cloudflare.com, github.com and fastly.com all failed
over HTTP/2 after transferring a few hundred KiB. The plaintext is now
taken out between reads.
Also
scripts/interop.sh sends one request to each of ~50 public endpoints
across Cloudflare, Google, Meta, Fastly, Akamai, LiteSpeed, nginx, Caddy,
HAProxy and ATS, over all three protocols. All 51 pass, including HTTP/3
against five different QUIC stacks (quiche, nginx, Google, mvfst, lsquic).
It is not part of cargo test: it depends on other people's servers.
The Huffman bug survived because the tests encoded with the same table they
decoded with, so they agreed with the mistake. Everything else this project
spells out from a specification has now been checked against an independent
implementation — the HPACK and QPACK static tables, HTTP/2 and HTTP/3 frame
types, flags, stream types and settings ids, the connection preface — and
the prefix-integer and QUIC varint codecs are tested against the worked
examples in RFC 7541 C.1 and RFC 9000 A.1.
Comparison
Re-measured on the current tree. Same method as before: nginx 1.31.4 on
loopback, 10 s per run, 16 threads for every tool.
| Protocol | Config | shb | wrk | h2load |
|---|---|---|---|---|
| HTTP/1.1 | 1000 connections | 1,043,685 | 915,864 | 828,703 |
| HTTP/2 (h2c) | 32 conns × 32 streams | 931,124 | — | 909,564 |
| HTTP/2 (h2c) | 100 conns × 100 streams | 1,252,889 | — | 1,199,664 |
| HTTP/3 | 32 conns × 32 streams | 1,938,777 | — | 1,430,755 |