Skip to main content

Context

Struct Context 

Source
pub struct Context<L: ListenerHandler + L7ListenerHandler> {
Show 20 fields pub streams: Vec<Stream>, pub pending_links: VecDeque<usize>, pub backend_streams: InlineTokenMap<LinkedStreams>, pub buffers: Box<dyn BufferSource>, pub listener: Rc<RefCell<L>>, pub session_ulid: Ulid, pub session_address: Option<SocketAddr>, pub public_address: SocketAddr, pub debug: DebugHistory, pub h2_stream_shrink_ratio: usize, pub tls_server_name: Option<String>, pub tls_cert_names: Option<Arc<Vec<String>>>, pub protocol: Protocol, pub tls_version: Option<&'static str>, pub tls_cipher: Option<&'static str>, pub tls_alpn: Option<&'static str>, pub now: Instant, pub now_wall_ms: u64, pub request_id_rng: StdRng, pub backend_deltas: Vec<BackendDelta>, /* private fields */
}

Fields§

§streams: Vec<Stream>§pending_links: VecDeque<usize>

Streams whose state is StreamState::Link and need backend connection. Replaces the O(n) scan of streams in the ready loop.

§backend_streams: InlineTokenMap<LinkedStreams>

Reverse index: backend token -> global stream IDs currently in StreamState::Linked(token). Eliminates O(n) scans of streams when handling backend connect/disconnect/timeout/close events.

