Skip to main content

ConnectionH2

Struct ConnectionH2 

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

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

Connection-level flow control state (send window, receive tracking, pending updates).

§highest_peer_stream_id: u32

Highest 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.

§cookie_buf: Vec<u8>

Reusable buffer for assembling cookie values in the H2 block converter.

§drain: H2DrainState

Connection draining state for graceful shutdown.

§zero: Kawa<Checkout>§bytes: H2ByteAccounting

Byte accounting for connection overhead attribution.

§flood_detector: H2FloodDetector

Flood 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: usize

Lifetime 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: H2ConnectionConfig

Per-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: Duration

Per-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>

Source

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.

Source

pub fn readable<E, L>( &mut self, context: &mut Context<L>, endpoint: E, ) -> MuxResult

Source

pub fn writable<E, L>( &mut self, context: &mut Context<L>, endpoint: E, ) -> MuxResult

Source

pub fn try_resume_reading<L>(&mut self, context: &Context<L>) -> bool

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.

Source

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.

Source

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>

Source

pub fn goaway(&mut self, error: H2Error) -> MuxResult

Source

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.

Source

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_at is None),
  • the knob is 0 / None (indefinite wait explicitly opted in),
  • or the elapsed time is still within the configured budget.
Source

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).

Source

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.

Source

pub fn has_pending_write_full<L>(&self, context: &Context<L>) -> bool

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.

Source

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).

Source

pub fn create_stream<L>( &mut self, stream_id: u32, context: &mut Context<L>, ) -> Option<usize>

Source

pub fn new_stream_id(&mut self) -> Option<u32>

Source

pub fn force_disconnect(&mut self) -> MuxResult

Source

pub fn close<E, L>(&mut self, context: &mut Context<L>, endpoint: E)

Source

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.

Source

pub fn end_stream<L>(&mut self, stream_gid: usize, context: &mut Context<L>)

Source

pub fn start_stream<L>( &mut self, stream: usize, _context: &mut Context<L>, ) -> bool

Trait Implementations§

Source§

impl<Front: SocketHandler> Debug for ConnectionH2<Front>

Source§

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

Formats the value using the given formatter. Read more
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.

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.

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<Front> !RefUnwindSafe for ConnectionH2<Front>

§

impl<Front> !Send for ConnectionH2<Front>

§

impl<Front> !Sync for ConnectionH2<Front>

§

impl<Front> !UnwindSafe for ConnectionH2<Front>

§

impl<Front> Freeze for ConnectionH2<Front>
where Front: Freeze,

§

impl<Front> Unpin for ConnectionH2<Front>
where Front: Unpin,

§

impl<Front> UnsafeUnpin for ConnectionH2<Front>
where Front: 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> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more