pub struct PingoraRequestCtx {Show 62 fields
pub _connection_permit: Option<OwnedSemaphorePermit>,
pub _global_connection_permit: Option<OwnedSemaphorePermit>,
pub client_addr: Option<IpAddr>,
pub client_http_version: Option<Version>,
pub cluster: Option<Arc<str>>,
pub cached_body_done_indices: Vec<bool>,
pub cached_executed_branch_filters: Vec<bool>,
pub cached_executed_filter_indices: Vec<bool>,
pub downstream_tls: bool,
pub peer_identity: Option<Arc<TlsPeerIdentity>>,
pub connection_upgraded: bool,
pub extensions: RequestExtensions,
pub filter_metadata: HashMap<String, String>,
pub pre_read_mutations: Vec<TrustedHeaderMutation>,
pub structured_metadata: HashMap<String, Value>,
pub mutated_request_body_len: Option<usize>,
pub pinned_pipeline: Option<Arc<FilterPipeline>>,
pub filter_results: HashMap<&'static str, FilterResultSet>,
pub filter_state: HashMap<usize, Box<dyn Any + Send + Sync>>,
pub metrics_cluster: Option<Arc<str>>,
pub metrics_cluster_shared: Option<SharedString>,
pub metrics_route: Option<SharedString>,
pub upstream_connect_start: Option<Instant>,
pub pre_read_body: Option<VecDeque<Bytes>>,
pub retained_pre_read_body: Option<VecDeque<Bytes>>,
pub adapted_request_body: Option<VecDeque<Bytes>>,
pub retained_adapted_request_body: Option<VecDeque<Bytes>>,
pub adapted_request_body_len: Option<usize>,
pub request_body_buffer: Option<BodyBuffer>,
pub request_body_bytes: u64,
pub request_body_mode: BodyMode,
pub request_body_released: bool,
pub request_is_idempotent: bool,
pub request_snapshot: Option<Request>,
pub request_span: Span,
pub upstream_exchange_span: Span,
pub request_start: Instant,
pub response_body_buffer: Option<BodyBuffer>,
pub response_body_bytes: u64,
pub response_body_mode: BodyMode,
pub response_body_released: bool,
pub response_header_snapshot: Option<Response>,
pub upstream_response_status: Option<u16>,
pub grpc_completion: Option<GrpcCompletion>,
pub response_phase_done: bool,
pub pending_rejection: Option<Rejection>,
pub response_delivery_complete: bool,
pub retries: u32,
pub selected_endpoint_index: Option<usize>,
pub attempted_endpoints: Vec<Arc<str>>,
pub retry_policy: Option<Arc<RetryPolicy>>,
pub route_retry_policy: Option<Arc<RetryPolicy>>,
pub cluster_retry_state: Option<Arc<ClusterRetryState>>,
pub cluster_retry_state_released: bool,
pub endpoint_reselector: Option<Arc<EndpointReselector>>,
pub pending_backoff: Option<Duration>,
pub reselect_on_retry: bool,
pub rewritten_path: Option<String>,
pub upstream: Option<Upstream>,
pub upstream_for_retry: Option<Upstream>,
pub upstream_contacted: bool,
pub ended_by_client: bool,
/* private fields */
}Expand description
Per-request context carrying filter pipeline results through Pingora hooks.
use std::sync::Arc;
use praxis_protocol::http::pingora::context::PingoraRequestCtx;
let mut ctx = PingoraRequestCtx::default();
ctx.cluster = Some(Arc::from("api-cluster"));
assert_eq!(ctx.cluster.as_deref(), Some("api-cluster"));Fields§
§_connection_permit: Option<OwnedSemaphorePermit>Connection permit from the per-listener semaphore.
Held for the request’s lifetime; released on drop, including error and timeout paths.
_global_connection_permit: Option<OwnedSemaphorePermit>Permit from the process-wide connection semaphore.
Present only when runtime.max_connections is configured.
client_addr: Option<IpAddr>Downstream client IP address.
client_http_version: Option<Version>HTTP version of the downstream client request.
Captured during request_filter so the response-phase Via
header can reflect the protocol the client used.
cluster: Option<Arc<str>>Name of the cluster selected by a cluster-selecting filter.
cached_body_done_indices: Vec<bool>Cached per-filter body-done indices. Swapped into each
HttpFilterContext and written back after execution.
cached_executed_branch_filters: Vec<bool>Cached branch-filter execution record. Same lifecycle as
cached_body_done_indices.
cached_executed_filter_indices: Vec<bool>Cached per-filter execution indices. Same lifecycle as
cached_body_done_indices.
downstream_tls: boolWhether the downstream connection uses TLS.
Set during request_filter. Used by the forwarded headers
filter to set X-Forwarded-Proto correctly for HTTP/1.1
connections where the URI lacks a scheme.
peer_identity: Option<Arc<TlsPeerIdentity>>Verified downstream TLS peer identity.
Set once from the SSL digest in request_filter before the first
filter runs. Shared via Arc with each HttpFilterContext so it
is available in both pre-read body phases and the main filter
pipeline. None for non-mTLS or no-client-cert connections.
connection_upgraded: boolWhether the connection was upgraded via 101 Switching Protocols.
Set during response_filter when the upstream returns 101.
Body filter hooks skip processing when true, since post-upgrade
bytes are raw protocol frames (e.g. WebSocket), not HTTP bodies.
extensions: RequestExtensionsType-safe request-scoped extension container. Swapped into each
HttpFilterContext and written back after filter execution,
following the same lifecycle as filter_metadata.
filter_metadata: HashMap<String, String>Durable per-request metadata that persists across all lifecycle
phases. Swapped into each HttpFilterContext and written back
after filter execution.
pre_read_mutations: Vec<TrustedHeaderMutation>Ordered log of trusted header mutations from pre-read body
processing. Swapped into each HttpFilterContext and written
back after filter execution.
structured_metadata: HashMap<String, Value>Structured per-request metadata keyed by namespace.
Swapped into each HttpFilterContext and written back
after filter execution.
mutated_request_body_len: Option<usize>Post-mutation request body length produced during StreamBuffer
pre-read.
Stored so upstream_request_filter can repair request framing
before Pingora sends headers to the backend.
pinned_pipeline: Option<Arc<FilterPipeline>>Pipeline pinned for this request’s entire lifecycle.
Set once during request_filter by cloning the Arc from the
listener’s ArcSwap. All subsequent hooks (request body, response,
response body, logging) use this reference instead of re-loading
from the ArcSwap, ensuring that a hot configuration reload
cannot change the pipeline mid-request.
filter_results: HashMap<&'static str, FilterResultSet>Filter results from body pre-read. Carried into the next
HttpFilterContext so that branch chains attached to the
first on_request filter can evaluate body-derived results.
filter_state: HashMap<usize, Box<dyn Any + Send + Sync>>Typed per-filter state that persists across all lifecycle
phases. Keyed by stable filter invocation ID, unique within
the request’s pinned FilterPipeline. Swapped into each
HttpFilterContext and written back after filter execution,
following the same pattern as filter_metadata.
metrics_cluster: Option<Arc<str>>Cluster name snapshot retained for metrics emission in the
logging() hook, after cluster has been consumed by filter
context construction.
Pre-built SharedString for the metrics cluster label, set
alongside metrics_cluster.
metrics_route: Option<SharedString>Matched route path-match pattern for the route metric label.
upstream_connect_start: Option<Instant>When the current upstream connect attempt started.
pre_read_body: Option<VecDeque<Bytes>>Pre-read body chunks (StreamBuffer mode). When StreamBuffer is
active, the body is read during request_filter (before upstream
selection) so that body-based routing can influence upstream_peer.
The request_body_filter hook then forwards these stored chunks
instead of reading from the session.
retained_pre_read_body: Option<VecDeque<Bytes>>Retained copy of the mutated pre-read body for retry replay.
The first attempt drains pre_read_body as it forwards the mutated
body. A retry replays from Pingora’s fixed retry buffer, which holds the
ORIGINAL (pre-mutation) bytes, while apply_mutated_content_length
re-stamps the mutated length. This retained copy re-seeds pre_read_body
on each retry so the replayed body matches the stamped Content-Length,
closing a request-smuggling mismatch. Set only when a body writer ran.
adapted_request_body: Option<VecDeque<Bytes>>Adapted request body captured by the selected-upstream phase (#1139).
When a selected-upstream body writer ran, the phase output is frozen
here as the exchange-local body Pingora forwards to the selected
upstream. Drained by request_body_filter in preference to
pre_read_body; kept separate from the canonical pre-read body so the
canonical bytes are never forwarded once adaptation ran (#1138).
Uses VecDeque so draining from the front is O(1).
retained_adapted_request_body: Option<VecDeque<Bytes>>Retained copy of the adapted body for retry replay (#1139).
Some(_) is the durable marker that adaptation ran for this request; it
persists for the request’s lifetime (unlike adapted_request_body,
which drains to None). reseed_retry_body restores
adapted_request_body from this copy on each retry so replays match the
stamped Content-Length.
adapted_request_body_len: Option<usize>Authoritative length of the adapted body sent to the selected upstream
(#1139). Takes precedence over mutated_request_body_len in
apply_mutated_content_length and the retry-body replay guard.
request_body_buffer: Option<BodyBuffer>Buffer for request body accumulation in StreamBuffer mode.
request_body_bytes: u64Accumulated request body bytes seen so far.
request_body_mode: BodyModePer-request body delivery mode for the request direction.
Seeded from static pipeline capabilities, then potentially
upgraded by filters during on_request.
request_body_released: boolWhether the request body has been released (StreamBuffer mode).
Once true, remaining chunks bypass buffering and stream through.
request_is_idempotent: boolWhether the request method is idempotent (GET, HEAD, OPTIONS).
request_snapshot: Option<Request>Snapshot of the original request for body/response body phases.
request_span: SpanRoot tracing span for this request’s lifecycle.
Created during request_filter with OpenTelemetry HTTP semantic
convention attributes. Response-phase attributes
(http.response.status_code, http.route, error.type,
upstream.address, upstream.cluster) are recorded in the
logging hook before the span is dropped.
upstream_exchange_span: SpanChild span covering upstream request/response exchange.
Created in connected_to_upstream after the connection is
established (or reused). Response-phase attributes
(http.response.status_code, http.response.body.size) are
recorded in the logging hook before the span is dropped.
request_start: InstantWhen this request was received.
response_body_buffer: Option<BodyBuffer>Buffer for response body accumulation in StreamBuffer mode.
response_body_bytes: u64Accumulated response body bytes seen so far.
response_body_mode: BodyModePer-request body delivery mode for the response direction.
Seeded from static pipeline capabilities, then potentially
upgraded by filters during on_response.
response_body_released: boolWhether the response body has been released (StreamBuffer mode).
response_header_snapshot: Option<Response>Snapshot of response headers after response-phase filters.
Used to evaluate response_conditions consistently during
response-body hooks, where Pingora no longer exposes mutable
response headers.
upstream_response_status: Option<u16>Upstream response status code, captured during response_filter
for passive health recording in the logging hook.
grpc_completion: Option<GrpcCompletion>How the upstream gRPC call ended.
Captured from the response trailers, or from the response header block of a Trailers-Only response. Held here rather than in the filter context because trailers arrive after the response-header phase, and the access log runs later still.
response_phase_done: boolWhether the response phase has been executed. Used to ensure
cleanup (e.g. least-connections counter release) in the
logging() hook when errors bypass response_filter.
pending_rejection: Option<Rejection>Rejection raised during the response phase, carried across the
Pingora error boundary so fail_to_proxy can deliver its full
configured headers and body instead of a bare status envelope.
response_delivery_complete: boolWhether the response was delivered to completion: a bodyless
response finished its response phase, or the response body
reached end-of-stream. When still false in the logging()
hook, the request ended early (rejection, upstream failure, or
aborted stream) and a fallback access record is emitted.
retries: u32Number of upstream connection retries attempted.
selected_endpoint_index: Option<usize>Index of the selected endpoint in the cluster’s endpoint list. Set during load balancing; used for passive health recording in the logging hook.
attempted_endpoints: Vec<Arc<str>>Endpoints already attempted (for alternate-host retry).
retry_policy: Option<Arc<RetryPolicy>>Snapshot of the resolved retry policy for this request.
route_retry_policy: Option<Arc<RetryPolicy>>Optional route-level retry policy override (merged by the load balancer).
cluster_retry_state: Option<Arc<ClusterRetryState>>Shared cluster retry state (budget + active requests).
cluster_retry_state_released: boolWhether cluster_retry_state.leave() has already been called.
endpoint_reselector: Option<Arc<EndpointReselector>>Reselector for alternate-host selection on retry.
pending_backoff: Option<Duration>Pending backoff delay to apply before the next upstream_peer call.
reselect_on_retry: boolWhether the next upstream attempt should re-select (alternate host).
rewritten_path: Option<String>Rewritten URI path for the upstream request.
Set by the path_rewrite filter via HttpFilterContext and
applied in upstream_request_filter.
upstream: Option<Upstream>Upstream endpoint selected by the load balancer filter.
upstream_for_retry: Option<Upstream>Saved upstream for retry (cloned before first use).
upstream_contacted: boolWhether the upstream was contacted (a peer was resolved for at least
one attempt) during this request. Unlike upstream_for_retry, which a
retry decision clears to force reselection, this stays set once the
upstream has been reached, so response-phase health accounting (passive
health, circuit breaker) can tell a genuine connect/read failure from a
request that never reached the cluster. Reset per request.
ended_by_client: boolSet in the logging phase when the client went away before the upstream answered, so the cleanup response pass does not report the upstream as reached and a circuit breaker releases instead of recording a failure.
Implementations§
Source§impl PingoraRequestCtx
impl PingoraRequestCtx
Sourcepub fn build_filter_context<'a>(
&mut self,
pipeline: &'a FilterPipeline,
request: &'a Request,
response_header: Option<&'a mut Response>,
) -> HttpFilterContext<'a>
pub fn build_filter_context<'a>( &mut self, pipeline: &'a FilterPipeline, request: &'a Request, response_header: Option<&'a mut Response>, ) -> HttpFilterContext<'a>
Build an HttpFilterContext using an external request reference.
Takes cluster and upstream from self (leaving None
behind) so that filters can reassign them. The caller must
write those fields back after filter execution.
use praxis_filter::{FilterPipeline, FilterRegistry, Request};
use praxis_protocol::http::pingora::context::PingoraRequestCtx;
let registry = FilterRegistry::with_builtins();
let pipeline = FilterPipeline::build(&mut [], ®istry).unwrap();
let request = Request {
method: http::Method::GET,
uri: http::Uri::from_static("/"),
headers: http::HeaderMap::new(),
};
let mut ctx = PingoraRequestCtx::default();
let filter_ctx = ctx.build_filter_context(&pipeline, &request, None);
assert!(filter_ctx.cluster.is_none());Sourcepub fn filter_context_for<'a>(
&'a mut self,
pipeline: &'a FilterPipeline,
response_header: Option<&'a mut Response>,
) -> Option<HttpFilterContext<'a>>
pub fn filter_context_for<'a>( &'a mut self, pipeline: &'a FilterPipeline, response_header: Option<&'a mut Response>, ) -> Option<HttpFilterContext<'a>>
Build an HttpFilterContext from the stored request_snapshot.
Returns None when request_snapshot is not set.
use praxis_filter::{FilterPipeline, FilterRegistry, Request};
use praxis_protocol::http::pingora::context::PingoraRequestCtx;
let registry = FilterRegistry::with_builtins();
let pipeline = FilterPipeline::build(&mut [], ®istry).unwrap();
let mut ctx = PingoraRequestCtx::default();
ctx.request_snapshot = Some(Request {
method: http::Method::GET,
uri: http::Uri::from_static("/"),
headers: http::HeaderMap::new(),
});
let filter_ctx = ctx.filter_context_for(&pipeline, None);
assert!(filter_ctx.is_some());Sourcepub fn response_body_context_for<'a>(
&'a mut self,
pipeline: &'a FilterPipeline,
) -> Option<(HttpFilterContext<'a>, Option<&'a Response>)>
pub fn response_body_context_for<'a>( &'a mut self, pipeline: &'a FilterPipeline, ) -> Option<(HttpFilterContext<'a>, Option<&'a Response>)>
Build an HttpFilterContext plus the saved response header for body conditions.
Response body hooks do not receive mutable headers, but their
response_conditions still need the response status and headers
captured during the response phase.
Sourcepub fn pin_pipeline(
&mut self,
swap: &ArcSwap<FilterPipeline>,
) -> Arc<FilterPipeline> ⓘ
pub fn pin_pipeline( &mut self, swap: &ArcSwap<FilterPipeline>, ) -> Arc<FilterPipeline> ⓘ
Pin the current pipeline for this request’s entire lifecycle.
Clones the Arc from the ArcSwap and stores it in
pinned_pipeline. All subsequent hooks should call
pipeline instead of re-loading from the ArcSwap.
Called by early_request_filter before compression negotiation;
repeated calls in later hooks reuse the same generation.
Sourcepub fn pipeline(&self, swap: &ArcSwap<FilterPipeline>) -> Arc<FilterPipeline> ⓘ
pub fn pipeline(&self, swap: &ArcSwap<FilterPipeline>) -> Arc<FilterPipeline> ⓘ
Return the pinned pipeline, falling back to a fresh
ArcSwap load when no pipeline was pinned.
The fallback covers early-failure paths where a lifecycle
hook runs before request_filter (e.g. after
early_request_filter rejection triggers logging).