pub struct ConnectionH2<Front: SocketHandler> {Show 34 fields
pub session_ulid: Ulid,
pub decoder: Decoder<'static>,
pub encoder: Encoder<'static>,
pub expect_read: Option<(H2StreamId, usize)>,
pub expect_write: Option<H2StreamId>,
pub last_stream_id: u32,
pub local_settings: H2Settings,
pub peer_settings: H2Settings,
pub position: Position,
pub prioriser: Prioriser,
pub readiness: Readiness,
pub socket: Front,
pub state: H2State,
pub streams: HashMap<u32, usize>,
pub timeout_container: TimeoutContainer,
pub flow_control: H2FlowControl,
pub highest_peer_stream_id: u32,
pub pending_table_size_update: Option<u32>,
pub converter_buf: Vec<u8>,
pub lowercase_buf: Vec<u8>,
pub cookie_buf: Vec<u8>,
pub drain: H2DrainState,
pub zero: Kawa<Checkout>,
pub bytes: H2ByteAccounting,
pub flood_detector: H2FloodDetector,
pub settings_sent_at: Option<Instant>,
pub pending_rst_streams: Vec<(u32, H2Error)>,
pub rst_sent: HashSet<u32>,
pub total_rst_streams_queued: usize,
pub connection_config: H2ConnectionConfig,
pub stream_last_activity_at: HashMap<u32, Instant>,
pub stream_fc_stalled_since: HashMap<u32, Instant>,
pub stream_fc_stalled_progress: HashMap<u32, usize>,
pub stream_idle_timeout: Duration,
/* private fields */
}Fields§
§session_ulid: UlidConnection/session ULID propagated from the parent [Mux]. Used to
stamp the session slot of the [session req cluster backend] log
prefix emitted by this module’s log_context! / log_context_stream!
macros.
decoder: Decoder<'static>§encoder: Encoder<'static>§expect_read: Option<(H2StreamId, usize)>§expect_write: Option<H2StreamId>§last_stream_id: u32§local_settings: H2Settings§peer_settings: H2Settings§position: Position§prioriser: Prioriser§readiness: Readiness§socket: Front§state: H2State§streams: HashMap<u32, usize>§timeout_container: TimeoutContainer§flow_control: H2FlowControlConnection-level flow control state (send window, receive tracking, pending updates).
highest_peer_stream_id: u32Highest stream ID accepted from the peer (used for GoAway last_stream_id).
pending_table_size_update: Option<u32>RFC 7541 §4.2 / §6.3 pending dynamic-table-size-update signal.
Some(new_size) when a peer SETTINGS frame adjusted
SETTINGS_HEADER_TABLE_SIZE and we have not yet prepended the
matching 001xxxxx HPACK directive to a header block. Consumed and
cleared by [H2BlockConverter::emit_pending_size_update_if_new_block]
on the next Block::StatusLine or Block::Header encoded for the
connection. Until then the peer’s decoder still has its previous
(possibly larger) table cap, so emitting is a correctness
requirement, not a nicety — see the RFC 9113 encoder-decoder
synchronisation contract (§6.5.2).
converter_buf: Vec<u8>Reusable buffer for HPACK-encoded headers in the H2 block converter.
lowercase_buf: Vec<u8>Reusable buffer for lowercasing header keys in the H2 block converter.
Reusable buffer for assembling cookie values in the H2 block converter.
drain: H2DrainStateConnection draining state for graceful shutdown.
zero: Kawa<Checkout>§bytes: H2ByteAccountingByte accounting for connection overhead attribution.
flood_detector: H2FloodDetectorFlood detector for CVE mitigations (Rapid Reset, CONTINUATION, Ping, Settings floods).
settings_sent_at: Option<Instant>RFC 9113 §6.5: timestamp when we sent SETTINGS and are awaiting ACK. If the peer does not ACK within SETTINGS_ACK_TIMEOUT, we send GOAWAY with SettingsTimeout error.
pending_rst_streams: Vec<(u32, H2Error)>Queued RST_STREAM frames to send: Vec<(stream_id, error_code)>. Used when refusing streams (MAX_CONCURRENT_STREAMS, buffer exhaustion) during readable — the actual write happens in the writable preamble to avoid conflicting with kawa.storage usage for frame payload discard.
rst_sent: HashSet<u32>RFC 9113 §6.8: tracks stream IDs for which RST_STREAM has already been sent, preventing duplicate RST_STREAM frames on the wire.
total_rst_streams_queued: usizeLifetime counter of RST_STREAM frames queued (pending + already flushed). Used to detect sustained misbehavior even when writable() drains the pending queue between readable() calls.
connection_config: H2ConnectionConfigPer-listener H2 connection tuning (window size, max streams, shrink ratio).
stream_last_activity_at: HashMap<u32, Instant>Per-stream wall-clock timestamp of last meaningful activity (DATA or
HEADERS frame receipt). Used to cancel streams that make no forward
progress within Self::stream_idle_timeout — mitigates slow-multiplex
Slowloris: connection-level idle timers reset on every frame, so a
misbehaving peer can otherwise pin up to max_concurrent_streams slots
for the full nominal connection timeout.
Initialized when the stream is created and refreshed on each non-empty inbound DATA frame and on HEADERS for an existing stream (trailers). Empty DATA frames (CVE-2019-9518 vector) do NOT refresh the timer.
stream_fc_stalled_since: HashMap<u32, Instant>Per-stream timestamp of when the stream first became flow-control-stalled
on the OUTBOUND (response) side — it holds buffered response data it
cannot drain because its effective send window min(stream.window, connection.window) is exhausted (the HTTP/2 window-stall /
WINDOW_UPDATE-drip vector). Distinct from Self::stream_last_activity_at:
this map is armed/cleared ONLY by outbound flow-control progress and is
NEVER refreshed by inbound DATA/HEADERS or connection-level frames, so a
peer dribbling 1-byte DATA on a stalled stream cannot keep it warm (the
liveness timer alone misses this because inbound drips refresh it). Reaped
by Self::cancel_timed_out_streams after Self::stream_idle_timeout.
stream_fc_stalled_progress: HashMap<u32, usize>Cumulative outbound flow-control bytes drained on a window-stalled stream
SINCE its Self::stream_fc_stalled_since deadline was armed (M2
cumulative-stall budget). An entry exists IFF stream_fc_stalled_since
has one for the stream; the two maps are kept in lockstep at every
arm/clear/evict site. Closes the WINDOW_UPDATE(+1)-drip residual: a
1-byte drain no longer clears the deadline — only cumulative progress
reaching [FC_STALL_CLEAR_FLOOR] does.
stream_idle_timeout: DurationPer-stream idle cap. Streams with no activity for longer than this are
RST_STREAM(CANCEL)’d by Self::cancel_timed_out_streams.
Implementations§
Source§impl<Front: SocketHandler> ConnectionH2<Front>
impl<Front: SocketHandler> ConnectionH2<Front>
Sourcepub fn initiate_close_notify(&mut self) -> bool
pub fn initiate_close_notify(&mut self) -> bool
Start TLS close_notify on the frontend and keep the session alive until rustls has flushed the generated records.
pub fn readable<E, L>( &mut self, context: &mut Context<L>, endpoint: E, ) -> MuxResult
pub fn writable<E, L>( &mut self, context: &mut Context<L>, endpoint: E, ) -> MuxResult
Sourcepub fn try_resume_reading<L>(&mut self, context: &Context<L>) -> boolwhere
L: ListenerHandler + L7ListenerHandler,
pub fn try_resume_reading<L>(&mut self, context: &Context<L>) -> boolwhere
L: ListenerHandler + L7ListenerHandler,
Re-enable READABLE if this connection is parked waiting for buffer space and the target stream’s buffer now has enough room.
This is the cross-readiness counterpart to the same-connection check in
writable(). When the other side of a stream (frontend or backend)
drains data via its own writable(), it frees buffer space that this
connection was waiting for. Without this explicit wake-up the connection
stays parked and the session deadlocks until a timeout fires.
Returns true if READABLE was re-enabled.
Sourcepub fn cancel_timed_out_streams<E, L>(
&mut self,
context: &mut Context<L>,
endpoint: &mut E,
)
pub fn cancel_timed_out_streams<E, L>( &mut self, context: &mut Context<L>, endpoint: &mut E, )
Cancel streams that have been idle longer than Self::stream_idle_timeout.
A stream is considered idle when no meaningful application data (non-empty
DATA frames or HEADERS) has been received since the last activity timestamp
in Self::stream_last_activity_at.
Mitigates slow-multiplex Slowloris (Pass 4 Medium #3): the connection-level
idle timer resets on every frame, so a peer sending periodic control frames
can pin max_concurrent_streams slots for the full nominal connection timeout.
Per-stream idle deadlines guarantee each stream terminates if it stops making
forward progress, regardless of connection-level liveness.
Timed-out streams receive RST_STREAM(CANCEL) and are immediately removed from the streams map so they no longer count against MAX_CONCURRENT_STREAMS. Backend endpoints are notified and metrics are finalized.
Sourcepub fn handle_flood_violation(
&mut self,
violation: H2FloodViolation,
) -> MuxResult
pub fn handle_flood_violation( &mut self, violation: H2FloodViolation, ) -> MuxResult
Log a flood violation with full session context and emit the GOAWAY.
Centralises the “flood detected” reporting so every site that observes a
[H2FloodViolation] gets the same session-scoped log line, matching the
RUSTLS log-context convention. Also emits the per-kind statsd counter
(h2.flood.violation.<kind>) so SOC dashboards can window the trip
rate without parsing logs — every CVE-mitigation in the H2 family
(Rapid Reset, MadeYouReset, CONTINUATION/PING/SETTINGS floods, header
overflow, glitch) funnels through this site.
Source§impl<Front: SocketHandler> ConnectionH2<Front>
impl<Front: SocketHandler> ConnectionH2<Front>
pub fn goaway(&mut self, error: H2Error) -> MuxResult
Sourcepub fn graceful_goaway(&mut self) -> MuxResult
pub fn graceful_goaway(&mut self) -> MuxResult
RFC 9113 §6.8: Initiate graceful shutdown using the double-GOAWAY pattern.
First call sends GOAWAY with last_stream_id = 0x7FFFFFFF (MAX) to signal
the intent to stop accepting new streams while allowing in-flight streams
to complete. The connection enters draining mode.
When draining is already true (second invocation), sends the final GOAWAY
with the actual highest_peer_stream_id so the peer knows which streams
were processed.
Sourcepub fn graceful_shutdown_deadline_elapsed(&self) -> bool
pub fn graceful_shutdown_deadline_elapsed(&self) -> bool
Returns true when the graceful-shutdown budget armed by
Self::graceful_goaway has elapsed. A return of true signals
the enclosing session loop that the proxy-initiated drain must
transition to a forced close: remaining streams will not complete
in time and keeping the connection open past the deadline defeats
the soft-stop SLA.
Returns false when:
- drain has not started yet (
started_atisNone), - the knob is
0/None(indefinite wait explicitly opted in), - or the elapsed time is still within the configured budget.
Sourcepub fn has_pending_write(&self) -> bool
pub fn has_pending_write(&self) -> bool
Returns true if there is data queued waiting to be flushed:
- H2 control frames in the zero buffer (GOAWAY, SETTINGS ACK, etc.)
- A partially-written stream or control frame (
expect_write) - Encrypted TLS records in rustls’s output buffer not yet flushed to TCP
The TLS check is critical: shutting_down() uses this to prevent
premature session close while response DATA is still in rustls’s
buffer (accepted by socket_write_vectored but not yet on the wire).
Does NOT check per-stream back.out/back.blocks; use
Self::has_pending_write_full on paths that must honour
LIFECYCLE invariant 16 (e.g. shutdown-drain).
Sourcepub fn has_pending_control_write(&self) -> bool
pub fn has_pending_control_write(&self) -> bool
True when the reaper has queued control frames (RST_STREAM) into
pending_rst_streams that have not yet been serialized. Kept SEPARATE
from Self::has_pending_write because that probe gates connection close
(the mod.rs close-gating sites) and must NOT treat a queued RST as a
reason to keep the connection open; this probe is consulted ONLY by the
MuxState::timeout flush gate to push a silent-peer RST_STREAM(CANCEL)
onto the wire before the connection closes.
Sourcepub fn has_pending_write_full<L>(&self, context: &Context<L>) -> boolwhere
L: ListenerHandler + L7ListenerHandler,
pub fn has_pending_write_full<L>(&self, context: &Context<L>) -> boolwhere
L: ListenerHandler + L7ListenerHandler,
Connection-level Self::has_pending_write extended with a per-stream
back-buffer probe (LIFECYCLE §9 invariant 16). Used by shutdown-drain
paths that must not close while any open stream still has outbound
kawa bytes queued — a voluntary scheduler yield can leave back.out
or back.blocks non-empty without expect_write being set.
Sourcepub fn flush_zero_buffer(&mut self)
pub fn flush_zero_buffer(&mut self)
Directly flush the zero buffer to the socket without going through the full writable() path. Used during shutdown when the event loop won’t deliver new epoll events for this session (edge-triggered).
pub fn create_stream<L>(
&mut self,
stream_id: u32,
context: &mut Context<L>,
) -> Option<usize>where
L: ListenerHandler + L7ListenerHandler,
pub fn new_stream_id(&mut self) -> Option<u32>
pub fn force_disconnect(&mut self) -> MuxResult
pub fn close<E, L>(&mut self, context: &mut Context<L>, endpoint: E)
Sourcepub fn reset_stream<E, L>(
&mut self,
wire_stream_id: u32,
stream_id: usize,
context: &mut Context<L>,
endpoint: E,
error: H2Error,
) -> MuxResult
pub fn reset_stream<E, L>( &mut self, wire_stream_id: u32, stream_id: usize, context: &mut Context<L>, endpoint: E, error: H2Error, ) -> MuxResult
Reset a stream: tear down kawa state, emit RST_STREAM on the wire,
and record MadeYouReset accounting.
wire_stream_id is the on-wire StreamId; stream_id is the internal
GlobalStreamId slot. Callers already carry both so we pass them
explicitly rather than scanning self.streams. The wire id is threaded
into Self::enqueue_rst which queues the frame for serialisation in
Self::flush_pending_control_frames on the next writable tick —
independent of whether the caller immediately evicts the slot via
remove_dead_stream (which they usually do). This is what guarantees
the RST reaches the peer for malformed HEADERS / flow-control /
content-length violations flagged by h2spec 2.0.
pub fn end_stream<L>(&mut self, stream_gid: usize, context: &mut Context<L>)where
L: ListenerHandler + L7ListenerHandler,
pub fn start_stream<L>(
&mut self,
stream: usize,
_context: &mut Context<L>,
) -> boolwhere
L: ListenerHandler + L7ListenerHandler,
Trait Implementations§
Source§impl<Front: SocketHandler> Debug for ConnectionH2<Front>
impl<Front: SocketHandler> Debug for ConnectionH2<Front>
Source§impl<Front: SocketHandler> Drop for ConnectionH2<Front>
Symmetric tear-down for the three h2.connection.* aggregate gauges:
whatever positive contribution this connection made via
ConnectionH2::gauge_connection_state is subtracted back out when the
connection is dropped.
impl<Front: SocketHandler> Drop for ConnectionH2<Front>
Symmetric tear-down for the three h2.connection.* aggregate gauges:
whatever positive contribution this connection made via
ConnectionH2::gauge_connection_state is subtracted back out when the
connection is dropped.
Using Drop (rather than wiring decrements into every close path —
graceful_goaway, force_disconnect, handle_goaway_frame, Mux::close,
stream-id exhaustion, panic-unwind) is what guarantees the gauge is
arithmetically symmetric regardless of which path teardown took. Past
underflow incidents (commits a650ad69, d2f01ed4) have all been
missing-decrement bugs that Drop makes structurally impossible.