http: HTTP/2 #72

Open
opened 2026-10-05 21:07:16 +00:00 by orion · 6 comments
Collaborator

Context

HTTP/2 in std/http, client and server. Needed by gRPC (#75), by HTTP/3's ordering (#73) and by h2 in the client's ALPN offer (step 3 of #71). The messages of NETWORK.md section 6 do not change: a stream is a request and a response, HPACK and the flow-control windows are TorbScript over a connection task with a channel per stream (NETWORK.md section 12).

This is an epic: it is far more than one PR. Each step is its own ready issue, filed just in time when the one before has landed. No ready on the epic itself.

Source

Steps

  • 1. HPACK (RFC 7541): integers, strings, the Huffman code, static and dynamic table, HpackEncoder and HpackDecoder #563
  • 2. Frames (RFC 9113 sections 4 to 6): the connection preface, the frame header, the ten frame types, SETTINGS and the error codes, as an encoder and decoder with no connection #685
  • 3. The connection core, sans-IO (h2.trb): stream state machine, flow control (connection and stream windows), SETTINGS handshake and ack, PING, GOAWAY, RST_STREAM, CONTINUATION sequence, the limits and the rapid-reset guard; two ends tested in one program #699
  • 3b. The connection task: the core over a Transport with a channel per stream, the SETTINGS and PING timeouts, tested over loopback (filed when step 3 has landed)
  • 4. The server: h2 in the ALPN list of https, the preface detected on cleartext, a stream is a Request and the handler's Response is the stream's answer
  • 5. The client: h2 in supportedProtocols (completes step 3 of #71), one multiplexed connection per origin in the pool, a retry after GOAWAY
  • 6. The conformance suite: the groups of h2spec as conformance programs against our server and our client

Decided by the loop

  • Where. Internal modules of std/http (hpack.trb, h2frame.trb, h2.trb, ...), imported by relative path like parse.trb; no new public name except what a Client and a Server already are. HTTP/2 is a transport detail of the same API: a Client offers h2 and http/1.1 through ALPN and uses what the server picks, a Server accepts both. No switch to turn it on or off (no flags, nothing to migrate).
  • Cleartext. Prior knowledge only (RFC 9113 section 3.3); the Upgrade: h2c of RFC 7540 is gone from RFC 9113 and is not built. A server detects the preface on http (gRPC inside a cluster needs it). A client that starts h2 on cleartext is an option decided with gRPC (#75), not here.
  • Not built, by design. Server push (the client announces SETTINGS_ENABLE_PUSH = 0, the server never pushes; RFC 9113 deprecates it), stream priorities (the deprecated tree of RFC 7540 is parsed and ignored; RFC 9218 priority is a later issue), extended CONNECT of RFC 8441 (WebSocket over HTTP/2; the live UI's 101 upgrade stays on http/1.1, which browsers open for a WebSocket when the server does not advertise it; a handler that answers Response.upgrade on an h2 stream gets 501).
  • Trailers. Read and dropped as HTTP/1.1 does today (ChunkedReader). A public trailers API on Request and Response is what gRPC needs; it is its own issue, filed with #75, and not part of #72.
  • Conformance (acceptance 2). "h2spec or an equivalent suite": the equivalent. The groups of h2spec (generic, HPACK, section 3 to 8 of RFC 9113) become conformance programs driven by a raw-frame peer built on step 2, in VM and --native, so forge CI needs no Go binary and no download. Running the real h2spec in CI is a possible later check, not a condition of this epic.
  • Limits. Defaults follow Go's net/http2: SETTINGS_MAX_CONCURRENT_STREAMS 100, initial window 65535 raised by WINDOW_UPDATE, SETTINGS_MAX_FRAME_SIZE 16384, SETTINGS_MAX_HEADER_LIST_SIZE 64 KiB; a frame beyond what was announced is a connection error. The numbers are settled in steps 3 to 5 with tests, not asked.

Acceptance criteria

  • HTTP/2 client and server in std/http
  • h2spec or an equivalent suite passes

Design record / seed

Implements NETWORK.md slice 12 (HTTP/2 only; HTTP/3 is #73). Seed: no for every step planned (std only, no syntax, no native, no string lookup by the compiler); a step that needs one says so in its own issue.

Dependencies

#118 (tls: ALPN) is closed and landed; the client's pool (slice 7) and Transport are built. Unblocks step 3 of #71, #73 and #75.

Moved from the planning lists of the docs; the design record stays the specification.

## Context HTTP/2 in `std/http`, client and server. Needed by gRPC (#75), by HTTP/3's ordering (#73) and by `h2` in the client's ALPN offer (step 3 of #71). The messages of NETWORK.md section 6 do not change: a stream is a request and a response, HPACK and the flow-control windows are TorbScript over a connection task with a channel per stream (NETWORK.md section 12). This is an epic: it is far more than one PR. Each step is its own `ready` issue, filed just in time when the one before has landed. No `ready` on the epic itself. ## Source - [NETWORK.md slice 12](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/NETWORK.md) (section 12) - [FIRST-PARTY.md gap 25](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/FIRST-PARTY.md) ## Steps - [x] 1. HPACK (RFC 7541): integers, strings, the Huffman code, static and dynamic table, `HpackEncoder` and `HpackDecoder` #563 - [x] 2. Frames (RFC 9113 sections 4 to 6): the connection preface, the frame header, the ten frame types, `SETTINGS` and the error codes, as an encoder and decoder with no connection #685 - [ ] 3. The connection core, sans-IO (`h2.trb`): stream state machine, flow control (connection and stream windows), `SETTINGS` handshake and ack, `PING`, `GOAWAY`, `RST_STREAM`, `CONTINUATION` sequence, the limits and the rapid-reset guard; two ends tested in one program #699 - [ ] 3b. The connection task: the core over a `Transport` with a channel per stream, the `SETTINGS` and `PING` timeouts, tested over loopback (filed when step 3 has landed) - [ ] 4. The server: `h2` in the ALPN list of `https`, the preface detected on cleartext, a stream is a `Request` and the handler's `Response` is the stream's answer - [ ] 5. The client: `h2` in `supportedProtocols` (completes step 3 of #71), one multiplexed connection per origin in the pool, a retry after `GOAWAY` - [ ] 6. The conformance suite: the groups of h2spec as conformance programs against our server and our client ## Decided by the loop - **Where.** Internal modules of `std/http` (`hpack.trb`, `h2frame.trb`, `h2.trb`, ...), imported by relative path like `parse.trb`; no new public name except what a `Client` and a `Server` already are. HTTP/2 is a transport detail of the same API: a `Client` offers `h2` and `http/1.1` through ALPN and uses what the server picks, a `Server` accepts both. No switch to turn it on or off (no flags, nothing to migrate). - **Cleartext.** Prior knowledge only (RFC 9113 section 3.3); the `Upgrade: h2c` of RFC 7540 is gone from RFC 9113 and is not built. A server detects the preface on `http` (gRPC inside a cluster needs it). A client that starts `h2` on cleartext is an option decided with gRPC (#75), not here. - **Not built, by design.** Server push (the client announces `SETTINGS_ENABLE_PUSH = 0`, the server never pushes; RFC 9113 deprecates it), stream priorities (the deprecated tree of RFC 7540 is parsed and ignored; RFC 9218 `priority` is a later issue), extended `CONNECT` of RFC 8441 (WebSocket over HTTP/2; the live UI's `101` upgrade stays on `http/1.1`, which browsers open for a WebSocket when the server does not advertise it; a handler that answers `Response.upgrade` on an `h2` stream gets `501`). - **Trailers.** Read and dropped as HTTP/1.1 does today (`ChunkedReader`). A public trailers API on `Request` and `Response` is what gRPC needs; it is its own issue, filed with #75, and not part of #72. - **Conformance (acceptance 2).** "h2spec or an equivalent suite": the equivalent. The groups of h2spec (generic, HPACK, section 3 to 8 of RFC 9113) become conformance programs driven by a raw-frame peer built on step 2, in VM and `--native`, so forge CI needs no Go binary and no download. Running the real h2spec in CI is a possible later check, not a condition of this epic. - **Limits.** Defaults follow Go's `net/http2`: `SETTINGS_MAX_CONCURRENT_STREAMS` 100, initial window 65535 raised by `WINDOW_UPDATE`, `SETTINGS_MAX_FRAME_SIZE` 16384, `SETTINGS_MAX_HEADER_LIST_SIZE` 64 KiB; a frame beyond what was announced is a connection error. The numbers are settled in steps 3 to 5 with tests, not asked. ## Acceptance criteria - [ ] HTTP/2 client and server in `std/http` - [ ] h2spec or an equivalent suite passes ## Design record / seed Implements NETWORK.md slice 12 (HTTP/2 only; HTTP/3 is #73). Seed: no for every step planned (std only, no syntax, no native, no string lookup by the compiler); a step that needs one says so in its own issue. ## Dependencies #118 (tls: ALPN) is closed and landed; the client's pool (slice 7) and `Transport` are built. Unblocks step 3 of #71, #73 and #75. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), sources NETWORK.md slice 12 and FIRST-PARTY.md gap 25, not a duplicate. Large (client and server, conformance suite); refine splits it. Depends on #118 (TLS ALPN). next: waiting (dependency #118) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), sources NETWORK.md slice 12 and FIRST-PARTY.md gap 25, not a duplicate. Large (client and server, conformance suite); refine splits it. Depends on #118 (TLS ALPN). next: waiting (dependency #118) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T05:26:35Z

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T05:26:35Z
Author
Collaborator

[loop:torbscript] decided by the loop: #72 is an epic (HPACK, frames, connection core, server, client, conformance suite), six steps ticked off in the body, only step 1 filed (just in time; keyword search for HTTP/2, h2, HPACK, Huffman found no duplicate; backlog gate off; main CI not red). Step 1 is HPACK as a pure codec (#563): the narrowest piece with its own test vectors (RFC 7541 Appendix C), no connection and no seed change, so it fits one PR of two commits. Design questions settled and recorded in the epic body: internal modules of std/http, no switch (the client offers h2 and http/1.1 through ALPN, the server accepts both); cleartext is prior knowledge only, no h2c upgrade; no server push, no priorities, no RFC 8441 (the live UI's 101 stays on http/1.1); trailers are dropped as in HTTP/1.1 today, a trailers API belongs to gRPC (#75); acceptance 2 is met by "an equivalent suite": the h2spec groups as conformance programs over our own frame codec, no Go binary in CI. Seed impact: none.
[loop:torbscript] refine: epic body rewritten to the definition of ready, step 1 filed as #563 with ready, next: waiting (step #563; steps 2 to 6 follow just in time). Tokens: ~75000

[loop:torbscript] decided by the loop: #72 is an epic (HPACK, frames, connection core, server, client, conformance suite), six steps ticked off in the body, only step 1 filed (just in time; keyword search for HTTP/2, h2, HPACK, Huffman found no duplicate; backlog gate off; `main` CI not red). Step 1 is HPACK as a pure codec (#563): the narrowest piece with its own test vectors (RFC 7541 Appendix C), no connection and no seed change, so it fits one PR of two commits. Design questions settled and recorded in the epic body: internal modules of `std/http`, no switch (the client offers `h2` and `http/1.1` through ALPN, the server accepts both); cleartext is prior knowledge only, no `h2c` upgrade; no server push, no priorities, no RFC 8441 (the live UI's `101` stays on `http/1.1`); trailers are dropped as in HTTP/1.1 today, a trailers API belongs to gRPC (#75); acceptance 2 is met by "an equivalent suite": the h2spec groups as conformance programs over our own frame codec, no Go binary in CI. Seed impact: none. [loop:torbscript] refine: epic body rewritten to the definition of ready, step 1 filed as #563 with `ready`, next: waiting (step #563; steps 2 to 6 follow just in time). Tokens: ~75000
Author
Collaborator

[loop:torbscript] refine: step 1 landed (#563 closed, HPACK). Step 2 filed as #685 (loop, ready): the frame layer of RFC 9113 sections 4 to 6, no connection, no seed change. Steps 3 to 6 follow just in time.
next: waiting (step #685)

[loop:torbscript] refine: step 1 landed (#563 closed, HPACK). Step 2 filed as #685 (loop, ready): the frame layer of RFC 9113 sections 4 to 6, no connection, no seed change. Steps 3 to 6 follow just in time. next: waiting (step #685)
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-30T17:38Z

[loop:torbscript] claim refine by session:loop-torbscript-20261030 @anthropic:tkoehn until 2026-10-30T17:38Z
Author
Collaborator

[loop:torbscript] refine: step 2 landed (#685, frames, in f0d81eb6 and 264ec931). Step 3 filed as #699 (loop, ready): the connection core of RFC 9113 sections 5 and 6.9 as a sans-IO state machine (std/http/src/h2.trb: stream states, flow-control windows, SETTINGS handshake, PING, GOAWAY, RST_STREAM, CONTINUATION sequence, limits, rapid-reset guard), two ends tested in one program, no seed change. The epic's step list is refreshed: step 2 ticked, step 3 links #699, and the connection task over a Transport with a channel per stream is now step 3b, filed just in time after #699 has landed. Steps 4 to 6 keep their numbers.
next: waiting (step #699)

[loop:torbscript] refine: step 2 landed (#685, frames, in f0d81eb6 and 264ec931). Step 3 filed as #699 (loop, ready): the connection core of RFC 9113 sections 5 and 6.9 as a sans-IO state machine (`std/http/src/h2.trb`: stream states, flow-control windows, SETTINGS handshake, PING, GOAWAY, RST_STREAM, CONTINUATION sequence, limits, rapid-reset guard), two ends tested in one program, no seed change. The epic's step list is refreshed: step 2 ticked, step 3 links #699, and the connection task over a `Transport` with a channel per stream is now step 3b, filed just in time after #699 has landed. Steps 4 to 6 keep their numbers. next: waiting (step #699)
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#72
No description provided.