update vendor/ dir to latest w/o heroku, moby

had to lock a lot of things in place
This commit is contained in:
Reed Allman
2017-08-03 02:38:15 -07:00
parent 780791da1c
commit 30f3c45dbc
5637 changed files with 191713 additions and 1133103 deletions

View File

@@ -48,6 +48,8 @@ import (
"net/http"
"net/textproto"
"net/url"
"os"
"reflect"
"runtime"
"strconv"
"strings"
@@ -192,28 +194,76 @@ func ConfigureServer(s *http.Server, conf *Server) error {
if testHookOnConn != nil {
testHookOnConn()
}
conf.handleConn(hs, c, h)
conf.ServeConn(c, &ServeConnOpts{
Handler: h,
BaseConfig: hs,
})
}
s.TLSNextProto[NextProtoTLS] = protoHandler
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
return nil
}
func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
// ServeConnOpts are options for the Server.ServeConn method.
type ServeConnOpts struct {
// BaseConfig optionally sets the base configuration
// for values. If nil, defaults are used.
BaseConfig *http.Server
// Handler specifies which handler to use for processing
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
Handler http.Handler
}
func (o *ServeConnOpts) baseConfig() *http.Server {
if o != nil && o.BaseConfig != nil {
return o.BaseConfig
}
return new(http.Server)
}
func (o *ServeConnOpts) handler() http.Handler {
if o != nil {
if o.Handler != nil {
return o.Handler
}
if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
return o.BaseConfig.Handler
}
}
return http.DefaultServeMux
}
// ServeConn serves HTTP/2 requests on the provided connection and
// blocks until the connection is no longer readable.
//
// ServeConn starts speaking HTTP/2 assuming that c has not had any
// reads or writes. It writes its initial settings frame and expects
// to be able to read the preface and settings frame from the
// client. If c has a ConnectionState method like a *tls.Conn, the
// ConnectionState is used to verify the TLS ciphersuite and to set
// the Request.TLS field in Handlers.
//
// ServeConn does not support h2c by itself. Any h2c support must be
// implemented in terms of providing a suitably-behaving net.Conn.
//
// The opts parameter is optional. If nil, default values are used.
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc := &serverConn{
srv: srv,
hs: hs,
srv: s,
hs: opts.baseConfig(),
conn: c,
remoteAddrStr: c.RemoteAddr().String(),
bw: newBufferedWriter(c),
handler: h,
handler: opts.handler(),
streams: make(map[uint32]*stream),
readFrameCh: make(chan readFrameResult),
wantWriteFrameCh: make(chan frameWriteMsg, 8),
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
doneServing: make(chan struct{}),
advMaxStreams: srv.maxConcurrentStreams(),
advMaxStreams: s.maxConcurrentStreams(),
writeSched: writeScheduler{
maxFrameSize: initialMaxFrameSize,
},
@@ -225,14 +275,14 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
sc.flow.add(initialWindowSize)
sc.inflow.add(initialWindowSize)
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, nil)
sc.hpackDecoder.SetMaxStringLength(sc.maxHeaderStringLen())
fr := NewFramer(sc.bw, c)
fr.SetMaxReadFrameSize(srv.maxReadFrameSize())
fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
fr.MaxHeaderListSize = sc.maxHeaderListSize()
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
sc.framer = fr
if tc, ok := c.(*tls.Conn); ok {
if tc, ok := c.(connectionStater); ok {
sc.tlsState = new(tls.ConnectionState)
*sc.tlsState = tc.ConnectionState()
// 9.2 Use of TLS Features
@@ -262,7 +312,7 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
// So for now, do nothing here again.
}
if !srv.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
// "Endpoints MAY choose to generate a connection error
// (Section 5.4.1) of type INADEQUATE_SECURITY if one of
// the prohibited cipher suites are negotiated."
@@ -324,7 +374,6 @@ type serverConn struct {
bw *bufferedWriter // writing to conn
handler http.Handler
framer *Framer
hpackDecoder *hpack.Decoder
doneServing chan struct{} // closed when serverConn.serve ends
readFrameCh chan readFrameResult // written by serverConn.readFrames
wantWriteFrameCh chan frameWriteMsg // from handlers -> serve
@@ -351,7 +400,6 @@ type serverConn struct {
headerTableSize uint32
peerMaxHeaderListSize uint32 // zero means unknown (default)
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
req requestParam // non-zero while reading request headers
writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh
needsFrameFlush bool // last frame write wasn't a flush
writeSched writeScheduler
@@ -360,22 +408,13 @@ type serverConn struct {
goAwayCode ErrCode
shutdownTimerCh <-chan time.Time // nil until used
shutdownTimer *time.Timer // nil until used
freeRequestBodyBuf []byte // if non-nil, a free initialWindowSize buffer for getRequestBodyBuf
// Owned by the writeFrameAsync goroutine:
headerWriteBuf bytes.Buffer
hpackEncoder *hpack.Encoder
}
func (sc *serverConn) maxHeaderStringLen() int {
v := sc.maxHeaderListSize()
if uint32(int(v)) == v {
return int(v)
}
// They had a crazy big number for MaxHeaderBytes anyway,
// so give them unlimited header lengths:
return 0
}
func (sc *serverConn) maxHeaderListSize() uint32 {
n := sc.hs.MaxHeaderBytes
if n <= 0 {
@@ -388,21 +427,6 @@ func (sc *serverConn) maxHeaderListSize() uint32 {
return uint32(n + typicalHeaders*perFieldOverhead)
}
// requestParam is the state of the next request, initialized over
// potentially several frames HEADERS + zero or more CONTINUATION
// frames.
type requestParam struct {
// stream is non-nil if we're reading (HEADER or CONTINUATION)
// frames for a request (but not DATA).
stream *stream
header http.Header
method, path string
scheme, authority string
sawRegularHeader bool // saw a non-pseudo header already
invalidHeader bool // an invalid header was seen
headerListSize int64 // actually uint32, but easier math this way
}
// stream represents a stream. This is the minimal metadata needed by
// the serve goroutine. Most of the actual stream state is owned by
// the http.Handler's goroutine in the responseWriter. Because the
@@ -429,6 +453,7 @@ type stream struct {
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
reqBuf []byte
trailer http.Header // accumulated trailers
reqTrailer http.Header // handler's Request.Trailer
@@ -482,12 +507,55 @@ func (sc *serverConn) logf(format string, args ...interface{}) {
}
}
// errno returns v's underlying uintptr, else 0.
//
// TODO: remove this helper function once http2 can use build
// tags. See comment in isClosedConnError.
func errno(v error) uintptr {
if rv := reflect.ValueOf(v); rv.Kind() == reflect.Uintptr {
return uintptr(rv.Uint())
}
return 0
}
// isClosedConnError reports whether err is an error from use of a closed
// network connection.
func isClosedConnError(err error) bool {
if err == nil {
return false
}
// TODO: remove this string search and be more like the Windows
// case below. That might involve modifying the standard library
// to return better error types.
str := err.Error()
if strings.Contains(str, "use of closed network connection") {
return true
}
// TODO(bradfitz): x/tools/cmd/bundle doesn't really support
// build tags, so I can't make an http2_windows.go file with
// Windows-specific stuff. Fix that and move this, once we
// have a way to bundle this into std's net/http somehow.
if runtime.GOOS == "windows" {
if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
if se, ok := oe.Err.(*os.SyscallError); ok && se.Syscall == "wsarecv" {
const WSAECONNABORTED = 10053
const WSAECONNRESET = 10054
if n := errno(se.Err); n == WSAECONNRESET || n == WSAECONNABORTED {
return true
}
}
}
}
return false
}
func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
if err == nil {
return
}
str := err.Error()
if err == io.EOF || strings.Contains(str, "use of closed network connection") {
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) {
// Boring, expected errors.
sc.vlogf(format, args...)
} else {
@@ -495,87 +563,6 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
}
}
func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
sc.serveG.check()
if VerboseLogs {
sc.vlogf("http2: server decoded %v", f)
}
switch {
case !validHeaderFieldValue(f.Value): // f.Name checked _after_ pseudo check, since ':' is invalid
sc.req.invalidHeader = true
case strings.HasPrefix(f.Name, ":"):
if sc.req.sawRegularHeader {
sc.logf("pseudo-header after regular header")
sc.req.invalidHeader = true
return
}
var dst *string
switch f.Name {
case ":method":
dst = &sc.req.method
case ":path":
dst = &sc.req.path
case ":scheme":
dst = &sc.req.scheme
case ":authority":
dst = &sc.req.authority
default:
// 8.1.2.1 Pseudo-Header Fields
// "Endpoints MUST treat a request or response
// that contains undefined or invalid
// pseudo-header fields as malformed (Section
// 8.1.2.6)."
sc.logf("invalid pseudo-header %q", f.Name)
sc.req.invalidHeader = true
return
}
if *dst != "" {
sc.logf("duplicate pseudo-header %q sent", f.Name)
sc.req.invalidHeader = true
return
}
*dst = f.Value
case !validHeaderFieldName(f.Name):
sc.req.invalidHeader = true
default:
sc.req.sawRegularHeader = true
sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
const headerFieldOverhead = 32 // per spec
sc.req.headerListSize += int64(len(f.Name)) + int64(len(f.Value)) + headerFieldOverhead
if sc.req.headerListSize > int64(sc.maxHeaderListSize()) {
sc.hpackDecoder.SetEmitEnabled(false)
}
}
}
func (st *stream) onNewTrailerField(f hpack.HeaderField) {
sc := st.sc
sc.serveG.check()
if VerboseLogs {
sc.vlogf("http2: server decoded trailer %v", f)
}
switch {
case strings.HasPrefix(f.Name, ":"):
sc.req.invalidHeader = true
return
case !validHeaderFieldName(f.Name) || !validHeaderFieldValue(f.Value):
sc.req.invalidHeader = true
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 *serverConn) canonicalHeader(v string) string {
sc.serveG.check()
cv, ok := commonCanonHeader[v]
@@ -610,10 +597,11 @@ type readFrameResult struct {
// It's run on its own goroutine.
func (sc *serverConn) readFrames() {
gate := make(gate)
gateDone := gate.Done
for {
f, err := sc.framer.ReadFrame()
select {
case sc.readFrameCh <- readFrameResult{f, err, gate.Done}:
case sc.readFrameCh <- readFrameResult{f, err, gateDone}:
case <-sc.doneServing:
return
}
@@ -1031,7 +1019,7 @@ func (sc *serverConn) processFrameFromReader(res readFrameResult) bool {
sc.goAway(ErrCodeFrameSize)
return true // goAway will close the loop
}
clientGone := err == io.EOF || strings.Contains(err.Error(), "use of closed network connection")
clientGone := err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err)
if clientGone {
// TODO: could we also get into this state if
// the peer does a half close
@@ -1067,7 +1055,7 @@ func (sc *serverConn) processFrameFromReader(res readFrameResult) bool {
return true // goAway will handle shutdown
default:
if res.err != nil {
sc.logf("http2: server closing client connection; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
sc.vlogf("http2: server closing client connection; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
} else {
sc.logf("http2: server closing client connection: %v", err)
}
@@ -1089,10 +1077,8 @@ func (sc *serverConn) processFrame(f Frame) error {
switch f := f.(type) {
case *SettingsFrame:
return sc.processSettings(f)
case *HeadersFrame:
case *MetaHeadersFrame:
return sc.processHeaders(f)
case *ContinuationFrame:
return sc.processContinuation(f)
case *WindowUpdateFrame:
return sc.processWindowUpdate(f)
case *PingFrame:
@@ -1192,6 +1178,18 @@ func (sc *serverConn) closeStream(st *stream, err error) {
}
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
sc.writeSched.forgetStream(st.id)
if st.reqBuf != nil {
// Stash this request body buffer (64k) away for reuse
// by a future POST/PUT/etc.
//
// TODO(bradfitz): share on the server? sync.Pool?
// Server requires locks and might hurt contention.
// sync.Pool might work, or might be worse, depending
// on goroutine CPU migrations. (get and put on
// separate CPUs). Maybe a mix of strategies. But
// this is an easy win for now.
sc.freeRequestBodyBuf = st.reqBuf
}
}
func (sc *serverConn) processSettings(f *SettingsFrame) error {
@@ -1348,7 +1346,7 @@ func (st *stream) copyTrailersToHandlerRequest() {
}
}
func (sc *serverConn) processHeaders(f *HeadersFrame) error {
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
sc.serveG.check()
id := f.Header().StreamID
if sc.inGoAway {
@@ -1377,13 +1375,11 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
// endpoint has opened or reserved. [...] An endpoint that
// receives an unexpected stream identifier MUST respond with
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
if id <= sc.maxStreamID || sc.req.stream != nil {
if id <= sc.maxStreamID {
return ConnectionError(ErrCodeProtocol)
}
sc.maxStreamID = id
if id > sc.maxStreamID {
sc.maxStreamID = id
}
st = &stream{
sc: sc,
id: id,
@@ -1407,50 +1403,6 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
if sc.curOpenStreams == 1 {
sc.setConnState(http.StateActive)
}
sc.req = requestParam{
stream: st,
header: make(http.Header),
}
sc.hpackDecoder.SetEmitFunc(sc.onNewHeaderField)
sc.hpackDecoder.SetEmitEnabled(true)
return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
}
func (st *stream) processTrailerHeaders(f *HeadersFrame) error {
sc := st.sc
sc.serveG.check()
if st.gotTrailerHeader {
return ConnectionError(ErrCodeProtocol)
}
st.gotTrailerHeader = true
if !f.StreamEnded() {
return StreamError{st.id, ErrCodeProtocol}
}
sc.resetPendingRequest() // we use invalidHeader from it for trailers
return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
}
func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
sc.serveG.check()
st := sc.streams[f.Header().StreamID]
if st.gotTrailerHeader {
return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
}
return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
}
func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
sc.serveG.check()
if _, err := sc.hpackDecoder.Write(frag); err != nil {
return ConnectionError(ErrCodeCompression)
}
if !end {
return nil
}
if err := sc.hpackDecoder.Close(); err != nil {
return ConnectionError(ErrCodeCompression)
}
defer sc.resetPendingRequest()
if sc.curOpenStreams > sc.advMaxStreams {
// "Endpoints MUST NOT exceed the limit set by their
// peer. An endpoint that receives a HEADERS frame
@@ -1470,7 +1422,7 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
return StreamError{st.id, ErrCodeRefusedStream}
}
rw, req, err := sc.newWriterAndRequest()
rw, req, err := sc.newWriterAndRequest(st, f)
if err != nil {
return err
}
@@ -1482,7 +1434,7 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
st.declBodyBytes = req.ContentLength
handler := sc.handler.ServeHTTP
if !sc.hpackDecoder.EmitEnabled() {
if f.Truncated {
// Their header list was too long. Send a 431 error.
handler = handleHeaderListTooLong
}
@@ -1491,27 +1443,27 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
return nil
}
func (st *stream) processTrailerHeaderBlockFragment(frag []byte, end bool) error {
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
sc := st.sc
sc.serveG.check()
sc.hpackDecoder.SetEmitFunc(st.onNewTrailerField)
if _, err := sc.hpackDecoder.Write(frag); err != nil {
return ConnectionError(ErrCodeCompression)
if st.gotTrailerHeader {
return ConnectionError(ErrCodeProtocol)
}
if !end {
return nil
st.gotTrailerHeader = true
if !f.StreamEnded() {
return StreamError{st.id, ErrCodeProtocol}
}
rp := &sc.req
if rp.invalidHeader {
return StreamError{rp.stream.id, ErrCodeProtocol}
if len(f.PseudoFields()) > 0 {
return StreamError{st.id, ErrCodeProtocol}
}
if st.trailer != nil {
for _, hf := range f.RegularFields() {
key := sc.canonicalHeader(hf.Name)
st.trailer[key] = append(st.trailer[key], hf.Value)
}
}
err := sc.hpackDecoder.Close()
st.endStream()
if err != nil {
return ConnectionError(ErrCodeCompression)
}
return nil
}
@@ -1556,29 +1508,21 @@ func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority
}
}
// resetPendingRequest zeros out all state related to a HEADERS frame
// and its zero or more CONTINUATION frames sent to start a new
// request.
func (sc *serverConn) resetPendingRequest() {
func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*responseWriter, *http.Request, error) {
sc.serveG.check()
sc.req = requestParam{}
}
func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
sc.serveG.check()
rp := &sc.req
method := f.PseudoValue("method")
path := f.PseudoValue("path")
scheme := f.PseudoValue("scheme")
authority := f.PseudoValue("authority")
if rp.invalidHeader {
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
}
isConnect := rp.method == "CONNECT"
isConnect := method == "CONNECT"
if isConnect {
if rp.path != "" || rp.scheme != "" || rp.authority == "" {
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
if path != "" || scheme != "" || authority == "" {
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
} else if rp.method == "" || rp.path == "" ||
(rp.scheme != "https" && rp.scheme != "http") {
} else if method == "" || path == "" ||
(scheme != "https" && scheme != "http") {
// See 8.1.2.6 Malformed Requests and Responses:
//
// Malformed requests or responses that are detected
@@ -1589,35 +1533,40 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
// "All HTTP/2 requests MUST include exactly one valid
// value for the :method, :scheme, and :path
// pseudo-header fields"
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
bodyOpen := rp.stream.state == stateOpen
if rp.method == "HEAD" && bodyOpen {
bodyOpen := !f.StreamEnded()
if method == "HEAD" && bodyOpen {
// HEAD requests can't have bodies
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
var tlsState *tls.ConnectionState // nil if not scheme https
if rp.scheme == "https" {
if scheme == "https" {
tlsState = sc.tlsState
}
authority := rp.authority
if authority == "" {
authority = rp.header.Get("Host")
header := make(http.Header)
for _, hf := range f.RegularFields() {
header.Add(sc.canonicalHeader(hf.Name), hf.Value)
}
needsContinue := rp.header.Get("Expect") == "100-continue"
if authority == "" {
authority = header.Get("Host")
}
needsContinue := header.Get("Expect") == "100-continue"
if needsContinue {
rp.header.Del("Expect")
header.Del("Expect")
}
// Merge Cookie headers into one "; "-delimited value.
if cookies := rp.header["Cookie"]; len(cookies) > 1 {
rp.header.Set("Cookie", strings.Join(cookies, "; "))
if cookies := header["Cookie"]; len(cookies) > 1 {
header.Set("Cookie", strings.Join(cookies, "; "))
}
// Setup Trailers
var trailer http.Header
for _, v := range rp.header["Trailer"] {
for _, v := range header["Trailer"] {
for _, key := range strings.Split(v, ",") {
key = http.CanonicalHeaderKey(strings.TrimSpace(key))
switch key {
@@ -1632,31 +1581,31 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
}
}
}
delete(rp.header, "Trailer")
delete(header, "Trailer")
body := &requestBody{
conn: sc,
stream: rp.stream,
stream: st,
needsContinue: needsContinue,
}
var url_ *url.URL
var requestURI string
if isConnect {
url_ = &url.URL{Host: rp.authority}
requestURI = rp.authority // mimic HTTP/1 server behavior
url_ = &url.URL{Host: authority}
requestURI = authority // mimic HTTP/1 server behavior
} else {
var err error
url_, err = url.ParseRequestURI(rp.path)
url_, err = url.ParseRequestURI(path)
if err != nil {
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
requestURI = rp.path
requestURI = path
}
req := &http.Request{
Method: rp.method,
Method: method,
URL: url_,
RemoteAddr: sc.remoteAddrStr,
Header: rp.header,
Header: header,
RequestURI: requestURI,
Proto: "HTTP/2.0",
ProtoMajor: 2,
@@ -1667,11 +1616,12 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
Trailer: trailer,
}
if bodyOpen {
st.reqBuf = sc.getRequestBodyBuf()
body.pipe = &pipe{
b: &fixedBuffer{buf: make([]byte, initialWindowSize)}, // TODO: garbage
b: &fixedBuffer{buf: st.reqBuf},
}
if vv, ok := rp.header["Content-Length"]; ok {
if vv, ok := header["Content-Length"]; ok {
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
} else {
req.ContentLength = -1
@@ -1684,7 +1634,7 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
rws.conn = sc
rws.bw = bwSave
rws.bw.Reset(chunkWriter{rws})
rws.stream = rp.stream
rws.stream = st
rws.req = req
rws.body = body
@@ -1692,6 +1642,15 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
return rw, req, nil
}
func (sc *serverConn) getRequestBodyBuf() []byte {
sc.serveG.check()
if buf := sc.freeRequestBodyBuf; buf != nil {
sc.freeRequestBodyBuf = nil
return buf
}
return make([]byte, initialWindowSize)
}
// Run on its own goroutine.
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
didPanic := true
@@ -1928,7 +1887,9 @@ func (rws *responseWriterState) declareTrailer(k string) {
// Forbidden by RFC 2616 14.40.
return
}
rws.trailers = append(rws.trailers, k)
if !strSliceContains(rws.trailers, k) {
rws.trailers = append(rws.trailers, k)
}
}
// writeChunk writes chunks from the bufio.Writer. But because
@@ -1996,6 +1957,10 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
return 0, nil
}
if rws.handlerDone {
rws.promoteUndeclaredTrailers()
}
endStream := rws.handlerDone && !rws.hasTrailers()
if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream.
@@ -2016,6 +1981,58 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
return len(p), nil
}
// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys
// that, if present, signals that the map entry is actually for
// the response trailers, and not the response headers. The prefix
// is stripped after the ServeHTTP call finishes and the values are
// sent in the trailers.
//
// This mechanism is intended only for trailers that are not known
// prior to the headers being written. If the set of trailers is fixed
// or known before the header is written, the normal Go trailers mechanism
// is preferred:
// https://golang.org/pkg/net/http/#ResponseWriter
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
const TrailerPrefix = "Trailer:"
// promoteUndeclaredTrailers permits http.Handlers to set trailers
// after the header has already been flushed. Because the Go
// ResponseWriter interface has no way to set Trailers (only the
// Header), and because we didn't want to expand the ResponseWriter
// interface, and because nobody used trailers, and because RFC 2616
// says you SHOULD (but not must) predeclare any trailers in the
// header, the official ResponseWriter rules said trailers in Go must
// be predeclared, and then we reuse the same ResponseWriter.Header()
// map to mean both Headers and Trailers. When it's time to write the
// Trailers, we pick out the fields of Headers that were declared as
// trailers. That worked for a while, until we found the first major
// user of Trailers in the wild: gRPC (using them only over http2),
// and gRPC libraries permit setting trailers mid-stream without
// predeclarnig them. So: change of plans. We still permit the old
// way, but we also permit this hack: if a Header() key begins with
// "Trailer:", the suffix of that key is a Trailer. Because ':' is an
// invalid token byte anyway, there is no ambiguity. (And it's already
// filtered out) It's mildly hacky, but not terrible.
//
// This method runs after the Handler is done and promotes any Header
// fields to be trailers.
func (rws *responseWriterState) promoteUndeclaredTrailers() {
for k, vv := range rws.handlerHeader {
if !strings.HasPrefix(k, TrailerPrefix) {
continue
}
trailerKey := strings.TrimPrefix(k, TrailerPrefix)
rws.declareTrailer(trailerKey)
rws.handlerHeader[http.CanonicalHeaderKey(trailerKey)] = vv
}
if len(rws.trailers) > 1 {
sorter := sorterPool.Get().(*sorter)
sorter.SortStrings(rws.trailers)
sorterPool.Put(sorter)
}
}
func (w *responseWriter) Flush() {
rws := w.rws
if rws == nil {