|
|
@@ -2252,6 +2252,7 @@ type responseWriterState struct {
|
|
|
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
|
|
|
sentHeader bool // have we sent the header frame?
|
|
|
handlerDone bool // handler has finished
|
|
|
+ dirty bool // a Write failed; don't reuse this responseWriterState
|
|
|
|
|
|
sentContentLen int64 // non-zero if handler set a Content-Length header
|
|
|
wroteBytes int64
|
|
|
@@ -2333,6 +2334,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|
|
date: date,
|
|
|
})
|
|
|
if err != nil {
|
|
|
+ rws.dirty = true
|
|
|
return 0, err
|
|
|
}
|
|
|
if endStream {
|
|
|
@@ -2354,6 +2356,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|
|
if len(p) > 0 || endStream {
|
|
|
// only send a 0 byte DATA frame if we're ending the stream.
|
|
|
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
|
|
|
+ rws.dirty = true
|
|
|
return 0, err
|
|
|
}
|
|
|
}
|
|
|
@@ -2365,6 +2368,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|
|
trailers: rws.trailers,
|
|
|
endStream: true,
|
|
|
})
|
|
|
+ if err != nil {
|
|
|
+ rws.dirty = true
|
|
|
+ }
|
|
|
return len(p), err
|
|
|
}
|
|
|
return len(p), nil
|
|
|
@@ -2504,7 +2510,7 @@ func cloneHeader(h http.Header) http.Header {
|
|
|
//
|
|
|
// * Handler calls w.Write or w.WriteString ->
|
|
|
// * -> rws.bw (*bufio.Writer) ->
|
|
|
-// * (Handler migth call Flush)
|
|
|
+// * (Handler might call Flush)
|
|
|
// * -> chunkWriter{rws}
|
|
|
// * -> responseWriterState.writeChunk(p []byte)
|
|
|
// * -> responseWriterState.writeChunk (most of the magic; see comment there)
|
|
|
@@ -2543,10 +2549,19 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
|
|
|
|
|
|
func (w *responseWriter) handlerDone() {
|
|
|
rws := w.rws
|
|
|
+ dirty := rws.dirty
|
|
|
rws.handlerDone = true
|
|
|
w.Flush()
|
|
|
w.rws = nil
|
|
|
- responseWriterStatePool.Put(rws)
|
|
|
+ if !dirty {
|
|
|
+ // Only recycle the pool if all prior Write calls to
|
|
|
+ // the serverConn goroutine completed successfully. If
|
|
|
+ // they returned earlier due to resets from the peer
|
|
|
+ // there might still be write goroutines outstanding
|
|
|
+ // from the serverConn referencing the rws memory. See
|
|
|
+ // issue 20704.
|
|
|
+ responseWriterStatePool.Put(rws)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Push errors.
|