]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: document GODEBUG settings, update bundled http2
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 7 Jan 2016 23:43:52 +0000 (23:43 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Fri, 8 Jan 2016 00:56:28 +0000 (00:56 +0000)
Document the three GODEBUG environment variables in the package doc.

Updates the bundled http2 to x/net git rev 415f1917
for https://golang.org/cl/18372.

Fixes #13611

Change-Id: I3116c5d7de70d3d15242d7198f3758b1fb7d94b9
Reviewed-on: https://go-review.googlesource.com/18373
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/http/doc.go
src/net/http/h2_bundle.go
src/net/http/server.go
src/net/http/transport.go
src/vendor/golang.org/x/net/http2/hpack/hpack.go

index b1216e8dafa5b701a59cd4f167085d1bfcd8553e..4ec8272f62861b7c397278e9479acc99928809b0 100644 (file)
@@ -76,5 +76,20 @@ custom Server:
                MaxHeaderBytes: 1 << 20,
        }
        log.Fatal(s.ListenAndServe())
+
+The http package has transparent support for the HTTP/2 protocol when
+using HTTPS. Programs that must disable HTTP/2 can do so by setting
+Transport.TLSNextProto (for clients) or Server.TLSNextProto (for
+servers) to a non-nil, empty map. Alternatively, the following GODEBUG
+environment variables are currently supported:
+
+       GODEBUG=http2client=0  # disable HTTP/2 client support
+       GODEBUG=http2server=0  # disable HTTP/2 server support
+       GODEBUG=http2debug=1   # enable verbose HTTP/2 debug logs
+       GODEBUG=http2debug=2   # ... even more verbose, with frame dumps
+
+The GODEBUG variables are not covered by Go's API compatibility promise.
+HTTP/2 support was added in Go 1.6. Please report any issues instead of
+disabling HTTP/2 support: https://golang.org/s/http2bug
 */
 package http
