net.http: HTTP/2 server stream state machine (RFC 9113 §5.1/§6.1/§6.4) #4
quaesitor-scientiam
## What Adds the server-side HTTP/2 stream state machine (RFC 9113 §5.1/§6.1/§6.4). A new `classify_stream(id) -> active | idle | closed` helper drives frame-acceptance: - **DATA / RST_STREAM / WINDOW_UPDATE on an idle stream** (one never opened) → connection error `PROTOCOL_ERROR`. - **DATA on a closed / half-closed-remote stream** → `STREAM_CLOSED` stream error (`RST_STREAM`), crediting the connection flow window first. - **HEADERS opening a stream beyond `SETTINGS_MAX_CONCURRENT_STREAMS`** → still HPACK-decoded (the decoder is stateful; skipping it would desync the dynamic table) but answered with `RST_STREAM(REFUSED_STREAM)` instead of being served. This is the second PR in the server-conformance series (follows #27569, request header / HPACK-scope validation). ## h2spec results Closes **8** h2spec cases — baseline trimmed 16 → 8, verified with the in-repo gate (`run_h2spec.sh`, h2spec v2.6.0, `--timeout 5`): **137 passed / 8 failed, no regressions**. Cases now passing: `http2/5.1` idle DATA/RST/WINDOW_UPDATE, `5.1` closed DATA, `5.1` closed-after-RST DATA, `5.1` half-closed DATA, `http2/6.1`, `http2/6.4`. `http2/5.1.2` (concurrency limit) stays baselined: the probe opens both streams with END_STREAM, so this serial server processes and closes the first before reading the second — they are never concurrent. The `refused`-stream path is correct for pipelined bodyful requests, but the h2spec case needs true concurrent stream handling (a planned follow-up). ## Tests Five new scripted-peer unit tests in `h2_server_test.v` cover the idle (connection-error), closed (`STREAM_CLOSED`), and over-limit (`REFUSED_STREAM`) paths. `vlib/net/http/h2_server_test.v` passes under gcc. 🧙 Built with [WOZCODE](https://wozcode.com)