123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- // 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 err != nil {
- return nil, fmt.Errorf("could not read from the buffer: %s", err)
- }
- 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 its
- // 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
- }
|