diff options
| author | Damien Neil <dneil@google.com> | 2021-03-12 13:53:11 -0800 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2021-06-04 17:33:24 +0000 |
| commit | 831f9376d8d730b16fb33dfd775618dffe13ce7a (patch) | |
| tree | 284b178840910a1140ff6739b3f1ac31befdede6 /src/net/http/server.go | |
| parent | 3a9d906edcfd0fa574ecd5498f8999b56f1e5fa1 (diff) | |
| download | go-831f9376d8d730b16fb33dfd775618dffe13ce7a.tar.xz | |
net/http: fix ResponseWriter.ReadFrom with short reads
CL 249238 changes ResponseWriter.ReadFrom to probe the source with
a single read of sniffLen bytes before writing the response header.
If the source returns less than sniffLen bytes without reaching
EOF, this can cause Content-Type and Content-Length detection to
fail.
Fix ResponseWrite.ReadFrom to copy a full sniffLen bytes from
the source as a probe.
Drop the explicit call to w.WriteHeader; writing the probe will
trigger a WriteHeader call.
Consistently use io.CopyBuffer; ReadFrom has already acquired a
copy buffer, so it may as well use it.
Fixes #44953.
Change-Id: Ic49305fb827a2bd7da4764b68d64b797b5157dc0
Reviewed-on: https://go-review.googlesource.com/c/go/+/301449
Trust: Damien Neil <dneil@google.com>
Run-TryBot: Damien Neil <dneil@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/net/http/server.go')
| -rw-r--r-- | src/net/http/server.go | 40 |
1 files changed, 10 insertions, 30 deletions
diff --git a/src/net/http/server.go b/src/net/http/server.go index 4e73508973..430019de50 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -577,37 +577,17 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { return io.CopyBuffer(writerOnly{w}, src, buf) } - // sendfile path: - - // Do not start actually writing response until src is readable. - // If body length is <= sniffLen, sendfile/splice path will do - // little anyway. This small read also satisfies sniffing the - // body in case Content-Type is missing. - nr, er := src.Read(buf[:sniffLen]) - atEOF := errors.Is(er, io.EOF) - n += int64(nr) - - if nr > 0 { - // Write the small amount read normally. - nw, ew := w.Write(buf[:nr]) - if ew != nil { - err = ew - } else if nr != nw { - err = io.ErrShortWrite + // Copy the first sniffLen bytes before switching to ReadFrom. + // This ensures we don't start writing the response before the + // source is available (see golang.org/issue/5660) and provides + // enough bytes to perform Content-Type sniffing when required. + if !w.cw.wroteHeader { + n0, err := io.CopyBuffer(writerOnly{w}, io.LimitReader(src, sniffLen), buf) + n += n0 + if err != nil || n0 < sniffLen { + return n, err } } - if err == nil && er != nil && !atEOF { - err = er - } - - // Do not send StatusOK in the error case where nothing has been written. - if err == nil && !w.wroteHeader { - w.WriteHeader(StatusOK) // nr == 0, no error (or EOF) - } - - if err != nil || atEOF { - return n, err - } w.w.Flush() // get rid of any previous writes w.cw.flush() // make sure Header is written; flush data to rwc @@ -620,7 +600,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { return n, err } - n0, err := io.Copy(writerOnly{w}, src) + n0, err := io.CopyBuffer(writerOnly{w}, src, buf) n += n0 return n, err } |
