Skip to content

Commit 873af2a

Browse files
committed
net/http: call requestTooLarge on unwrapped ResponseWriter in MaxBytesReader
The current implementation of MaxBytesReader only calls requestTooLarge when the provided ResponseWriter directly implements the internal requestTooLarger interface. This breaks expected behavior when the ResponseWriter is wrapped (e.g., middleware) and the inner writer implements requestTooLarger. This change adds an unwrapping loop, similar to what http.newResponseController does, to traverse through any Unwrap() chain and properly call requestTooLarge if found. This improves compatibility with custom or middleware-wrapped writers while preserving current behavior for unwrapped ones. Fixes #73754
1 parent 045b5c1 commit 873af2a

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

src/net/http/http_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package http
99
import (
1010
"bytes"
1111
"internal/testenv"
12+
"io"
1213
"io/fs"
1314
"net/url"
1415
"os"
@@ -189,6 +190,45 @@ func TestNoUnicodeStrings(t *testing.T) {
189190
}
190191
}
191192

193+
type requestTooLargerResponseWriter struct {
194+
called bool
195+
}
196+
197+
func (rw *requestTooLargerResponseWriter) Header() Header { return Header{} }
198+
func (rw *requestTooLargerResponseWriter) Write(b []byte) (int, error) { return len(b), nil }
199+
func (rw *requestTooLargerResponseWriter) WriteHeader(statusCode int) {}
200+
func (rw *requestTooLargerResponseWriter) requestTooLarge() {
201+
rw.called = true
202+
}
203+
204+
type wrapper struct {
205+
ResponseWriter
206+
}
207+
208+
func (w *wrapper) Unwrap() ResponseWriter {
209+
return w.ResponseWriter
210+
}
211+
212+
func TestMaxBytesReaderUnwrapTriggersRequestTooLarge(t *testing.T) {
213+
body := strings.NewReader("123456")
214+
limit := int64(5)
215+
216+
innerRw := &requestTooLargerResponseWriter{}
217+
wrappedRw := &wrapper{ResponseWriter: innerRw}
218+
219+
l := MaxBytesReader(wrappedRw, io.NopCloser(body), limit)
220+
221+
buf := make([]byte, 10)
222+
_, err := l.Read(buf)
223+
224+
if _, ok := err.(*MaxBytesError); !ok {
225+
t.Errorf("expected MaxBytesError, got %T", err)
226+
}
227+
if !innerRw.called {
228+
t.Errorf("expected requestTooLarge to be called, but it wasn't")
229+
}
230+
}
231+
192232
func TestProtocols(t *testing.T) {
193233
var p Protocols
194234
if p.HTTP1() {

src/net/http/request.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,9 +1243,23 @@ func (l *maxBytesReader) Read(p []byte) (n int, err error) {
12431243
type requestTooLarger interface {
12441244
requestTooLarge()
12451245
}
1246-
if res, ok := l.w.(requestTooLarger); ok {
1247-
res.requestTooLarge()
1246+
// Unwrap the ResponseWriter wrappers until we find one that implements
1247+
// the server-only requestTooLarger interface, then call requestTooLarge().
1248+
// This ensures that even if the ResponseWriter is wrapped by a custom implementation,
1249+
// the underlying server writer can be notified when the request body is too large.
1250+
rw := l.w
1251+
for {
1252+
if res, ok := rw.(requestTooLarger); ok {
1253+
res.requestTooLarge()
1254+
break
1255+
}
1256+
unwrapper, ok := rw.(rwUnwrapper)
1257+
if !ok {
1258+
break
1259+
}
1260+
rw = unwrapper.Unwrap()
12481261
}
1262+
12491263
l.err = &MaxBytesError{l.i}
12501264
return n, l.err
12511265
}

0 commit comments

Comments
 (0)