net.http: add TLS termination to Server (HTTPS support)#27373
Merged
Conversation
net.http.Server has been plain-HTTP only. Add TLS termination so an HTTP/1.1 server can listen on https:// directly, without a separate reverse proxy. - Server gains three opt-in fields: `cert`, `cert_key`, and `in_memory_verification`, mirroring the client-side SSL config naming. When both `cert` and `cert_key` are set, `listen_and_serve` delegates to `listen_and_serve_tls`, which uses the mbedtls SSL listener to accept TLS connections, hands them off to a separate worker pool, and serves HTTP/1.1 requests over those TLS sockets with the existing Handler interface. - New default port `default_https_server_port = 9043` for HTTPS listeners. - The TLS path is provided only on the default mbedtls backend (server_tls_notd_use_openssl.v). On `-d use_openssl`, a matching server_tls_d_use_openssl.v provides a clear-error stub at runtime so the module builds and existing plain-HTTP servers keep working; an OpenSSL server listener is a follow-up. - Existing plain-HTTP behavior is completely unchanged when `cert` / `cert_key` are not set: no new code on that path, same Handler contract, same workers. Hermetic test: spins up a local TLS Server with an in-memory cert/key, exercises it via http.fetch(validate: false), and asserts the 200 response body. The test skips itself under -d use_openssl with a clear message. The full vlib/net/http suite is green on both backends, and the TLS test passes under -W -cstrict -cc clang. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
JalonSolov
pushed a commit
that referenced
this pull request
Jun 8, 2026
* net.http: add server-side HTTP/2 (ALPN + h2 frame demux) Build on TLS termination (#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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
net.http.Serverhas been plain-HTTP only. This PR adds TLS termination soan HTTP/1.1 server can listen on
https://directly, without a separate reverseproxy. It is the prerequisite for landing server-side HTTP/2 (the next PR in the
series).
Files
server.vcert,cert_key,in_memory_verificationfields;default_https_server_port = 9043;listen_and_servedelegates tolisten_and_serve_tlswhen cert+key are setserver_tls_notd_use_openssl.vHandlercontractserver_tls_d_use_openssl.v-d use_openssl: prints a clear runtime error, so the module still builds and existing plain-HTTP code keeps workingserver_tls_test.vDesign
cert/cert_keyare unset, the existing plainTCP path is unchanged — no new code on that branch, same
Handlercontract,same workers.
*_notd_use_openssl.v(default backend) and the matching stub in*_d_use_openssl.vreports a clear runtime error. This avoids pullingmbedtls into
-d use_opensslbuilds that only used the OpenSSL client.Handlerinterface is untouched.Tests
server_tls_test.vspins up a local TLSServerwith an in-memoryself-signed cert/key, exercises it via
http.fetch(url, validate: false), andasserts a
200response body. Skips itself under-d use_opensslwith a clearmessage so the suite stays green there.
Notes / follow-ups
server-side listener in
net.openssl. The stub here reports a clear error;adding an OpenSSL listener (parallel to the mbedtls one in this PR) is the
natural follow-up.
on the TLS listener and an h2 path in the worker that demuxes streams onto
the existing
Handler.handle(Request) Responsecontract — again with nopublic-API change.
🤖 Generated with Claude Code