Skip to main content

HttpContext

Struct HttpContext 

Source
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: bool

set 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: bool

set 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

§authority: Option<String>

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: bool

signals wether Kawa should write a “Connection” header with a “close” value (request and response)

§session_id: Ulid

Connection/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: Ulid

Request 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: Protocol

the value of the protocol Kawa should write in the Forwarded headers of the request

§public_address: SocketAddr

the 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: String

the 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: bool

Whether 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: bool

When 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: bool

When 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: ForwardedHeaders

Which 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: u32

Most 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: u32

Trailer 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: String

Name 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: Option<String>

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.

§tags: Option<Rc<CachedTags>>

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.

§backends_unavailable: bool

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

Source

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

Source

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 in TRAILER_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 in http.trailer.spoof_vector_elided or http.trailer.forbidden_field_elided.
  • Every field, elided or not, counts against max_trailer_fields, as pkawa::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), and http.trailer.field_limit_exceeded is 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.

Source

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.

Source

pub fn extract_route( &self, ) -> Result<(&str, &str, &Method), RetrieveClusterError>

Source

pub fn get_route(&self) -> String

Source

pub fn websocket_context(&self) -> WebSocketContext

Source

pub fn log_context(&self) -> LogContext<'_>

Trait Implementations§

Source§

impl Debug for HttpContext

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl ParserCallbacks<Checkout> for HttpContext

Source§

fn on_headers(&mut self, stream: &mut Kawa<Checkout>)

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.