Skip to content

Axum: enforce configurable buffered request-body limits #393

Description

@ChristianPavilonis

Part of #391.

Description

Replace unlimited JSON request-body buffering in the Axum adapter with a configurable limit. Keep this issue focused on a small, testable safeguard with an established default; defer controls that need workload measurements or application-policy decisions.

Current implementation on audited main

JSON and application/*+json requests call to_bytes(axum_body, usize::MAX) before entering the core router. Core extractor checks cannot prevent this earlier allocation. Other request bodies remain streams.

See request.rs, lines 20-35.

Rationale for the default

Use 2 MiB, exactly 2,097,152 bytes, matching Axum's existing default for buffering extractors such as Bytes, String, Json, and Form. Our adapter's direct body collection bypasses that protection.

Sources:

This is alignment with an established framework default, not a claim that 2 MiB suits every application or makes the whole process memory-safe. Applications must be able to configure a different limit. Fastly and Cloudflare resource quotas are not the basis for this value.

Prerequisite PR and scope correction

PR #275 is the prerequisite for this issue. It adds route-aware ingress admission, bounded body handling, request-start-relative read deadlines, application-owned grants, platform/memory metadata, and egress completion resources. The standard Axum path calls begin_ingress before body conversion and dispatch_admitted afterwards; see service.rs at the inspected PR head. Its request conversion keeps the body lazy and wraps reads in a deadline; see request.rs at the inspected PR head.

The old unlimited JSON prebuffer and per-request block_in_place bridge described in the main baseline are replaced by the PR. Do not fix those superseded paths independently. Re-audit the accepted version only for the remaining buffered-body limit, configuration override, 413 mapping, and conversion-owned cleanup. Reuse its existing bounded-body/admission mechanisms; active-request limits, handler cancellation, and general overload policy remain outside this issue.

Raw parser accounting and some memory/deadline guarantees are explicitly unsupported or BestEffort in the PR. Preserve those distinctions.

Proposed solution

  1. Inspect the accepted PR feat: implement portable outbound HTTP and request lifecycle contracts #275 request/body path and record which buffered JSON checks already exist. Do not reintroduce the retired eager prebuffer or enforce the same limit twice.
  2. Apply a configurable 2 MiB default to the buffered JSON/+json path through the accepted existing mechanism. Keep configuration validation and error mapping at the owning boundary.
  3. Enforce actual received bytes, not Content-Length alone. Prove exactly-at-limit success and one-byte-over HTTP 413 through the real service path.
  4. Stop consuming rejected/interrupted bodies and release conversion-owned resources using the existing lifecycle contract.
  5. Preserve incremental non-JSON handling. This issue adds no general stream-size/duration, concurrency, queue, or handler-deadline policy. Keep the explicitly deferred section below authoritative.

Done when

  • Re-audit the accepted PR feat: implement portable outbound HTTP and request lifecycle contracts #275 implementation and record which checks are already covered. Add only residual changes and missing production acceptance evidence.
  • The accepted JSON buffering path is bounded; if feat: implement portable outbound HTTP and request lifecycle contracts #275 removed the legacy eager collection, do not recreate it just to satisfy this check.
  • The default is 2 MiB, with an explicit configuration override.
  • The limit applies to both application/json and application/*+json.
  • Enforcement checks bytes as they arrive, including chunked bodies without Content-Length.
  • A body exactly at the limit is accepted; a body one byte beyond it receives HTTP 413 rather than 500.
  • Tests cover declared-length and chunked bodies, the configured override, and interrupted/erroring reads. Verify oversized-body status through the adapter service path, not only a conversion helper.
  • Rejected or failed collection stops consuming the body and releases conversion-owned resources.
  • Non-JSON request bodies remain incremental, with no new total-size or duration restriction.
  • Documentation explains that this bounds adapter-owned request-body buffering, not total process memory, handler allocations, or concurrency.

Explicitly deferred

These controls are not required to close this issue. Do not choose numerical defaults for them without the relevant evidence:

Control Evidence or decision needed
Maximum active requests Load tests on a specified CPU/memory allocation with representative request costs
Body idle timeout Expected client behavior, upload speeds, and reverse-proxy settings
Total upload deadline Supported upload sizes and minimum acceptable throughput
Handler deadline Application latency requirements, upstream budgets, and caller deadlines
Total streamed-body size Application payload requirements, separate from retained-memory bounds
Maximum stream lifetime Application support for SSE, long polling, and other long-lived responses
Application queue A demonstrated benefit for the intended workload before introducing one

General handler cancellation, overload protection, capacity recovery, and execution-bridge changes are also deferred from this narrowed issue. Cleanup here covers the body-conversion path only; it does not promise cancellation of arbitrary CPU-bound or blocking handler work.

Related work and boundaries

Affected areas

Adapter -- Axum; Documentation; Core only if a minimal shared error hook is necessary.

Dependencies

Primary prerequisite: PR #275. Build residual production work on its accepted contracts, not the superseded main implementation.

Implementation and relationship tracking

Implementation: draft PR #407 on issue-393-buffered-body-limit-spec, based on PR #406. This records the implementation branch order, not a new requirement that every #392/#396 acceptance item must finish before buffered-body work can proceed. Accepted-prerequisite integration and remaining acceptance are still required before merge. GitHub Development now links this issue to PR #407, whose description includes Closes #393. The issue remains open while the PR is draft.

Activity

  1. changed the title [-]Axum: enforce request limits, deadlines, and overload protection[/-] [+]Axum: enforce configurable buffered request-body limits[/+] on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions