NVIDIA / NVIDIA/OpenShell

feat(supervisor): add streaming HTTP request middleware evaluation

Open
#2,431 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:supervisor topic:l7
Dominant language
Rust
Stars
8.7k
Forks
1.3k
Avg merge
2d 11h
Merged PRs (30d)
253

Description

Problem Statement

Parent: #1733

The current supervisor middleware contract exposes a unary EvaluateHttpRequest operation. The supervisor buffers the complete HTTP request body, constructs one HttpRequestEvaluation, and sends it to middleware. Request and replacement bodies are limited to 4 MiB.

This works for bounded, full-body inspection, but it prevents several important use cases:

  • Middleware that can decide from request headers without buffering the body.
  • Large uploads whose bodies should pass through unchanged, such as headers-only SigV4 signing.
  • Incremental inspection or transformation with backpressure.
  • Long-lived or unknown-length request bodies.
  • Streaming HTTP/2 and gRPC requests that cannot be represented as one bounded unary payload.

HTTP/2 support in #2426 also needs middleware evaluation to operate per logical HTTP stream rather than on raw connection bytes. The streaming gRPC portion of #2166 needs request method, metadata, body, duration, rate, and cancellation controls without requiring middleware to implement HTTP/2 framing.

Proposed Design

Add an optional request-scoped streaming evaluation capability for the existing HTTP_REQUEST middleware operation.

The interface should expose normalized HTTP request semantics that are independent of the wire version:

  • Request context, admitted destination, method, path, query, and safe headers.
  • Ordered, bounded body chunks.
  • Request trailers.
  • End-of-request and cancellation events.
  • Middleware decisions, transformations, findings, metadata, and failure outcomes.

The supervisor remains responsible for HTTP parsing, HTTP/2 framing and multiplexing, flow control, policy evaluation, credential injection, and upstream connections. An HTTP/1.x request and an HTTP/2 request stream should enter the same logical middleware session model.

Middleware bindings should advertise the capabilities they support. The final representation should be designed during implementation, but it must distinguish at least:

  • Unary versus streaming invocation.
  • Headers-only, full-body, digest, or incremental body processing.
  • Holding content until a final decision versus releasing approved chunks upstream.
  • Applicable body, chunk, timeout, and resource limits.
  • Whether unary and streaming modes provide equivalent enforcement semantics.

The supervisor should use one canonical request-session and chain execution model internally. Native streaming middleware maps directly onto that model. Existing unary middleware runs through an adapter that requests a full-body hold, buffers or otherwise retains the bounded body, invokes EvaluateHttpRequest, and translates the result into the common session outcome.

This keeps the existing API available for simple middleware implementations without making the supervisor maintain separate unary and streaming chain semantics. Whether the unary API remains permanently supported or is eventually deprecated should be decided after compatibility and migration impact are understood.

If middleware supports both interfaces, selection should happen before body processing based on declared capabilities, body metadata, limits, and preference. The supervisor must not silently switch to weaker processing semantics after evaluation or upstream disclosure has begun.

The canonical chain runner should continue to own:

  • Middleware ordering and short-circuit behavior.
  • Accumulated header and body transformations.
  • Policy re-evaluation after transformations.
  • on_error handling.
  • Findings and metadata.
  • Backpressure, timeouts, cancellation, and resource limits.
  • Whether any part of the request has already been disclosed upstream.

For HTTP/2, each logical request stream should have an independent middleware session. A denial, timeout, or cancellation on one stream must not terminate unrelated streams on the same connection.

The exact protobuf messages, state machine, buffering or spooling strategy, and migration policy are intentionally left to the implementation design.

Relationship to Other Issues

#2426

#2426 records the current HTTP/2 transport gap: the supervisor L7 proxy only negotiates HTTP/1.1, so HTTP/2 traffic requires tls: skip and bypasses L7 inspection.

The HTTP/2 work may be implemented as part of this issue so the result is one complete vertical slice: negotiate HTTP/1.1 or HTTP/2, relay HTTP/2 as independent logical request streams, and run both wire versions through the same normalized middleware session. If implementation planning keeps #2426 as a separate delivery unit, the two issues must be coordinated and this issue depends on its inspected HTTP/2 relay. In either case, completing this issue requires a working HTTP/2 middleware path rather than opaque tls: skip passthrough.

