Repository navigation
fix(gateway): give the request body limit one source and raise it to 277 MiB - #272
Merged
Merged
Conversation
…277 MiB The limit lives on two layers: axum's extractor (`DefaultBodyLimit::max`, the only writer of the `DefaultBodyLimitKind` extension that overrides the 2 MiB `DEFAULT_LIMIT`) and tower-http's outer guard (`RequestBodyLimitLayer::new`, which answers 413 without reading the body when `Content-Length` exceeds its own number). The outer layer cannot raise the inner one, but it wins whenever it is smaller. Raising the constant from 8 MiB to 277 MiB while the outer layer still carried `70 * 1024 * 1024` would have capped the effective limit at 70 MB - measured, a 71 MiB body came back `413 length limit exceeded` (tower-http's own body text, not axum's `Failed to buffer the request body: ...`). - `GATEWAY_BODY_LIMIT` 8 MiB -> 277 MiB, and the outer layer now references that same constant instead of a literal of its own - move the outer layer into `routes::router()` (it was built in `main.rs` with its own number) so the stack the tests exercise is the production stack - new gate `src/body_limit_gate.rs`: every layer constructor must take `GATEWAY_BODY_LIMIT`, and no second `...BODY_LIMIT` constant may exist - probe sizes: 3 MiB -> 9 MiB (crosses the old 8 MiB) plus a new 71 MiB leg that fails on the pre-fix outer layer; the login negative control is unchanged Tests: 302 -> 308 passed, fmt/clippy clean.
Owner
Author
|
✅ LGTM (self-review) Verified on the branch head:
Two notes recorded in the PR body rather than fixed here:
|
argszero
added a commit
that referenced
this pull request
Sep 21, 2026
Request body limit follows the single-constant refactor from #272.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The request body limit was stored on two layers with two numbers, and the smaller one silently wins. This raises the gateway limit from 8 MiB to 277 MiB and gives the number a single source, so the two layers can no longer disagree.
Continues rant
2026-09-18T09:14:18("the configured limit is not the effective limit").Related Issue
rant
2026-09-18T09:14:18.500174+08:00(request body limit). Itsv0.7.26half moved the limit to the layer axum actually reads; this PR covers the follow-up observed on dev, where a 10 MB request was still rejected.Changes
The limit lives on two layers:
DefaultBodyLimit::max(…)— the only writer of theDefaultBodyLimitKindextension that overrides axum-core's 2 MiBDEFAULT_LIMITString/JsonbodiesRequestBodyLimitLayer::new(…)413without reading the body whenContent-Lengthexceeds its own numberThe outer layer cannot raise the inner one (it never writes that extension — that was the original defect), but it clamps it from below whenever it is smaller. That is exactly what would have happened here: with the constant raised to 277 MiB and the outer layer still carrying its own
70 * 1024 * 1024, the effective limit would have been 70 MB, not 277 MiB. Measured on the pre-fix tree: a 71 MiB body returned413 length limit exceeded— tower-http's own body text, not axum'sFailed to buffer the request body: ….GATEWAY_BODY_LIMIT:8 MiB→277 MiB, with the two-layer trap documented at the constant.GATEWAY_BODY_LIMITtoo, and is constructed inroutes::router()instead ofmain.rs— the router is what the tests build, so the tested stack is the production stack.src/body_limit_gate.rs(#[cfg(test)] mod, not in the release binary, no new dependencies): every layer constructor must receiveGATEWAY_BODY_LIMIT, and no second…BODY_LIMITconstant may exist anywhere undersrc/.CHANGELOG.mdis untouched: by convention it is written only bychore(release)PRs.Tests
Mutation matrix (each leg declares the expected red set; working tree restored byte-exactly, md5-verified):
base(this PR)70 * 1024 * 1024the_outer_limit_does_not_clamp_below_the_gateway_limit,body_limit_gate::every_body_limit_layer_is_the_same_single_numbergateway_routes_accept_bodies_past_the_default_limit,the_outer_limit_does_not_clamp_below_the_gateway_limitSECOND_BODY_LIMIT)body_limit_gate::every_body_limit_layer_is_the_same_single_numberbody_limit_gate::every_body_limit_layer_is_the_same_single_number(call-site positive control)Measured witness for the outer clamp, on the mutant that keeps
70 * 1024 * 1024:v0.7.26; a real 71 MiB / 100 MiB request against a deployed build has not been run yet.chat_completionsclones it once for the forward, so a single request peaks at roughly 2–3× this number. The prod host advertises 1.8 GiB total with ~0.57 GiB available, no swap, and the application container has nomem_limit. A 277 MiB request is therefore a host-level OOM risk there, not just a container one.Checklist
cargo test)cargo fmt --checkandcargo clippy -- -D warningspassstd-only, test-only)