An entry may be empty: remove_backend_stream keeps an emptied Vec for the next link on the same live connection (#1583), so “has linked streams” is the entry’s emptiness, not the key’s presence.

§buffers: Box<dyn BufferSource>

Where every buffer this session’s streams and connections need comes from, and the only thing allowed to refuse one.

Boxed rather than concrete so the core names the contract rather than the worker’s Pool: a refusal is the same event whatever stands behind it, and the REFUSED_STREAM answer to one is the same answer. Context::new installs the PoolBufferSource the proxy runs on.

§listener: Rc<RefCell<L>>§session_ulid: Ulid

Connection/session ULID — mirrors Mux.session_ulid. Stored here so per-stream HttpContext construction in Self::create_stream can stamp the session slot of the log-context bracket without reaching back into the parent Mux.

§session_address: Option<SocketAddr>§public_address: SocketAddr§debug: DebugHistory§h2_stream_shrink_ratio: usize

Shrink threshold ratio for recycled stream slots. Vec is shrunk when total_slots > active_streams * ratio.

§tls_server_name: Option<String>

TLS SNI value negotiated at handshake, propagated to every per-stream HttpContext so the routing layer can enforce the SNI ↔ :authority binding on every H2 stream (and the single H1 request). None for plaintext listeners or when the client omitted the SNI extension. Stored pre-lowercased and without a port for cheap 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. 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.

§protocol: Protocol

Whether this session’s listener speaks HTTPS or plaintext HTTP.

The one listener value that is genuinely per-connection rather than per-request: a listener’s protocol is fixed by its type and no update_config patch can change it, so capturing it once in Self::new frees both readers — Self::create_stream and the H2 write pass’s :scheme decision — from borrowing the listener at all. Contrast the per-request knobs, which Self::create_stream re-reads on every stream precisely because a reload can change them (LIFECYCLE.md §2.5).

§tls_version: Option<&'static str>

Negotiated TLS protocol version short-form (e.g. "TLSv1.3"). Captured once at handshake completion in https.rs and propagated to every per-stream HttpContext so the access log can record it without reaching back into the rustls session per request. None for plaintext listeners.

§tls_cipher: Option<&'static str>

Negotiated TLS cipher suite short-form (e.g. "TLS_AES_128_GCM_SHA256"). Captured once at handshake completion and propagated to every per-stream HttpContext. None for plaintext listeners.

§tls_alpn: Option<&'static str>

Negotiated ALPN protocol short-form (e.g. "h2", "http/1.1"). Captured once at handshake completion and propagated to every per-stream HttpContext. None for plaintext listeners or when no ALPN was negotiated.

§now: Instant

Clock snapshot for the pass currently executing.

Mux is the only clock sampler in the mux: it refreshes this field once per outer Mux::ready pass and at the top of Mux::timeout and Mux::shutting_down. Every time-based decision in the H2 core reads this snapshot — mirrored into h2::ConnectionH2::now at each public entry point — instead of calling Instant::now itself, so one pass sees one consistent “now”. Every deadline armed or evaluated inside a pass is therefore accurate to within that pass — in either direction. The error is not one-sided: an arm site runs at an arbitrary depth into its pass (a DATA or HEADERS refresh, an outbound-byte refresh) and stamps the snapshot taken at the START of that pass, so the stored instant is older than the event it records; an eval site runs near the top of a pass (cancel_timed_out_streams is the first thing h2::ConnectionH2::poll_read_target does). The measured age is therefore inflated by the arm site’s depth, and a deadline can fire up to one pass EARLY as well as one pass late. At base both ends read the real clock and the comparison was exact; this is the cost of the snapshot.

The flood and back-pressure windows are the one asymmetric case, and they fail closed: now is constant for the whole pass, so a window cannot decay part-way through one. A burst arriving during a pass is weighed in full against the window that was open when the pass started, where before the change a long pass could decay the counters under the burst. The window BOUNDARY still carries the same one-pass error in either direction as every other deadline.

Note that crate::SessionMetrics and crate::timer::TimeoutContainer deliberately stay on the real clock: metrics want true elapsed time, and the timeout containers are the embedder’s timer, held by the Mux adapter in Mux::timeouts and never touched by a core — which publishes a deadline stamped from this snapshot instead.

§now_wall_ms: u64

Wall-clock sibling of Self::now: Unix milliseconds for the pass currently executing.

Self::now is an Instant — an opaque monotonic point with no epoch and no defined conversion to civil time — so it cannot supply the 48-bit Unix-millisecond prefix a ULID carries in its high bits (see Self::next_request_id). This field is that value, and it is refreshed by Mux at exactly the three sites that refresh Self::now, from the same pass. The two are siblings: a site that sets one and not the other lets a request id carry the timestamp of an older pass.

It is a snapshot rather than a provider closure for the same reason Self::now is: the mux’s discipline is one clock sample per pass, read by the core from a field, and a boxed provider would let the core reach the host at an arbitrary depth into a pass instead.

§request_id_rng: StdRng

Entropy source for the 80-bit random suffix of a request ULID.

Ulid::generate() is exactly Ulid::from_timestamp_with_rng(unix_epoch_ms(), &mut rand::rng()) — two ambient reaches (the wall clock and a thread-local RNG) hidden inside a dependency, invisible to any grep over this module for Instant::now / SystemTime::now / rand. Holding the RNG here replaces the second with a field the embedder owns, exactly as Self::now_wall_ms replaces the first.

Seeded once per session in Self::new from rand::rng() — the same OS-backed reseeding CSPRNG Ulid::generate() draws from, so the unpredictability of a request id is unchanged. A request id that repeats across sessions would be a regression, so nothing in the mux re-seeds it; a deterministic simulator assigns this field directly after construction instead.

§backend_deltas: Vec<BackendDelta>

Backend accounting the core decided and the embedder has not performed yet (#1340, Question 12).

The core’s outbound channel for registry writes, in the same place udp’s Output queue sits: a site that used to reach a Rc<RefCell<Backend>> through Position::Client and mutate it pushes a BackendDelta here instead. Mux::apply_backend_deltas is the only drain, and the only writer of Backend::active_requests / Backend::active_connections on the mux path.

Entries are applied in the order they were pushed, and the drain runs before anything reads backend load state — in practice, before each Router::plan_connect, whose load balancer is that sole reader. FIFO plus drain-before-read leaves the counters at every read point exactly where mutating in place left them, saturation included.

Implementations§

Source§

impl<L: ListenerHandler + L7ListenerHandler> Context<L>

Source

pub fn new( session_ulid: Ulid, pool: Weak<RefCell<Pool>>, listener: Rc<RefCell<L>>, session_address: Option<SocketAddr>, public_address: SocketAddr, ) -> Self

Source

pub fn next_request_id(&mut self) -> Ulid

Mint the request ULID for a stream this session is about to open, or for the next request of an H1 keep-alive connection.

The mux’s two production ULID sites call it: ConnectionH2::create_stream (lib/src/protocol/mux/h2.rs) for each H2 stream, and the keep-alive branch of ConnectionH1::writable (lib/src/protocol/mux/h1.rs), which hands it to HttpContext::reset. It replaces Ulid::generate() there. generate() composes two ambient reaches — the wall clock and a thread-local RNG — inside rusty_ulid; this composes the same ULID from Self::now_wall_ms and Self::request_id_rng, the two sources the embedder owns. Same bytes, same layout, no host reach from inside the core.

Ulid::from_timestamp_with_rng panics above 0xFFFF_FFFF_FFFF ms (year 10889); Self::now_wall_ms is a Unix-millisecond count, so reaching it requires a host clock set eight millennia ahead.

Source

pub fn active_len(&self) -> usize

Source

pub fn http_context(&self, stream_id: usize) -> &HttpContext

Shared accessor for the HttpContext owned by a stream.

Prefer this over &self.streams[stream_id].context at call sites that only need read access — it keeps the Stream/HttpContext relationship encapsulated and reads the same regardless of whether the caller is inside Router::plan_connect, the H2 mux, or a free helper. Panics on an out-of-bounds stream_id, which is the same behaviour as the raw streams[sid] indexing it replaces.

Source

pub fn http_context_mut(&mut self, stream_id: usize) -> &mut HttpContext

Mutable sibling of Self::http_context. Use when routing decisions need to stamp cluster_id / backend_id on the stream’s HttpContext (e.g. Router::plan_connect at the fill-cluster / fill-backend points).

Register a stream as linked to a backend token in the reverse index.

Remove a stream from the backend reverse index if it is currently Linked. Returns the backend token if one was removed.

Source

pub fn create_stream(&mut self, request_id: Ulid, window: u32) -> Option<usize>

Open (or recycle) the stream slot for a request that has just arrived, capturing the listener configuration it will run on.

This is the mux’s single listener-snapshot point. ConnectionH2 calls it from its HEADERS branch, so an H2 stream captures the listener as the operator had it configured when that request’s HEADERS landed, and finishes on that capture whatever a concurrent reload does; the next HEADERS on the same connection takes a fresh one. The H1 sessions in http.rs / https.rs call it once per connection instead, because one H1 connection owns one slot that HttpContext::reset deliberately carries across keep-alive requests. LIFECYCLE.md §2.5 states the resulting staleness window for both.

Every value read under the borrow below is re-read on each call for that reason. The two that are NOT read here — Self::protocol and the TLS fields — are connection-scoped by nature and captured once.

window seeds Stream::front_window, the stream’s send window on the frontend connection. The backend leg’s window is the backend’s to size: ConnectionH2::start_stream sets Stream::back_window.

Source

pub fn shrink_trailing_recycle(&mut self)

Remove consecutive Recycle entries from the end of the streams Vec.

This prevents unbounded growth when H2 streams are created and recycled over time, reclaiming memory from slots that are no longer needed.

Auto Trait Implementations§

§

impl<L> !RefUnwindSafe for Context<L>

§

impl<L> !Send for Context<L>

§

impl<L> !Sync for Context<L>

§

impl<L> !UnwindSafe for Context<L>

§

impl<L> Freeze for Context<L>
where Rc<RefCell<L>>: Freeze,

§

impl<L> Unpin for Context<L>
where Rc<RefCell<L>>: Unpin,

§

impl<L> UnsafeUnpin for Context<L>
where Rc<RefCell<L>>: UnsafeUnpin,

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.