]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: updated bundled copy of x/net/http2
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 16 Dec 2015 20:29:13 +0000 (20:29 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 16 Dec 2015 21:00:35 +0000 (21:00 +0000)
Updates to x/net/http2 git rev c24de9d5

Change-Id: I3d929ae38dca1a93e9a262d4eaaafee1d36fa839
Reviewed-on: https://go-review.googlesource.com/17896
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>

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

index 155796af14f0112b77396f6bfacf9cc2a255dd8f..89a8fd094a3478f16d58e1c8896f051fed2a8dce 100644 (file)
@@ -31,6 +31,7 @@ import (
        "net/url"
        "os"
        "runtime"
+       "sort"
        "strconv"
        "strings"
        "sync"
@@ -1674,6 +1675,7 @@ func init() {
                "server",
                "set-cookie",
                "strict-transport-security",
+               "trailer",
                "transfer-encoding",
                "user-agent",
                "vary",
@@ -1937,11 +1939,12 @@ func http2bodyAllowedForStatus(status int) bool {
 // io.Pipe except there are no PipeReader/PipeWriter halves, and the
 // underlying buffer is an interface. (io.Pipe is always unbuffered)
 type http2pipe struct {
-       mu    sync.Mutex
-       c     sync.Cond // c.L must point to
-       b     http2pipeBuffer
-       err   error         // read error once empty. non-nil means closed.
-       donec chan struct{} // closed on error
+       mu     sync.Mutex
+       c      sync.Cond // c.L must point to
+       b      http2pipeBuffer
+       err    error         // read error once empty. non-nil means closed.
+       donec  chan struct{} // closed on error
+       readFn func()        // optional code to run in Read before error
 }
 
 type http2pipeBuffer interface {
@@ -1963,6 +1966,10 @@ func (p *http2pipe) Read(d []byte) (n int, err error) {
                        return p.b.Read(d)
                }
                if p.err != nil {
+                       if p.readFn != nil {
+                               p.readFn()
+                               p.readFn = nil
+                       }
                        return 0, p.err
                }
                p.c.Wait()
@@ -1986,13 +1993,18 @@ func (p *http2pipe) Write(d []byte) (n int, err error) {
        return p.b.Write(d)
 }
 
-// CloseWithError causes Reads to wake up and return the
-// provided err after all data has been read.
+// CloseWithError causes the next Read (waking up a current blocked
+// Read if needed) to return the provided err after all data has been
+// read.
 //
 // The error must be non-nil.
-func (p *http2pipe) CloseWithError(err error) {
+func (p *http2pipe) CloseWithError(err error) { p.closeWithErrorAndCode(err, nil) }
+
+// closeWithErrorAndCode is like CloseWithError but also sets some code to run
+// in the caller's goroutine before returning the error.
+func (p *http2pipe) closeWithErrorAndCode(err error, fn func()) {
        if err == nil {
-               panic("CloseWithError must be non-nil")
+               panic("CloseWithError err must be non-nil")
        }
        p.mu.Lock()
        defer p.mu.Unlock()
@@ -2000,11 +2012,14 @@ func (p *http2pipe) CloseWithError(err error) {
                p.c.L = &p.mu
        }
        defer p.c.Signal()
-       if p.err == nil {
-               p.err = err
-               if p.donec != nil {
-                       close(p.donec)
-               }
+       if p.err != nil {
+
+               return
+       }
+       p.readFn = fn
+       p.err = err
+       if p.donec != nil {
+               close(p.donec)
        }
 }
 
@@ -2191,7 +2206,7 @@ func (srv *http2Server) handleConn(hs *Server, c net.Conn, h Handler) {
        sc.flow.add(http2initialWindowSize)
        sc.inflow.add(http2initialWindowSize)
        sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
-       sc.hpackDecoder = hpack.NewDecoder(http2initialHeaderTableSize, sc.onNewHeaderField)
+       sc.hpackDecoder = hpack.NewDecoder(http2initialHeaderTableSize, nil)
        sc.hpackDecoder.SetMaxStringLength(sc.maxHeaderStringLen())
 
        fr := http2NewFramer(sc.bw, c)
@@ -2348,20 +2363,26 @@ type http2requestParam struct {
 // responseWriter's state field.
 type http2stream struct {
        // immutable:
+       sc   *http2serverConn
        id   uint32
        body *http2pipe       // non-nil if expecting DATA frames
        cw   http2closeWaiter // closed wait stream transitions to closed state
 
        // owned by serverConn's serve loop:
-       bodyBytes     int64        // body bytes seen so far
-       declBodyBytes int64        // or -1 if undeclared
-       flow          http2flow    // limits writing from Handler to client
-       inflow        http2flow    // what the client is allowed to POST/etc to us
-       parent        *http2stream // or nil
-       weight        uint8
-       state         http2streamState
-       sentReset     bool // only true once detached from streams map
-       gotReset      bool // only true once detacted from streams map
+       bodyBytes        int64        // body bytes seen so far
+       declBodyBytes    int64        // or -1 if undeclared
+       flow             http2flow    // limits writing from Handler to client
+       inflow           http2flow    // what the client is allowed to POST/etc to us
+       parent           *http2stream // or nil
+       numTrailerValues int64
+       weight           uint8
+       state            http2streamState
+       sentReset        bool // only true once detached from streams map
+       gotReset         bool // only true once detacted from streams map
+       gotTrailerHeader bool // HEADER frame for trailers was seen
+
+       trailer    Header // accumulated trailers
+       reqTrailer Header // handler's Request.Trailer
 }
 
 func (sc *http2serverConn) Framer() *http2Framer { return sc.framer }
@@ -2468,6 +2489,33 @@ 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)
+       switch {
+       case !http2validHeader(f.Name):
+
+               return
+       case strings.HasPrefix(f.Name, ":"):
+
+               return
+       default:
+               key := sc.canonicalHeader(f.Name)
+               if st.trailer != nil {
+                       vv := append(st.trailer[key], f.Value)
+                       st.trailer[key] = vv
+
+                       // arbitrary; TODO: read spec about header list size limits wrt trailers
+                       const tooBig = 1000
+                       if len(vv) >= tooBig {
+                               sc.hpackDecoder.SetEmitEnabled(false)
+                       }
+
+               }
+       }
+}
+
 func (sc *http2serverConn) canonicalHeader(v string) string {
        sc.serveG.check()
        cv, ok := http2commonCanonHeader[v]
@@ -3116,7 +3164,7 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
 
        id := f.Header().StreamID
        st, ok := sc.streams[id]
-       if !ok || st.state != http2stateOpen {
+       if !ok || st.state != http2stateOpen || st.gotTrailerHeader {
 
                return http2StreamError{id, http2ErrCodeStreamClosed}
        }
@@ -3145,17 +3193,38 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
                st.bodyBytes += int64(len(data))
        }
        if f.StreamEnded() {
-               if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
-                       st.body.CloseWithError(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes",
-                               st.declBodyBytes, st.bodyBytes))
-               } else {
-                       st.body.CloseWithError(io.EOF)
-               }
-               st.state = http2stateHalfClosedRemote
+               st.endStream()
        }
        return nil
 }
 
+// endStream closes a Request.Body's pipe. It is called when a DATA
+// frame says a request body is over (or after trailers).
+func (st *http2stream) endStream() {
+       sc := st.sc
+       sc.serveG.check()
+
+       if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
+               st.body.CloseWithError(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes",
+                       st.declBodyBytes, st.bodyBytes))
+       } else {
+               st.body.closeWithErrorAndCode(io.EOF, st.copyTrailersToHandlerRequest)
+               st.body.CloseWithError(io.EOF)
+       }
+       st.state = http2stateHalfClosedRemote
+}
+
+// copyTrailersToHandlerRequest is run in the Handler's goroutine in
+// its Request.Body.Read just before it gets io.EOF.
+func (st *http2stream) copyTrailersToHandlerRequest() {
+       for k, vv := range st.trailer {
+               if _, ok := st.reqTrailer[k]; ok {
+
+                       st.reqTrailer[k] = vv
+               }
+       }
+}
+
 func (sc *http2serverConn) processHeaders(f *http2HeadersFrame) error {
        sc.serveG.check()
        id := f.Header().StreamID
@@ -3164,14 +3233,24 @@ func (sc *http2serverConn) processHeaders(f *http2HeadersFrame) error {
                return nil
        }
 
-       if id%2 != 1 || id <= sc.maxStreamID || sc.req.stream != nil {
+       if id%2 != 1 {
+               return http2ConnectionError(http2ErrCodeProtocol)
+       }
 
+       st := sc.streams[f.Header().StreamID]
+       if st != nil {
+               return st.processTrailerHeaders(f)
+       }
+
+       if id <= sc.maxStreamID || sc.req.stream != nil {
                return http2ConnectionError(http2ErrCodeProtocol)
        }
+
        if id > sc.maxStreamID {
                sc.maxStreamID = id
        }
-       st := &http2stream{
+       st = &http2stream{
+               sc:    sc,
                id:    id,
                state: http2stateOpen,
        }
@@ -3197,16 +3276,30 @@ func (sc *http2serverConn) processHeaders(f *http2HeadersFrame) error {
                stream: st,
                header: make(Header),
        }
+       sc.hpackDecoder.SetEmitFunc(sc.onNewHeaderField)
        sc.hpackDecoder.SetEmitEnabled(true)
        return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
 }
 
+func (st *http2stream) processTrailerHeaders(f *http2HeadersFrame) error {
+       sc := st.sc
+       sc.serveG.check()
+       if st.gotTrailerHeader {
+               return http2ConnectionError(http2ErrCodeProtocol)
+       }
+       st.gotTrailerHeader = true
+       return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
 func (sc *http2serverConn) processContinuation(f *http2ContinuationFrame) error {
        sc.serveG.check()
        st := sc.streams[f.Header().StreamID]
        if st == nil || sc.curHeaderStreamID() != st.id {
                return http2ConnectionError(http2ErrCodeProtocol)
        }
+       if st.gotTrailerHeader {
+               return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
+       }
        return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
 }
 
@@ -3236,6 +3329,10 @@ func (sc *http2serverConn) processHeaderBlockFragment(st *http2stream, frag []by
        if err != nil {
                return err
        }
+       st.reqTrailer = req.Trailer
+       if st.reqTrailer != nil {
+               st.trailer = make(Header)
+       }
        st.body = req.Body.(*http2requestBody).pipe
        st.declBodyBytes = req.ContentLength
 
@@ -3249,6 +3346,24 @@ func (sc *http2serverConn) processHeaderBlockFragment(st *http2stream, frag []by
        return nil
 }
 
+func (st *http2stream) processTrailerHeaderBlockFragment(frag []byte, end bool) error {
+       sc := st.sc
+       sc.serveG.check()
+       sc.hpackDecoder.SetEmitFunc(st.onNewTrailerField)
+       if _, err := sc.hpackDecoder.Write(frag); err != nil {
+               return http2ConnectionError(http2ErrCodeCompression)
+       }
+       if !end {
+               return nil
+       }
+       err := sc.hpackDecoder.Close()
+       st.endStream()
+       if err != nil {
+               return http2ConnectionError(http2ErrCodeCompression)
+       }
+       return nil
+}
+
 func (sc *http2serverConn) processPriority(f *http2PriorityFrame) error {
        http2adjustStreamPriority(sc.streams, f.StreamID, f.http2PriorityParam)
        return nil
@@ -3320,6 +3435,25 @@ func (sc *http2serverConn) newWriterAndRequest() (*http2responseWriter, *Request
        if cookies := rp.header["Cookie"]; len(cookies) > 1 {
                rp.header.Set("Cookie", strings.Join(cookies, "; "))
        }
+
+       // Setup Trailers
+       var trailer Header
+       for _, v := range rp.header["Trailer"] {
+               for _, key := range strings.Split(v, ",") {
+                       key = CanonicalHeaderKey(strings.TrimSpace(key))
+                       switch key {
+                       case "Transfer-Encoding", "Trailer", "Content-Length":
+
+                       default:
+                               if trailer == nil {
+                                       trailer = make(Header)
+                               }
+                               trailer[key] = nil
+                       }
+               }
+       }
+       delete(rp.header, "Trailer")
+
        body := &http2requestBody{
                conn:          sc,
                stream:        rp.stream,
@@ -3343,6 +3477,7 @@ func (sc *http2serverConn) newWriterAndRequest() (*http2responseWriter, *Request
                TLS:        tlsState,
                Host:       authority,
                Body:       body,
+               Trailer:    trailer,
        }
        if bodyOpen {
                body.pipe = &http2pipe{
@@ -5086,11 +5221,19 @@ func (w *http2writeResHeaders) writeFrame(ctx http2writeContext) error {
        enc, buf := ctx.HeaderEncoder()
        buf.Reset()
        enc.WriteField(hpack.HeaderField{Name: ":status", Value: http2httpCodeString(w.httpResCode)})
-       for k, vv := range w.h {
+
+       keys := make([]string, 0, len(w.h))
+       for k := range w.h {
+               keys = append(keys, k)
+       }
+       sort.Strings(keys)
+       for _, k := range keys {
+               vv := w.h[k]
                k = http2lowerHeader(k)
+               isTE := k == "transfer-encoding"
                for _, v := range vv {
 
-                       if k == "transfer-encoding" && v != "trailers" {
+                       if isTE && v != "trailers" {
                                continue
                        }
                        enc.WriteField(hpack.HeaderField{Name: k, Value: v})
index 8e9b2f2ebf52e807ec8d2791174ac82b208d2945..329a8d036db45a31dcef7ab7fdababff675f792a 100644 (file)
@@ -102,6 +102,13 @@ func (d *Decoder) SetMaxStringLength(n int) {
        d.maxStrLen = n
 }
 
+// SetEmitFunc changes the callback used when new header fields
+// are decoded.
+// It must be non-nil. It does not affect EmitEnabled.
+func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) {
+       d.emit = emitFunc
+}
+
 // SetEmitEnabled controls whether the emitFunc provided to NewDecoder
 // should be called. The default is true.
 //