Update ingress godeps
This commit is contained in:
parent
d43021b3f1
commit
28db8fb16d
1068 changed files with 461467 additions and 117300 deletions
473
vendor/golang.org/x/net/http2/server.go
generated
vendored
473
vendor/golang.org/x/net/http2/server.go
generated
vendored
|
|
@ -51,7 +51,6 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -251,10 +250,14 @@ func (o *ServeConnOpts) handler() http.Handler {
|
|||
//
|
||||
// The opts parameter is optional. If nil, default values are used.
|
||||
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||
baseCtx, cancel := serverConnBaseContext(c, opts)
|
||||
defer cancel()
|
||||
|
||||
sc := &serverConn{
|
||||
srv: s,
|
||||
hs: opts.baseConfig(),
|
||||
conn: c,
|
||||
baseCtx: baseCtx,
|
||||
remoteAddrStr: c.RemoteAddr().String(),
|
||||
bw: newBufferedWriter(c),
|
||||
handler: opts.handler(),
|
||||
|
|
@ -273,13 +276,14 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
serveG: newGoroutineLock(),
|
||||
pushEnabled: true,
|
||||
}
|
||||
|
||||
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.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
fr.MaxHeaderListSize = sc.maxHeaderListSize()
|
||||
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
|
||||
sc.framer = fr
|
||||
|
||||
|
|
@ -335,30 +339,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
sc.serve()
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
|
||||
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
|
||||
// ignoring errors. hanging up anyway.
|
||||
|
|
@ -374,8 +354,8 @@ type serverConn struct {
|
|||
conn net.Conn
|
||||
bw *bufferedWriter // writing to conn
|
||||
handler http.Handler
|
||||
baseCtx contextContext
|
||||
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
|
||||
|
|
@ -402,7 +382,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
|
||||
|
|
@ -411,22 +390,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 {
|
||||
|
|
@ -439,21 +409,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
|
||||
|
|
@ -463,10 +418,12 @@ type requestParam struct {
|
|||
// responseWriter's state field.
|
||||
type stream struct {
|
||||
// immutable:
|
||||
sc *serverConn
|
||||
id uint32
|
||||
body *pipe // non-nil if expecting DATA frames
|
||||
cw closeWaiter // closed wait stream transitions to closed state
|
||||
sc *serverConn
|
||||
id uint32
|
||||
body *pipe // non-nil if expecting DATA frames
|
||||
cw closeWaiter // closed wait stream transitions to closed state
|
||||
ctx contextContext
|
||||
cancelCtx func()
|
||||
|
||||
// owned by serverConn's serve loop:
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
|
|
@ -480,6 +437,8 @@ 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
|
||||
wroteHeaders bool // whether we wrote headers (not status 100)
|
||||
reqBuf []byte
|
||||
|
||||
trailer http.Header // accumulated trailers
|
||||
reqTrailer http.Header // handler's Request.Trailer
|
||||
|
|
@ -589,87 +548,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]
|
||||
|
|
@ -704,10 +582,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
|
||||
}
|
||||
|
|
@ -946,7 +825,23 @@ func (sc *serverConn) writeFrameFromHandler(wm frameWriteMsg) error {
|
|||
// If you're not on the serve goroutine, use writeFrameFromHandler instead.
|
||||
func (sc *serverConn) writeFrame(wm frameWriteMsg) {
|
||||
sc.serveG.check()
|
||||
sc.writeSched.add(wm)
|
||||
|
||||
var ignoreWrite bool
|
||||
|
||||
// Don't send a 100-continue response if we've already sent headers.
|
||||
// See golang.org/issue/14030.
|
||||
switch wm.write.(type) {
|
||||
case *writeResHeaders:
|
||||
wm.stream.wroteHeaders = true
|
||||
case write100ContinueHeadersFrame:
|
||||
if wm.stream.wroteHeaders {
|
||||
ignoreWrite = true
|
||||
}
|
||||
}
|
||||
|
||||
if !ignoreWrite {
|
||||
sc.writeSched.add(wm)
|
||||
}
|
||||
sc.scheduleFrameWrite()
|
||||
}
|
||||
|
||||
|
|
@ -1183,10 +1078,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:
|
||||
|
|
@ -1265,6 +1158,7 @@ func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
|||
}
|
||||
if st != nil {
|
||||
st.gotReset = true
|
||||
st.cancelCtx()
|
||||
sc.closeStream(st, StreamError{f.StreamID, f.ErrCode})
|
||||
}
|
||||
return nil
|
||||
|
|
@ -1286,6 +1180,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 {
|
||||
|
|
@ -1442,7 +1348,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 {
|
||||
|
|
@ -1471,17 +1377,18 @@ 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
|
||||
}
|
||||
ctx, cancelCtx := contextWithCancel(sc.baseCtx)
|
||||
st = &stream{
|
||||
sc: sc,
|
||||
id: id,
|
||||
state: stateOpen,
|
||||
sc: sc,
|
||||
id: id,
|
||||
state: stateOpen,
|
||||
ctx: ctx,
|
||||
cancelCtx: cancelCtx,
|
||||
}
|
||||
if f.StreamEnded() {
|
||||
st.state = stateHalfClosedRemote
|
||||
|
|
@ -1501,50 +1408,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
|
||||
|
|
@ -1564,7 +1427,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
|
||||
}
|
||||
|
|
@ -1576,36 +1439,44 @@ 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
|
||||
} else if err := checkValidHTTP2Request(req); err != nil {
|
||||
handler = new400Handler(err)
|
||||
}
|
||||
|
||||
go sc.runHandler(rw, req, handler)
|
||||
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)
|
||||
if !ValidTrailerHeader(key) {
|
||||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
return StreamError{st.id, ErrCodeProtocol}
|
||||
}
|
||||
st.trailer[key] = append(st.trailer[key], hf.Value)
|
||||
}
|
||||
}
|
||||
|
||||
err := sc.hpackDecoder.Close()
|
||||
st.endStream()
|
||||
if err != nil {
|
||||
return ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1650,29 +1521,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
|
||||
|
|
@ -1683,35 +1546,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 {
|
||||
|
|
@ -1726,31 +1594,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,
|
||||
|
|
@ -1760,12 +1628,18 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
Body: body,
|
||||
Trailer: trailer,
|
||||
}
|
||||
req = requestWithContext(req, st.ctx)
|
||||
if bodyOpen {
|
||||
// Disabled, per golang.org/issue/14960:
|
||||
// st.reqBuf = sc.getRequestBodyBuf()
|
||||
// TODO: remove this 64k of garbage per request (again, but without a data race):
|
||||
buf := make([]byte, initialWindowSize)
|
||||
|
||||
body.pipe = &pipe{
|
||||
b: &fixedBuffer{buf: make([]byte, initialWindowSize)}, // TODO: garbage
|
||||
b: &fixedBuffer{buf: buf},
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -1778,7 +1652,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
|
||||
|
||||
|
|
@ -1786,10 +1660,20 @@ 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
|
||||
defer func() {
|
||||
rw.rws.stream.cancelCtx()
|
||||
if didPanic {
|
||||
e := recover()
|
||||
// Same as net/http:
|
||||
|
|
@ -1942,7 +1826,7 @@ type requestBody struct {
|
|||
|
||||
func (b *requestBody) Close() error {
|
||||
if b.pipe != nil {
|
||||
b.pipe.CloseWithError(errClosedBody)
|
||||
b.pipe.BreakWithError(errClosedBody)
|
||||
}
|
||||
b.closed = true
|
||||
return nil
|
||||
|
|
@ -2017,9 +1901,9 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
|||
// written in the trailers at the end of the response.
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
switch k {
|
||||
case "Transfer-Encoding", "Content-Length", "Trailer":
|
||||
if !ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 2616 14.40.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
}
|
||||
if !strSliceContains(rws.trailers, k) {
|
||||
|
|
@ -2160,7 +2044,12 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
|
|||
rws.declareTrailer(trailerKey)
|
||||
rws.handlerHeader[http.CanonicalHeaderKey(trailerKey)] = vv
|
||||
}
|
||||
sort.Strings(rws.trailers)
|
||||
|
||||
if len(rws.trailers) > 1 {
|
||||
sorter := sorterPool.Get().(*sorter)
|
||||
sorter.SortStrings(rws.trailers)
|
||||
sorterPool.Put(sorter)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) Flush() {
|
||||
|
|
@ -2306,3 +2195,69 @@ func foreachHeaderElement(v string, fn func(string)) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.2
|
||||
var connHeaders = []string{
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Proxy-Connection",
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
}
|
||||
|
||||
// checkValidHTTP2Request checks whether req is a valid HTTP/2 request,
|
||||
// per RFC 7540 Section 8.1.2.2.
|
||||
// The returned error is reported to users.
|
||||
func checkValidHTTP2Request(req *http.Request) error {
|
||||
for _, h := range connHeaders {
|
||||
if _, ok := req.Header[h]; ok {
|
||||
return fmt.Errorf("request header %q is not valid in HTTP/2", h)
|
||||
}
|
||||
}
|
||||
te := req.Header["Te"]
|
||||
if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) {
|
||||
return errors.New(`request header "TE" may only be "trailers" in HTTP/2`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func new400Handler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See: http://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = http.CanonicalHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue