From 68774999b97206f4ca2aa7408dc3546af1471664 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 12:03:30 -0500 Subject: [PATCH 1/6] drop use of old h2c package --- network/h2c.go | 69 ----------------------------------- network/transports.go | 85 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 83 deletions(-) delete mode 100644 network/h2c.go diff --git a/network/h2c.go b/network/h2c.go deleted file mode 100644 index d0608be722..0000000000 --- a/network/h2c.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2019 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package network - -import ( - "context" - "crypto/tls" - "net" - "net/http" - "time" - - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -// NewServer returns a new HTTP Server with HTTP2 handler. -func NewServer(addr string, h http.Handler) *http.Server { - h1s := &http.Server{ - Addr: addr, - Handler: h2c.NewHandler(h, &http2.Server{}), - ReadHeaderTimeout: time.Minute, // https://medium.com/a-journey-with-go/go-understand-and-mitigate-slowloris-attack-711c1b1403f6 - } - - return h1s -} - -// NewH2CTransport constructs a new H2C transport. -// That transport will reroute all HTTPS traffic to HTTP. This is -// to explicitly allow h2c (http2 without TLS) transport. -// See https://github.com/golang/go/issues/14141 for more details. -func NewH2CTransport() http.RoundTripper { - return newH2CTransport(false) -} - -func newH2CTransport(disableCompression bool) http.RoundTripper { - return &http2.Transport{ - AllowHTTP: true, - DisableCompression: disableCompression, - DialTLS: func(netw, addr string, _ *tls.Config) (net.Conn, error) { - return DialWithBackOff(context.Background(), - netw, addr) - }, - } -} - -// newH2Transport constructs a neew H2 transport. That transport will handles HTTPS traffic -// with TLS config. -func newH2Transport(disableCompression bool, tlsContext DialTLSContextFunc) http.RoundTripper { - return &http2.Transport{ - DisableCompression: disableCompression, - DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { - return tlsContext(ctx, network, addr) - }, - } -} diff --git a/network/transports.go b/network/transports.go index ce8a72b4eb..21a86f119f 100644 --- a/network/transports.go +++ b/network/transports.go @@ -117,7 +117,15 @@ func dialBackOffHelper(ctx context.Context, network, address string, bo wait.Bac return nil, fmt.Errorf("%w %s after %.2fs", ErrTimeoutDialing, address, elapsed.Seconds()) } -func newHTTPTransport(disableKeepAlives, disableCompression bool, maxIdle, maxIdlePerHost int) http.RoundTripper { +func newHTTPTransport( + disableKeepAlives, + disableCompression bool, + maxIdle, + maxIdlePerHost int, +) *http.Transport { + var protocols http.Protocols + protocols.SetHTTP1(true) + transport := http.DefaultTransport.(*http.Transport).Clone() transport.DialContext = DialWithBackOff transport.DisableKeepAlives = disableKeepAlives @@ -125,12 +133,22 @@ func newHTTPTransport(disableKeepAlives, disableCompression bool, maxIdle, maxId transport.MaxIdleConnsPerHost = maxIdlePerHost transport.ForceAttemptHTTP2 = false transport.DisableCompression = disableCompression + transport.Protocols = &protocols return transport } type DialTLSContextFunc func(ctx context.Context, network, addr string) (net.Conn, error) -func newHTTPSTransport(disableKeepAlives, disableCompression bool, maxIdle, maxIdlePerHost int, tlsContext DialTLSContextFunc) http.RoundTripper { +func newHTTPSTransport( + disableKeepAlives, + disableCompression bool, + maxIdle, + maxIdlePerHost int, + tlsContext DialTLSContextFunc, +) *http.Transport { + var protocols http.Protocols + protocols.SetHTTP1(true) + transport := http.DefaultTransport.(*http.Transport).Clone() transport.DisableKeepAlives = disableKeepAlives transport.MaxIdleConns = maxIdle @@ -145,33 +163,72 @@ func newHTTPSTransport(disableKeepAlives, disableCompression bool, maxIdle, maxI // NewProberTransport creates a RoundTripper that is useful for probing, // since it will not cache connections. func NewProberTransport() http.RoundTripper { - return newAutoTransport( - newHTTPTransport(true /*disable keep-alives*/, false /*disable auto-compression*/, 0, 0 /*no caching*/), - NewH2CTransport()) + http := newHTTPTransport( + true, /*disable keep-alives*/ + false, /*disable auto-compression*/ + 0, /*max idle*/ + 0, /*no caching*/ + ) + + // h2 prior knowledge + h2 := http.Clone() + h2.Protocols.SetHTTP1(false) + h2.Protocols.SetUnencryptedHTTP2(true) + + return newAutoTransport(http, h2) } // NewProxyAutoTLSTransport is same with NewProxyAutoTransport but it has DialTLSContextFunc to create HTTPS request. func NewProxyAutoTLSTransport(maxIdle, maxIdlePerHost int, tlsContext DialTLSContextFunc) http.RoundTripper { - return newAutoTransport( - newHTTPSTransport(false /*disable keep-alives*/, true /*disable auto-compression*/, maxIdle, maxIdlePerHost, tlsContext), - newH2Transport(true /*disable auto-compression*/, tlsContext)) + https := newHTTPSTransport( + false, /*disable keep-alives*/ + true, /*disable auto-compression*/ + maxIdle, + maxIdlePerHost, + tlsContext, + ) + + h2 := https.Clone() + h2.Protocols.SetHTTP1(false) + h2.Protocols.SetHTTP2(true) + h2.Protocols.SetUnencryptedHTTP2(true) + + return newAutoTransport(https, h2) } // NewAutoTransport creates a RoundTripper that can use appropriate transport // based on the request's HTTP version. func NewAutoTransport(maxIdle, maxIdlePerHost int) http.RoundTripper { - return newAutoTransport( - newHTTPTransport(false /*disable keep-alives*/, false /*disable auto-compression*/, maxIdle, maxIdlePerHost), - newH2CTransport(false /*disable auto-compression*/)) + http := newHTTPTransport( + false, /*disable keep-alives*/ + false, /*disable auto-compression*/ + maxIdle, + maxIdlePerHost, + ) + + h2 := http.Clone() + h2.Protocols.SetHTTP1(false) + h2.Protocols.SetUnencryptedHTTP2(true) + + return newAutoTransport(http, h2) } // NewProxyAutoTransport creates a RoundTripper suitable for use by a reverse // proxy. The returned transport uses HTTP or H2C based on the request's HTTP // version. The transport has DisableCompression set to true. func NewProxyAutoTransport(maxIdle, maxIdlePerHost int) http.RoundTripper { - return newAutoTransport( - newHTTPTransport(false /*disable keep-alives*/, true /*disable auto-compression*/, maxIdle, maxIdlePerHost), - newH2CTransport(true /*disable auto-compression*/)) + http := newHTTPTransport( + false, /*disable keep-alives*/ + true, /*disable auto-compression*/ + maxIdle, + maxIdlePerHost, + ) + + h2 := http.Clone() + h2.Protocols.SetHTTP1(false) + h2.Protocols.SetUnencryptedHTTP2(true) + + return newAutoTransport(http, h2) } // AutoTransport uses h2c for HTTP2 requests and falls back to `http.DefaultTransport` for all others From 374623c37191903cfeed6ecba9b4dd6c3c845744 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 12:06:19 -0500 Subject: [PATCH 2/6] run ./hack/update-dep.sh --- go.mod | 2 +- vendor/golang.org/x/net/http2/h2c/h2c.go | 234 ----------------------- vendor/modules.txt | 1 - 3 files changed, 1 insertion(+), 236 deletions(-) delete mode 100644 vendor/golang.org/x/net/http2/h2c/h2c.go diff --git a/go.mod b/go.mod index 429639a0bf..07b12e41f0 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,6 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 go.uber.org/automaxprocs v1.6.0 go.uber.org/zap v1.27.1 - golang.org/x/net v0.47.0 golang.org/x/sync v0.18.0 golang.org/x/tools v0.39.0 gomodules.xyz/jsonpatch/v2 v2.5.0 @@ -89,6 +88,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect diff --git a/vendor/golang.org/x/net/http2/h2c/h2c.go b/vendor/golang.org/x/net/http2/h2c/h2c.go deleted file mode 100644 index 19e94791df..0000000000 --- a/vendor/golang.org/x/net/http2/h2c/h2c.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package h2c implements the unencrypted "h2c" form of HTTP/2. -// -// The h2c protocol is the non-TLS version of HTTP/2 which is not available from -// net/http or golang.org/x/net/http2. -package h2c - -import ( - "bufio" - "bytes" - "encoding/base64" - "errors" - "fmt" - "io" - "log" - "net" - "net/http" - "net/textproto" - "os" - "strings" - - "golang.org/x/net/http/httpguts" - "golang.org/x/net/http2" -) - -var ( - http2VerboseLogs bool -) - -func init() { - e := os.Getenv("GODEBUG") - if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") { - http2VerboseLogs = true - } -} - -// h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic -// that should be h2c traffic. There are two ways to begin a h2c connection -// (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this -// works by starting an h2c connection with a string of bytes that is valid -// HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to -// h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to -// h2c. When either of those situations occur we hijack the HTTP/1 connection, -// convert it to an HTTP/2 connection and pass the net.Conn to http2.ServeConn. -type h2cHandler struct { - Handler http.Handler - s *http2.Server -} - -// NewHandler returns an http.Handler that wraps h, intercepting any h2c -// traffic. If a request is an h2c connection, it's hijacked and redirected to -// s.ServeConn. Otherwise the returned Handler just forwards requests to h. This -// works because h2c is designed to be parseable as valid HTTP/1, but ignored by -// any HTTP server that does not handle h2c. Therefore we leverage the HTTP/1 -// compatible parts of the Go http library to parse and recognize h2c requests. -// Once a request is recognized as h2c, we hijack the connection and convert it -// to an HTTP/2 connection which is understandable to s.ServeConn. (s.ServeConn -// understands HTTP/2 except for the h2c part of it.) -// -// The first request on an h2c connection is read entirely into memory before -// the Handler is called. To limit the memory consumed by this request, wrap -// the result of NewHandler in an http.MaxBytesHandler. -func NewHandler(h http.Handler, s *http2.Server) http.Handler { - return &h2cHandler{ - Handler: h, - s: s, - } -} - -// extractServer extracts existing http.Server instance from http.Request or create an empty http.Server -func extractServer(r *http.Request) *http.Server { - server, ok := r.Context().Value(http.ServerContextKey).(*http.Server) - if ok { - return server - } - return new(http.Server) -} - -// ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler. -func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // Handle h2c with prior knowledge (RFC 7540 Section 3.4) - if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" { - if http2VerboseLogs { - log.Print("h2c: attempting h2c with prior knowledge.") - } - conn, err := initH2CWithPriorKnowledge(w) - if err != nil { - if http2VerboseLogs { - log.Printf("h2c: error h2c with prior knowledge: %v", err) - } - return - } - defer conn.Close() - s.s.ServeConn(conn, &http2.ServeConnOpts{ - Context: r.Context(), - BaseConfig: extractServer(r), - Handler: s.Handler, - SawClientPreface: true, - }) - return - } - // Handle Upgrade to h2c (RFC 7540 Section 3.2) - if isH2CUpgrade(r.Header) { - conn, settings, err := h2cUpgrade(w, r) - if err != nil { - if http2VerboseLogs { - log.Printf("h2c: error h2c upgrade: %v", err) - } - w.WriteHeader(http.StatusInternalServerError) - return - } - defer conn.Close() - s.s.ServeConn(conn, &http2.ServeConnOpts{ - Context: r.Context(), - BaseConfig: extractServer(r), - Handler: s.Handler, - UpgradeRequest: r, - Settings: settings, - }) - return - } - s.Handler.ServeHTTP(w, r) - return -} - -// initH2CWithPriorKnowledge implements creating a h2c connection with prior -// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn. -// All we have to do is look for the client preface that is suppose to be part -// of the body, and reforward the client preface on the net.Conn this function -// creates. -func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) { - rc := http.NewResponseController(w) - conn, rw, err := rc.Hijack() - if err != nil { - return nil, err - } - - const expectedBody = "SM\r\n\r\n" - - buf := make([]byte, len(expectedBody)) - n, err := io.ReadFull(rw, buf) - if err != nil { - return nil, fmt.Errorf("h2c: error reading client preface: %s", err) - } - - if string(buf[:n]) == expectedBody { - return newBufConn(conn, rw), nil - } - - conn.Close() - return nil, errors.New("h2c: invalid client preface") -} - -// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2). -func h2cUpgrade(w http.ResponseWriter, r *http.Request) (_ net.Conn, settings []byte, err error) { - settings, err = getH2Settings(r.Header) - if err != nil { - return nil, nil, err - } - - body, err := io.ReadAll(r.Body) - if err != nil { - return nil, nil, err - } - r.Body = io.NopCloser(bytes.NewBuffer(body)) - - rc := http.NewResponseController(w) - conn, rw, err := rc.Hijack() - if err != nil { - return nil, nil, err - } - - rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" + - "Connection: Upgrade\r\n" + - "Upgrade: h2c\r\n\r\n")) - return newBufConn(conn, rw), settings, nil -} - -// isH2CUpgrade returns true if the header properly request an upgrade to h2c -// as specified by Section 3.2. -func isH2CUpgrade(h http.Header) bool { - return httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Upgrade")], "h2c") && - httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Connection")], "HTTP2-Settings") -} - -// getH2Settings returns the settings in the HTTP2-Settings header. -func getH2Settings(h http.Header) ([]byte, error) { - vals, ok := h[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")] - if !ok { - return nil, errors.New("missing HTTP2-Settings header") - } - if len(vals) != 1 { - return nil, fmt.Errorf("expected 1 HTTP2-Settings. Got: %v", vals) - } - settings, err := base64.RawURLEncoding.DecodeString(vals[0]) - if err != nil { - return nil, err - } - return settings, nil -} - -func newBufConn(conn net.Conn, rw *bufio.ReadWriter) net.Conn { - rw.Flush() - if rw.Reader.Buffered() == 0 { - // If there's no buffered data to be read, - // we can just discard the bufio.ReadWriter. - return conn - } - return &bufConn{conn, rw.Reader} -} - -// bufConn wraps a net.Conn, but reads drain the bufio.Reader first. -type bufConn struct { - net.Conn - *bufio.Reader -} - -func (c *bufConn) Read(p []byte) (int, error) { - if c.Reader == nil { - return c.Conn.Read(p) - } - n := c.Reader.Buffered() - if n == 0 { - c.Reader = nil - return c.Conn.Read(p) - } - if n < len(p) { - p = p[:n] - } - return c.Reader.Read(p) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index ed2f4defa2..28e2fd2474 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -305,7 +305,6 @@ golang.org/x/mod/semver ## explicit; go 1.24.0 golang.org/x/net/http/httpguts golang.org/x/net/http2 -golang.org/x/net/http2/h2c golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/httpcommon From c2e7b3e873d43a7200148b71d803c70aee74c583 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 12:17:53 -0500 Subject: [PATCH 3/6] add NewServer back --- network/server.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 network/server.go diff --git a/network/server.go b/network/server.go new file mode 100644 index 0000000000..4b142fd74f --- /dev/null +++ b/network/server.go @@ -0,0 +1,37 @@ +/* +Copyright 2025 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package network + +import ( + "net/http" + "time" +) + +func NewServer(addr string, h http.Handler) *http.Server { + var protocols http.Protocols + protocols.SetHTTP1(true) + protocols.SetUnencryptedHTTP2(true) + + return &http.Server{ + Addr: addr, + Handler: h, + Protocols: &protocols, + + // https://medium.com/a-journey-with-go/go-understand-and-mitigate-slowloris-attack-711c1b1403f6 + ReadHeaderTimeout: time.Minute, + } +} From 9c9a3972b119a628c9dcbc9e06cf75918c94ddbf Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 12:23:37 -0500 Subject: [PATCH 4/6] fix license --- network/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/server.go b/network/server.go index 4b142fd74f..fbf68ced1e 100644 --- a/network/server.go +++ b/network/server.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, From 779b85906b731cfb7b102fac649e3b17665b6589 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 13:18:29 -0500 Subject: [PATCH 5/6] fix nil pointer --- network/transports.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/network/transports.go b/network/transports.go index 21a86f119f..27ce287fd4 100644 --- a/network/transports.go +++ b/network/transports.go @@ -134,6 +134,7 @@ func newHTTPTransport( transport.ForceAttemptHTTP2 = false transport.DisableCompression = disableCompression transport.Protocols = &protocols + return transport } @@ -156,6 +157,7 @@ func newHTTPSTransport( transport.ForceAttemptHTTP2 = false transport.DisableCompression = disableCompression transport.DialTLSContext = tlsContext + transport.Protocols = &protocols return transport } From b24adc693a81e6443b092818abbdc9264c8efa6b Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Sun, 30 Nov 2025 16:31:49 -0500 Subject: [PATCH 6/6] enable HTTP2 on the server --- network/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/network/server.go b/network/server.go index fbf68ced1e..9f57982a81 100644 --- a/network/server.go +++ b/network/server.go @@ -24,6 +24,7 @@ import ( func NewServer(addr string, h http.Handler) *http.Server { var protocols http.Protocols protocols.SetHTTP1(true) + protocols.SetHTTP2(true) protocols.SetUnencryptedHTTP2(true) return &http.Server{