pub struct HttpContext {Show 45 fields
pub keep_alive_backend: bool,
pub keep_alive_frontend: bool,
pub sticky_session_found: Option<String>,
pub method: Option<Method>,
pub authority: Option<String>,
pub path: Option<String>,
pub status: Option<u16>,
pub reason: Option<Cow<'static, str>>,
pub user_agent: Option<String>,
pub x_request_id: Option<Rc<str>>,
pub xff_chain: Option<String>,
pub closing: bool,
pub session_id: Ulid,
pub id: Ulid,
pub backend_id: Option<Rc<str>>,
pub cluster_id: Option<ClusterId>,
pub affinity_key: Option<u64>,
pub protocol: Protocol,
pub public_address: SocketAddr,
pub session_address: Option<SocketAddr>,
pub sticky_name: String,
pub sticky_session: Option<String>,
pub backend_address: Option<SocketAddr>,
pub tls_server_name: Option<String>,
pub tls_cert_names: Option<Arc<Vec<String>>>,
pub strict_sni_binding: bool,
pub elide_x_real_ip: bool,
pub send_x_real_ip: bool,
pub forwarded_headers: ForwardedHeaders,
pub max_trailer_fields: u32,
pub trailer_fields: u32,
pub tls_version: Option<&'static str>,
pub tls_cipher: Option<&'static str>,
pub tls_alpn: Option<&'static str>,
pub sozu_id_header: String,
pub redirect_location: Option<String>,
pub www_authenticate: Option<String>,
pub original_authority: Option<String>,
pub headers_response: Vec<HeaderEditSnapshot>,
pub retry_after_seconds: Option<u32>,
pub frontend_redirect_template: Option<String>,
pub redirect_status: Option<u16>,
pub tags: Option<Rc<CachedTags>>,
pub access_log_message: Option<&'static str>,
pub backends_unavailable: bool,
/* private fields */
}Expand description
This is the container used to store and use information about the session from within a Kawa parser callback
Fields§
§keep_alive_backend: boolset to false if Kawa finds a “Connection” header with a “close” value in the response, or if the response is a non-persistent HTTP/1.0 one (RFC 9112 §9.3)
keep_alive_frontend: boolset to false if Kawa finds a “Connection” header with a “close” value in the request
sticky_session_found: Option<String>the value of the sticky session cookie in the request
method: Option<Method>the value of the method in the request line
the value of the authority of the request (in the request line of “Host” header)
path: Option<String>the value of the path in the request line
status: Option<u16>the value of the status code in the response line
reason: Option<Cow<'static, str>>the value of the reason in the response line: the 'static phrase
RFC 9110 §15 registers for the status code when the backend sent
exactly that phrase, a copy otherwise (standard_reason)
user_agent: Option<String>§x_request_id: Option<Rc<str>>Value of the x-request-id header observed (if propagated from the
client/upstream LB) or generated (from self.id). Universal correlation
header — populated unconditionally by on_request_headers so the access
log can record the exact value forwarded to the backend. Rc because
a generated value is the one rendering of self.id that the forwarded
X-Request-Id and both correlation headers share
(kawa::Store::Shared).
xff_chain: Option<String>Verbatim value of the client-supplied X-Forwarded-For header as
observed before Sōzu appended its own hop. Captured here, not at
request edit time, so the access log records the upstream-attested
chain even when Sōzu also appends its own peer to the forwarded
header. None if the request had no X-Forwarded-For header.
closing: boolsignals wether Kawa should write a “Connection” header with a “close” value (request and response)
session_id: UlidConnection/session ULID — stable across all requests multiplexed on this
TCP or TLS connection. Used as the first slot in the legacy log-context
bracket [session req cluster backend] and emitted into
ProtobufAccessLog.session_id.
id: UlidRequest ULID: the value of the correlation header, named “Sozu-Id” by
default, that Kawa should write (request and response), of a generated
X-Request-Id and of %REQUEST_ID. Request-scoped: reset replaces
it for each keep-alive request, unlike Self::session_id.
backend_id: Option<Rc<str>>§cluster_id: Option<ClusterId>§affinity_key: Option<u64>The client affinity key Router::plan_connect
(lib/src/protocol/mux/router.rs) derived for this request when its
cluster selects with HRW or MAGLEV: the hash of the cluster’s
affinity_header / affinity_cookie value, else of the client source
IP. None under every other policy. Stored next to cluster_id for the
same reason: a replay finds the request’s header blocks drained and
reuses the key the first attempt derived.
protocol: Protocolthe value of the protocol Kawa should write in the Forwarded headers of the request
public_address: SocketAddrthe value of the public address Kawa should write in the Forwarded headers of the request
session_address: Option<SocketAddr>the value of the session address Kawa should write in the Forwarded headers of the request
sticky_name: Stringthe name of the cookie Kawa should read from the request to get the sticky session
sticky_session: Option<String>the sticky session that should be used used to create a “Set-Cookie” header in the response in case it differs from sticky_session_found
backend_address: Option<SocketAddr>the address of the backend server
tls_server_name: Option<String>The TLS Server Name Indication (SNI) hostname negotiated at handshake.
Populated for HTTPS listeners when the client sent an SNI extension (see
https.rs::upgrade_handshake). Used by the routing layer to enforce the
TLS trust boundary against the HTTP :authority / Host header — without
this check, an attacker holding a valid certificate for tenant A could
open TLS with SNI=A then send requests with :authority=tenantB and
reach tenant B’s backend (CWE-346 / CWE-444).
None when the listener is plaintext HTTP or the client omitted SNI.
Stored pre-lowercased and without a port for direct exact-match comparison.
tls_cert_names: Option<Arc<Vec<String>>>Snapshot of the SAN set of the certificate Sōzu actually served at
the TLS handshake. Captured once in https.rs::upgrade_handshake
from the resolver and frozen for the connection lifetime so H2
stream coalescing (RFC 7540 §9.1.1 / RFC 9113 §9.1.1) accepts any
:authority covered by the certificate, with RFC 6125 §6.4.3
wildcard handling. None for plaintext listeners or when SNI was
absent (router falls back to the legacy exact-match predicate).
Some(empty) when the default cert was served — every
:authority is rejected. Arc so the snapshot is shared across
every per-stream HttpContext without re-allocation.
strict_sni_binding: boolWhether the router must reject this request when tls_server_name
does not exact-match its authority (CWE-346 / CWE-444). Mirrors
HttpsListenerConfig::strict_sni_binding. Set from the mux
Context at stream creation time (see Context::create_stream).
Plaintext listeners still never hit the check because
tls_server_name is None.
elide_x_real_ip: boolWhen true, the request-side block walk in on_request_headers
strips any client-supplied X-Real-IP header before forwarding
(anti-spoofing). Mirrors HttpListenerConfig::elide_x_real_ip /
HttpsListenerConfig::elide_x_real_ip. Set from the mux Context
at stream creation (see Context::create_stream); listener-scoped
and never reset across keep-alive requests. Independent of
send_x_real_ip. The same flag is plumbed into
pkawa::handle_trailer so trailer HEADERS frames cannot bypass
the elision.
send_x_real_ip: boolWhen true, on_request_headers injects a proxy-generated
X-Real-IP header carrying session_address.ip() (post-PROXY-v2
unwrap, i.e. the original client IP). Mirrors
HttpListenerConfig::send_x_real_ip /
HttpsListenerConfig::send_x_real_ip. Set from the mux Context
at stream creation (see Context::create_stream); listener-scoped
and never reset across keep-alive requests. Independent of
elide_x_real_ip. When session_address is None (raw socket
without a peer), no header is appended — identical to the
existing X-Forwarded-For / Forwarded synthesis behaviour.
forwarded_headers: ForwardedHeadersWhich forwarding header family on_request_headers adds to the
request. Mirrors HttpListenerConfig::forwarded_headers /
HttpsListenerConfig::forwarded_headers. Set from the mux Context
at stream creation (see Context::create_stream); listener-scoped
and never reset across keep-alive requests. Independent of
elide_x_real_ip and send_x_real_ip: X-Real-IP belongs to
neither family. Both, the default, is the historical behaviour.
max_trailer_fields: u32Most trailer fields an H1 chunked request may carry, elided ones
included; one more makes HttpContext::filter_request_trailers
reject the request. Mirrors the listener’s h2_max_header_fields,
the bound pkawa::handle_trailer (lib/src/protocol/mux/pkawa.rs)
applies to an H2 trailer block, so both frontends admit the same
number of trailer fields. Set from the mux Context at stream
creation (see Context::create_stream); listener-scoped and never
reset across keep-alive requests.
trailer_fields: u32Trailer fields of the current H1 request counted so far by
HttpContext::filter_request_trailers. Request-scoped: reset
zeroes it, so each pipelined request has its own budget.
tls_version: Option<&'static str>Negotiated TLS protocol version as a short label (e.g. "TLSv1.3").
Captured from rustls_version_label at handshake completion and
propagated from the mux Context. None for plaintext listeners.
tls_cipher: Option<&'static str>Negotiated TLS cipher suite as a short label (e.g.
"TLS_AES_128_GCM_SHA256"). Captured from rustls_ciphersuite_label
at handshake completion and propagated from the mux Context. None
for plaintext listeners.
tls_alpn: Option<&'static str>Negotiated ALPN protocol (e.g. "h2", "http/1.1"). Captured from
rustls at handshake completion and propagated from the mux Context.
None for plaintext listeners or when no ALPN was negotiated.
sozu_id_header: StringName of the correlation header Sozu injects into every request and
response. Defaults to "Sozu-Id" via crate::L7ListenerHandler::get_sozu_id_header.
Populated at stream creation from the listener config’s sozu_id_header
knob. Stored as an owned String so it survives a listener hot-reload
that changes the value.
redirect_location: Option<String>Resolved Location URL stashed by the routing layer when a frontend
triggers a permanent redirect (RedirectPolicy::PERMANENT or the
legacy cluster.https_redirect). Read by the default-answer 301
path so the response carries the correct target URL — including
optional cluster.https_redirect_port and rewrite-template captures.
None when the request is not redirecting.
www_authenticate: Option<String>WWW-Authenticate realm stashed by the routing layer when a
frontend rejects an unauthenticated request (required_auth = true
without a valid Authorization: Basic header, or
RedirectPolicy::UNAUTHORIZED). Read by the default-answer 401
path so the response carries the cluster’s configured
www_authenticate value. None falls back to template default
(header is elided when no realm is configured).
Original authority captured before a rewrite-host fired; emitted
back to the backend as X-Forwarded-Host so the backend can
reconstruct the public URL even though :authority / Host was
rewritten on the wire. None when no host rewrite happened.
headers_response: Vec<HeaderEditSnapshot>Per-frontend response-side header edits (set/replace/delete)
stashed by the routing layer for the emission boundary in
mux/h1.rs::writable and mux/h2.rs::write_streams to apply
before kawa.prepare(...). Empty when the frontend has no
response-side header policy. An entry with an empty val
deletes the header by name (HAProxy del-header parity); a
non-empty val set/replaces.
retry_after_seconds: Option<u32>Resolved Retry-After value (seconds) for an HTTP 429 default
answer. Computed in Router::plan_connect when the per-(cluster,
source-IP) connection limit is hit, by folding the cluster’s
retry_after override over the global default. None (or
Some(0)) tells the answer engine to omit the Retry-After
header entirely — Retry-After: 0 invites an immediate retry
that defeats the limit. Unused for any other status code.
frontend_redirect_template: Option<String>Frontend-supplied template body that overrides the listener /
cluster default http.301.redirection for a single
RedirectPolicy::PERMANENT request. Stashed by the routing layer
from RouteResult::redirect_template and consumed by the 301
branch of mux::answers::set_default_answer_with_retry_after,
which compiles the body via HttpAnswers::render_inline_301 and
renders it with the same (REDIRECT_LOCATION, ROUTE, REQUEST_ID) variable schema as the persistent template chain.
None falls back to the cluster / listener default. Unused for
any other status code or for the legacy
cluster.https_redirect = true path (which never sets it).
redirect_status: Option<u16>Resolved redirect status code stashed by the routing layer when
a frontend’s RedirectPolicy is one of the redirect variants.
301 = Permanent, 302 = Found, 308 = PermanentRedirect.
None falls back to 301 for the legacy
cluster.https_redirect = true path. Closes #1009.
Access-log tags of the frontend rule that matched this request,
stashed by the routing layer from RouteResult::tags (see
mux/router.rs::route_from_request) and read back by
mux/stream.rs::generate_access_log.
The matched frontend is the only correct owner of these tags: the
operator configures them per frontend RULE (*.example.com,
/foo.*/.example.com, a Pre/Post string), while the request only
ever carries a concrete authority. Resolving them here — rather
than by looking the authority up in the listener’s
BTreeMap<String, CachedTags> — is what makes a wildcard, regex,
ported or differently-cased frontend log its tags at all
(sozu#1379). Rc because the same CachedTags is shared by every
stream routed to that frontend, and because it must outlive a
frontend removed mid-request.
None until the request is routed, and for a request that matched
no frontend at all.
access_log_message: Option<&'static str>Stable, structured discriminator surfaced as the access-log
message field when the session terminates on a timeout. Set by
the MuxState::timeout handler
before the default-answer or forcefully_terminate_answer
path consumes it. The vocabulary is operator-visible API once
shipped — see the access-log section of doc/configure.md for
the full token list (client_timeout,
client_timeout_during_response, backend_timeout,
backend_response_timeout). None for non-timeout sessions, in
which case the access log emits message: None as before.
The routed cluster has backends, but none could be selected for this
request — every one failing its health check or backing off after
connection failures — so Sōzu answered 503 without contacting one.
That is a backend outage, not a routing miss: the H2 RST caps count
such a stream as routed to a backend (routed_to_a_backend,
lib/src/protocol/mux/h2.rs). Request-scoped: reset clears it.
Implementations§
Source§impl HttpContext
impl HttpContext
Sourcepub fn new(
session_id: Ulid,
request_id: Ulid,
protocol: Protocol,
public_address: SocketAddr,
session_address: Option<SocketAddr>,
sticky_name: String,
sozu_id_header: String,
elide_x_real_ip: bool,
send_x_real_ip: bool,
) -> Self
pub fn new( session_id: Ulid, request_id: Ulid, protocol: Protocol, public_address: SocketAddr, session_address: Option<SocketAddr>, sticky_name: String, sozu_id_header: String, elide_x_real_ip: bool, send_x_real_ip: bool, ) -> Self
Creates a new instance
Sourcepub fn filter_request_trailers(
&mut self,
kawa: &mut Kawa<Checkout>,
first_new_block: usize,
) -> TrailerFilterOutcome
pub fn filter_request_trailers( &mut self, kawa: &mut Kawa<Checkout>, first_new_block: usize, ) -> TrailerFilterOutcome
Police the trailer fields that the last kawa::h1::parse appended
to the trailer section of a chunked H1 request.
- A field in
TRAILER_SPOOF_VECTOR_HEADERS(client attribution, sozu-proxy/sozu#1689) or inTRAILER_FORBIDDEN_FIELDS(framing, routing, request modifiers, authentication, content processing and connection-specific fields, RFC 9110 §6.5.1, sozu-proxy/sozu#1701) is elided, and counted inhttp.trailer.spoof_vector_elidedorhttp.trailer.forbidden_field_elided. - Every field, elided or not, counts against
max_trailer_fields, aspkawa::handle_trailer(lib/src/protocol/mux/pkawa.rs) counts an H2 trailer block. The field that goes over the limit marks the request in error, so the caller’s existing parse-error path answers 400 (or cuts a response that already started), andhttp.trailer.field_limit_exceededis incremented.
kawa’s H1 parser has no trailer callback: its ParsingPhase::Trailers
arm pushes each trailer field as a Block::Header after the Flags
block that carries end_body (the last chunk), then closes the
section with a Flags block carrying end_header and end_stream.
Call this after each kawa::h1::parse of a frontend request, with
first_new_block set to kawa.blocks.len() read just before that
parse. It walks back from the end of the queue over the closing block
and the trailer fields, and stops at the first other block (the
end_body marker) or at first_new_block. It never reaches the
header block, which ends with its own end_header marker before the
chunks.
Stopping at first_new_block keeps the total walk linear in the
number of trailer fields: every field queued before that parse was
already examined, and counted in trailer_fields, by the call that
followed the parse which queued it, so a client that trickles one
trailer line per segment while the backend is not writable does not
make each call re-walk the whole section. Parsing only appends
blocks, and prepare only drains them from the front between parses,
so the fields past first_new_block are exactly the new ones even
when earlier trailer fields were already forwarded. That is also why
the count lives in trailer_fields rather than in the queue.
An elided field is skipped by kawa’s H1 converter and by the H2 one, so the last chunk and the closing empty line are still written: the chunk framing stays valid even when every trailer field is dropped. A request that is not chunked, or has not reached its trailer section, returns before touching a block, so the walk costs nothing and allocates nothing on the common path.
Only requests are filtered: a response trailer travels towards the client, which does not take client attribution from it.
Sourcepub fn reset(&mut self, request_id: Ulid)
pub fn reset(&mut self, request_id: Ulid)
Prepare this context for the next request of a keep-alive connection,
which request_id identifies.
The request id is request-scoped like the rest of what this clears:
Sozu-Id, a generated X-Request-Id, %REQUEST_ID and the access
log’s request_id all read self.id, so keeping it would give every
request of the connection the first one’s id. The caller mints it —
ConnectionH1 from Context::next_request_id
(lib/src/protocol/mux/mod.rs), the same source that numbers H2
streams — because this struct owns neither a clock nor an RNG.