index 7e7b494d55cce1c3c2e0b08f612c4632282c257d..69fad37bec7f439d82f0554c1018a4aed13e5773 100644 (file)
@@ -583,6 +583,12 @@ func (h http2FrameHeader) Header() http2FrameHeader { return h }
 func (h http2FrameHeader) String() string {
        var buf bytes.Buffer
        buf.WriteString("[FrameHeader ")
+       h.writeDebug(&buf)
+       buf.WriteByte(']')
+       return buf.String()
+}
+
+func (h http2FrameHeader) writeDebug(buf *bytes.Buffer) {
        buf.WriteString(h.Type.String())
        if h.Flags != 0 {
                buf.WriteString(" flags=")
@@ -599,15 +605,14 @@ func (h http2FrameHeader) String() string {
                        if name != "" {
                                buf.WriteString(name)
                        } else {
-                               fmt.Fprintf(&buf, "0x%x", 1<<i)
+                               fmt.Fprintf(buf, "0x%x", 1<<i)
                        }
                }
        }
        if h.StreamID != 0 {
-               fmt.Fprintf(&buf, " stream=%d", h.StreamID)
+               fmt.Fprintf(buf, " stream=%d", h.StreamID)
        }
-       fmt.Fprintf(&buf, " len=%d]", h.Length)
-       return buf.String()
+       fmt.Fprintf(buf, " len=%d", h.Length)
 }
 
 func (h *http2FrameHeader) checkValid() {
@@ -700,6 +705,9 @@ type http2Framer struct {
        // This is for testing and permits using the Framer to test
        // other HTTP/2 implementations' conformance to the spec.
        AllowIllegalReads bool
+
+       debugFramer    *http2Framer // only use for logging written writes
+       debugFramerBuf *bytes.Buffer
 }
 
 func (f *http2Framer) startWrite(ftype http2FrameType, flags http2Flags, streamID uint32) {
@@ -726,6 +734,10 @@ func (f *http2Framer) endWrite() error {
                byte(length>>16),
                byte(length>>8),
                byte(length))
+       if http2logFrameWrites {
+               f.logWrite()
+       }
+
        n, err := f.w.Write(f.wbuf)
        if err == nil && n != len(f.wbuf) {
                err = io.ErrShortWrite
@@ -733,6 +745,22 @@ func (f *http2Framer) endWrite() error {
        return err
 }
 
+func (f *http2Framer) logWrite() {
+       if f.debugFramer == nil {
+               f.debugFramerBuf = new(bytes.Buffer)
+               f.debugFramer = http2NewFramer(nil, f.debugFramerBuf)
+
+               f.debugFramer.AllowIllegalReads = true
+       }
+       f.debugFramerBuf.Write(f.wbuf)
+       fr, err := f.debugFramer.ReadFrame()
+       if err != nil {
+               log.Printf("http2: Framer %p: failed to decode just-written frame", f)
+               return
+       }
+       log.Printf("http2: Framer %p: wrote %v", f, http2summarizeFrame(fr))
+}
+
 func (f *http2Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) }
 
 func (f *http2Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) }
@@ -1563,6 +1591,49 @@ type http2headersEnder interface {
        HeadersEnded() bool
 }
 
+func http2summarizeFrame(f http2Frame) string {
+       var buf bytes.Buffer
+       f.Header().writeDebug(&buf)
+       switch f := f.(type) {
+       case *http2SettingsFrame:
+               n := 0
+               f.ForeachSetting(func(s http2Setting) error {
+                       n++
+                       if n == 1 {
+                               buf.WriteString(", settings:")
+                       }
+                       fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val)
+                       return nil
+               })
+               if n > 0 {
+                       buf.Truncate(buf.Len() - 1)
+               }
+       case *http2DataFrame:
+               data := f.Data()
+               const max = 256
+               if len(data) > max {
+                       data = data[:max]
+               }
+               fmt.Fprintf(&buf, " data=%q", data)
+               if len(f.Data()) > max {
+                       fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max)
+               }
+       case *http2WindowUpdateFrame:
+               if f.StreamID == 0 {
+                       buf.WriteString(" (conn)")
+               }
+               fmt.Fprintf(&buf, " incr=%v", f.Increment)
+       case *http2PingFrame:
+               fmt.Fprintf(&buf, " ping=%q", f.Data[:])
+       case *http2GoAwayFrame:
+               fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q",
+                       f.LastStreamID, f.ErrCode, f.debugData)
+       case *http2RSTStreamFrame:
+               fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode)
+       }
+       return buf.String()
+}
+
 func http2requestCancel(req *Request) <-chan struct{} { return req.Cancel }
 
 var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
@@ -1784,7 +1855,21 @@ func http2lowerHeader(v string) string {
        return strings.ToLower(v)
 }
 
