Skip to content

net.http: pool HTTP/2 connections over the Windows SChannel backend#27712

Open
quaesitor-scientiam wants to merge 4 commits into
vlang:masterfrom
quaesitor-scientiam:h2-phase4-vschannel-pool
Open

net.http: pool HTTP/2 connections over the Windows SChannel backend#27712
quaesitor-scientiam wants to merge 4 commits into
vlang:masterfrom
quaesitor-scientiam:h2-phase4-vschannel-pool

Conversation

@quaesitor-scientiam

Copy link
Copy Markdown
Contributor

Summary

Extends the existing HTTP/2 connection-pooling work (#27412/#27413/#27643, all
merged) to native Windows, which previously bypassed pooling entirely — every
https request on the SChannel backend (i.e. without -d no_vschannel) went
straight to the old one-shot req.ssl_do path before any pooling logic ran.

  • Adds VSchannelPooledTransport, an H2Transport adapter around the
    SChannel backend's TlsContext. TlsContext is safely embeddable by value
    (verified directly — unlike mbedTLS's SSL context, it wires no
    self-referential pointer into its own address), so this needs no second
    heap allocation the way H2PooledTransport does for &ssl.SSLConn.
  • Adds two small, purely-additive helpers to thirdparty/vschannel/vschannel.c
    (vschannel_set_io_timeouts, vschannel_wait_writable) — the one-shot
    client had no timeout mechanism at all, and a pooled adapter's read()
    must be bounded so close() (which shares the same lock a blocked reader
    would hold) can't deadlock.
  • Extracts the mbedTLS/OpenSSL ALPN-probing dial behind a new
    h2_dial_probe/H2ProbeResult abstraction so h2_dial_and_do's
    singleflight/dial-id/eviction machinery stays backend-agnostic; the
    Windows branch dials via VSchannelPooledTransport instead.
  • Narrows Transport.round_trip so only origins already proven
    http/1.1-only (or with h2 disabled) keep the one-shot path — h2-enabled
    requests now fall through to pooling like every other platform.

h1-over-vschannel pooling stays explicitly out of scope (tracked
separately): an h2-enabled request to an h1-only origin on Windows falls
back to the proven one-shot req.ssl_do via a new h2_fallback_h1 helper,
rather than silently switching TLS backends for that fallback.

Un-skips 11 h2-pool tests that were previously platform-gated on this being
unimplemented, adds VSchannelPooledTransport's own adapter tests (stable
address, concurrent read/write/close, read-timeout, write-survives-stall),
and adds one end-to-end fallback-path integration test. Also fixes
vschannel_validation_windows_test.v's server to accept up to 2 connections
on this platform (the ALPN probe plus the h1 fallback's independent dial) —
bounded rather than unbounded, so the certificate-rejection test's
abort-mid-handshake case still returns promptly (an unbounded accept loop
there previously stalled ~41s, since SSLListener.shutdown() only frees the
listening socket, not a connection already stuck mid-handshake).

Fixes #27702.

Test plan

  • ./vnew fmt -verify on every touched file
  • ./vnew test vlib/net/http/ — 25 passed, 2 skipped (by design; see
    skip messages), default config (native SChannel pooling exercised)
  • ./vnew -d no_vschannel test vlib/net/http/transport_h2_test.v — 1
    passed, confirming zero behavior change for the mbedTLS/OpenSSL path
  • /vreview (full A–G angles + mechanical detectors) — no findings
  • Manual smoke test against a real HTTPS h2 origin (https://www.google.com/)

Co-Authored-By: WOZCODE contact@withwoz.com

quaesitor-scientiam and others added 2 commits July 8, 2026 23:59
…channel

vschannel_read()/vschannel_write() call raw blocking recv()/send() with no
timeout mechanism at all. A pooled H2Transport adapter needs a bounded
read() -- close() takes the same lock a blocked reader would be holding, so
a timeout-free reader would deadlock close() forever (exactly why
h2_pooled_io_timeout exists for the mbedTLS/OpenSSL adapter). This adds two
small, purely-additive functions needed to build that adapter on top of
vschannel in a follow-up commit:

- vschannel_set_io_timeouts: SO_RCVTIMEO/SO_SNDTIMEO on the connection's
  socket, set once at connect time. Unlike mbedTLS/OpenSSL's single shared
  duration field (reused for both directions' internal retry loop, needing
  widen-then-restore around every write), these are independent Winsock
  options.
- vschannel_wait_writable: a pure socket-level select() on the write side,
  mirroring net.TcpConn.wait_for_write's role for the mbedTLS/OpenSSL
  adapter -- needed because vschannel's socket handle is otherwise opaque to
  V.

No existing function's control flow changes; the one-shot request paths
never call either function.

Co-Authored-By: WOZCODE <contact@withwoz.com>
Extends Phase 3's default-on h2 pooling (mbedTLS/OpenSSL) to native Windows,
which previously bypassed pooling entirely for every https request. Adds
VSchannelPooledTransport, an H2Transport adapter around the SChannel one-shot
client's TlsContext (safely embeddable by value, unlike mbedTLS's
self-referential SSL context). Wires it into the dial path via a new
h2_dial_probe/H2ProbeResult abstraction that extracts the mbedTLS/OpenSSL
ALPN-probing dial behind the same interface, and narrows Transport.round_trip
so only origins already proven http/1.1-only (or h2 disabled) keep the
one-shot path; h2-enabled requests fall through to pooling like every other
platform.

h1-over-vschannel pooling stays out of scope (tracked separately); an
h2-enabled request to an h1-only origin on Windows falls back to the proven
one-shot req.ssl_do via h2_fallback_h1 rather than switching TLS backends.

Un-skips 11 h2-pool tests that were previously platform-gated on this being
unimplemented, adds VSchannelPooledTransport's own adapter tests, and adds
end-to-end fallback coverage. vschannel_validation_windows_test.v's server now
accepts up to 2 connections on this platform (the ALPN probe plus the h1
fallback's independent dial), bounded rather than looping so the existing
certificate-rejection test's abort-mid-handshake case still returns promptly.

Fixes vlang#27702.

Co-Authored-By: WOZCODE <contact@withwoz.com>
quaesitor-scientiam and others added 2 commits July 9, 2026 11:40
CI builds tools with -N (notices promoted to errors), so the checker's
'interface field must be initialized' notice on H2ProbeResult's bare
transport/closer interface fields -- unavoidable in the non-h2 probe
outcome, which has nothing to initialize them with -- failed every job
compiling anything that imports net.http, on all platforms. Option fields
are the checker's sanctioned escape for exactly this (set-only-sometimes)
shape; h2_dial_and_do unwraps them through h2_dial_failed so even a
structurally-impossible miss still wakes singleflight waiters instead of
stranding them.

Repro: v -N -W -check cmd/tools/vdownload.v (errored before, clean now).
Full net.http suite green in both default and -d no_vschannel configs.

Co-Authored-By: WOZCODE <contact@withwoz.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

net.http: HTTP/2 connection pooling for the Windows SChannel backend (Phase 4)

1 participant