The middleware API should not expose HTTP/2 frames, HPACK state, stream identifiers as routing authority, or connection-level framing details.

#2166

This interface provides a foundation for governing the gRPC portion of #2166 at the HTTP stream level:

  • Host, service, and method admission through authority and path.
  • Metadata restrictions.
  • Request byte, rate, duration, and concurrency limits.
  • Streaming cancellation and audit outcomes.

First-class gRPC message boundaries, protobuf parsing, and application-specific media semantics can be added as a separate typed operation if required. Generic HTTP middleware body chunks must not be treated as gRPC messages.

WebSocket message processing is tracked separately by #2428.

Alternatives Considered

Replace the unary operation immediately

A streaming-only contract would simplify the long-term protocol surface, but it would make simple middleware harder to implement and could break existing integrations. The implementation should first establish the canonical session model and compatibility adapter, then make deprecation a separate decision.

Add an HTTP/2-specific middleware API

HTTP/1.x and HTTP/2 carry the same normalized request concepts. Separate middleware APIs would duplicate policy, chaining, transformation, and failure semantics while leaking transport details to middleware authors.

Advertise only a streaming boolean

Transport streaming does not say whether middleware needs the complete body, can process incrementally, or permits upstream disclosure. A single boolean cannot safely drive buffering, fallback, or chain composition.

Keep buffering every request

This preserves atomic whole-request decisions but cannot support large uploads, long-lived streams, headers-only processing, or the streaming gRPC use cases motivating this work.

Agent Investigation

  • Confirmed that SupervisorMiddleware.EvaluateHttpRequest currently accepts one buffered HttpRequestEvaluation.
  • Confirmed that request and replacement bodies have a 4 MiB platform limit.
  • Confirmed that the HTTP relay buffers the body before invoking the middleware chain.
  • Reviewed RFC 0009, which reserves a separate operation-specific streaming method and distinguishes transport streaming from incremental processing.
  • Reviewed #2166, #2426, #2428, and #2284 to understand the overlap between the middleware contract, HTTP/2 transport, WebSocket messages, and buffered HTTP parser consolidation.
  • Reviewed prior design notes covering SigV4, large uploads, body retention, partial disclosure, HTTP/2 multiplexing, and unary compatibility.

Definition of Done

  • The implementation includes a reviewed design for capability negotiation, stream events and actions, lifecycle transitions, and compatibility behavior.
  • The supervisor negotiates HTTP/1.1 or HTTP/2 for inspected traffic and routes HTTP/2 connections through an L7 relay without requiring tls: skip.
  • HTTP/1.x requests and HTTP/2 request streams use the same normalized middleware session contract.
  • The HTTP/2 relay preserves multiplexing, flow control, trailers, stream cancellation, and isolation between concurrent request streams.
  • Middleware can complete evaluation from headers without forcing body buffering.
  • Full-body middleware retains the request under explicit, bounded resource limits and releases no bytes before final approval.
  • Incremental middleware has defined backpressure, transformation, denial, and partial-disclosure semantics.
  • Unary, streaming, and mixed middleware chains use one canonical chain runner.
  • Existing unary middleware either remains compatible through an adapter or has an explicit migration and deprecation plan.
  • Capability selection cannot silently weaken enforcement semantics.
  • HTTP/2 cancellation and failures are isolated to the affected request stream.
  • Existing ordering, mutation validation, policy re-evaluation, findings, metadata, timeout, fail-open, and fail-closed behavior remain consistent.
  • Automated tests cover unary-only, streaming-only, and mixed chains over HTTP/1.x and HTTP/2.
  • RFC 0009, architecture documentation, and published middleware documentation describe the resulting contract and compatibility policy.

Non-Goals

  • Parsing raw HTTP/2 frames in middleware.
  • Treating arbitrary HTTP body chunks as gRPC message boundaries.
  • Protobuf or application-specific media inspection.
  • WebSocket message middleware tracked by #2428.
  • Response-body or bidirectional semantic inspection in the first iteration.
  • HTTP/3 support.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with SupervisorMiddleware.EvaluateHttpRequest and RFC 0009, then review related issues #2426 and #2166 to understand the transport and middleware constraints. Done means a reviewed streaming design and implementation covering HTTP/1.x and HTTP/2 sessions, capability negotiation, unary compatibility, isolated cancellation, and automated mixed-chain tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
grpc, rust
Domain
api, backend-api-design, networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.