| // 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" |
| "encoding/binary" |
| "errors" |
| "fmt" |
| "io" |
| "log" |
| "net" |
| "net/http" |
| "net/textproto" |
| "os" |
| "strings" |
| |
| "golang.org/x/net/http/httpguts" |
| "golang.org/x/net/http2" |
| "golang.org/x/net/http2/hpack" |
| ) |
| |
| 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 a 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.) |
| func NewHandler(h http.Handler, s *http2.Server) http.Handler { |
| return &h2cHandler{ |
| Handler: h, |
| s: s, |
| } |
| } |
| |
| // 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{Handler: s.Handler}) |
| return |
| } |
| // Handle Upgrade to h2c (RFC 7540 Section 3.2) |
| if conn, err := h2cUpgrade(w, r); err == nil { |
| defer conn.Close() |
| |
| s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler}) |
| 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) { |
| hijacker, ok := w.(http.Hijacker) |
| if !ok { |
| panic("Hijack not supported.") |
| } |
| conn, rw, err := hijacker.Hijack() |
| if err != nil { |
| panic(fmt.Sprintf("Hijack failed: %v", err)) |
| } |
| |
| const expectedBody = "SM\r\n\r\n" |
| |
| buf := make([]byte, len(expectedBody)) |
| n, err := io.ReadFull(rw, buf) |
| |
| if string(buf[:n]) == expectedBody { |
| c := &rwConn{ |
| Conn: conn, |
| Reader: io.MultiReader(strings.NewReader(http2.ClientPreface), rw), |
| BufWriter: rw.Writer, |
| } |
| return c, nil |
| } |
| |
| conn.Close() |
| if http2VerboseLogs { |
| log.Printf( |
| "h2c: missing the request body portion of the client preface. Wanted: %v Got: %v", |
| []byte(expectedBody), |
| buf[0:n], |
| ) |
| } |
| return nil, errors.New("invalid client preface") |
| } |
| |
| // drainClientPreface reads a single instance of the HTTP/2 client preface from |
| // the supplied reader. |
| func drainClientPreface(r io.Reader) error { |
| var buf bytes.Buffer |
| prefaceLen := int64(len(http2.ClientPreface)) |
| n, err := io.CopyN(&buf, r, prefaceLen) |
| if err != nil { |
| return err |
| } |
| if n != prefaceLen || buf.String() != http2.ClientPreface { |
| return fmt.Errorf("Client never sent: %s", http2.ClientPreface) |
| } |
| return nil |
| } |
| |
| // h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2). |
| func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) { |
| if !isH2CUpgrade(r.Header) { |
| return nil, errors.New("non-conforming h2c headers") |
| } |
| |
| // Initial bytes we put into conn to fool http2 server |
| initBytes, _, err := convertH1ReqToH2(r) |
| if err != nil { |
| return nil, err |
| } |
| |
| hijacker, ok := w.(http.Hijacker) |
| if !ok { |
| return nil, errors.New("hijack not supported.") |
| } |
| conn, rw, err := hijacker.Hijack() |
| if err != nil { |
| return nil, fmt.Errorf("hijack failed: %v", err) |
| } |
| |
| rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" + |
| "Connection: Upgrade\r\n" + |
| "Upgrade: h2c\r\n\r\n")) |
| rw.Flush() |
| |
| // A conforming client will now send an H2 client preface which need to drain |
| // since we already sent this. |
| if err := drainClientPreface(rw); err != nil { |
| return nil, err |
| } |
| |
| c := &rwConn{ |
| Conn: conn, |
| Reader: io.MultiReader(initBytes, rw), |
| BufWriter: newSettingsAckSwallowWriter(rw.Writer), |
| } |
| return c, nil |
| } |
| |
| // convert the data contained in the HTTP/1 upgrade request into the HTTP/2 |
| // version in byte form. |
| func convertH1ReqToH2(r *http.Request) (*bytes.Buffer, []http2.Setting, error) { |
| h2Bytes := bytes.NewBuffer([]byte((http2.ClientPreface))) |
| framer := http2.NewFramer(h2Bytes, nil) |
| settings, err := getH2Settings(r.Header) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| if err := framer.WriteSettings(settings...); err != nil { |
| return nil, nil, err |
| } |
| |
| headerBytes, err := getH2HeaderBytes(r, getMaxHeaderTableSize(settings)) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| maxFrameSize := int(getMaxFrameSize(settings)) |
| needOneHeader := len(headerBytes) < maxFrameSize |
| err = framer.WriteHeaders(http2.HeadersFrameParam{ |
| StreamID: 1, |
| BlockFragment: headerBytes, |
| EndHeaders: needOneHeader, |
| }) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| for i := maxFrameSize; i < len(headerBytes); i += maxFrameSize { |
| if len(headerBytes)-i > maxFrameSize { |
| if err := framer.WriteContinuation(1, |
| false, // endHeaders |
| headerBytes[i:maxFrameSize]); err != nil { |
| return nil, nil, err |
| } |
| } else { |
| if err := framer.WriteContinuation(1, |
| true, // endHeaders |
| headerBytes[i:]); err != nil { |
| return nil, nil, err |
| } |
| } |
| } |
| |
| return h2Bytes, settings, nil |
| } |
| |
| // getMaxFrameSize returns the SETTINGS_MAX_FRAME_SIZE. If not present default |
| // value is 16384 as specified by RFC 7540 Section 6.5.2. |
| func getMaxFrameSize(settings []http2.Setting) uint32 { |
| for _, setting := range settings { |
| if setting.ID == http2.SettingMaxFrameSize { |
| return setting.Val |
| } |
| } |
| return 16384 |
| } |
| |
| // getMaxHeaderTableSize returns the SETTINGS_HEADER_TABLE_SIZE. If not present |
| // default value is 4096 as specified by RFC 7540 Section 6.5.2. |
| func getMaxHeaderTableSize(settings []http2.Setting) uint32 { |
| for _, setting := range settings { |
| if setting.ID == http2.SettingHeaderTableSize { |
| return setting.Val |
| } |
| } |
| return 4096 |
| } |
| |
| // bufWriter is a Writer interface that also has a Flush method. |
| type bufWriter interface { |
| io.Writer |
| Flush() error |
| } |
| |
| // rwConn implements net.Conn but overrides Read and Write so that reads and |
| // writes are forwarded to the provided io.Reader and bufWriter. |
| type rwConn struct { |
| net.Conn |
| io.Reader |
| BufWriter bufWriter |
| } |
| |
| // Read forwards reads to the underlying Reader. |
| func (c *rwConn) Read(p []byte) (int, error) { |
| return c.Reader.Read(p) |
| } |
| |
| // Write forwards writes to the underlying bufWriter and immediately flushes. |
| func (c *rwConn) Write(p []byte) (int, error) { |
| n, err := c.BufWriter.Write(p) |
| if err := c.BufWriter.Flush(); err != nil { |
| return 0, err |
| } |
| return n, err |
| } |
| |
| // settingsAckSwallowWriter is a writer that normally forwards bytes to it's |
| // underlying Writer, but swallows the first SettingsAck frame that it sees. |
| type settingsAckSwallowWriter struct { |
| Writer *bufio.Writer |
| buf []byte |
| didSwallow bool |
| } |
| |
| // newSettingsAckSwallowWriter returns a new settingsAckSwallowWriter. |
| func newSettingsAckSwallowWriter(w *bufio.Writer) *settingsAckSwallowWriter { |
| return &settingsAckSwallowWriter{ |
| Writer: w, |
| buf: make([]byte, 0), |
| didSwallow: false, |
| } |
| } |
| |
| // Write implements io.Writer interface. Normally forwards bytes to w.Writer, |
| // except for the first Settings ACK frame that it sees. |
| func (w *settingsAckSwallowWriter) Write(p []byte) (int, error) { |
| if !w.didSwallow { |
| w.buf = append(w.buf, p...) |
| // Process all the frames we have collected into w.buf |
| for { |
| // Append until we get full frame header which is 9 bytes |
| if len(w.buf) < 9 { |
| break |
| } |
| // Check if we have collected a whole frame. |
| fh, err := http2.ReadFrameHeader(bytes.NewBuffer(w.buf)) |
| if err != nil { |
| // Corrupted frame, fail current Write |
| return 0, err |
| } |
| fSize := fh.Length + 9 |
| if uint32(len(w.buf)) < fSize { |
| // Have not collected whole frame. Stop processing buf, and withold on |
| // forward bytes to w.Writer until we get the full frame. |
| break |
| } |
| |
| // We have now collected a whole frame. |
| if fh.Type == http2.FrameSettings && fh.Flags.Has(http2.FlagSettingsAck) { |
| // If Settings ACK frame, do not forward to underlying writer, remove |
| // bytes from w.buf, and record that we have swallowed Settings Ack |
| // frame. |
| w.didSwallow = true |
| w.buf = w.buf[fSize:] |
| continue |
| } |
| |
| // Not settings ack frame. Forward bytes to w.Writer. |
| if _, err := w.Writer.Write(w.buf[:fSize]); err != nil { |
| // Couldn't forward bytes. Fail current Write. |
| return 0, err |
| } |
| w.buf = w.buf[fSize:] |
| } |
| return len(p), nil |
| } |
| return w.Writer.Write(p) |
| } |
| |
| // Flush calls w.Writer.Flush. |
| func (w *settingsAckSwallowWriter) Flush() error { |
| return w.Writer.Flush() |
| } |
| |
| // 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 []http2.Setting that are encoded in the |
| // HTTP2-Settings header. |
| func getH2Settings(h http.Header) ([]http2.Setting, 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 := decodeSettings(vals[0]) |
| if err != nil { |
| return nil, fmt.Errorf("Invalid HTTP2-Settings: %q", vals[0]) |
| } |
| return settings, nil |
| } |
| |
| // decodeSettings decodes the base64url header value of the HTTP2-Settings |
| // header. RFC 7540 Section 3.2.1. |
| func decodeSettings(headerVal string) ([]http2.Setting, error) { |
| b, err := base64.RawURLEncoding.DecodeString(headerVal) |
| if err != nil { |
| return nil, err |
| } |
| if len(b)%6 != 0 { |
| return nil, err |
| } |
| settings := make([]http2.Setting, 0) |
| for i := 0; i < len(b)/6; i++ { |
| settings = append(settings, http2.Setting{ |
| ID: http2.SettingID(binary.BigEndian.Uint16(b[i*6 : i*6+2])), |
| Val: binary.BigEndian.Uint32(b[i*6+2 : i*6+6]), |
| }) |
| } |
| |
| return settings, nil |
| } |
| |
| // getH2HeaderBytes return the headers in r a []bytes encoded by HPACK. |
| func getH2HeaderBytes(r *http.Request, maxHeaderTableSize uint32) ([]byte, error) { |
| headerBytes := bytes.NewBuffer(nil) |
| hpackEnc := hpack.NewEncoder(headerBytes) |
| hpackEnc.SetMaxDynamicTableSize(maxHeaderTableSize) |
| |
| // Section 8.1.2.3 |
| err := hpackEnc.WriteField(hpack.HeaderField{ |
| Name: ":method", |
| Value: r.Method, |
| }) |
| if err != nil { |
| return nil, err |
| } |
| |
| err = hpackEnc.WriteField(hpack.HeaderField{ |
| Name: ":scheme", |
| Value: "http", |
| }) |
| if err != nil { |
| return nil, err |
| } |
| |
| err = hpackEnc.WriteField(hpack.HeaderField{ |
| Name: ":authority", |
| Value: r.Host, |
| }) |
| if err != nil { |
| return nil, err |
| } |
| |
| path := r.URL.Path |
| if r.URL.RawQuery != "" { |
| path = strings.Join([]string{path, r.URL.RawQuery}, "?") |
| } |
| err = hpackEnc.WriteField(hpack.HeaderField{ |
| Name: ":path", |
| Value: path, |
| }) |
| if err != nil { |
| return nil, err |
| } |
| |
| // TODO Implement Section 8.3 |
| |
| for header, values := range r.Header { |
| // Skip non h2 headers |
| if isNonH2Header(header) { |
| continue |
| } |
| for _, v := range values { |
| err := hpackEnc.WriteField(hpack.HeaderField{ |
| Name: strings.ToLower(header), |
| Value: v, |
| }) |
| if err != nil { |
| return nil, err |
| } |
| } |
| } |
| return headerBytes.Bytes(), nil |
| } |
| |
| // Connection specific headers listed in RFC 7540 Section 8.1.2.2 that are not |
| // suppose to be transferred to HTTP/2. The Http2-Settings header is skipped |
| // since already use to create the HTTP/2 SETTINGS frame. |
| var nonH2Headers = []string{ |
| "Connection", |
| "Keep-Alive", |
| "Proxy-Connection", |
| "Transfer-Encoding", |
| "Upgrade", |
| "Http2-Settings", |
| } |
| |
| // isNonH2Header returns true if header should not be transferred to HTTP/2. |
| func isNonH2Header(header string) bool { |
| for _, nonH2h := range nonH2Headers { |
| if header == nonH2h { |
| return true |
| } |
| } |
| return false |
| } |