Skip to content

Axum: preserve incremental streaming with bounded backpressure #394

Open

Description

@ChristianPavilonis

Part of #391.

Description

Support production uploads, long-lived responses, and large proxy transfers without collecting the entire body in memory. Keep the portable non-Send body contract intact.

Current implementation on audited main

  • Non-JSON incoming bodies become core streams in request.rs, lines 20-L35, which is a useful starting point.
  • into_axum_response consumes every Body::Stream through futures::executor::block_on into an unbounded Vec before returning an Axum body; see response.rs, lines 11-L43. First-byte delivery therefore waits for end-of-stream.
  • Proxy uploads collect streamed chunks in a Vec, and proxy responses call response.bytes(); see proxy.rs, lines 49-L79.
  • Core uses LocalBoxStream to remain WASM-compatible; see crates/edgezero-core/src/body.rs. Existing tests use finite, immediately available chunks and verify final content, not incremental delivery.

Prerequisite PR and scope correction

PR #275 is the prerequisite and already implements the main streaming/egress foundation. It replaces eager response collection with AxumEgressBody and connection-owned Hyper delivery, and adds streamed outbound request/response contracts. See response.rs at the inspected PR head and connection.rs at the inspected PR head.

Do not implement the originally suggested second channel bridge or change core bodies to Send. This issue becomes production acceptance and residual hardening of the new transport. Source inspection is not new runtime proof. Reuse existing delayed-stream, disconnect, deadline, and backpressure tests, then add only missing cases for the stated production workload.

The PR classifies response-egress completion, backpressure, abort, and write deadlines as BestEffort. Application/server terminal reports do not prove end-client receipt; this Epic must not relabel those guarantees without additional evidence.

Proposed solution

  1. Adopt PR feat: implement portable outbound HTTP and request lifecycle contracts #275's response-egress, owned Hyper connection, and outbound streaming implementations. Do not create a second adapter bridge.
  2. Review its existing tests and add the smallest missing production acceptance cases: delayed first chunk, long-lived streams, slow/zero-read peers, upload/download pressure, disconnects, and source failures.
  3. Verify retained byte/chunk bounds on the actual transport and document allocations outside guest-visible accounting. A passing finite in-memory body fixture is not backpressure evidence.
  4. Integrate process draining with existing ResponseEgressCompletion/Attempt ownership so each terminal path releases request-owned resources exactly once.
  5. State what completion means at the server/host boundary, and retain BestEffort classification where end-client receipt or finite teardown is not proved. Preserve the portable non-Send body contract.

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.
  • A delayed multi-chunk response reaches the client before the producer emits its final chunk.
  • An SSE-style or open-ended stream remains usable without waiting for end-of-stream.
  • A slow consumer keeps producer progress/retained body data within the documented transport bound. Record native runtime evidence without asserting end-client receipt.
  • Large uploads and proxied downloads have bounded retained body data and preserve bytes/status/headers.
  • Consumer disconnect, source error, deadline, and shutdown clean up producer work without hangs.
  • Tests use delayed and failing streams, not just ready-in-memory fixtures.
  • Existing WASM target checks and portable core tests still pass.

Related work and boundaries

Coordinate with #107 and #110 rather than introducing duplicate portable streaming helpers. Pooling, redirects, timeout policy, and header filtering belong to the proxy-correctness task. This task owns streaming transport only.

Affected areas

Adapter -- Axum; Core (routing, extractors, middleware) only if a small portable hook is required; Documentation.

Dependencies

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

Use PR #275 directly for the existing ingress, deadline, cancellation, and egress contracts. Coordinate with #392 for draining open streams. #393 owns only buffered JSON limits and is not a prerequisite for a broader admission or cancellation framework. Do not add arbitrary stream-lifetime or overload defaults here.

Implementation and relationship tracking

No published implementation/acceptance PR currently represents this issue. PR #275 supplies the HTTP foundation. #393 is not a prerequisite for this streaming acceptance work. This issue blocks #395's bounded-body-transport acceptance, not its independently deliverable pooling/header work.

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