golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/http2/h2c/h2c.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package h2c implements the unencrypted "h2c" form of HTTP/2. 6 // 7 // The h2c protocol is the non-TLS version of HTTP/2 which is not available from 8 // net/http or golang.org/x/net/http2. 9 package h2c 10 11 import ( 12 "bufio" 13 "bytes" 14 "encoding/base64" 15 "errors" 16 "fmt" 17 "io" 18 "log" 19 "net" 20 "net/http" 21 "net/textproto" 22 "os" 23 "strings" 24 25 "golang.org/x/net/http/httpguts" 26 "golang.org/x/net/http2" 27 ) 28 29 var ( 30 http2VerboseLogs bool 31 ) 32 33 func init() { 34 e := os.Getenv("GODEBUG") 35 if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") { 36 http2VerboseLogs = true 37 } 38 } 39 40 // h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic 41 // that should be h2c traffic. There are two ways to begin a h2c connection 42 // (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this 43 // works by starting an h2c connection with a string of bytes that is valid 44 // HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to 45 // h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to 46 // h2c. When either of those situations occur we hijack the HTTP/1 connection, 47 // convert it to an HTTP/2 connection and pass the net.Conn to http2.ServeConn. 48 type h2cHandler struct { 49 Handler http.Handler 50 s *http2.Server 51 } 52 53 // NewHandler returns an http.Handler that wraps h, intercepting any h2c 54 // traffic. If a request is an h2c connection, it's hijacked and redirected to 55 // s.ServeConn. Otherwise the returned Handler just forwards requests to h. This 56 // works because h2c is designed to be parseable as valid HTTP/1, but ignored by 57 // any HTTP server that does not handle h2c. Therefore we leverage the HTTP/1 58 // compatible parts of the Go http library to parse and recognize h2c requests. 59 // Once a request is recognized as h2c, we hijack the connection and convert it 60 // to an HTTP/2 connection which is understandable to s.ServeConn. (s.ServeConn 61 // understands HTTP/2 except for the h2c part of it.) 62 // 63 // The first request on an h2c connection is read entirely into memory before 64 // the Handler is called. To limit the memory consumed by this request, wrap 65 // the result of NewHandler in an http.MaxBytesHandler. 66 func NewHandler(h http.Handler, s *http2.Server) http.Handler { 67 return &h2cHandler{ 68 Handler: h, 69 s: s, 70 } 71 } 72 73 // extractServer extracts existing http.Server instance from http.Request or create an empty http.Server 74 func extractServer(r *http.Request) *http.Server { 75 server, ok := r.Context().Value(http.ServerContextKey).(*http.Server) 76 if ok { 77 return server 78 } 79 return new(http.Server) 80 } 81 82 // ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler. 83 func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 84 // Handle h2c with prior knowledge (RFC 7540 Section 3.4) 85 if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" { 86 if http2VerboseLogs { 87 log.Print("h2c: attempting h2c with prior knowledge.") 88 } 89 conn, err := initH2CWithPriorKnowledge(w) 90 if err != nil { 91 if http2VerboseLogs { 92 log.Printf("h2c: error h2c with prior knowledge: %v", err) 93 } 94 return 95 } 96 defer conn.Close() 97 s.s.ServeConn(conn, &http2.ServeConnOpts{ 98 Context: r.Context(), 99 BaseConfig: extractServer(r), 100 Handler: s.Handler, 101 SawClientPreface: true, 102 }) 103 return 104 } 105 // Handle Upgrade to h2c (RFC 7540 Section 3.2) 106 if isH2CUpgrade(r.Header) { 107 conn, settings, err := h2cUpgrade(w, r) 108 if err != nil { 109 if http2VerboseLogs { 110 log.Printf("h2c: error h2c upgrade: %v", err) 111 } 112 w.WriteHeader(http.StatusInternalServerError) 113 return 114 } 115 defer conn.Close() 116 s.s.ServeConn(conn, &http2.ServeConnOpts{ 117 Context: r.Context(), 118 BaseConfig: extractServer(r), 119 Handler: s.Handler, 120 UpgradeRequest: r, 121 Settings: settings, 122 }) 123 return 124 } 125 s.Handler.ServeHTTP(w, r) 126 return 127 } 128 129 // initH2CWithPriorKnowledge implements creating a h2c connection with prior 130 // knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn. 131 // All we have to do is look for the client preface that is suppose to be part 132 // of the body, and reforward the client preface on the net.Conn this function 133 // creates. 134 func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) { 135 hijacker, ok := w.(http.Hijacker) 136 if !ok { 137 return nil, errors.New("h2c: connection does not support Hijack") 138 } 139 conn, rw, err := hijacker.Hijack() 140 if err != nil { 141 return nil, err 142 } 143 144 const expectedBody = "SM\r\n\r\n" 145 146 buf := make([]byte, len(expectedBody)) 147 n, err := io.ReadFull(rw, buf) 148 if err != nil { 149 return nil, fmt.Errorf("h2c: error reading client preface: %s", err) 150 } 151 152 if string(buf[:n]) == expectedBody { 153 return newBufConn(conn, rw), nil 154 } 155 156 conn.Close() 157 return nil, errors.New("h2c: invalid client preface") 158 } 159 160 // h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2). 161 func h2cUpgrade(w http.ResponseWriter, r *http.Request) (_ net.Conn, settings []byte, err error) { 162 settings, err = getH2Settings(r.Header) 163 if err != nil { 164 return nil, nil, err 165 } 166 hijacker, ok := w.(http.Hijacker) 167 if !ok { 168 return nil, nil, errors.New("h2c: connection does not support Hijack") 169 } 170 171 body, err := io.ReadAll(r.Body) 172 if err != nil { 173 return nil, nil, err 174 } 175 r.Body = io.NopCloser(bytes.NewBuffer(body)) 176 177 conn, rw, err := hijacker.Hijack() 178 if err != nil { 179 return nil, nil, err 180 } 181 182 rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" + 183 "Connection: Upgrade\r\n" + 184 "Upgrade: h2c\r\n\r\n")) 185 return newBufConn(conn, rw), settings, nil 186 } 187 188 // isH2CUpgrade returns true if the header properly request an upgrade to h2c 189 // as specified by Section 3.2. 190 func isH2CUpgrade(h http.Header) bool { 191 return httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Upgrade")], "h2c") && 192 httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Connection")], "HTTP2-Settings") 193 } 194 195 // getH2Settings returns the settings in the HTTP2-Settings header. 196 func getH2Settings(h http.Header) ([]byte, error) { 197 vals, ok := h[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")] 198 if !ok { 199 return nil, errors.New("missing HTTP2-Settings header") 200 } 201 if len(vals) != 1 { 202 return nil, fmt.Errorf("expected 1 HTTP2-Settings. Got: %v", vals) 203 } 204 settings, err := base64.RawURLEncoding.DecodeString(vals[0]) 205 if err != nil { 206 return nil, err 207 } 208 return settings, nil 209 } 210 211 func newBufConn(conn net.Conn, rw *bufio.ReadWriter) net.Conn { 212 rw.Flush() 213 if rw.Reader.Buffered() == 0 { 214 // If there's no buffered data to be read, 215 // we can just discard the bufio.ReadWriter. 216 return conn 217 } 218 return &bufConn{conn, rw.Reader} 219 } 220 221 // bufConn wraps a net.Conn, but reads drain the bufio.Reader first. 222 type bufConn struct { 223 net.Conn 224 *bufio.Reader 225 } 226 227 func (c *bufConn) Read(p []byte) (int, error) { 228 if c.Reader == nil { 229 return c.Conn.Read(p) 230 } 231 n := c.Reader.Buffered() 232 if n == 0 { 233 c.Reader = nil 234 return c.Conn.Read(p) 235 } 236 if n < len(p) { 237 p = p[:n] 238 } 239 return c.Reader.Read(p) 240 }