Description
Summary
http.MaxBytesReader
in the standard library uses an internal interface (requestTooLarger
) on the provided http.ResponseWriter
to notify the server when a request body exceeds the specified limit. However, when a custom ResponseWriter
wrapper is used, this internal callback is not invoked because the library does not unwrap the writer chain to find an implementation.
This causes the server to miss the "request too large" event, not closing the connection.
Proposal
Modify http.MaxBytesReader
to recursively unwrap the given ResponseWriter
if it implements an Unwrap() http.ResponseWriter
method (similar to how http.ResponseController
is handled), and call the internal requestTooLarge()
callback on the innermost ResponseWriter
that implements it.
This change would maintain backward compatibility, and improve behavior in common scenarios where middleware wraps the writer.
Motivation
When developers wrap http.ResponseWriter
to implement middleware features like logging, metrics, or response modification, they typically embed the original ResponseWriter
.
The current design of MaxBytesReader
checks only the top-level writer for the internal requestTooLarger
interface. As a result, an oversized request does not close the connection.
Example and Reproduction Steps
Here is a minimal example that illustrates the problem when wrapping ResponseWriter
:
package main
import (
"io"
"log"
"net/http"
)
type myWrapper struct {
http.ResponseWriter
}
func handler(w http.ResponseWriter, r *http.Request) {
wrapped := myWrapper{w}
r.Body = http.MaxBytesReader(wrapped, r.Body, 10)
body, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Read error:", err)
} else {
log.Println("Read body:", string(body))
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("done"))
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Curl with a request body exceeding the limit doesn't close the connection.
I have a patch ready implementing this fix and can submit a PR upon approval.