|
@@ -409,6 +409,7 @@ type serverConn struct {
|
|
|
goAwayCode ErrCode
|
|
goAwayCode ErrCode
|
|
|
shutdownTimerCh <-chan time.Time // nil until used
|
|
shutdownTimerCh <-chan time.Time // nil until used
|
|
|
shutdownTimer *time.Timer // 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:
|
|
// Owned by the writeFrameAsync goroutine:
|
|
|
headerWriteBuf bytes.Buffer
|
|
headerWriteBuf bytes.Buffer
|
|
@@ -453,6 +454,7 @@ type stream struct {
|
|
|
sentReset bool // only true once detached from streams map
|
|
sentReset bool // only true once detached from streams map
|
|
|
gotReset bool // only true once detacted from streams map
|
|
gotReset bool // only true once detacted from streams map
|
|
|
gotTrailerHeader bool // HEADER frame for trailers was seen
|
|
gotTrailerHeader bool // HEADER frame for trailers was seen
|
|
|
|
|
+ reqBuf []byte
|
|
|
|
|
|
|
|
trailer http.Header // accumulated trailers
|
|
trailer http.Header // accumulated trailers
|
|
|
reqTrailer http.Header // handler's Request.Trailer
|
|
reqTrailer http.Header // handler's Request.Trailer
|
|
@@ -1176,6 +1178,18 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
|
|
}
|
|
}
|
|
|
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
|
|
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
|
|
|
sc.writeSched.forgetStream(st.id)
|
|
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 {
|
|
func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
|
@@ -1602,8 +1616,9 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|
|
Trailer: trailer,
|
|
Trailer: trailer,
|
|
|
}
|
|
}
|
|
|
if bodyOpen {
|
|
if bodyOpen {
|
|
|
|
|
+ st.reqBuf = sc.getRequestBodyBuf()
|
|
|
body.pipe = &pipe{
|
|
body.pipe = &pipe{
|
|
|
- b: &fixedBuffer{buf: make([]byte, initialWindowSize)}, // TODO: garbage
|
|
|
|
|
|
|
+ b: &fixedBuffer{buf: st.reqBuf},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if vv, ok := header["Content-Length"]; ok {
|
|
if vv, ok := header["Content-Length"]; ok {
|
|
@@ -1627,6 +1642,15 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|
|
return rw, req, nil
|
|
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.
|
|
// Run on its own goroutine.
|
|
|
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
|
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
|
|
didPanic := true
|
|
didPanic := true
|