h2: HTTP/2 Frames and HPACK
| (require h2) | package: h2 |
RFC 7540 frame codec and RFC 7541 HPACK for Racket — the protocol layer of HTTP/2, the sibling of the websocket package. Pure bytes in, structures out: connections, stream state machines, and flow control stay with the caller (and are the next milestone).
1 Frames
Every frame is a frame? — length, type, flags, stream id, payload. The nine-byte header is decoded with bytes->frame; building helpers exist for the payloads that matter early: SETTINGS, PING, GOAWAY, WINDOW_UPDATE, RST_STREAM, and HEADERS with padding.
struct
length : exact-nonnegative-integer? type : exact-nonnegative-integer? flags : exact-nonnegative-integer? stream-id : exact-nonnegative-integer? payload : bytes?
procedure
(frame->bytes f) → bytes?
f : frame?
procedure
(bytes->frame bstr pos) →
frame? exact-nonnegative-integer? bstr : bytes? pos : exact-nonnegative-integer?
Decode one frame at pos; the caller buffers at least length+9 bytes. Stream ids keep 31 bits (the reserved bit reads 0).
procedure
(frames->bytes fs) → bytes?
fs : (listof frame?)
value
The fixed client preface PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n (§3.5).
Frame type constants:
Error codes:
procedure
(make-settings-frame pairs) → frame?
pairs : (listof (cons/c exact-nonnegative-integer? exact-nonnegative-integer?))
procedure
(settings-payload->list payload)
→ (listof (cons/c exact-nonnegative-integer? exact-nonnegative-integer?)) payload : bytes?
procedure
(make-ping-frame ack? opaque) → frame?
ack? : boolean? opaque : exact-nonnegative-integer?
procedure
(make-goaway-frame last-stream-id error-code debug-bytes) → frame? last-stream-id : exact-nonnegative-integer? error-code : exact-nonnegative-integer? debug-bytes : bytes?
GOAWAY frames are always stream 0; last-stream-id rides in the payload.
procedure
(make-window-update-frame stream-id increment) → frame? stream-id : exact-nonnegative-integer? increment : exact-nonnegative-integer?
procedure
(make-rst-stream-frame stream-id error-code) → frame? stream-id : exact-nonnegative-integer? error-code : exact-nonnegative-integer?
procedure
(make-headers-frame stream-id block [ #:end-stream end-stream #:pad-length pad-length]) → frame? stream-id : exact-nonnegative-integer? block : bytes? end-stream : boolean? = #t pad-length : (or/c exact-nonnegative-integer? #f) = #f
procedure
(headers-block->bytes payload) → bytes?
payload : bytes?
Strips HPACK-unrelated padding from a PADDED HEADERS/DATA payload — call it only when the PADDED flag is set.
2 HPACK
procedure
(make-hpack-ctx max-size) → hpack-ctx?
max-size : exact-nonnegative-integer?
procedure
(hpack-ctx? v) → boolean?
v : any/c
procedure
ctx : hpack-ctx?
The dynamic-table context (in octets, per §4.1’s 32-byte entry overhead). Decoder contexts track inserts, evictions, and size updates; encoder contexts may stay fresh — the built-in encoder uses only static-table indexing and literals without indexing, which is always legal.
procedure
(hpack-decode-headers ctx bstr)
→ (listof (cons/c string? string?)) ctx : hpack-ctx? bstr : bytes?
Full §6: indexed, literal with/without/never indexing, dynamic table size updates. Invalid data (including an indexed field with index 0) raises.
procedure
(hpack-encode-headers ctx headers) → bytes?
ctx : hpack-ctx? headers : (listof (cons/c string? string?))
Exact static-table matches become one indexed byte (e.g. (:method . GET) → 130, same shape as the RFC’s own examples); everything else goes out as literal without indexing, Huffman-coded when the table actually makes it shorter (ASCII only).
procedure
(hpack-encode-integer value prefix-bits) → bytes?
value : exact-nonnegative-integer? prefix-bits : exact-integer?
procedure
(hpack-decode-integer bstr pos prefix-bits)
→
exact-nonnegative-integer? exact-nonnegative-integer? bstr : bytes? pos : exact-nonnegative-integer? prefix-bits : exact-integer?
§5.1; the prefix’s high bits are the caller’s opcode bits.
procedure
(hpack-encode-string s) → bytes?
s : string?
procedure
(hpack-decode-string bstr pos)
→
string? exact-nonnegative-integer? bstr : bytes? pos : exact-nonnegative-integer?
§5.2 string literals with the Huffman flag.
3 Huffman
procedure
(huffman-encode s) → bytes?
s : string?
RFC 7541 Appendix B coding, padded with 1 bits (never a whole byte).
procedure
(huffman-decode bstr) → string?
bstr : bytes?
Rejects padding that is not a prefix of EOS, mid-stream EOS, and invalid codes.
procedure
(huffman-encoded-length-bits s) → exact-nonnegative-integer?
s : string?
For size decisions before encoding.
4 Status and limits
Frames + HPACK are complete and golden-vector tested against RFC 7541’s appendix C. The connection layer — preface exchange, stream state machine, flow control, and a working h2c client — is the next milestone; until it lands, this package is the protocol core other code can build on, tested the same way the RFC tests itself.