reverseproxy: bump webtransport-go to v0.11.0 (WebTransport draft-15)

webtransport-go v0.11.0 implements draft-15, which renames the Extended
CONNECT :protocol token from "webtransport" to "webtransport-h3" (the
server keeps accepting the legacy token for older clients). Detect both
tokens in the reverse-proxy upgrade check and in the test echo handler.

Pulls quic-go v0.59.1 -> v0.60.0, required by webtransport-go v0.11.0;
its only breaking change is requiring Go 1.25 (already met), with no API
changes affecting Caddy core. Also picks up the v0.11.0 fix for a
stream-close race on concurrent session close, relevant to the long-lived
pump.
This commit is contained in:
tomholford 2026-06-23 19:26:15 +09:00
parent 34625fba75
commit 349f04696f
4 changed files with 29 additions and 15 deletions

View file

@ -41,9 +41,14 @@ func init() {
caddy.RegisterModule(WebTransportEcho{})
}
// webtransportEchoProtocol is the :protocol pseudo-header value for an
// HTTP/3 Extended CONNECT that establishes a WebTransport session.
const webtransportEchoProtocol = "webtransport"
// webtransportEchoProtocol values are the :protocol pseudo-header tokens for
// an HTTP/3 Extended CONNECT that establishes a WebTransport session. draft-15
// (webtransport-go v0.11.0+) uses "webtransport-h3"; older draft clients use
// the legacy "webtransport" token.
const (
webtransportEchoProtocol = "webtransport"
webtransportEchoProtocolDraft15 = "webtransport-h3"
)
// webtransportEchoWriter is the naked HTTP/3 response-writer shape that
// webtransport.Server.Upgrade type-asserts on.
@ -137,7 +142,7 @@ func (h *WebTransportEcho) echoStreams(session *webtransport.Session) {
func isWebTransportEchoUpgrade(r *http.Request) bool {
return r.ProtoMajor == 3 &&
r.Method == http.MethodConnect &&
r.Proto == webtransportEchoProtocol
(r.Proto == webtransportEchoProtocol || r.Proto == webtransportEchoProtocolDraft15)
}
// Interface guards.
@ -157,6 +162,7 @@ func TestIsWebTransportEchoUpgrade(t *testing.T) {
want bool
}{
{"h3 connect webtransport", "webtransport", 3, http.MethodConnect, true},
{"h3 connect webtransport-h3", "webtransport-h3", 3, http.MethodConnect, true},
{"h3 connect websocket", "websocket", 3, http.MethodConnect, false},
{"h2 connect webtransport", "webtransport", 2, http.MethodConnect, false},
{"h3 GET", "HTTP/3.0", 3, http.MethodGet, false},

4
go.mod
View file

@ -21,8 +21,8 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0
github.com/mholt/acmez/v3 v3.1.6
github.com/prometheus/client_golang v1.23.2
github.com/quic-go/quic-go v0.59.1
github.com/quic-go/webtransport-go v0.10.0
github.com/quic-go/quic-go v0.60.0
github.com/quic-go/webtransport-go v0.11.0
github.com/smallstep/certificates v0.30.2
github.com/smallstep/nosql v0.8.0
github.com/smallstep/truststore v0.13.0

10
go.sum
View file

@ -287,12 +287,14 @@ github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEo
github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI=
github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow=
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
github.com/quic-go/webtransport-go v0.11.0 h1:3afiZq7MHv3gmKCbMwZ8D5M1u0y/1RdONN9KlWp32J0=
github.com/quic-go/webtransport-go v0.11.0/go.mod h1:SHgEzUFVyj+9WUSuGB1P6Zd351Pww2leWV3SwlTovkA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=

View file

@ -30,10 +30,15 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
// webtransportProtocol is the :protocol pseudo-header value sent by a
// client that wants to establish a WebTransport session over an HTTP/3
// Extended CONNECT.
const webtransportProtocol = "webtransport"
// webtransportProtocol values are the :protocol pseudo-header tokens sent
// by a client that wants to establish a WebTransport session over an HTTP/3
// Extended CONNECT. draft-15 (webtransport-go v0.11.0+) uses "webtransport-h3";
// older draft clients use the legacy "webtransport" token, which the
// webtransport-go server still accepts. Detect both.
const (
webtransportProtocol = "webtransport"
webtransportProtocolDraft15 = "webtransport-h3"
)
// webtransportWriter is the naked HTTP/3 response-writer shape that
// webtransport.Server.Upgrade type-asserts on. Caddy's
@ -49,7 +54,8 @@ type webtransportWriter interface {
// CONNECT that requests a WebTransport session. Does not check whether
// WebTransport proxying is configured; callers gate on Handler state.
func isWebTransportExtendedConnect(r *http.Request) bool {
return r.ProtoMajor == 3 && r.Method == http.MethodConnect && r.Proto == webtransportProtocol
return r.ProtoMajor == 3 && r.Method == http.MethodConnect &&
(r.Proto == webtransportProtocol || r.Proto == webtransportProtocolDraft15)
}
// webTransportTransport is implemented by reverse-proxy transports that