Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 03:40
· 38 commits to master since this release
shb has its own QUIC. quinn-proto is gone from the dependency list; rustls
keeps the TLS handshake, the QUIC key schedule and the packet protection,
which is the part not worth anyone rewriting.

The reason was measurement, not preference. Profiling put roughly three
quarters of shb's userspace HTTP/3 work inside quinn-proto - 38% in
handle_event, 35% in poll_transmit - against 5.6% for all of shb's own HTTP/3
code put together. quinn-proto is a complete QUIC implementation, and a
benchmark client that talks to one known peer needs a fraction of it: no server
role, no path migration, no datagram extension, one congestion controller.

Against nginx, paired interleaved runs, medians of six rounds: 338k requests
per second against 304k, and 2908ns of userspace CPU per request against
4524ns. That is 36% less work for 11% more throughput, and the gap between
those two numbers is the honest part - HTTP/3 spends only half its CPU in
userspace, and the rest is the kernel's UDP path and packet crypto, which no
amount of rewriting touches.

What it does differently: packets are built straight into the datagram buffer
the kernel will send, so a datagram costs no allocation; decoded frames borrow
from the datagram rather than taking a reference count on it; and the streams a
client opens are numbered in order, so they live in a ring indexed by
arithmetic instead of a hash map. The binary is 13% smaller.

Correctness is pinned to the specification's own worked examples - packet
numbers against RFC 9000 Appendix A.2 and A.3, initial keys and header
protection against the client Initial in RFC 9001 Appendix A.2 - and then to
other people's implementations: 58 of 58 container checks from a cold start and
22 of 22 public HTTP/3 endpoints, thirteen independent QUIC implementations in
all.

Five of this stack's bugs only ever appeared against one of them, because a
server on loopback does not drop packets, does not reorder them and sends
nothing after a stream ends. One of the five was a deliberate decision with its
reasoning written down - not buffering out-of-order handshake data, since
reordering is rare on a single path - which held right up until it met a
certificate chain spanning several packets on a real network.