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  }