net.http: add server-side HTTP/2 (ALPN + h2 frame demux)#27382
Conversation
Build on TLS termination (vlang#27373) to let net.http.Server speak HTTP/2. - New `enable_http2` field on Server. When set on a TLS listener, the listener advertises ALPN `h2, http/1.1`. After the handshake, the TLS worker checks `negotiated_alpn()`: if `h2`, it dispatches to the new HTTP/2 driver; otherwise the existing HTTP/1.1 path is unchanged. - New h2_server.v (H2ServerConn): reads the client preface, exchanges SETTINGS (advertising SETTINGS_MAX_CONCURRENT_STREAMS=1 so requests serialize), and runs the frame loop. HEADERS+CONTINUATION are assembled and HPACK-decoded into a net.http.Request; DATA frames populate the body and replenish flow control; SETTINGS / PING / WINDOW_UPDATE / GOAWAY / RST_STREAM / PRIORITY are serviced inline. When the request stream closes, the existing Handler.handle(Request) Response interface is invoked unchanged; the Response is HPACK-encoded into HEADERS + DATA(END_STREAM) and sent back. - Hop-by-hop response headers are dropped (RFC 7540 Section 8.1.2.2). The request body is capped at 8 MiB with RST_STREAM(REFUSED_STREAM) on overflow. - The Handler contract is untouched: req.url is the request-target (the :path pseudo-header) and Host comes from :authority, so existing HTTP/1.1 handlers run with no changes on the new transport. Tests: h2_server_test.v drives the server through an in-memory blocking pipe with the existing HTTP/2 client (GET, POST with a body, non-200 status, all round-trip). server_tls_test.v adds a TLS + ALPN end-to-end test asserting http.fetch(enable_http2: true) negotiates h2 against the same listener that still serves HTTP/1.1 to non-h2 clients. Full vlib/net/http suite is green on both backends; passes under -W -cstrict -cc clang. This is opt-in and additive: with enable_http2 unset (or for non-TLS servers), behaviour is exactly as before. Stream multiplexing with a background reader is a planned follow-up (this driver serializes requests). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
Running local codex review given usage uses are exceeded • Findings
Tests Run
|
Address two findings from review of the server-side HTTP/2 PR: - Server response DATA now respects the HTTP/2 send flow-control windows (RFC 7540 Section 6.9). send_body bounds each DATA frame by min(connection, stream) window, decrements both after sending, and waits for WINDOW_UPDATE (servicing SETTINGS / PING / WINDOW_UPDATE, and a RST_STREAM for the stream being written) when a window is exhausted. apply_settings now also adjusts every active stream's send window by the delta when the peer changes SETTINGS_INITIAL_WINDOW_SIZE (Section 6.9.2). Previously a client that lowered its initial window could be sent more DATA than permitted and reset the stream with FLOW_CONTROL_ERROR. - Gate test_server_tls_h2_negotiation so it skips on the default Windows configuration: the SChannel client does not advertise ALPN, so it cannot negotiate HTTP/2 and the version assertion would fail. The path stays covered with `-d no_vschannel` (mbedtls client), matching how the rest of the suite treats the SChannel limitation. Adds test_h2_server_respects_send_window: a raw client advertises SETTINGS_INITIAL_WINDOW_SIZE=10, and the test asserts the server's first DATA frame is <= 10 bytes and that the full 100-byte body is delivered after a WINDOW_UPDATE. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Thanks for running the local review — both addressed in ed34646: High (Windows ALPN): Medium (server send flow control): Verified: |
|
Filed #27383 to track full SChannel ALPN / HTTP/2 support — removing the |
* net.http: add server-side HTTP/2 (ALPN + h2 frame demux) Build on TLS termination (vlang#27373) to let net.http.Server speak HTTP/2. - New `enable_http2` field on Server. When set on a TLS listener, the listener advertises ALPN `h2, http/1.1`. After the handshake, the TLS worker checks `negotiated_alpn()`: if `h2`, it dispatches to the new HTTP/2 driver; otherwise the existing HTTP/1.1 path is unchanged. - New h2_server.v (H2ServerConn): reads the client preface, exchanges SETTINGS (advertising SETTINGS_MAX_CONCURRENT_STREAMS=1 so requests serialize), and runs the frame loop. HEADERS+CONTINUATION are assembled and HPACK-decoded into a net.http.Request; DATA frames populate the body and replenish flow control; SETTINGS / PING / WINDOW_UPDATE / GOAWAY / RST_STREAM / PRIORITY are serviced inline. When the request stream closes, the existing Handler.handle(Request) Response interface is invoked unchanged; the Response is HPACK-encoded into HEADERS + DATA(END_STREAM) and sent back. - Hop-by-hop response headers are dropped (RFC 7540 Section 8.1.2.2). The request body is capped at 8 MiB with RST_STREAM(REFUSED_STREAM) on overflow. - The Handler contract is untouched: req.url is the request-target (the :path pseudo-header) and Host comes from :authority, so existing HTTP/1.1 handlers run with no changes on the new transport. Tests: h2_server_test.v drives the server through an in-memory blocking pipe with the existing HTTP/2 client (GET, POST with a body, non-200 status, all round-trip). server_tls_test.v adds a TLS + ALPN end-to-end test asserting http.fetch(enable_http2: true) negotiates h2 against the same listener that still serves HTTP/1.1 to non-h2 clients. Full vlib/net/http suite is green on both backends; passes under -W -cstrict -cc clang. This is opt-in and additive: with enable_http2 unset (or for non-TLS servers), behaviour is exactly as before. Stream multiplexing with a background reader is a planned follow-up (this driver serializes requests). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * net.http: server-side h2 send flow control + gate Windows ALPN test Address two findings from review of the server-side HTTP/2 PR: - Server response DATA now respects the HTTP/2 send flow-control windows (RFC 7540 Section 6.9). send_body bounds each DATA frame by min(connection, stream) window, decrements both after sending, and waits for WINDOW_UPDATE (servicing SETTINGS / PING / WINDOW_UPDATE, and a RST_STREAM for the stream being written) when a window is exhausted. apply_settings now also adjusts every active stream's send window by the delta when the peer changes SETTINGS_INITIAL_WINDOW_SIZE (Section 6.9.2). Previously a client that lowered its initial window could be sent more DATA than permitted and reset the stream with FLOW_CONTROL_ERROR. - Gate test_server_tls_h2_negotiation so it skips on the default Windows configuration: the SChannel client does not advertise ALPN, so it cannot negotiate HTTP/2 and the version assertion would fail. The path stays covered with `-d no_vschannel` (mbedtls client), matching how the rest of the suite treats the SChannel limitation. Adds test_h2_server_respects_send_window: a raw client advertises SETTINGS_INITIAL_WINDOW_SIZE=10, and the test asserts the server's first DATA frame is <= 10 bytes and that the full 100-byte body is delivered after a WINDOW_UPDATE. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Richard Wheeler <quaesitor.scientiam@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
What
Lets
net.http.Serverspeak HTTP/2, building on the TLS-termination PR(#27373). When a client negotiates
h2via ALPN, the server serves the requestthrough a new HTTP/2 driver; otherwise the existing HTTP/1.1 path is unchanged.
This completes a full client and server HTTP/2 stack in pure V (framing,
HPACK, ALPN, TLS, client, server) with no external dependencies.
Changes
h2_server.vH2ServerConn: preface, SETTINGS, frame loop, HEADERS→Request, Response→HEADERS/DATAserver.venable_http2fieldserver_tls_notd_use_openssl.vh2h2_server_test.v,server_tls_test.vDesign
enable_http2unset (or for non-TLS servers),behaviour is exactly as before — no ALPN advertised, no new code on that path.
Handlerinterface is untouched.Handler.handle(Request) Responseis invoked once per request stream.
req.urlis the request-target (the:pathpseudo-header) andHostcomes from:authority, so existingHTTP/1.1 handlers work as-is on the new transport.
SETTINGS_MAX_CONCURRENT_STREAMS=1,so the peer sends one request at a time. Stream multiplexing with a
background reader is a planned follow-up.
flow-control replenishment on DATA, hop-by-hop response headers dropped
(§8.1.2.2), an 8 MiB request-body cap with
RST_STREAM(REFUSED_STREAM), and abest-effort GOAWAY on protocol error.
backend;
-d use_opensslkeeps the TLS-server stub. An OpenSSL serverlistener is a follow-up.
Tests
h2_server_test.vdrives the server through an in-memory blocking pipewith the existing HTTP/2 client — GET, POST with a body, and a non-200
status all round-trip, exercising the real framing + HPACK on both sides.
server_tls_test.vadds a TLS + ALPN end-to-end test: a real HTTPSlistener with
enable_http2: trueservesh2tohttp.fetch(enable_http2: true)andhttp/1.1to a plainhttp.fetchon the same port.Follow-ups
-d use_opensslcan serve TLS / h2 too).🤖 Generated with Claude Code