net.ssl: add ALPN protocol support to mbedtls and openssl backends#27343
Conversation
Add an `alpn_protocols []string` option to the SSL connection config and a `negotiated_alpn() string` method to query the protocol selected during the TLS handshake. This is the foundation needed for HTTP/2 (ALPN `h2`) and other protocol negotiation, without changing behaviour for existing callers (an empty list sends no ALPN extension, exactly as before). mbedtls: - Finish the previously commented-out, leaky ALPN block in SSLConn.init, driving it from config.alpn_protocols and freeing the C array in shutdown. - Add the same advertisement to SSLListener so accepted server connections can negotiate a protocol. - Add `negotiated_alpn()` via mbedtls_ssl_get_alpn_protocol. openssl: - Set the ALPN list (length-prefixed wire format) in SSLConn.init via SSL_set_alpn_protos. - Add `negotiated_alpn()` via SSL_get0_alpn_selected. The option and method are exposed through the net.ssl facade automatically (embedded config + SSLConn type alias). Add a hermetic net.ssl test that stands up a local mbedtls TLS server advertising ALPN and verifies negotiation for h2, http/1.1 fallback, and the no-ALPN case. It passes on both the mbedtls and OpenSSL (-d use_openssl) client backends. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0dba1f47fe
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
People will definitely complain if this isn't supported on Windows, too, but it can be done in a separate PR. |
Working on it |
Two follow-up fixes to the ALPN support:
1. -cstrict / -Werror const-qualifier errors (clang):
- mbedtls_ssl_conf_alpn_protocols takes `const char **` and
mbedtls_ssl_get_alpn_protocol returns `const char *`; SSL_get0_alpn_selected
takes `const unsigned char **`. Pass the nested-pointer arguments through
voidptr at the call sites (void* converts cleanly to a const pointer in C)
and cast the const char* return before use.
2. Linking against OpenSSL versions older than 1.0.2 (predating ALPN):
route SSL_set_alpn_protos / SSL_get0_alpn_selected through small
version-guarded shims in openssl_compat.h, matching the existing
v_net_openssl_* pattern. On older OpenSSL the shims are no-ops (set returns
non-zero -> a clear runtime error only if ALPN was actually requested; get
reports no protocol), so HTTP/1.1-only users keep building and running.
LibreSSL reports a high version number and uses the native path.
Verified with `./vnew -W -cstrict -cc clang test vlib/net/ssl/ssl_alpn_test.v`
on both the mbedtls and OpenSSL backends, plus the net.mbedtls suite.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Reviewed the CI failures they all appear to be unrelated |
|
Preparing for Issue #5017 |
* net.http: add synchronous HTTP/2 client connection (H2Conn) Build on the ALPN (#27343), HPACK (#27353), and frame-codec (#27356) PRs to add a minimal, synchronous, single-stream HTTP/2 client. Additive only: new files in the http module, no change to existing code paths. Nothing wires it into http.fetch yet (that ALPN shim is a follow-up), so there is no user-visible behaviour change. - H2Transport interface — the byte transport the connection runs over. Its read/write signatures match net.ssl.SSLConn, so an ALPN-negotiated `h2` TLS socket satisfies it directly; tests use an in-memory mock, so the connection is exercised without a socket. - Connection preface + SETTINGS handshake (sent lazily on the first request). - H2Conn.do(req): HPACK-encodes the request headers, sends HEADERS (plus DATA for a body, chunked to the peer's max frame size and bounded by the connection flow-control window), then reads frames until the stream closes. - Inline servicing of connection-level frames: SETTINGS (apply + ACK), PING (echo ACK), WINDOW_UPDATE (grow the send window), GOAWAY (fail). Receive-side flow control is replenished with a WINDOW_UPDATE per DATA frame. - CONTINUATION reassembly for response header blocks; RST_STREAM on the request stream is surfaced as an error. Hermetic tests over the mock transport cover: a basic GET, a multi-DATA response, a POST with a body, GOAWAY, RST_STREAM, CONTINUATION-split response headers, and PING ACK. Passes under -W -cstrict -cc clang; the full vlib/net/http suite is green. Not included here, by design (follow-ups): stream multiplexing with a background reader thread, connection pooling, wiring into http.fetch via ALPN, and the server side. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * net.http: HTTP/2 client header splitting and per-stream flow control Address two send-path correctness gaps in H2Conn: - Split request header blocks larger than the peer's SETTINGS_MAX_FRAME_SIZE into a HEADERS frame followed by CONTINUATION frames (RFC 7540 Section 4.3), via send_header_block. Previously a large header set (e.g. big Cookie or Authorization) was sent as one oversized HEADERS frame that a compliant peer would reject. - Track the per-stream send window in addition to the connection window (RFC 7540 Section 6.9). DATA is now bounded by min(connection, stream) window, stream-level WINDOW_UPDATE credits the active stream, and a change to SETTINGS_INITIAL_WINDOW_SIZE retroactively adjusts the stream window by the delta (Section 6.9.2). Previously only the connection window was tracked, so a peer lowering the initial window or granting stream-level updates could be overrun or could stall the client. Adds tests for a request whose header block spans HEADERS + CONTINUATION, and for a body larger than the initial window that resumes after the peer grows both windows (no DATA frame exceeding the max frame size). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Richard Wheeler <quaesitor.scientiam@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
First half of vlang#27383: give the SChannel (vschannel) TLS backend the ability to advertise ALPN protocols and read the protocol the server selected, reaching parity with the mbedtls/OpenSSL backends (vlang#27343) on the negotiation primitive. thirdparty/vschannel/vschannel.c: - TlsContext gains an ALPN protocol list to advertise and a slot for the negotiated protocol. - perform_client_handshake() passes a SECBUFFER_APPLICATION_PROTOCOLS input buffer (SEC_APPLICATION_PROTOCOLS / SecApplicationProtocolNegotiationExt_ALPN) into the ClientHello when a list is configured; the path is unchanged when it is not, so existing HTTP/1.1 requests are byte-identical. - After the handshake, SECPKG_ATTR_APPLICATION_PROTOCOL is queried and stored. - New C entry points: vschannel_set_alpn(), vschannel_get_alpn(), and vschannel_alpn_probe() (handshake-only, no application data). vlib/net/http (Windows only): - vschannel_alpn_windows.c.v exposes the C API, an alpn_wire() encoder, and schannel_alpn_probe(). - vschannel_alpn_windows_test.v: a wire-encoding unit test plus `-d network` tests that probe a public HTTP/2 server and assert `h2` is negotiated (and that offering only http/1.1 falls back). This deliberately does NOT change fetch(): the one-shot vschannel request() path still speaks HTTP/1.1, so advertising `h2` in real requests waits for the HTTP/2-driver wiring (the second half of vlang#27383). It satisfies the issue's first acceptance criterion (a negotiated_alpn()-equivalent on SChannel). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* net.http: add ALPN negotiation to the Windows SChannel backend First half of #27383: give the SChannel (vschannel) TLS backend the ability to advertise ALPN protocols and read the protocol the server selected, reaching parity with the mbedtls/OpenSSL backends (#27343) on the negotiation primitive. thirdparty/vschannel/vschannel.c: - TlsContext gains an ALPN protocol list to advertise and a slot for the negotiated protocol. - perform_client_handshake() passes a SECBUFFER_APPLICATION_PROTOCOLS input buffer (SEC_APPLICATION_PROTOCOLS / SecApplicationProtocolNegotiationExt_ALPN) into the ClientHello when a list is configured; the path is unchanged when it is not, so existing HTTP/1.1 requests are byte-identical. - After the handshake, SECPKG_ATTR_APPLICATION_PROTOCOL is queried and stored. - New C entry points: vschannel_set_alpn(), vschannel_get_alpn(), and vschannel_alpn_probe() (handshake-only, no application data). vlib/net/http (Windows only): - vschannel_alpn_windows.c.v exposes the C API, an alpn_wire() encoder, and schannel_alpn_probe(). - vschannel_alpn_windows_test.v: a wire-encoding unit test plus `-d network` tests that probe a public HTTP/2 server and assert `h2` is negotiated (and that offering only http/1.1 falls back). This deliberately does NOT change fetch(): the one-shot vschannel request() path still speaks HTTP/1.1, so advertising `h2` in real requests waits for the HTTP/2-driver wiring (the second half of #27383). It satisfies the issue's first acceptance criterion (a negotiated_alpn()-equivalent on SChannel). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * thirdparty/vschannel: add ALPN type shim for older SDK headers (tcc) tcc bundles Windows SDK headers that predate SChannel ALPN, so the ALPN structs, enums and constants were undeclared. Define them when the SDK headers do not, matching the official layout exactly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Richard Wheeler <quaesitor.scientiam@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Alexander Medvednikov <alexander@medvednikov.com>
What
Adds ALPN (Application-Layer Protocol Negotiation) support to V's TLS layer:
alpn_protocols []stringoption on the SSL connection config.negotiated_alpn() stringmethod to read the protocol selected duringthe TLS handshake.
Both are exposed through the
net.sslfacade automatically (embedded config +SSLConntype alias) and implemented for both the mbedtls (default) andOpenSSL (
-d use_openssl) backends.Why
This is the first, self-contained piece of HTTP/2 support: a client must
advertise
h2via ALPN and confirm the server selected it before speaking theHTTP/2 framing protocol. Splitting it out keeps the eventual HTTP/2 work
reviewable in small steps (per the discussion on #26776), and ALPN is useful on
its own for any protocol negotiation.
Details
mbedtls (
vlib/net/mbedtls/ssl_connection.c.v)SSLConn.initwith aworking implementation driven by
config.alpn_protocols.malloc'dprotocol array had a literal
TODO free alpn_listand was never released.The array is now owned by the connection and freed in
shutdown()(mbedtlsstores the pointer without copying, so it must outlive the config).
SSLListener, so accepted server connectionscan negotiate a protocol.
negotiated_alpn()viambedtls_ssl_get_alpn_protocol.openssl (
vlib/net/openssl/ssl_connection.c.v)SSLConn.initviaSSL_set_alpn_protos.negotiated_alpn()viaSSL_get0_alpn_selected.openssl_compat.h(the existingv_net_openssl_*pattern), so the modulestill links against OpenSSL versions older than 1.0.2 that predate ALPN.
Portability
-W -cstrict -cc clang(const-qualifier handling for the
const char **/const char *params andreturn values).
Compatibility
No behaviour change for existing callers: an empty
alpn_protocolssends noALPN extension, exactly as before. The additions are purely additive (new
optional config field + new method).
Tests
Adds
vlib/net/ssl/ssl_alpn_test.v, a hermetic test (no network) that stands upa local mbedtls TLS server advertising ALPN and verifies, through the
net.sslfacade client:
h2when both sides offer it,http/1.1,Passing locally on both backends:
vlib/net/mbedtls/andvlib/net/ssl/suites pass on both backends, includingunder
-W -cstrict -cc clang;net.httpcompiles on both.Notes
The new method returns
''and the option is ignored on that path, so HTTP/1.1behaviour is unaffected.
🤖 Generated with Claude Code