mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
update vendor/ dir to latest w/o heroku, moby
had to lock a lot of things in place
This commit is contained in:
489
vendor/golang.org/x/net/http2/server.go
generated
vendored
489
vendor/golang.org/x/net/http2/server.go
generated
vendored
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user