mirror of
https://github.com/caddyserver/caddy.git
synced 2026-08-04 14:58:47 +00:00
caddyhttp, reverseproxy: gate WebTransport behind enable_webtransport server flag
steadytao raised an architectural concern in review of #7669: the PR put experimental WebTransport handling directly into Caddy's core HTTP/3 accept path, so every HTTP/3 deployment paid for the feature whether or not they used it. Collapse the enablement surface to a single server-level opt-in that matches Caddy's existing precedent for protocol-level features (`protocols`, `allow_0rtt`, `enable_full_duplex`), and detect the request shape at the handler the same way `reverse_proxy` detects a WebSocket upgrade today — no per-handler config flag. Core HTTP/3 path changes (modules/caddyhttp/server.go): * New `EnableWebTransport bool` field on Server, marked EXPERIMENTAL. * buildHTTP3Server now only calls webtransport.ConfigureHTTP3Server and sets EnableStreamResetPartialDelivery when the flag is true. When false, the constructed http3.Server is bit-for-bit identical to the pre-WebTransport implementation. * wtServer is constructed only when the flag is true. * serveH3AcceptLoop falls back to http3.Server.ServeListener when the flag is false — no varint peek, no per-connection dispatch. Caddyfile wiring (caddyconfig/httpcaddyfile/serveroptions.go): * New `enable_webtransport` global server option, modeled on `enable_full_duplex`. Reverse-proxy simplifications (modules/caddyhttp/reverseproxy/): * Removed HTTPTransport.WebTransport field and its Provision-time exclusivity check (no longer needed; H3 is validated separately). * Removed the `webtransport` Caddyfile subdirective under `transport http { }` — this neutralizes the prior commit that introduced it. * Removed Handler.webtransportEnabled cache. ServeHTTP now branches on isWebTransportExtendedConnect(r) alone, matching how the WebSocket upgrade branch works. * serveWebTransport gains fail-fast guards with clear errors when the parent server has enable_webtransport=false or when the handler's transport does not include HTTP/3. Tests: * Existing TestServer_BuildHTTP3ServerEnablesWebTransport now sets EnableWebTransport=true explicitly; new TestServer_BuildHTTP3ServerWithoutWebTransport locks in the regression guard that flag-off produces the pre-PR http3.Server. * Integration tests updated: enable_webtransport: true added to every H3 server block; "webtransport": true dropped from the reverse_proxy transport JSON (auto-detected now). * Caddyfile adapt test for the deleted `webtransport` subdirective is removed; `enable_webtransport` is added to the existing global_server_options_single adapt test alongside its peers. No runtime behavior change when enable_webtransport is false. Diff against master on the core HTTP/3 path is effectively zero in that configuration.
This commit is contained in:
parent
8d86214d1c
commit
43adbee168
10 changed files with 117 additions and 124 deletions
|
|
@ -48,6 +48,7 @@ type serverOptions struct {
|
|||
KeepAliveCount int
|
||||
MaxHeaderBytes int
|
||||
EnableFullDuplex bool
|
||||
EnableWebTransport bool
|
||||
ExpectedUnderscoreHeaders []string
|
||||
Protocols []string
|
||||
StrictSNIHost *bool
|
||||
|
|
@ -219,6 +220,12 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
|
|||
}
|
||||
serverOpts.EnableFullDuplex = true
|
||||
|
||||
case "enable_webtransport":
|
||||
if d.NextArg() {
|
||||
return nil, d.ArgErr()
|
||||
}
|
||||
serverOpts.EnableWebTransport = true
|
||||
|
||||
case "expected_underscore_headers":
|
||||
args := d.RemainingArgs()
|
||||
if len(args) == 0 {
|
||||
|
|
@ -388,6 +395,7 @@ func applyServerOptions(
|
|||
server.KeepAliveCount = opts.KeepAliveCount
|
||||
server.MaxHeaderBytes = opts.MaxHeaderBytes
|
||||
server.EnableFullDuplex = opts.EnableFullDuplex
|
||||
server.EnableWebTransport = opts.EnableWebTransport
|
||||
server.ExpectedUnderscoreHeaders = opts.ExpectedUnderscoreHeaders
|
||||
server.Protocols = opts.Protocols
|
||||
server.StrictSNIHost = opts.StrictSNIHost
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
}
|
||||
max_header_size 100MB
|
||||
enable_full_duplex
|
||||
enable_webtransport
|
||||
log_credentials
|
||||
protocols h1 h2 h2c h3
|
||||
strict_sni_host
|
||||
|
|
@ -54,6 +55,7 @@ foo.com {
|
|||
"keepalive_count": 10,
|
||||
"max_header_bytes": 100000000,
|
||||
"enable_full_duplex": true,
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"match": [
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
:8443
|
||||
|
||||
reverse_proxy https://backend:9443 {
|
||||
transport http {
|
||||
versions 3
|
||||
webtransport
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
----------
|
||||
{
|
||||
"apps": {
|
||||
"http": {
|
||||
"servers": {
|
||||
"srv0": {
|
||||
"listen": [
|
||||
":8443"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
{
|
||||
"handler": "reverse_proxy",
|
||||
"transport": {
|
||||
"protocol": "http",
|
||||
"tls": {
|
||||
"insecure_skip_verify": true
|
||||
},
|
||||
"versions": [
|
||||
"3"
|
||||
],
|
||||
"webtransport": true
|
||||
},
|
||||
"upstreams": [
|
||||
{
|
||||
"dial": "backend:9443"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +63,7 @@ func TestWebTransport_EchoHandlerBidi(t *testing.T) {
|
|||
"srv0": {
|
||||
"listen": [":9443"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"handle": [{"handler": "webtransport"}]
|
||||
|
|
@ -181,6 +182,7 @@ func TestWebTransport_ReverseProxyEndToEnd(t *testing.T) {
|
|||
"proxy": {
|
||||
"listen": [":9443"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
|
|
@ -189,7 +191,6 @@ func TestWebTransport_ReverseProxyEndToEnd(t *testing.T) {
|
|||
"transport": {
|
||||
"protocol": "http",
|
||||
"versions": ["3"],
|
||||
"webtransport": true,
|
||||
"tls": {"insecure_skip_verify": true}
|
||||
},
|
||||
"upstreams": [{"dial": "127.0.0.1:9444"}]
|
||||
|
|
@ -207,6 +208,7 @@ func TestWebTransport_ReverseProxyEndToEnd(t *testing.T) {
|
|||
"upstream": {
|
||||
"listen": [":9444"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{"handle": [{"handler": "webtransport"}]}
|
||||
],
|
||||
|
|
@ -329,6 +331,7 @@ func TestWebTransport_ReverseProxyForwardsHeaders(t *testing.T) {
|
|||
"proxy": {
|
||||
"listen": [":9443"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
|
|
@ -337,7 +340,6 @@ func TestWebTransport_ReverseProxyForwardsHeaders(t *testing.T) {
|
|||
"transport": {
|
||||
"protocol": "http",
|
||||
"versions": ["3"],
|
||||
"webtransport": true,
|
||||
"tls": {"insecure_skip_verify": true}
|
||||
},
|
||||
"headers": {
|
||||
|
|
@ -451,6 +453,7 @@ func TestWebTransport_UpstreamDialFailureSurfaces5xx(t *testing.T) {
|
|||
"proxy": {
|
||||
"listen": [":9443"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
|
|
@ -459,7 +462,6 @@ func TestWebTransport_UpstreamDialFailureSurfaces5xx(t *testing.T) {
|
|||
"transport": {
|
||||
"protocol": "http",
|
||||
"versions": ["3"],
|
||||
"webtransport": true,
|
||||
"tls": {"insecure_skip_verify": true}
|
||||
},
|
||||
"upstreams": [{"dial": "127.0.0.1:%d"}]
|
||||
|
|
@ -580,6 +582,7 @@ func TestWebTransport_InFlightRequestsTracked(t *testing.T) {
|
|||
"proxy": {
|
||||
"listen": [":9443"],
|
||||
"protocols": ["h3"],
|
||||
"enable_webtransport": true,
|
||||
"routes": [
|
||||
{
|
||||
"handle": [
|
||||
|
|
@ -588,7 +591,6 @@ func TestWebTransport_InFlightRequestsTracked(t *testing.T) {
|
|||
"transport": {
|
||||
"protocol": "http",
|
||||
"versions": ["3"],
|
||||
"webtransport": true,
|
||||
"tls": {"insecure_skip_verify": true}
|
||||
},
|
||||
"upstreams": [{"dial": "%s"}]
|
||||
|
|
|
|||
|
|
@ -1315,15 +1315,6 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
return d.ArgErr()
|
||||
}
|
||||
|
||||
case "webtransport":
|
||||
// Accepts no arguments: `webtransport` alone enables it.
|
||||
// Exclusivity with `versions 3` is enforced at Provision
|
||||
// time so parsing is order-independent.
|
||||
if d.NextArg() {
|
||||
return d.Errf("webtransport does not take arguments")
|
||||
}
|
||||
h.WebTransport = true
|
||||
|
||||
case "compression":
|
||||
if d.NextArg() {
|
||||
if d.Val() == "off" {
|
||||
|
|
|
|||
|
|
@ -139,20 +139,6 @@ type HTTPTransport struct {
|
|||
// to change or removal while experimental.
|
||||
Versions []string `json:"versions,omitempty"`
|
||||
|
||||
// WebTransport enables reverse-proxying of WebTransport sessions
|
||||
// (https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/) to
|
||||
// the upstream. Requires Versions to be exactly ["3"]. When
|
||||
// enabled, the frontend Caddy server must itself be serving HTTP/3,
|
||||
// and any Extended CONNECT request with :protocol=webtransport will
|
||||
// have its streams and datagrams pumped between the client and the
|
||||
// upstream — bypassing the normal HTTP round-trip path.
|
||||
//
|
||||
// EXPERIMENTAL: subject to change or removal. The upstream
|
||||
// WebTransport protocol draft is still evolving; this lands with
|
||||
// whatever draft version the webtransport-go library supports at
|
||||
// build time.
|
||||
WebTransport bool `json:"webtransport,omitempty"`
|
||||
|
||||
// Specify the address to bind to when connecting to an upstream. In other words,
|
||||
// it is the address the upstream sees as the remote address.
|
||||
LocalAddress string `json:"local_address,omitempty"`
|
||||
|
|
@ -539,12 +525,6 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
|||
return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported")
|
||||
}
|
||||
|
||||
// WebTransport rides on HTTP/3 exclusively and reuses the TLS client
|
||||
// config built for h3Transport above.
|
||||
if h.WebTransport && !(len(h.Versions) == 1 && h.Versions[0] == "3") {
|
||||
return nil, fmt.Errorf("webtransport requires versions to be exactly [\"3\"]")
|
||||
}
|
||||
|
||||
// if h2/c is enabled, configure it explicitly
|
||||
if slices.Contains(h.Versions, "2") || slices.Contains(h.Versions, "h2c") {
|
||||
if err := http2.ConfigureTransport(rt); err != nil {
|
||||
|
|
|
|||
|
|
@ -274,12 +274,6 @@ type Handler struct {
|
|||
CB CircuitBreaker `json:"-"`
|
||||
DynamicUpstreams UpstreamSource `json:"-"`
|
||||
|
||||
// webtransportEnabled is set at Provision time to true iff
|
||||
// Transport is *HTTPTransport with WebTransport enabled. Checked on
|
||||
// the ServeHTTP hot path so non-WT transports skip the type
|
||||
// assertion on every request.
|
||||
webtransportEnabled bool
|
||||
|
||||
// transportHeaderOps is a set of header operations provided
|
||||
// by the transport at provision time, if the transport
|
||||
// implements TransportHeaderOpsProvider. These ops are
|
||||
|
|
@ -349,12 +343,6 @@ func (h *Handler) Provision(ctx caddy.Context) error {
|
|||
h.ResponseBuffers = respBuffers
|
||||
}
|
||||
}
|
||||
|
||||
// Cache WebTransport enablement so ServeHTTP can short-circuit
|
||||
// the per-request type assertion on non-WT paths.
|
||||
if ht, ok := h.Transport.(*HTTPTransport); ok {
|
||||
h.webtransportEnabled = ht.WebTransport
|
||||
}
|
||||
}
|
||||
if h.LoadBalancing != nil && h.LoadBalancing.SelectionPolicyRaw != nil {
|
||||
mod, err := ctx.LoadModule(h.LoadBalancing, "SelectionPolicyRaw")
|
||||
|
|
@ -550,8 +538,12 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
|||
// WebTransport: HTTP/3 Extended CONNECT with :protocol=webtransport
|
||||
// can't flow through the normal HTTP round-trip — the session hosts
|
||||
// many QUIC streams and datagrams that need bidirectional pumping.
|
||||
// Branch out early before anything else touches the request.
|
||||
if h.webtransportEnabled && isWebTransportExtendedConnect(r) {
|
||||
// Detect it here the same way the handler detects a WebSocket
|
||||
// upgrade: by request shape, not by a per-handler config flag. The
|
||||
// underlying *webtransport.Server only exists when the parent
|
||||
// server has enable_webtransport set, so serveWebTransport fails
|
||||
// fast and clearly if a WT request reaches a non-WT server.
|
||||
if isWebTransportExtendedConnect(r) {
|
||||
return h.serveWebTransport(w, r)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (h *Handler) serveWebTransport(w http.ResponseWriter, r *http.Request) erro
|
|||
wtServer, ok := srv.WebTransportServer().(*webtransport.Server)
|
||||
if !ok || wtServer == nil {
|
||||
return caddyhttp.Error(http.StatusInternalServerError,
|
||||
errors.New("webtransport: HTTP/3 is not enabled on this server; WebTransport requires H3"))
|
||||
errors.New("webtransport: server has enable_webtransport=false or HTTP/3 is not enabled"))
|
||||
}
|
||||
|
||||
if h.LoadBalancing == nil || h.LoadBalancing.SelectionPolicy == nil {
|
||||
|
|
@ -139,11 +139,24 @@ func (h *Handler) serveWebTransport(w http.ResponseWriter, r *http.Request) erro
|
|||
errors.New("webtransport: response writer does not support WebTransport upgrade"))
|
||||
}
|
||||
|
||||
// A WT CONNECT reached this handler because the parent server has
|
||||
// enable_webtransport=true. But the handler's transport still has to
|
||||
// speak HTTP/3 to dial WT upstream. Fail fast and clearly if it
|
||||
// doesn't, the same way we'd fail on an unreachable upstream.
|
||||
ht, ok := h.Transport.(*HTTPTransport)
|
||||
if !ok {
|
||||
return caddyhttp.Error(http.StatusBadGateway,
|
||||
errors.New("webtransport: requires the 'http' transport with versions [\"3\"]"))
|
||||
}
|
||||
if ht.h3Transport == nil {
|
||||
return caddyhttp.Error(http.StatusBadGateway,
|
||||
errors.New("webtransport: transport does not include HTTP/3; set versions to [\"3\"]"))
|
||||
}
|
||||
|
||||
// Dial the upstream BEFORE upgrading the client. If the upstream is
|
||||
// unreachable or refuses the CONNECT, a proper 5xx goes back over the
|
||||
// H3 stream and the client's Dial sees the real status — instead of
|
||||
// an already-upgraded session closing immediately.
|
||||
ht := h.Transport.(*HTTPTransport)
|
||||
upstreamURL := buildWebTransportUpstreamURL(dialInfo.Address, clonedReq)
|
||||
upstreamResp, upstreamSess, err := dialUpstreamWebTransport(r.Context(), ht.h3Transport.TLSClientConfig, upstreamURL, clonedReq.Header)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,27 @@ type Server struct {
|
|||
// TODO: This is an EXPERIMENTAL feature. Subject to change or removal.
|
||||
EnableFullDuplex bool `json:"enable_full_duplex,omitempty"`
|
||||
|
||||
// EnableWebTransport enables WebTransport (draft-ietf-webtrans-http3)
|
||||
// on this server's HTTP/3 listener. When true, the HTTP/3 server
|
||||
// advertises WebTransport in SETTINGS, enables HTTP/3 DATAGRAMs and
|
||||
// QUIC stream-reset partial delivery, and dispatches each QUIC
|
||||
// connection through webtransport.Server.ServeQUICConn so that
|
||||
// handlers can upgrade Extended CONNECT requests with
|
||||
// `:protocol=webtransport`. When false, the HTTP/3 path is
|
||||
// bit-for-bit identical to the pre-WebTransport behavior: clients
|
||||
// that don't speak WebTransport see nothing new.
|
||||
//
|
||||
// This is a server-level opt-in that matches how other
|
||||
// protocol-level features are enabled (see `protocols`,
|
||||
// `allow_0rtt`, `enable_full_duplex`). Handlers that want to proxy
|
||||
// or terminate WebTransport sessions auto-detect the request shape
|
||||
// once this is on — no per-handler configuration is needed.
|
||||
//
|
||||
// Requires HTTP/3.
|
||||
//
|
||||
// TODO: This is an EXPERIMENTAL feature. Subject to change or removal.
|
||||
EnableWebTransport bool `json:"enable_webtransport,omitempty"`
|
||||
|
||||
// A list of header field names containing underscores that should
|
||||
// be preserved instead of being dropped. By default, Caddy drops
|
||||
// ALL headers with underscores to prevent ambiguity with
|
||||
|
|
@ -823,7 +844,9 @@ func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error
|
|||
// create HTTP/3 server if not done already
|
||||
if s.h3server == nil {
|
||||
s.h3server = s.buildHTTP3Server(tlsCfg)
|
||||
s.wtServer = s.buildWebTransportServer()
|
||||
if s.EnableWebTransport {
|
||||
s.wtServer = s.buildWebTransportServer()
|
||||
}
|
||||
}
|
||||
|
||||
s.quicListeners = append(s.quicListeners, h3ln)
|
||||
|
|
@ -834,14 +857,17 @@ func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error
|
|||
}
|
||||
|
||||
// serveH3AcceptLoop accepts incoming QUIC connections from the HTTP/3
|
||||
// listener and dispatches each to the WebTransport-aware serve loop.
|
||||
// webtransport.Server.ServeQUICConn wraps http3.Server: non-WebTransport
|
||||
// streams are transparently forwarded to the normal HTTP/3 request path
|
||||
// (at the cost of one varint peek per stream), so behavior for non-WT
|
||||
// clients is unchanged. This replaces http3.Server.ServeListener's
|
||||
// accept loop so webtransport.Server.Upgrade has the per-connection
|
||||
// session manager state it requires.
|
||||
// listener. When EnableWebTransport is false, the listener is handed
|
||||
// directly to http3.Server — the code path is identical to pre-WebTransport
|
||||
// Caddy. When true, each connection is dispatched through
|
||||
// webtransport.Server.ServeQUICConn, which demultiplexes WebTransport
|
||||
// streams from normal HTTP/3 streams (forwarding the latter to the
|
||||
// http3.Server request path at the cost of one varint peek per stream).
|
||||
func (s *Server) serveH3AcceptLoop(h3ln http3.QUICListener) {
|
||||
if !s.EnableWebTransport {
|
||||
_ = s.h3server.ServeListener(h3ln)
|
||||
return
|
||||
}
|
||||
for {
|
||||
conn, err := h3ln.Accept(s.ctx)
|
||||
if err != nil {
|
||||
|
|
@ -853,25 +879,32 @@ func (s *Server) serveH3AcceptLoop(h3ln http3.QUICListener) {
|
|||
}
|
||||
}
|
||||
|
||||
// buildHTTP3Server constructs the http3.Server used by this server for HTTP/3.
|
||||
// WebTransport support is advertised in SETTINGS and the underlying *quic.Conn
|
||||
// is stashed in each request's context, which is a prerequisite for any
|
||||
// WebTransport-aware handler or transport to call webtransport.Server.Upgrade.
|
||||
// The extra SETTINGS and ConnContext hook are harmless for clients that do not
|
||||
// speak WebTransport.
|
||||
// buildHTTP3Server constructs the http3.Server used by this server for
|
||||
// HTTP/3. When EnableWebTransport is true, the server is additionally
|
||||
// configured for WebTransport: WT enablement is advertised in SETTINGS,
|
||||
// DATAGRAMs are enabled, QUIC stream-reset partial delivery is enabled,
|
||||
// and a ConnContext hook stashes the *quic.Conn in each request's context
|
||||
// so handlers can call webtransport.Server.Upgrade. When false, none of
|
||||
// those modifications are applied and the returned server is
|
||||
// bit-for-bit identical to the pre-WebTransport implementation.
|
||||
func (s *Server) buildHTTP3Server(tlsCfg *tls.Config) *http3.Server {
|
||||
qc := &quic.Config{
|
||||
Versions: []quic.Version{quic.Version1, quic.Version2},
|
||||
Tracer: h3qlog.DefaultConnectionTracer,
|
||||
}
|
||||
if s.EnableWebTransport {
|
||||
qc.EnableStreamResetPartialDelivery = true
|
||||
}
|
||||
h3 := &http3.Server{
|
||||
Handler: s,
|
||||
TLSConfig: tlsCfg,
|
||||
MaxHeaderBytes: s.MaxHeaderBytes,
|
||||
QUICConfig: &quic.Config{
|
||||
Versions: []quic.Version{quic.Version1, quic.Version2},
|
||||
Tracer: h3qlog.DefaultConnectionTracer,
|
||||
EnableStreamResetPartialDelivery: true,
|
||||
},
|
||||
IdleTimeout: time.Duration(s.IdleTimeout),
|
||||
QUICConfig: qc,
|
||||
IdleTimeout: time.Duration(s.IdleTimeout),
|
||||
}
|
||||
if s.EnableWebTransport {
|
||||
webtransport.ConfigureHTTP3Server(h3)
|
||||
}
|
||||
webtransport.ConfigureHTTP3Server(h3)
|
||||
return h3
|
||||
}
|
||||
|
||||
|
|
@ -879,6 +912,7 @@ func (s *Server) buildHTTP3Server(tlsCfg *tls.Config) *http3.Server {
|
|||
// the http3.Server. It owns the per-connection session state needed by
|
||||
// webtransport.Server.Upgrade and demultiplexes WebTransport streams
|
||||
// from normal HTTP/3 streams on each accepted QUIC connection.
|
||||
// Only constructed when EnableWebTransport is true.
|
||||
func (s *Server) buildWebTransportServer() *webtransport.Server {
|
||||
return &webtransport.Server{H3: s.h3server}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1005,13 +1005,14 @@ func TestServer_DetermineTrustedProxy_MatchRightMostUntrustedFirst(t *testing.T)
|
|||
assert.Equal(t, clientIP, "90.100.110.120")
|
||||
}
|
||||
|
||||
// TestServer_BuildHTTP3ServerEnablesWebTransport asserts that the http3.Server
|
||||
// Caddy builds advertises WebTransport in its SETTINGS and wires the
|
||||
// prerequisites webtransport.Server.Upgrade relies on: DATAGRAM support,
|
||||
// a non-nil ConnContext hook (used to stash the underlying *quic.Conn for
|
||||
// Upgrade to retrieve), and QUIC stream reset partial delivery.
|
||||
// TestServer_BuildHTTP3ServerEnablesWebTransport asserts that with
|
||||
// EnableWebTransport=true the http3.Server advertises WebTransport in
|
||||
// its SETTINGS and wires the prerequisites webtransport.Server.Upgrade
|
||||
// relies on: DATAGRAM support, a non-nil ConnContext hook (used to stash
|
||||
// the underlying *quic.Conn for Upgrade to retrieve), and QUIC
|
||||
// stream-reset partial delivery.
|
||||
func TestServer_BuildHTTP3ServerEnablesWebTransport(t *testing.T) {
|
||||
s := &Server{}
|
||||
s := &Server{EnableWebTransport: true}
|
||||
h3 := s.buildHTTP3Server(&tls.Config{})
|
||||
|
||||
assert.NotNil(t, h3, "expected non-nil http3.Server")
|
||||
|
|
@ -1022,6 +1023,23 @@ func TestServer_BuildHTTP3ServerEnablesWebTransport(t *testing.T) {
|
|||
assert.True(t, h3.QUICConfig.EnableStreamResetPartialDelivery, "EnableStreamResetPartialDelivery is required by webtransport-go")
|
||||
}
|
||||
|
||||
// TestServer_BuildHTTP3ServerWithoutWebTransport asserts that with
|
||||
// EnableWebTransport=false (the default) the http3.Server does NOT
|
||||
// advertise WebTransport and does not enable the related QUIC/HTTP/3
|
||||
// features. This is the load-bearing regression guard: non-WT HTTP/3
|
||||
// deployments must pay zero cost for the WebTransport feature.
|
||||
func TestServer_BuildHTTP3ServerWithoutWebTransport(t *testing.T) {
|
||||
s := &Server{}
|
||||
h3 := s.buildHTTP3Server(&tls.Config{})
|
||||
|
||||
assert.NotNil(t, h3)
|
||||
assert.False(t, h3.EnableDatagrams, "EnableDatagrams must be false when WebTransport is disabled")
|
||||
assert.Empty(t, h3.AdditionalSettings, "AdditionalSettings must be empty when WebTransport is disabled")
|
||||
assert.Nil(t, h3.ConnContext, "ConnContext must be nil when WebTransport is disabled")
|
||||
assert.NotNil(t, h3.QUICConfig)
|
||||
assert.False(t, h3.QUICConfig.EnableStreamResetPartialDelivery, "EnableStreamResetPartialDelivery must be false when WebTransport is disabled")
|
||||
}
|
||||
|
||||
// TestServer_BuildHTTP3ServerAppliesHandlerAndTLS is a smoke test for the
|
||||
// non-WebTransport fields of the constructed http3.Server, guarding against a
|
||||
// refactor accidentally dropping them.
|
||||
|
|
@ -1038,7 +1056,7 @@ func TestServer_BuildHTTP3ServerAppliesHandlerAndTLS(t *testing.T) {
|
|||
// TestServer_BuildWebTransportServerWrapsHTTP3Server asserts that the
|
||||
// webtransport.Server wraps the correct http3.Server.
|
||||
func TestServer_BuildWebTransportServerWrapsHTTP3Server(t *testing.T) {
|
||||
s := &Server{}
|
||||
s := &Server{EnableWebTransport: true}
|
||||
s.h3server = s.buildHTTP3Server(&tls.Config{})
|
||||
wt := s.buildWebTransportServer()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue