@marcel91200 / caddy Mirror · read-only / commits / 7fb39ec1
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 19:45 UTC

reverseproxy: Use http1.1 upgrade for websocket for extended connect of http2 and http3 (#7305)

Co-authored-by: WeidiDeng <weidi_deng@icloud.com>

Anthony Biondo committed Oct 15, 2025 at 22:20 UTC 7fb39ec1e56751c82950fa8a35a189ea39dfa005
2 files changed +9 -5
modules/caddyhttp/reverseproxy/reverseproxy.go
+7 -3
@@ -409,12 +409,16 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
409 return caddyhttp.Error(http.StatusInternalServerError,
410 fmt.Errorf("preparing request for upstream round-trip: %v", err))
411 }
412 - // websocket over http2, assuming backend doesn't support this, the request will be modified to http1.1 upgrade
412 +
413 + // websocket over http2 or http3 if extended connect is enabled, assuming backend doesn't support this, the request will be modified to http1.1 upgrade
414 + // Both use the same upgrade mechanism: server advertizes extended connect support, and client sends the pseudo header :protocol in a CONNECT request
415 + // The quic-go http3 implementation also puts :protocol in r.Proto for CONNECT requests (quic-go/http3/headers.go@70-72,185,203)
416 // TODO: once we can reliably detect backend support this, it can be removed for those backends
414 - if r.ProtoMajor == 2 && r.Method == http.MethodConnect && r.Header.Get(":protocol") == "websocket" {
417 + if (r.ProtoMajor == 2 && r.Method == http.MethodConnect && r.Header.Get(":protocol") == "websocket") ||
418 + (r.ProtoMajor == 3 && r.Method == http.MethodConnect && r.Proto == "websocket") {
419 clonedReq.Header.Del(":protocol")
420 // keep the body for later use. http1.1 upgrade uses http.NoBody
417 - caddyhttp.SetVar(clonedReq.Context(), "h2_websocket_body", clonedReq.Body)
421 + caddyhttp.SetVar(clonedReq.Context(), "extended_connect_websocket_body", clonedReq.Body)
422 clonedReq.Body = http.NoBody
423 clonedReq.Method = http.MethodGet
424 clonedReq.Header.Set("Upgrade", "websocket")
modules/caddyhttp/reverseproxy/streaming.go
+2 -2
@@ -94,9 +94,9 @@ func (h *Handler) handleUpgradeResponse(logger *zap.Logger, wg *sync.WaitGroup,
94 conn io.ReadWriteCloser
95 brw *bufio.ReadWriter
96 )
97 - // websocket over http2, assuming backend doesn't support this, the request will be modified to http1.1 upgrade
97 + // websocket over http2 or http3 if extended connect is enabled, assuming backend doesn't support this, the request will be modified to http1.1 upgrade
98 // TODO: once we can reliably detect backend support this, it can be removed for those backends
99 - if body, ok := caddyhttp.GetVar(req.Context(), "h2_websocket_body").(io.ReadCloser); ok {
99 + if body, ok := caddyhttp.GetVar(req.Context(), "extended_connect_websocket_body").(io.ReadCloser); ok {
100 req.Body = body
101 rw.Header().Del("Upgrade")
102 rw.Header().Del("Connection")