Skip to main content

infer_stream_capability

Function infer_stream_capability 

Source
fn infer_stream_capability(
    policy_nodes: &[NodeConfig],
    nodes: &HashMap<String, Box<dyn Plugin>>,
    edges: &HashMap<String, HashMap<String, String>>,
) -> (HashSet<String>, Vec<BufferingReason>)
Expand description

For each upstream node, walks only the success/outcome ports forward to see whether anything before client reads the response body.

error ports are never walked, on the upstream itself or on any node further along the chain: an error exit runs through the node’s own error edge (unwalked here for the same reason), the policy’s error_handler catch-all, or the engine’s built-in fallbacks — none of which this walk can see.

This is safe only because of two separate facts, not one general guarantee:

  • Every gateway-generated error body — ErrorHandlerPlugin::execute, the engine’s no-handler 500 fallback, and the NODE_NOT_FOUND fallback, all in this crate — explicitly clears response.stream before writing response.body, so none of those three specific sites can leave a stale stream alongside a generated body.
  • An error edge can in principle route to any node, including one that writes response.body itself without going through those three sites. Nothing here walks that edge to rule it out; it is safe today only because no plugin that opts out of reads_response_body() (the set that can run downstream of a stream-capable upstream without forcing it to buffer: client, opentelemetry, prometheus, proxy-rewrite, request-id, response-rewrite, skywalking, traffic-label, zipkin, and proxy-cache in its lookup role only) ever returns Err from execute — every return Err in the first nine plugins’ source is in from_config (construction-time validation), never in execute; proxy-cache’s lookup role does call a fallible backend (ResponseCache::get) from execute, but matches on the result and degrades a failure to a miss rather than propagating Err. This is exactly why proxy-cache’s store and purge roles do not opt out despite neither reading the response body: store because it reads it to cache it, and purge because it can return Err from execute on a failed backend, which this invariant forbids for an opt-out node. If a future change to any of these plugins starts erroring from execute where it previously didn’t, this walk would not catch it, and nothing else currently enforces it either.

Iterates policy_nodes (a Vec), not the nodes map, and visits each node’s outgoing ports in sorted-name order: the same precedent as the cycle check in compile_policy, so the reported blocker for an unchanged policy is stable across compiles instead of depending on HashMap iteration order.

An edge whose target doesn’t resolve to a real node in nodes is treated as blocking rather than assumed harmless — to_nodes aren’t validated against the node table the way from_nodes are, so this walk cannot assume every edge target exists.