http: HTTP/2 frames (step 2 of #72) #685
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Step 2 of #72 (HTTP/2): the frame layer of RFC 9113 sections 4 to 6 as an encoder and decoder of its own in
std/http, with no connection, no streams state machine and no change to what aClientor aServerdoes. Step 3 (the connection core) reads and writes frames through it. HPACK (step 1, #563) has landed; header blocks stay opaque octets here.Rules
A new module
std/http/src/h2frame.trb(internal to the package, imported by relative path likehpack.trb):PRI * HTTP/2.0\r\n\r\nSM\r\n\r\nas a constant plusisPreface(buffer)for the server's detection on cleartext (prefix match of what has arrived so far: yes, no, or need more).FrameHeaderencodes and decodes it; a reserved bit set on read is ignored, written as 0.Framevariant: DATA (with padding), HEADERS (padding, priority fields parsed and kept asPriorityFieldsbut ignored, as decided in the epic), PRIORITY, RST_STREAM, SETTINGS (with ack), PUSH_PROMISE (decoded so a violation can be reported, never encoded), PING (with ack), GOAWAY (last stream, error code, debug data), WINDOW_UPDATE, CONTINUATION. Unknown frame types decode toUnknown(type, flags, streamId, payload)and are to be ignored by the connection (section 4.1).SettingsIdwith the six defined identifiers (unknown ones are kept and ignored),ErrorCodewith the fourteen codes (an unknown code is kept as its number).FrameDecoder(of(maxFrameSize)):decode(buffer): Result<Option<Frame>, FrameError>takes what has arrived, returns the next complete frame and consumes it, orNonewhen more octets are needed; it never allocates for a length beyondmaxFrameSize(refused as soon as the header is read,FRAME_SIZE_ERROR). Refused with the error code section 4.2, 6 and 7 name: a frame of the wrong size for its type (PING not 8, RST_STREAM not 4, WINDOW_UPDATE not 4, PRIORITY not 5, SETTINGS not a multiple of 6 or an ack with a payload), a stream identifier where the type forbids one or demands one (SETTINGS, PING, GOAWAY on stream 0; DATA, HEADERS, PRIORITY, RST_STREAM, CONTINUATION off stream 0), padding that is as long as or longer than the payload, a SETTINGS value out of range (ENABLE_PUSHnot 0 or 1,INITIAL_WINDOW_SIZEabove 2^31-1,MAX_FRAME_SIZEoutside 16384 to 16777215), a WINDOW_UPDATE of 0 (reported as the error the connection or the stream turns into: PROTOCOL_ERROR on either).FrameErrorsays whether it is a connection or a stream error and carries the code.FrameEncoder(encode(frame): Buffer<UInt8>): the inverse for every type except PUSH_PROMISE;splitHeaderBlock(streamId, block, maxFrameSize, endStream)returns HEADERS plus CONTINUATION frames with END_HEADERS only on the last. DATA padding is written only when the caller gives a pad length.Acceptance criteria
std/http/tests/h2frame.test.trb(VM and--native, as the otherstd/http/tests):isPrefaceanswers yes, no and need-more for the right prefixes.Nonein between and nothing lost; a length beyondmaxFrameSizeis refused on the header alone.splitHeaderBlockwith a block of 3 frames' worth gives HEADERS and two CONTINUATION frames and the flags are right; anUnknownframe passes through decode untouched.tools/gates.sh aandbgreen in forge CI. NETWORK.md section 12 "as built" gets the frame layer and slice 12's row says "Partly done: HPACK (step 1 of #72), frames (step 2)"; FIRST-PARTY.md gap 25 gets the same note (it stays open);skills/regenerated if the docs change reaches them.Design record / seed
Implements NETWORK.md slice 12 (HTTP/2). Seed: no (std only, no syntax, no native, no string lookup by the compiler).
Size
One PR, two Conventional Commits:
feat(http): HTTP/2 frame header, preface and the simple frame types, thenfeat(http): HTTP/2 HEADERS, CONTINUATION, SETTINGS and the frame decoder limits(with the docs).Dependencies
#563 (HPACK, landed). Part of #72; steps 3 to 6 follow just in time.
[loop:torbscript] claim implement by session:loop-torbscript-20261030 @anthropic:theorben until 2026-10-30T11:26:25Z
[loop:torbscript] release: the worker hit its 30-minute run limit before pushing anything (no branch, no PR). The item stays ready; the next worker gets a longer run limit.
next: ready
[loop:torbscript] claim implement by session:loop-torbscript-20261030 @anthropic:theorben until 2026-10-30T11:59:40Z
[loop:torbscript] release #685 by session:loop-torbscript-20261030: PR #689 ready for review (head
32dc97a2), argus requested; CI pending.next: waiting (CI and Argus review on #689)
[loop:torbscript] claim implementation (CI fix) by session:loop-torbscript-20261030 @anthropic:theorben until 2026-10-30T13:39:07Z
PR #689: CI gate format --check fails on std/http/tests/h2frame.test.trb only (1 of 1612 files); a worker formats it and force-pushes.
next: waiting (format fix on #689)
[loop:torbscript] release implementation (CI fix) by session:loop-torbscript-20261030: the format fix is on loop/685-h2-frames as
59fdb46d(style(http): format h2frame tests, only std/http/tests/h2frame.test.trb); torb format --check on it reports 0 of 1 file would change. CI reruns on the new head.next: waiting (CI and Argus review on #689)