-
Notifications
You must be signed in to change notification settings - Fork 18k
net/http: MaxBytesReader's behavior does not match the comments #10884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Either the docs should be amended to say "returns a non-EOF error for a Read up to or beyond the limit", or the behaviour should be changed. IMO, the more useful functionality is the documented behaviour, but the implementation would be a little more complex. What say you @bradfitz? |
@adg for test2, even it read up to the limit, the maxBytesReader still returned io.EOF |
Here is my change, for MaxBytesReader and maxBytesReader func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
if n < 0 {
panic("n should be greater than or equal to 0")
}
return &maxBytesReader{w: w, r: r, n: n + 1}
}
type maxBytesReader struct {
w ResponseWriter
r io.ReadCloser // underlying reader
n int64 // max bytes remaining
stopped bool
}
func (l *maxBytesReader) Read(p []byte) (n int, err error) {
// l.n > 0
if int64(len(p)) > l.n {
p = p[:l.n]
}
n, err = l.r.Read(p)
l.n -= int64(n)
if l.n <= 0 { // beyond, and n >= l.n > 0
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
}
n--
err = errors.New("http: request body too large")
return
}
return
} |
if the docs be amended to say "returns a non-EOF error for a Read up to the limit", we can change and I think this is better 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()
}
}
return 0, errors.New("http: request body too large")
}
if int64(len(p)) > l.n {
p = p[:l.n]
}
n, err = l.r.Read(p)
l.n -= int64(n)
if l.n <= 0 {
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
}
err = errors.New("http: request body too large")
return
}
return
} |
CL https://golang.org/cl/11961 mentions this issue. |
I think there are still a little problem:
func (l *maxBytesReader) Read(p []byte) (n int, err error) {
toRead := l.n
if l.n == 0 {
if l.sawEOF {
return l.tooLarge() // -------> I think that this will never trigger
}
// 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)) > 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
} Here is my code: type maxBytesReader struct {
w ResponseWriter
r io.ReadCloser // underlying reader
n int64 // max bytes remaining
stopped 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 {
return l.tooLarge() // Reached if and only if maxBytesReader initialized with n<0
}
toRead := l.n
if l.n == 0 {
// 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)) > toRead {
p = p[:toRead]
}
n, err = l.r.Read(p)
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 // Usually 0, io.EOF
}
l.n -= int64(n)
return
} But I think the following code is better, type maxBytesReader struct {
w ResponseWriter
r io.ReadCloser // underlying reader
n int64 // max bytes remaining
stopped bool
}
func (l *maxBytesReader) Read(p []byte) (n int, err error) {
// The underlying io.Reader may not return (0, io.EOF)
// at EOF if the requested size is 0, so read 1 more 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 := l.n + 1
if toRead <= 0 {
// Reached if and only if maxBytesReader initialized with n<0
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
}
return 0, errors.New("http: request body too large")
}
if int64(len(p)) > toRead {
p = p[:toRead]
}
n, err = l.r.Read(p)
if l.n < int64(n) {
// means we went over our limit.
if !l.stopped {
l.stopped = true
if res, ok := l.w.(*response); ok {
res.requestTooLarge()
}
}
return int(l.n), errors.New("http: request body too large")
}
l.n -= int64(n)
return
} |
for test1:
the reader rd 's content does not beyond the http.MaxBytesReader mr 's limit, but we got
for test2(use postman, post 12345, use raw, not form-data, x-www-form-urlencoded):
we got
see
https://github.com/golang/go/blob/master/src/net/http/request.go#L730
if n==l.n and err==nil and next call return n==0 and err==io.EOF, The program does not consider this
The text was updated successfully, but these errors were encountered: