Return ErrResponseTooLarge instead of truncating response body

WithMaxResponseBody wrapped the body in io.LimitedReader, which returns EOF
at the cap, so Bytes/JSON/XML silently returned a truncated body with a nil
error despite the documented contract. Read one byte past the limit and
return the new ErrResponseTooLarge sentinel when exceeded; bodies exactly at
the limit still succeed.
This commit is contained in:
2026-05-23 13:47:13 +03:00
parent 2d4a06e715
commit e8c4577c6f
5 changed files with 48 additions and 12 deletions

View File

@@ -98,17 +98,26 @@ func (r *Response) BodyReader() io.Reader {
return r.Body
}
// limitedReadCloser wraps an io.LimitedReader with a separate Closer
// so the original body can be closed.
// limitedReadCloser enforces a maximum number of bytes that may be read from
// a response body. Reading more than limit bytes returns ErrResponseTooLarge
// rather than silently truncating the body. The original body is closed via
// the separate Closer.
type limitedReadCloser struct {
R io.LimitedReader
C io.Closer
r io.Reader // an io.LimitReader over the original body (limit+1 bytes)
c io.Closer // the original body, for Close
limit int64
read int64
}
func (l *limitedReadCloser) Read(p []byte) (int, error) {
return l.R.Read(p)
n, err := l.r.Read(p)
l.read += int64(n)
if l.read > l.limit {
return n, ErrResponseTooLarge
}
return n, err
}
func (l *limitedReadCloser) Close() error {
return l.C.Close()
return l.c.Close()
}