feat(middleware)!: replace unary HTTP request evaluation with a streaming hook
@pimlock ci sta già lavorando.
Dal 14/9/2026.
- Lingua principale
- Rust
- Stelle
- 8.7k
- Fork
- 1.3k
- Merge medio
- 2g 11h
- PR unite (30g)
- 253
Descrizione
User Story
As a supervisor middleware author, I want one streaming HTTP request hook with headers-only, whole-body, and incremental inspection, so that I can handle bounded requests and large uploads through a contract consistent with HTTP response middleware.
Problem Statement
SupervisorMiddleware.EvaluateHttpRequest is unary. It receives one buffered HttpRequestEvaluation and returns one HttpRequestResult; request and replacement bodies have a 4 MiB platform limit. Even a stage that needs only headers enters the buffered request path.
The response contract introduced in #3073 and implemented by #3074 uses the hook-specific bidirectional HttpResponsePreReturn.Evaluate service, with preflight, body units, trailers, and session termination. Keeping the unary request hook would stabilize two different processing models for closely related extension operations.
Parent: #2565. This is an intentional breaking change before 0.1.0. Remove the unary request hook and replace it with the streaming contract; preserving the old RPC or providing a unary compatibility adapter is not a requirement.
Impact / Why This Matters
Middleware authors cannot inspect request headers without whole-body buffering or incrementally process bodies larger than the inspection limit. Operators must accept buffering latency and capacity limits, bypass inspection where fail-open permits it, or use another proxy. Bypassing does not provide the requested inspection, and another proxy duplicates policy and credential-boundary integration.
Delaying replacement until after 0.1.0 would leave extension authors with a compatibility migration during the stable-contract period. Making the change now lets request and response middleware share lifecycle conventions while preserving their different enforcement boundaries.
Proposed Design
Replace EvaluateHttpRequest with a hook-specific bidirectional service for HTTP_REQUEST/PRE_CREDENTIALS, following the HttpResponsePreReturn.Evaluate pattern. HttpRequestPreCredentials.Evaluate is the proposed name. Retain the existing registration, policy attachment, host selection, and ordering workflow.
Each selected stage receives a preflight containing request context, admitted target, safe headers, configuration, effective payload limits, and permitted body modes. It can skip, inspect, or explicitly block the request. Inspection supports headers-only processing, a bounded whole-body value, or normalized byte units. Whole-body inspection remains available through this stream for simple middleware implementations.
Align with the finalized response contract from #3073/#3074:
- One ordered stream per request and stage, with stage-local body sequences and exactly one ordered result for each preflight, body, and trailer event.
- Explicit pass-through, replacement, block, and skip-remaining actions. Skip and fail-open end only the affected stage; later stages continue with the current validated input.
- A final result-bearing body unit marked
end_of_stream, defined empty-body behavior, trailers for normally completed body inspection, and one best-effort sharedMiddlewareSessionEndnotification without a result. - Stage-local body-mode eligibility and limits. Whole-body input and replacement must fit the effective limit; streaming bounds each input and replacement without treating that limit as a total upload cap.
- Unit-local streaming, with each result accounting for its entire input. Cross-unit retention, rolling-window inspection, and an author SDK are follow-up work.
Define request-specific semantics explicitly. Headers-only middleware must not itself force body accumulation. A whole-body decision must finish before any request bytes reach upstream. Incremental processing can release approved bytes, so a later block stops further forwarding but cannot undo upstream disclosure or side effects. Specify downstream error behavior separately from upstream commitment, including early upstream responses, cancellation, and connection reuse. An explicit block applies regardless of on_error; malformed results and transport failures follow on_error. Never replay a partially forwarded request to recover a failed stage.
Preserve policy admission and post-transformation checks before later stages or upstream delivery. Derive permitted modes from the complete request path: body-aware GraphQL/JSON-RPC/MCP policy, credential body rewrites, or signing may still require buffering. Unsupported combinations must have explicit behavior and must not silently weaken enforcement. Keep credential injection after inspection and retain direction-specific header/trailer protections and supervisor-owned framing.
Acceptance Criteria
- The public unary RPC and request-only messages/adapters are removed. Built-ins, remote services, examples, fixtures, and affected generated bindings use the new request stream. Shared response/WebSocket types remain valid.
- The protobuf contract documents preflight actions, body modes, ordered results, replacements including empty bytes, trailers, terminal events, invalid transitions, and diagnostic limits consistently with the response hook.
- Eligible headers-only and streaming requests can exceed the old whole-body limit without whole-request buffering; whole-body mode remains bounded and withholds all upstream request bytes until approval.
- Mixed-mode and mixed-limit chains preserve policy order, transformations, skip-remaining, explicit denial, and fail-open/fail-closed behavior without replay or loss of input.
- Request commitment, partial forwarding, early upstream responses, downstream errors, cancellation, disconnects, and policy reload have documented and tested outcomes.
- Backpressure, concurrent-stream admission, per-result timeouts, whole-body accumulation deadlines, and cleanup bound resource use, including for slow or unknown-length uploads.
- Tests cover bodyless and empty requests, Content-Length and chunked bodies, trailers,
Expect: 100-continue, size-changing transformations, over-capacity input/replacements, invalid results, and failures before and after upstream forwarding. - Tests preserve post-transformation body-aware policy enforcement, credential non-disclosure, protected-header validation, and correct upstream framing. OCSF events omit bodies, credentials, query secrets, and free-form middleware reasons.
- External middleware e2e coverage demonstrates the new hook alongside response middleware, including whole-body and streaming use cases.
- Architecture, RFC 0009's contract description, published middleware/config documentation, and relevant skills describe the replacement. The
0.1.0upgrade guide in #3063 includes a concrete unary-to-stream migration and coordinated supervisor/service upgrade requirements, with no silent unary fallback.
Alternatives Considered
Keep unary and streaming APIs with a compatibility adapter. This is the approach proposed in #2431, but it retains a contract we intend to remove before 0.1.0. Whole-body mode provides the bounded inspection workflow through the replacement API.
Wrap the buffered request in a stream without incremental processing. This changes transport cardinality but retains the buffering and upload-size limitations.
Reuse the response RPC or introduce a universal inspection RPC. Requests have different metadata, mutation authority, policy checks, and upstream commitment semantics. Share conventions and internal machinery where useful while keeping the public hooks distinct.
Agent Investigation
- Current protobuf contains both the unary request RPC and the new response streaming contract. Removing
HttpRequestResultalso requires updating response diagnostic comments that currently reference its reason-code format. - Request relay buffers requests before middleware. Known-length overflow can remain recoverable before consumption; chunked overflow cannot resume the raw stream safely. The current buffered collector also rejects nonempty request trailers.
- Middleware architecture assigns chain sequencing and validation to the middleware runtime, with HTTP parsing, policy integration, and forwarding owned by the network supervisor. Both built-in and remote invocation paths need migration.
- #3073 defines the response protocol; #3074 is still open at filing and supplies the runtime reference. Align with its final contract rather than freezing details from an intermediate revision.
- #2431 already tracks broader request streaming and inspected HTTP/2 support. This issue supersedes its unary-retention proposal for the
0.1.0API decision. Coordinate overlapping implementation; HTTP/2 transport delivery remains tracked by #2431/#2426 and is not an additional acceptance gate here. The new API should remain independent of HTTP wire framing. - Coordinate lifecycle conventions with #3056 and extension compatibility discovery with #3057. This issue does not introduce a separate version-negotiation scheme.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Valutazione
Questa issue non è ancora stata valutata.