-var http2VerboseLogs = strings.Contains(os.Getenv("GODEBUG"), "h2debug=1")
+var (
+       http2VerboseLogs    bool
+       http2logFrameWrites bool
+)
+
+func init() {
+       e := os.Getenv("GODEBUG")
+       if strings.Contains(e, "http2debug=1") {
+               http2VerboseLogs = true
+       }
+       if strings.Contains(e, "http2debug=2") {
+               http2VerboseLogs = true
+               http2logFrameWrites = true
+       }
+}
 
 const (
        // ClientPreface is the string that must be sent by new
@@ -2374,7 +2459,7 @@ func http2isBadCipher(cipher uint16) bool {
 }
 
 func (sc *http2serverConn) rejectConn(err http2ErrCode, debug string) {
-       sc.vlogf("REJECTING conn: %v, %s", err, debug)
+       sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
 
        sc.framer.WriteGoAway(0, err, []byte(debug))
        sc.bw.Flush()
@@ -2559,7 +2644,9 @@ func (sc *http2serverConn) condlogf(err error, format string, args ...interface{
 
 func (sc *http2serverConn) onNewHeaderField(f hpack.HeaderField) {
        sc.serveG.check()
-       sc.vlogf("got header field %+v", f)
+       if http2VerboseLogs {
+               sc.vlogf("http2: server decoded %v", f)
+       }
        switch {
        case !http2validHeader(f.Name):
                sc.req.invalidHeader = true
@@ -2605,13 +2692,15 @@ func (sc *http2serverConn) onNewHeaderField(f hpack.HeaderField) {
 func (st *http2stream) onNewTrailerField(f hpack.HeaderField) {
        sc := st.sc
        sc.serveG.check()
-       sc.vlogf("got trailer field %+v", f)
+       if http2VerboseLogs {
+               sc.vlogf("http2: server decoded trailer %v", f)
+       }
        switch {
        case !http2validHeader(f.Name):
-
+               sc.req.invalidHeader = true
                return
        case strings.HasPrefix(f.Name, ":"):
-
+               sc.req.invalidHeader = true
                return
        default:
                key := sc.canonicalHeader(f.Name)
@@ -2624,7 +2713,6 @@ func (st *http2stream) onNewTrailerField(f hpack.HeaderField) {
                        if len(vv) >= tooBig {
                                sc.hpackDecoder.SetEmitEnabled(false)
                        }
-
                }
        }
 }
@@ -2733,7 +2821,9 @@ func (sc *http2serverConn) serve() {
        defer sc.stopShutdownTimer()
        defer close(sc.doneServing)
 
-       sc.vlogf("HTTP/2 connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
+       if http2VerboseLogs {
+               sc.vlogf("http2: server connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
+       }
 
        sc.writeFrame(http2frameWriteMsg{
                write: http2writeSettings{
@@ -2745,7 +2835,7 @@ func (sc *http2serverConn) serve() {
        sc.unackedSettings++
 
        if err := sc.readPreface(); err != nil {
-               sc.condlogf(err, "error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
+               sc.condlogf(err, "http2: server: error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
                return
        }
 
@@ -2808,7 +2898,9 @@ func (sc *http2serverConn) readPreface() error {
                return errors.New("timeout waiting for client preface")
        case err := <-errc:
                if err == nil {
-                       sc.vlogf("client %v said hello", sc.conn.RemoteAddr())
+                       if http2VerboseLogs {
+                               sc.vlogf("http2: server: client %v said hello", sc.conn.RemoteAddr())
+                       }
                }
                return err
        }
@@ -3064,7 +3156,9 @@ func (sc *http2serverConn) processFrameFromReader(res http2readFrameResult) bool
                }
        } else {
                f := res.f
-               sc.vlogf("got %v: %#v", f.Header(), f)
+               if http2VerboseLogs {
+                       sc.vlogf("http2: server read frame %v", http2summarizeFrame(f))
+               }
                err = sc.processFrame(f)
                if err == nil {
                        return true
@@ -3079,14 +3173,14 @@ func (sc *http2serverConn) processFrameFromReader(res http2readFrameResult) bool
                sc.goAway(http2ErrCodeFlowControl)
                return true
        case http2ConnectionError:
-               sc.logf("%v: %v", sc.conn.RemoteAddr(), ev)
+               sc.logf("http2: server connection error from %v: %v", sc.conn.RemoteAddr(), ev)
                sc.goAway(http2ErrCode(ev))
                return true
        default:
                if res.err != nil {
-                       sc.logf("disconnecting; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
+                       sc.logf("http2: server closing client connection; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
                } else {
-                       sc.logf("disconnection due to other error: %v", err)
+                       sc.logf("http2: server closing client connection: %v", err)
                }
                return false
        }
@@ -3123,7 +3217,7 @@ func (sc *http2serverConn) processFrame(f http2Frame) error {
 
                return http2ConnectionError(http2ErrCodeProtocol)
        default:
-               sc.vlogf("Ignoring frame: %v", f.Header())
+               sc.vlogf("http2: server ignoring frame: %v", f.Header())
                return nil
        }
 }
@@ -3219,7 +3313,9 @@ func (sc *http2serverConn) processSetting(s http2Setting) error {
        if err := s.Valid(); err != nil {
                return err
        }
-       sc.vlogf("processing setting %v", s)
+       if http2VerboseLogs {
+               sc.vlogf("http2: server processing setting %v", s)
+       }
        switch s.ID {
        case http2SettingHeaderTableSize:
                sc.headerTableSize = s.Val
@@ -3236,6 +3332,9 @@ func (sc *http2serverConn) processSetting(s http2Setting) error {
                sc.peerMaxHeaderListSize = s.Val
        default:
 
+               if http2VerboseLogs {
+                       sc.vlogf("http2: server ignoring unknown setting %v", s)
+               }
        }
        return nil
 }
@@ -3384,6 +3483,10 @@ func (st *http2stream) processTrailerHeaders(f *http2HeadersFrame) error {
                return http2ConnectionError(http2ErrCodeProtocol)
        }
        st.gotTrailerHeader = true
+       if !f.StreamEnded() {
+               return http2StreamError{st.id, http2ErrCodeProtocol}
+       }
+       sc.resetPendingRequest()
        return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
 }
 
@@ -3449,6 +3552,12 @@ func (st *http2stream) processTrailerHeaderBlockFragment(frag []byte, end bool)
        if !end {
                return nil
        }
+
+       rp := &sc.req
+       if rp.invalidHeader {
+               return http2StreamError{rp.stream.id, http2ErrCodeProtocol}
+       }
+
        err := sc.hpackDecoder.Close()
        st.endStream()
        if err != nil {
@@ -3571,7 +3680,6 @@ func (sc *http2serverConn) newWriterAndRequest() (*http2responseWriter, *Request
                requestURI = rp.authority
        } else {
                var err error
-
                url_, err = url.ParseRequestURI(rp.path)
                if err != nil {
                        return nil, nil, http2StreamError{rp.stream.id, http2ErrCodeProtocol}
@@ -4128,10 +4236,29 @@ type http2Transport struct {
        // uncompressed.
        DisableCompression bool
 
+       // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
+       // send in the initial settings frame. It is how many bytes
+       // of response headers are allow. Unlike the http2 spec, zero here
+       // means to use a default limit (currently 10MB). If you actually
+       // want to advertise an ulimited value to the peer, Transport
+       // interprets the highest possible value here (0xffffffff or 1<<32-1)
+       // to mean no limit.
+       MaxHeaderListSize uint32
+
        connPoolOnce  sync.Once
        connPoolOrDef http2ClientConnPool // non-nil version of ConnPool
 }
 
+func (t *http2Transport) maxHeaderListSize() uint32 {
+       if t.MaxHeaderListSize == 0 {
+               return 10 << 20
+       }
+       if t.MaxHeaderListSize == 0xffffffff {
+               return 0
+       }
+       return t.MaxHeaderListSize
+}
+
 func (t *http2Transport) disableCompression() bool {
        if t.DisableCompression {
                return true
@@ -4309,7 +4436,7 @@ func (t *http2Transport) RoundTripOpt(req *Request, opt http2RoundTripOpt) (*Res
        for {
                cc, err := t.connPool().GetClientConn(req, addr)
                if err != nil {
-                       t.vlogf("failed to get client conn: %v", err)
+                       t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
                        return nil, err
                }
                res, err := cc.RoundTrip(req)
@@ -4397,7 +4524,7 @@ func (t *http2Transport) dialTLSDefault(network, addr string, cfg *tls.Config) (
 
 func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) {
        if http2VerboseLogs {
-               t.vlogf("creating client conn to %v", c.RemoteAddr())
+               t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr())
        }
        if _, err := c.Write(http2clientPreface); err != nil {
                t.vlogf("client preface write error: %v", err)
@@ -4420,6 +4547,7 @@ func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) {
        cc.bw = bufio.NewWriter(http2stickyErrWriter{c, &cc.werr})
        cc.br = bufio.NewReader(c)
        cc.fr = http2NewFramer(cc.bw, cc.br)
+
        cc.henc = hpack.NewEncoder(&cc.hbuf)
 
        type connectionStater interface {
@@ -4430,10 +4558,14 @@ func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) {
                cc.tlsState = &state
        }
 
-       cc.fr.WriteSettings(
+       initialSettings := []http2Setting{
                http2Setting{ID: http2SettingEnablePush, Val: 0},
                http2Setting{ID: http2SettingInitialWindowSize, Val: http2transportDefaultStreamFlow},
-       )
+       }
+       if max := t.maxHeaderListSize(); max != 0 {
+               initialSettings = append(initialSettings, http2Setting{ID: http2SettingMaxHeaderListSize, Val: max})
+       }
+       cc.fr.WriteSettings(initialSettings...)
        cc.fr.WriteWindowUpdate(0, http2transportDefaultConnFlow)
        cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize)
        cc.bw.Flush()
@@ -4870,6 +5002,9 @@ func (cc *http2ClientConn) encodeTrailers(req *Request) []byte {
 }
 
 func (cc *http2ClientConn) writeHeader(name, value string) {
+       if http2VerboseLogs {
+               log.Printf("http2: Transport encoding header %q = %q", name, value)
+       }
        cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
 }
 
@@ -4923,6 +5058,7 @@ type http2clientConnReadLoop struct {
        sawRegHeader         bool  // saw non-pseudo header
        reqMalformed         error // non-nil once known to be malformed
        lastHeaderEndsStream bool
+       headerListSize       int64 // actually uint32, but easier math this way
 }
 
 // readLoop runs in its own goroutine and reads and dispatches frames.
@@ -4931,7 +5067,6 @@ func (cc *http2ClientConn) readLoop() {
                cc:        cc,
                activeRes: make(map[uint32]*http2clientStream),
        }
-
        rl.hdec = hpack.NewDecoder(http2initialHeaderTableSize, rl.onNewHeaderField)
 
        defer rl.cleanup()
@@ -4982,7 +5117,9 @@ func (rl *http2clientConnReadLoop) run() error {
                } else if err != nil {
                        return err
                }
-               cc.vlogf("Transport received %v: %#v", f.Header(), f)
+               if http2VerboseLogs {
+                       cc.vlogf("http2: Transport received %s", http2summarizeFrame(f))
+               }
 
                switch f := f.(type) {
                case *http2HeadersFrame:
@@ -5016,11 +5153,13 @@ func (rl *http2clientConnReadLoop) processHeaders(f *http2HeadersFrame) error {
        rl.sawRegHeader = false
        rl.reqMalformed = nil
        rl.lastHeaderEndsStream = f.StreamEnded()
+       rl.headerListSize = 0
        rl.nextRes = &Response{
                Proto:      "HTTP/2.0",
                ProtoMajor: 2,
                Header:     make(Header),
        }
+       rl.hdec.SetEmitEnabled(true)
        return rl.processHeaderBlockFragment(f.HeaderBlockFragment(), f.StreamID, f.HeadersEnded())
 }
 
@@ -5037,7 +5176,7 @@ func (rl *http2clientConnReadLoop) processHeaderBlockFragment(frag []byte, strea
                return nil
        }
        if cs.pastHeaders {
-               rl.hdec.SetEmitFunc(cs.onNewTrailerField)
+               rl.hdec.SetEmitFunc(func(f hpack.HeaderField) { rl.onNewTrailerField(cs, f) })
        } else {
                rl.hdec.SetEmitFunc(rl.onNewHeaderField)
        }
@@ -5208,9 +5347,6 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error {
                return nil
        }
        data := f.Data()
-       if http2VerboseLogs {
-               rl.cc.logf("DATA: %q", data)
-       }
 
        cc.mu.Lock()
        if cs.inflow.available() >= int32(len(data)) {
@@ -5231,9 +5367,17 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error {
        return nil
 }
 
+var http2errInvalidTrailers = errors.New("http2: invalid trailers")
+
 func (rl *http2clientConnReadLoop) endStream(cs *http2clientStream) {
 
-       cs.bufPipe.closeWithErrorAndCode(io.EOF, cs.copyTrailers)
+       err := io.EOF
+       code := cs.copyTrailers
+       if rl.reqMalformed != nil {
+               err = rl.reqMalformed
+               code = nil
+       }
+       cs.bufPipe.closeWithErrorAndCode(err, code)
        delete(rl.activeRes, cs.ID)
 }
 
@@ -5343,20 +5487,57 @@ func (cc *http2ClientConn) writeStreamReset(streamID uint32, code http2ErrCode,
        cc.wmu.Unlock()
 }
 
+var (
+       http2errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
+       http2errInvalidHeaderKey       = errors.New("http2: invalid header key")
+       http2errPseudoTrailers         = errors.New("http2: invalid pseudo header in trailers")
+)
+
+func (rl *http2clientConnReadLoop) checkHeaderField(f hpack.HeaderField) bool {
+       if rl.reqMalformed != nil {
+               return false
+       }
+
+       const headerFieldOverhead = 32 // per spec
+       rl.headerListSize += int64(len(f.Name)) + int64(len(f.Value)) + headerFieldOverhead
+       if max := rl.cc.t.maxHeaderListSize(); max != 0 && rl.headerListSize > int64(max) {
+               rl.hdec.SetEmitEnabled(false)
+               rl.reqMalformed = http2errResponseHeaderListSize
+               return false
+       }
+
+       if !http2validHeader(f.Name) {
+               rl.reqMalformed = http2errInvalidHeaderKey
+               return false
+       }
+
+       isPseudo := strings.HasPrefix(f.Name, ":")
+       if isPseudo {
+               if rl.sawRegHeader {
+                       rl.reqMalformed = errors.New("http2: invalid pseudo header after regular header")
+                       return false
+               }
+       } else {
+               rl.sawRegHeader = true
+       }
+
+       return true
+}
+
 // onNewHeaderField runs on the readLoop goroutine whenever a new
 // hpack header field is decoded.
 func (rl *http2clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
        cc := rl.cc
        if http2VerboseLogs {
-               cc.logf("Header field: %+v", f)
+               cc.logf("http2: Transport decoded %v", f)
+       }
+
+       if !rl.checkHeaderField(f) {
+               return
        }
 
        isPseudo := strings.HasPrefix(f.Name, ":")
        if isPseudo {
-               if rl.sawRegHeader {
-                       rl.reqMalformed = errors.New("http2: invalid pseudo header after regular header")
-                       return
-               }
                switch f.Name {
                case ":status":
                        code, err := strconv.Atoi(f.Value)
@@ -5370,39 +5551,43 @@ func (rl *http2clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
 
                        rl.reqMalformed = fmt.Errorf("http2: unknown response pseudo header %q", f.Name)
                }
+               return
+       }
+
+       key := CanonicalHeaderKey(f.Name)
+       if key == "Trailer" {
+               t := rl.nextRes.Trailer
+               if t == nil {
+                       t = make(Header)
+                       rl.nextRes.Trailer = t
+               }
+               http2foreachHeaderElement(f.Value, func(v string) {
+                       t[CanonicalHeaderKey(v)] = nil
+               })
        } else {
-               rl.sawRegHeader = true
-               key := CanonicalHeaderKey(f.Name)
-               if key == "Trailer" {
-                       t := rl.nextRes.Trailer
-                       if t == nil {
-                               t = make(Header)
-                               rl.nextRes.Trailer = t
-                       }
-                       http2foreachHeaderElement(f.Value, func(v string) {
-                               t[CanonicalHeaderKey(v)] = nil
-                       })
-               } else {
-                       rl.nextRes.Header.Add(key, f.Value)
-               }
+               rl.nextRes.Header.Add(key, f.Value)
        }
 }
 
-func (cs *http2clientStream) onNewTrailerField(f hpack.HeaderField) {
-       isPseudo := strings.HasPrefix(f.Name, ":")
-       if isPseudo {
+func (rl *http2clientConnReadLoop) onNewTrailerField(cs *http2clientStream, f hpack.HeaderField) {
+       if http2VerboseLogs {
+               rl.cc.logf("http2: Transport decoded trailer %v", f)
+       }
+       if !rl.checkHeaderField(f) {
+               return
+       }
+       if strings.HasPrefix(f.Name, ":") {
 
+               rl.reqMalformed = http2errPseudoTrailers
                return
        }
+
        key := CanonicalHeaderKey(f.Name)
        if _, ok := cs.resTrailer[key]; ok {
                if cs.trailer == nil {
                        cs.trailer = make(Header)
                }
-               const tooBig = 1000 // TODO: arbitrary; use max header list size limits
-               if cur := cs.trailer[key]; len(cur) < tooBig {
-                       cs.trailer[key] = append(cur, f.Value)
-               }
+               cs.trailer[key] = append(cs.trailer[key], f.Value)
        }
 }
 
@@ -5580,27 +5765,31 @@ type http2writeResHeaders struct {
        contentLength string
 }
 
+func http2encKV(enc *hpack.Encoder, k, v string) {
+       if http2VerboseLogs {
+               log.Printf("http2: server encoding header %q = %q", k, v)
+       }
+       enc.WriteField(hpack.HeaderField{Name: k, Value: v})
+}
+
 func (w *http2writeResHeaders) writeFrame(ctx http2writeContext) error {
        enc, buf := ctx.HeaderEncoder()
        buf.Reset()
 
        if w.httpResCode != 0 {
-               enc.WriteField(hpack.HeaderField{
-                       Name:  ":status",
-                       Value: http2httpCodeString(w.httpResCode),
-               })
+               http2encKV(enc, ":status", http2httpCodeString(w.httpResCode))
        }
 
        http2encodeHeaders(enc, w.h, w.trailers)
 
        if w.contentType != "" {
-               enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType})
+               http2encKV(enc, "content-type", w.contentType)
        }
        if w.contentLength != "" {
-               enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength})
+               http2encKV(enc, "content-length", w.contentLength)
        }
        if w.date != "" {
-               enc.WriteField(hpack.HeaderField{Name: "date", Value: w.date})
+               http2encKV(enc, "date", w.date)
        }
 
        headerBlock := buf.Bytes()
@@ -5650,7 +5839,7 @@ type http2write100ContinueHeadersFrame struct {
 func (w http2write100ContinueHeadersFrame) writeFrame(ctx http2writeContext) error {
        enc, buf := ctx.HeaderEncoder()
        buf.Reset()
-       enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
+       http2encKV(enc, ":status", "100")
        return ctx.Framer().WriteHeaders(http2HeadersFrameParam{
                StreamID:      w.streamID,
                BlockFragment: buf.Bytes(),
@@ -5686,7 +5875,7 @@ func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) {
                        if isTE && v != "trailers" {
                                continue
                        }
-                       enc.WriteField(hpack.HeaderField{Name: k, Value: v})
+                       http2encKV(enc, k, v)
                }
        }
 }
index 5bbd05e813afc7a9b6c8c24ae46a661b912d36e3..ac7086c21efdfec47cf287aaf9f0460aba4d66d1 100644 (file)
@@ -2267,6 +2267,9 @@ func (srv *Server) setupHTTP2() error {
 // configured otherwise. (by setting srv.TLSNextProto non-nil)
 // It must only be called via srv.nextProtoOnce (use srv.setupHTTP2).
 func (srv *Server) onceSetNextProtoDefaults() {
+       if strings.Contains(os.Getenv("GODEBUG"), "http2server=0") {
+               return
+       }
        // Enable HTTP/2 by default if the user hasn't otherwise
        // configured their TLSNextProto map.
        if srv.TLSNextProto == nil {
index 01e3a05a3a9a9f589777e2a9bd1d956f9c9b71e8..6c083917668514779d447114c8cab93f4a3446bc 100644 (file)
@@ -151,7 +151,7 @@ type Transport struct {
 // onceSetNextProtoDefaults initializes TLSNextProto.
 // It must be called via t.nextProtoOnce.Do.
 func (t *Transport) onceSetNextProtoDefaults() {
-       if strings.Contains(os.Getenv("GODEBUG"), "h2client=0") {
+       if strings.Contains(os.Getenv("GODEBUG"), "http2client=0") {
                return
        }
        if t.TLSNextProto != nil {
index 329a8d036db45a31dcef7ab7fdababff675f792a..2ea4949ab0890e1b37f7439fad9ab9c9839740f2 100644 (file)
@@ -41,6 +41,14 @@ type HeaderField struct {
        Sensitive bool
 }
 
+func (hf HeaderField) String() string {
+       var suffix string
+       if hf.Sensitive {
+               suffix = " (sensitive)"
+       }
+       return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
+}
+
 func (hf *HeaderField) size() uint32 {
        // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
        // "The size of the dynamic table is the sum of the size of