Skip to content

Commit

Permalink
net/http: fix MaxBytesReader at EOF
Browse files Browse the repository at this point in the history
Fixes #10884

Change-Id: I7cab3c96548867612f579d2cd4ec736309787443
Reviewed-on: https://go-review.googlesource.com/11961
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
  • Loading branch information
bradfitz committed Jul 7, 2015
1 parent 1438225 commit d6e6baa
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 9 deletions.
47 changes: 38 additions & 9 deletions src/net/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,23 +720,52 @@ type maxBytesReader struct {
r io.ReadCloser // underlying reader
n int64 // max bytes remaining
stopped bool
sawEOF bool
}

func (l *maxBytesReader) tooLarge() (n int, err error) {
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
}
return 0, errors.New("http: request body too large")
}

func (l *maxBytesReader) Read(p []byte) (n int, err error) {
if l.n <= 0 {
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
toRead := l.n
if l.n == 0 {
if l.sawEOF {
return l.tooLarge()
}
return 0, errors.New("http: request body too large")
// The underlying io.Reader may not return (0, io.EOF)
// at EOF if the requested size is 0, so read 1 byte
// instead. The io.Reader docs are a bit ambiguous
// about the return value of Read when 0 bytes are
// requested, and {bytes,strings}.Reader gets it wrong
// too (it returns (0, nil) even at EOF).
toRead = 1
}
if int64(len(p)) > l.n {
p = p[:l.n]
if int64(len(p)) > toRead {
p = p[:toRead]
}
n, err = l.r.Read(p)
if err == io.EOF {
l.sawEOF = true
}
if l.n == 0 {
// If we had zero bytes to read remaining (but hadn't seen EOF)
// and we get a byte here, that means we went over our limit.
if n > 0 {
return l.tooLarge()
}
return 0, err
}
l.n -= int64(n)
if l.n < 0 {
l.n = 0
}
return
}

Expand Down
32 changes: 32 additions & 0 deletions src/net/http/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,38 @@ func TestStarRequest(t *testing.T) {
}
}

type responseWriterJustWriter struct {
io.Writer
}

func (responseWriterJustWriter) Header() Header { panic("should not be called") }
func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") }

// delayedEOFReader never returns (n > 0, io.EOF), instead putting
// off the io.EOF until a subsequent Read call.
type delayedEOFReader struct {
r io.Reader
}

func (dr delayedEOFReader) Read(p []byte) (n int, err error) {
n, err = dr.r.Read(p)
if n > 0 && err == io.EOF {
err = nil
}
return
}

func TestIssue10884_MaxBytesEOF(t *testing.T) {
dst := ioutil.Discard
_, err := io.Copy(dst, MaxBytesReader(
responseWriterJustWriter{dst},
ioutil.NopCloser(delayedEOFReader{strings.NewReader("12345")}),
5))
if err != nil {
t.Fatal(err)
}
}

func testMissingFile(t *testing.T, req *Request) {
f, fh, err := req.FormFile("missing")
if f != nil {
Expand Down

0 comments on commit d6e6baa

Please sign in to comment.