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/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/server.go b/network/server.go new file mode 100644 index 0000000000..9f57982a81 --- /dev/null +++ b/network/server.go @@ -0,0 +1,38 @@ +/* +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 + + 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 ( + "net/http" + "time" +) + +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{ + Addr: addr, + Handler: h, + Protocols: &protocols, + + // https://medium.com/a-journey-with-go/go-understand-and-mitigate-slowloris-attack-711c1b1403f6 + ReadHeaderTimeout: time.Minute, + } +} diff --git a/network/transports.go b/network/transports.go index ce8a72b4eb..27ce287fd4 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,23 @@ 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 @@ -138,6 +157,7 @@ func newHTTPSTransport(disableKeepAlives, disableCompression bool, maxIdle, maxI transport.ForceAttemptHTTP2 = false transport.DisableCompression = disableCompression transport.DialTLSContext = tlsContext + transport.Protocols = &protocols return transport } @@ -145,33 +165,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 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