Cannot set http 413 status from http.MaxBytesReader

99 views
Skip to first unread message

Rory Campbell-Lange

unread,
Aug 29, 2023, 4:44:51 PM8/29/23
to golang-nuts
I've made an http middleware that uses http.MaxBytesReader to limit the accepted size of requests.

The middleware is as follows:

func bodyLimitMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 1<<2) // example limited size
next.ServeHTTP(w, r)
})
}

It seems sensible to send a 413 status code to any clients transgressing limits. However I can't find a way of raising an http.StatusRequestEntityTooLarge either from the middleware or from any http endpoint handler.

For example the test below fails with a 200 status, as shown at https://go.dev/play/p/bcU20WP9Op0.

Advice gratefully received.

func TestBodyLimit(t *testing.T) {
r := httptest.NewRequest(http.MethodPost, "http://www.example.com/", strings.NewReader(bodyMaker(1<<3)))
w := httptest.NewRecorder()
testHandler := http.HandlerFunc(func(w http.ResponseWriter, ri *http.Request) {
_, err := io.Copy(w, ri.Body)
if err != nil {
e := new(http.MaxBytesError)
if errors.As(err, &e) {
w.WriteHeader(http.StatusRequestEntityTooLarge)
} else {
t.Fatal(err)
}
}
})
bodyLimitMiddleware(testHandler).ServeHTTP(w, r)
if got, want := w.Result().StatusCode, http.StatusRequestEntityTooLarge; got != want {
t.Fatalf("bad status: got %v want %v", got, want)
}
}

Nagaev Boris

unread,
Aug 29, 2023, 5:54:31 PM8/29/23
to Rory Campbell-Lange, golang-nuts
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/ZO5YoHWGg6H5/DFO%40campbell-lange.net.

Hi!

HTTP status is sent before the body, that is why you can not set HTTP
status in response after you start writing the body in io.Copy(w, ...)
call. Writes to w resulted in HTTP status and headers being sent.


--
Best regards,
Boris Nagaev

Rory Campbell-Lange

unread,
Aug 30, 2023, 12:34:48 AM8/30/23
to Nagaev Boris, golang-nuts
On 29/08/23, Nagaev Boris (bna...@gmail.com) wrote:
> On Tue, Aug 29, 2023 at 5:44 PM Rory Campbell-Lange
> <ro...@campbell-lange.net> wrote:
> >
> > I've made an http middleware that uses http.MaxBytesReader to limit the accepted size of requests.
...
> > It seems sensible to send a 413 status code to any clients transgressing limits. However I can't find a way of raising an http.StatusRequestEntityTooLarge either from the middleware or from any http endpoint handler.
...

> > testHandler := http.HandlerFunc(func(w http.ResponseWriter, ri *http.Request) {
> > _, err := io.Copy(w, ri.Body)
> > if err != nil {
> > e := new(http.MaxBytesError)
> > if errors.As(err, &e) {
> > w.WriteHeader(http.StatusRequestEntityTooLarge)
> > } else {
> > t.Fatal(err)
> > }
> > }
> > })

> HTTP status is sent before the body, that is why you can not set HTTP
> status in response after you start writing the body in io.Copy(w, ...)
> call. Writes to w resulted in HTTP status and headers being sent.

Hi Boris

Thanks very much for your help.

I changed the testHander as follows to create a temporary buffer to set the 413 status header before writing the body (as you suggested) and the test passed. (https://go.dev/play/p/Ur7RTDIBMf0). Thanks!

testHandler := http.HandlerFunc(func(w http.ResponseWriter, ri *http.Request) {
buf := bytes.Buffer{}
_, err := io.Copy(&buf, ri.Body)
if err != nil {
e := new(http.MaxBytesError)
if errors.As(err, &e) {
w.WriteHeader(http.StatusRequestEntityTooLarge)
} else {
t.Fatal(err)
}
}
buf.WriteTo(w)
})

Cheers,
Rory
Reply all
Reply to author
Forward
0 new messages