Skip to content

Commit d6e6baa

Browse files
committed
net/http: fix MaxBytesReader at EOF
Fixes #10884 Change-Id: I7cab3c96548867612f579d2cd4ec736309787443 Reviewed-on: https://go-review.googlesource.com/11961 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 1438225 commit d6e6baa

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

src/net/http/request.go

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -720,23 +720,52 @@ type maxBytesReader struct {
720720
r io.ReadCloser // underlying reader
721721
n int64 // max bytes remaining
722722
stopped bool
723+
sawEOF bool
724+
}
725+
726+
func (l *maxBytesReader) tooLarge() (n int, err error) {
727+
if !l.stopped {
728+
l.stopped = true
729+
if res, ok := l.w.(*response); ok {
730+
res.requestTooLarge()
731+
}
732+
}
733+
return 0, errors.New("http: request body too large")
723734
}
724735

725736
func (l *maxBytesReader) Read(p []byte) (n int, err error) {
726-
if l.n <= 0 {
727-
if !l.stopped {
728-
l.stopped = true
729-
if res, ok := l.w.(*response); ok {
730-
res.requestTooLarge()
731-
}
737+
toRead := l.n
738+
if l.n == 0 {
739+
if l.sawEOF {
740+
return l.tooLarge()
732741
}
733-
return 0, errors.New("http: request body too large")
742+
// The underlying io.Reader may not return (0, io.EOF)
743+
// at EOF if the requested size is 0, so read 1 byte
744+
// instead. The io.Reader docs are a bit ambiguous
745+
// about the return value of Read when 0 bytes are
746+
// requested, and {bytes,strings}.Reader gets it wrong
747+
// too (it returns (0, nil) even at EOF).
748+
toRead = 1
734749
}
735-
if int64(len(p)) > l.n {
736-
p = p[:l.n]
750+
if int64(len(p)) > toRead {
751+
p = p[:toRead]
737752
}
738753
n, err = l.r.Read(p)
754+
if err == io.EOF {
755+
l.sawEOF = true
756+
}
757+
if l.n == 0 {
758+
// If we had zero bytes to read remaining (but hadn't seen EOF)
759+
// and we get a byte here, that means we went over our limit.
760+
if n > 0 {
761+
return l.tooLarge()
762+
}
763+
return 0, err
764+
}
739765
l.n -= int64(n)
766+
if l.n < 0 {
767+
l.n = 0
768+
}
740769
return
741770
}
742771

src/net/http/request_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,38 @@ func TestStarRequest(t *testing.T) {
539539
}
540540
}
541541

542+
type responseWriterJustWriter struct {
543+
io.Writer
544+
}
545+
546+
func (responseWriterJustWriter) Header() Header { panic("should not be called") }
547+
func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") }
548+
549+
// delayedEOFReader never returns (n > 0, io.EOF), instead putting
550+
// off the io.EOF until a subsequent Read call.
551+
type delayedEOFReader struct {
552+
r io.Reader
553+
}
554+
555+
func (dr delayedEOFReader) Read(p []byte) (n int, err error) {
556+
n, err = dr.r.Read(p)
557+
if n > 0 && err == io.EOF {
558+
err = nil
559+
}
560+
return
561+
}
562+
563+
func TestIssue10884_MaxBytesEOF(t *testing.T) {
564+
dst := ioutil.Discard
565+
_, err := io.Copy(dst, MaxBytesReader(
566+
responseWriterJustWriter{dst},
567+
ioutil.NopCloser(delayedEOFReader{strings.NewReader("12345")}),
568+
5))
569+
if err != nil {
570+
t.Fatal(err)
571+
}
572+
}
573+
542574
func testMissingFile(t *testing.T, req *Request) {
543575
f, fh, err := req.FormFile("missing")
544576
if f != nil {

0 commit comments

Comments
 (0)