|
@@ -315,6 +315,20 @@ type ServeConnOpts struct {
|
|
|
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
|
|
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
|
|
Handler http.Handler
|
|
|
+
|
|
|
+ // UpgradeRequest is an initial request received on a connection
|
|
|
+ // undergoing an h2c upgrade. The request body must have been
|
|
|
+ // completely read from the connection before calling ServeConn,
|
|
|
+ // and the 101 Switching Protocols response written.
|
|
|
+ UpgradeRequest *http.Request
|
|
|
+
|
|
|
+ // Settings is the decoded contents of the HTTP2-Settings header
|
|
|
+ // in an h2c upgrade request.
|
|
|
+ Settings []byte
|
|
|
+
|
|
|
+ // SawClientPreface is set if the HTTP/2 connection preface
|
|
|
+ // has already been read from the connection.
|
|
|
+ SawClientPreface bool
|
|
|
}
|
|
|
|
|
|
func (o *ServeConnOpts) context() context.Context {
|
|
@@ -383,6 +397,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|
|
headerTableSize: initialHeaderTableSize,
|
|
|
serveG: newGoroutineLock(),
|
|
|
pushEnabled: true,
|
|
|
+ sawClientPreface: opts.SawClientPreface,
|
|
|
}
|
|
|
|
|
|
s.state.registerConn(sc)
|
|
@@ -400,7 +415,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|
|
if s.NewWriteScheduler != nil {
|
|
|
sc.writeSched = s.NewWriteScheduler()
|
|
|
} else {
|
|
|
- sc.writeSched = NewRandomWriteScheduler()
|
|
|
+ sc.writeSched = NewPriorityWriteScheduler(nil)
|
|
|
}
|
|
|
|
|
|
// These start at the RFC-specified defaults. If there is a higher
|
|
@@ -465,9 +480,27 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if opts.Settings != nil {
|
|
|
+ fr := &SettingsFrame{
|
|
|
+ FrameHeader: FrameHeader{valid: true},
|
|
|
+ p: opts.Settings,
|
|
|
+ }
|
|
|
+ if err := fr.ForeachSetting(sc.processSetting); err != nil {
|
|
|
+ sc.rejectConn(ErrCodeProtocol, "invalid settings")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ opts.Settings = nil
|
|
|
+ }
|
|
|
+
|
|
|
if hook := testHookGetServerConn; hook != nil {
|
|
|
hook(sc)
|
|
|
}
|
|
|
+
|
|
|
+ if opts.UpgradeRequest != nil {
|
|
|
+ sc.upgradeRequest(opts.UpgradeRequest)
|
|
|
+ opts.UpgradeRequest = nil
|
|
|
+ }
|
|
|
+
|
|
|
sc.serve()
|
|
|
}
|
|
|
|
|
@@ -512,6 +545,7 @@ type serverConn struct {
|
|
|
// Everything following is owned by the serve loop; use serveG.check():
|
|
|
serveG goroutineLock // used to verify funcs are on serve()
|
|
|
pushEnabled bool
|
|
|
+ sawClientPreface bool // preface has already been read, used in h2c upgrade
|
|
|
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
|
|
needToSendSettingsAck bool
|
|
|
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
|
@@ -974,6 +1008,9 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface")
|
|
|
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
|
|
// is invalid.
|
|
|
func (sc *serverConn) readPreface() error {
|
|
|
+ if sc.sawClientPreface {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
errc := make(chan error, 1)
|
|
|
go func() {
|
|
|
// Read the client preface
|
|
@@ -1334,6 +1371,9 @@ func (sc *serverConn) startGracefulShutdownInternal() {
|
|
|
func (sc *serverConn) goAway(code ErrCode) {
|
|
|
sc.serveG.check()
|
|
|
if sc.inGoAway {
|
|
|
+ if sc.goAwayCode == ErrCodeNo {
|
|
|
+ sc.goAwayCode = code
|
|
|
+ }
|
|
|
return
|
|
|
}
|
|
|
sc.inGoAway = true
|
|
@@ -1710,6 +1750,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|
|
|
|
|
// Sender sending more than they'd declared?
|
|
|
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
|
|
+ if sc.inflow.available() < int32(f.Length) {
|
|
|
+ return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
|
|
|
+ }
|
|
|
+ sc.inflow.take(int32(f.Length))
|
|
|
+ sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
|
|
|
+
|
|
|
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
|
|
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
|
|
// value of a content-length header field does not equal the sum of the
|
|
@@ -1915,6 +1961,26 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+func (sc *serverConn) upgradeRequest(req *http.Request) {
|
|
|
+ sc.serveG.check()
|
|
|
+ id := uint32(1)
|
|
|
+ sc.maxClientStreamID = id
|
|
|
+ st := sc.newStream(id, 0, stateHalfClosedRemote)
|
|
|
+ st.reqTrailer = req.Trailer
|
|
|
+ if st.reqTrailer != nil {
|
|
|
+ st.trailer = make(http.Header)
|
|
|
+ }
|
|
|
+ rw := sc.newResponseWriter(st, req)
|
|
|
+
|
|
|
+ // Disable any read deadline set by the net/http package
|
|
|
+ // prior to the upgrade.
|
|
|
+ if sc.hs.ReadTimeout != 0 {
|
|
|
+ sc.conn.SetReadDeadline(time.Time{})
|
|
|
+ }
|
|
|
+
|
|
|
+ go sc.runHandler(rw, req, sc.handler.ServeHTTP)
|
|
|
+}
|
|
|
+
|
|
|
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|
|
sc := st.sc
|
|
|
sc.serveG.check()
|
|
@@ -2145,6 +2211,11 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|
|
}
|
|
|
req = req.WithContext(st.ctx)
|
|
|
|
|
|
+ rw := sc.newResponseWriter(st, req)
|
|
|
+ return rw, req, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *responseWriter {
|
|
|
rws := responseWriterStatePool.Get().(*responseWriterState)
|
|
|
bwSave := rws.bw
|
|
|
*rws = responseWriterState{} // zero all the fields
|
|
@@ -2153,10 +2224,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|
|
rws.bw.Reset(chunkWriter{rws})
|
|
|
rws.stream = st
|
|
|
rws.req = req
|
|
|
- rws.body = body
|
|
|
-
|
|
|
- rw := &responseWriter{rws: rws}
|
|
|
- return rw, req, nil
|
|
|
+ return &responseWriter{rws: rws}
|
|
|
}
|
|
|
|
|
|
// Run on its own goroutine.
|
|
@@ -2164,6 +2232,9 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
|
|
|
didPanic := true
|
|
|
defer func() {
|
|
|
rw.rws.stream.cancelCtx()
|
|
|
+ if req.MultipartForm != nil {
|
|
|
+ req.MultipartForm.RemoveAll()
|
|
|
+ }
|
|
|
if didPanic {
|
|
|
e := recover()
|
|
|
sc.writeFrameFromHandler(FrameWriteRequest{
|
|
@@ -2316,17 +2387,18 @@ type requestBody struct {
|
|
|
_ incomparable
|
|
|
stream *stream
|
|
|
conn *serverConn
|
|
|
- closed bool // for use by Close only
|
|
|
- sawEOF bool // for use by Read only
|
|
|
- pipe *pipe // non-nil if we have a HTTP entity message body
|
|
|
- needsContinue bool // need to send a 100-continue
|
|
|
+ closeOnce sync.Once // for use by Close only
|
|
|
+ sawEOF bool // for use by Read only
|
|
|
+ pipe *pipe // non-nil if we have a HTTP entity message body
|
|
|
+ needsContinue bool // need to send a 100-continue
|
|
|
}
|
|
|
|
|
|
func (b *requestBody) Close() error {
|
|
|
- if b.pipe != nil && !b.closed {
|
|
|
- b.pipe.BreakWithError(errClosedBody)
|
|
|
- }
|
|
|
- b.closed = true
|
|
|
+ b.closeOnce.Do(func() {
|
|
|
+ if b.pipe != nil {
|
|
|
+ b.pipe.BreakWithError(errClosedBody)
|
|
|
+ }
|
|
|
+ })
|
|
|
return nil
|
|
|
}
|
|
|
|
|
@@ -2370,7 +2442,6 @@ type responseWriterState struct {
|
|
|
// immutable within a request:
|
|
|
stream *stream
|
|
|
req *http.Request
|
|
|
- body *requestBody // to close at end of request, if DATA frames didn't
|
|
|
conn *serverConn
|
|
|
|
|
|
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
|
@@ -2546,8 +2617,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|
|
// 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
|
|
|
+//
|
|
|
+// 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
|
|
@@ -2643,8 +2715,7 @@ func checkWriteHeaderCode(code int) {
|
|
|
// Issue 22880: require valid WriteHeader status codes.
|
|
|
// For now we only enforce that it's three digits.
|
|
|
// In the future we might block things over 599 (600 and above aren't defined
|
|
|
- // at http://httpwg.org/specs/rfc7231.html#status.codes)
|
|
|
- // and we might block under 200 (once we have more mature 1xx support).
|
|
|
+ // at http://httpwg.org/specs/rfc7231.html#status.codes).
|
|
|
// But for now any three digits.
|
|
|
//
|
|
|
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
|
@@ -2665,13 +2736,41 @@ func (w *responseWriter) WriteHeader(code int) {
|
|
|
}
|
|
|
|
|
|
func (rws *responseWriterState) writeHeader(code int) {
|
|
|
- if !rws.wroteHeader {
|
|
|
- checkWriteHeaderCode(code)
|
|
|
- rws.wroteHeader = true
|
|
|
- rws.status = code
|
|
|
- if len(rws.handlerHeader) > 0 {
|
|
|
- rws.snapHeader = cloneHeader(rws.handlerHeader)
|
|
|
+ if rws.wroteHeader {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ checkWriteHeaderCode(code)
|
|
|
+
|
|
|
+ // Handle informational headers
|
|
|
+ if code >= 100 && code <= 199 {
|
|
|
+ // Per RFC 8297 we must not clear the current header map
|
|
|
+ h := rws.handlerHeader
|
|
|
+
|
|
|
+ _, cl := h["Content-Length"]
|
|
|
+ _, te := h["Transfer-Encoding"]
|
|
|
+ if cl || te {
|
|
|
+ h = h.Clone()
|
|
|
+ h.Del("Content-Length")
|
|
|
+ h.Del("Transfer-Encoding")
|
|
|
}
|
|
|
+
|
|
|
+ if rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
|
|
+ streamID: rws.stream.id,
|
|
|
+ httpResCode: code,
|
|
|
+ h: h,
|
|
|
+ endStream: rws.handlerDone && !rws.hasTrailers(),
|
|
|
+ }) != nil {
|
|
|
+ rws.dirty = true
|
|
|
+ }
|
|
|
+
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ rws.wroteHeader = true
|
|
|
+ rws.status = code
|
|
|
+ if len(rws.handlerHeader) > 0 {
|
|
|
+ rws.snapHeader = cloneHeader(rws.handlerHeader)
|
|
|
}
|
|
|
}
|
|
|
|