HTTP: fix error_page handling for 413 with HTTP/2 and HTTP/3

When client_max_body_size is exceeded, ngx_http_discard_request_body()
is called to signal body discarding. For HTTP/2 it set stream->skip_data
but left r->discard_body unset; for HTTP/3 it returned immediately
without setting any discard flag.

The phase handler in ngx_http_core_find_config_phase() checks
!r->discard_body to avoid re-evaluating the body size limit during
subsequent internal redirects. Since the flag was never set for
HTTP/2 and HTTP/3, the check triggered again during the error_page
redirect, detected r->error_page already set, and fell back to the
built-in 413 response instead of serving the configured custom page.

Fix by setting r->discard_body = 1 in the HTTP/2 and HTTP/3 paths of
ngx_http_discard_request_body(), matching existing HTTP/1 behaviour.

Since r->discard_body may now be set for HTTP/2 and HTTP/3 requests,
guard ngx_http_set_write_handler() to always use ngx_http_test_reading()
for these protocols. The HTTP/1-specific
ngx_http_discarded_request_body_handler() must not be used there:
HTTP/2 fake connections share the real socket recv handler, while
HTTP/3 would incorrectly invoke QUIC stream recv callbacks.

Fixes #1356
This commit is contained in:
srujan-rai 2026-05-17 01:19:53 +05:30
parent eff1108854
commit b9ff619c79
2 changed files with 11 additions and 3 deletions

View file

@ -3008,9 +3008,15 @@ ngx_http_set_write_handler(ngx_http_request_t *r)
r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
r->read_event_handler = r->discard_body ?
ngx_http_discarded_request_body_handler:
ngx_http_test_reading;
r->read_event_handler =
#if (NGX_HTTP_V2)
r->stream ? ngx_http_test_reading :
#endif
#if (NGX_HTTP_V3)
r->connection->quic ? ngx_http_test_reading :
#endif
r->discard_body ? ngx_http_discarded_request_body_handler :
ngx_http_test_reading;
r->write_event_handler = ngx_http_writer;
wev = r->connection->write;

View file

@ -642,12 +642,14 @@ ngx_http_discard_request_body(ngx_http_request_t *r)
#if (NGX_HTTP_V2)
if (r->stream) {
r->stream->skip_data = 1;
r->discard_body = 1;
return NGX_OK;
}
#endif
#if (NGX_HTTP_V3)
if (r->http_version == NGX_HTTP_VERSION_30) {
r->discard_body = 1;
return NGX_OK;
}
#endif