h2 0.2.0
Changelog
All notable changes to h2 are documented here. This project follows Semantic Versioning.
[0.2.0] - 2026-04-13
First usable release with a full connection-layer client and server. Previous 0.1.0 shipped only frame/HPACK primitives.
Added
- Public API module
h2with client and server entry points aligned withquic_h3:h2:connect/2,3,h2:wait_connected/1,2h2:request/2,3,4,5,h2:send_data/3,4,h2:send_trailers/3h2:start_server/2,3,h2:stop_server/1,h2:server_port/1h2:send_response/4,h2:cancel/2,3,h2:cancel_stream/2,3h2:set_stream_handler/3,4,h2:unset_stream_handler/2h2:goaway/1,2,h2:close/1,h2:controlling_process/2h2:get_settings/1,h2:get_peer_settings/1
h2_connectiongen_statem implementing the full stream state machine (RFC 7540 §5.1) with flow control, SETTINGS negotiation, two-phase GOAWAY, and CONTINUATION handling.- CONNECT tunnel mode (RFC 7540 §8.3): bidirectional byte tunnels over a stream, half-close semantics, trailers rejection,
Content-Length/Transfer-Encodingrejection on 2xx. h2_serverwith TLS acceptor pool and ALPNh2negotiation.- Owner-process event messages:
{h2, Conn, {response, ...}},{data, ...},{trailers, ...},{stream_reset, ...},{goaway, ...},closed,connected. - Compliance test suite (Common Test) with 32 cases covering protocol conformance, API parity, and tunnel mode.
Changed
h2_hpack: persistent_term-backed precomputed Huffman encode tuple and sorted decode table; static table lookups viaelement/2(O(1)); dynamic table caches length and evicts with a singlelists:reverse.h2_connection: cachedpeer_max_frame_size,peer_initial_window_size,peer_max_concurrent_streamson the state record; CONTINUATION header buffer usesiodata()(flattened once on END_HEADERS) instead of per-frame binary concatenation.- Frame decoder (
h2_frame:decode/2) takesMaxFrameSizeand enforces per-type stream-id 0 rules (DATA/HEADERS/PRIORITY/RST_STREAM/PUSH_PROMISE/CONTINUATION reject 0; SETTINGS/PING/GOAWAY require 0).
Fixed
- RFC 7540/7541 compliance gaps:
- Padding bound in DATA/HEADERS frames now accepts
PadLength == byte_size(Rest). WINDOW_UPDATEincrement 0: stream-level RST_STREAM / connection-level GOAWAY withPROTOCOL_ERROR.SETTINGS_INITIAL_WINDOW_SIZEchange that overflows any open stream → connectionFLOW_CONTROL_ERROR.- Pseudo-header validation: order, lowercase header names, connection-specific headers rejected,
:path/:authoritychecks. - CONTINUATION interleaving on a different stream →
PROTOCOL_ERROR. - RST_STREAM on an idle stream →
PROTOCOL_ERROR. - HEADERS on a half-closed-remote or closed stream →
STREAM_CLOSED. - HPACK: dynamic-table size update larger than peer-advertised max →
COMPRESSION_ERROR; size update after a non-size-update representation →COMPRESSION_ERROR. - Huffman decoder: EOS symbol in the middle of a string rejected; padding must be < 8 bits of all-ones.
- Padding bound in DATA/HEADERS frames now accepts
- Body duplication when
send_datasplit buffers across frames. - Connection owner notified with
closedon termination. - CT suite flakiness: acceptor socket defaulted to active mode, losing data between
ssl:handshakereturn andsetopts({active, false}).
[0.1.0]
Initial release. Low-level HTTP/2 primitives (frames, HPACK, settings, capsule, varint).