Request Lifecycle
This page traces a single request from the moment a connection is accepted to the
moment the response is written back. It is source-verified against the runtime
handler (crates/octopus-runtime/src/handler.rs) and server setup
(crates/octopus-runtime/src/server.rs), so it reflects what actually runs today
rather than an aspirational design.
The path at a glance01
graph TD
A[Client connection] --> B[Listener / TLS termination]
B --> C{Probe or /metrics?}
C -->|Yes| C2[Answer directly, skip accounting]
C -->|No| D[Protocol detection]
D -->|WebSocket upgrade| WS[WebSocket proxy]
D -->|text/event-stream| SSE[SSE streaming proxy]
D -->|gRPC over HTTP/2| GRPC[gRPC proxy]
D -->|Internal __admin / __farp| INT[Admin / FARP handlers]
D -->|Plain HTTP| E[Host extraction + SNI check]
E --> F[Pre-match route, inject auth context]
F --> G[Middleware chain]
G --> H{Auth gateway}
H -->|401 / 403| R1[Error response]
H -->|Pass| I[Route match + prefix rewrite]
I --> J[Select upstream instance]
J --> K[Proxy with retry]
K --> L[Upstream response]
L --> M[Compression on the way out]
M --> Z[Client response]Step by step02
Listener and TLS
Each inbound connection is served by hyper's automatic protocol builder, which negotiates HTTP/1.1 or HTTP/2 per connection on the same port (h2c via prior-knowledge), and supports the connection upgrade that WebSocket needs. When TLS is enabled, the connection is terminated first — either with a static certificate or an operator-managed, hot-swappable certificate. The negotiated SNI is captured for the anti-spoofing check below. See TLS and HTTP/1.1 & HTTP/2.
Probes and metrics (before accounting)
Kubernetes-style health probes (/livez, /readyz, /startupz) and the
Prometheus endpoint (/metrics, /__metrics) are answered before the
in-flight request counter is incremented, so a readiness poll during drain never
inflates request metrics or holds up graceful shutdown. See
Probes & drain and Metrics.
Protocol detection
Before the request body is buffered, the handler checks for protocols that need streaming or upgrades, in this order:
WebSocket — a
Connection: Upgrade/Upgrade: websocketrequest is handled by the WebSocket proxy (the upstream is connected first, failing fast with502if unreachable, then a101 Switching Protocolsis returned). See WebSocket.SSE — an
Accept: text/event-streamrequest is streamed through the SSE proxy with zero buffering. See SSE.gRPC — a gRPC-over-HTTP/2 request is proxied transparently, propagating the
grpc-timeoutdeadline. See gRPC.
Internal routes are also intercepted here: /__admin (admin API/dashboard) and
the FARP API/push routes (/__farp, /__/farp, /_farp/v1, and the
/docs//swagger//redoc doc UIs). Everything else falls through to the plain
HTTP path, where the body is buffered into memory before proxying.
Host extraction and SNI anti-spoofing
The request host is taken from the HTTP/2 :authority (or the Host header),
lowercased with any port stripped. When TLS is in play and the check is enabled
(the default), a request whose host disagrees with the negotiated TLS SNI is
rejected with 421 Misdirected Request — this defends against host spoofing and
HTTP/2 connection coalescing across tenants.
Route pre-match and auth context
The handler pre-matches the route for (host, method, path) and injects the
matched route's auth settings (auth_provider, skip_auth, require_roles,
require_scopes, authz_rule) and any per-route CORS override into the request
extensions. This happens before the chain runs, so the auth gateway can honor
per-route auth. See Routing and Routes.
The middleware chain
The global chain is assembled once at startup and applied to every plain-HTTP request. It contains at most two entries, in this order:
Compression — added when
gateway.compression.enabled(the default). Registered first, it is the outermost wrapper and compresses the response body on the way out. See Compression.Auth gateway — added when there is at least one configured auth provider or
auth.global_enforceis on. It authenticates and authorizes the request, returning401/403and short-circuiting the chain on failure. See Authentication gateway.
If neither condition holds, the chain is empty and the request goes straight to routing and proxying.
You cannot declare an ordered middleware: array in YAML — that key does not
exist. Per-route rate_limit and cors are parsed and stored but not
enforced in the live chain, and the broader octopus-middleware catalogue
(CORS, rate limiting, caching, security headers, WAF, …) is builder-only. See
Middleware for the full wiring status.
Route match and prefix rewrite
Inside the final handler the route is matched in the trie router by host, method,
and path, with priority breaking ties. The matched route's strip_prefix is
removed and add_prefix is prepended (in that order) to form the upstream path;
the query string is preserved. A no match returns 404. See
Routing.
Upstream selection (load balancing)
The route resolves to an upstream cluster, and the load balancer selects an
instance. Convention/subdomain routes derive the {namespace, service} target
from the host (optionally via a Rhai script) and lazily register a Service-DNS
upstream. If no instance can be selected, the gateway returns 503. See
Load Balancing and
Service Discovery.
Load-balancing policy and instance weights are honored on the Kubernetes/operator path; static-config upstreams currently balance round-robin. See Upstreams.
Proxy and response
The proxy forwards the request to the selected instance (proxy_with_retry),
adding forwarding headers — including an X-Request-ID toward the upstream when
absent. The upstream response flows back out through the chain: the compression
middleware compresses the body, latency and outcome are recorded to metrics and
the activity log, and the response is written to the client. Upstream failures
surface as 502 Bad Gateway.
Status codes the gateway can produce03
| Status | When |
101 Switching Protocols | Successful WebSocket upgrade (after the upstream connects). |
401 / 403 | Auth gateway rejects authentication / authorization. |
404 | No route matches the request. |
421 Misdirected Request | Host disagrees with the negotiated TLS SNI (anti-spoofing). |
502 Bad Gateway | Upstream connection or proxy error. |
503 Service Unavailable | No healthy upstream instance available. |
Related04
Architecture — the components behind these steps.
Routing — trie matching, priority, prefix rewriting.
Middleware — the real chain and full catalogue.
Protocols — WebSocket, SSE, gRPC, and HTTP handling.
Security — TLS, SNI, and the auth model.