use bytes::{Buf, Bytes}; use futures_core::Stream; use std::io; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; use tokio::io::{AsyncRead, AsyncWrite};
// Extracted part of `Connection` which does not depend on `T`. Reduces the amount of duplicated // method instantiations. #[derive(Debug)] struct ConnectionInner<P, B: Buf = Bytes> where
P: Peer,
{ /// Tracks the connection level state transitions.
state: State,
/// An error to report back once complete. /// /// This exists separately from State in order to support /// graceful shutdown.
error: Option<frame::GoAway>,
/// Pending GOAWAY frames to write.
go_away: GoAway,
/// Ping/pong handler
ping_pong: PingPong,
/// Connection settings
settings: Settings,
/// Stream state handler
streams: Streams<B, P>,
/// A `tracing` span tracking the lifetime of the connection.
span: tracing::Span,
/// Send a new SETTINGS frame with an updated initial window size. pub(crate) fn set_initial_window_size(&mutself, size: WindowSize) -> Result<(), UserError> { letmut settings = frame::Settings::default();
settings.set_initial_window_size(Some(size)); self.inner.settings.send_settings(settings)
}
/// Send a new SETTINGS frame with extended CONNECT protocol enabled. pub(crate) fn set_enable_connect_protocol(&mutself) -> Result<(), UserError> { letmut settings = frame::Settings::default();
settings.set_enable_connect_protocol(Some(1)); self.inner.settings.send_settings(settings)
}
/// Returns the maximum number of concurrent streams that may be initiated /// by this peer. pub(crate) fn max_send_streams(&self) -> usize { self.inner.streams.max_send_streams()
}
/// Returns the maximum number of concurrent streams that may be initiated /// by the remote peer. pub(crate) fn max_recv_streams(&self) -> usize { self.inner.streams.max_recv_streams()
}
/// Returns `Ready` when the connection is ready to receive a frame. /// /// Returns `Error` as this may raise errors that are caused by delayed /// processing of received frames. fn poll_ready(&mutself, cx: &mut Context) -> Poll<Result<(), Error>> { let _e = self.inner.span.enter(); let span = tracing::trace_span!("poll_ready"); let _e = span.enter(); // The order of these calls don't really matter too much
ready!(self.inner.ping_pong.send_pending_pong(cx, &mutself.codec))?;
ready!(self.inner.ping_pong.send_pending_ping(cx, &mutself.codec))?;
ready!(self
.inner
.settings
.poll_send(cx, &mutself.codec, &mutself.inner.streams))?;
ready!(self.inner.streams.send_pending_refusal(cx, &mutself.codec))?;
Poll::Ready(Ok(()))
}
/// Send any pending GOAWAY frames. /// /// This will return `Some(reason)` if the connection should be closed /// afterwards. If this is a graceful shutdown, this returns `None`. fn poll_go_away(&mutself, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> { self.inner.go_away.send_pending_go_away(cx, &mutself.codec)
}
match (ours, theirs) {
(Reason::NO_ERROR, Reason::NO_ERROR) => Ok(()),
(ours, Reason::NO_ERROR) => Err(Error::GoAway(Bytes::new(), ours, initiator)), // If both sides reported an error, give their // error back to th user. We assume our error // was a consequence of their error, and less // important.
(_, theirs) => Err(Error::remote_go_away(debug_data, theirs)),
}
}
/// Closes the connection by transitioning to a GOAWAY state /// iff there are no streams or references pubfn maybe_close_connection_if_no_streams(&mutself) { // If we poll() and realize that there are no streams or references // then we can close the connection by transitioning to GOAWAY if !self.inner.streams.has_streams_or_other_references() { self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
}
}
/// Advances the internal state of the connection. pubfn poll(&mutself, cx: &mut Context) -> Poll<Result<(), Error>> { // XXX(eliza): cloning the span is unfortunately necessary here in // order to placate the borrow checker — `self` is mutably borrowed by // `poll2`, which means that we can't borrow `self.span` to enter it. // The clone is just an atomic ref bump. let span = self.inner.span.clone(); let _e = span.enter(); let span = tracing::trace_span!("poll"); let _e = span.enter();
loop {
tracing::trace!(connection.state = ?self.inner.state); // TODO: probably clean up this glob of code matchself.inner.state { // When open, continue to poll a frame
State::Open => { let result = matchself.poll2(cx) {
Poll::Ready(result) => result, // The connection is not ready to make progress
Poll::Pending => { // Ensure all window updates have been sent. // // This will also handle flushing `self.codec`
ready!(self.inner.streams.poll_complete(cx, &mutself.codec))?;
if (self.inner.error.is_some()
|| self.inner.go_away.should_close_on_idle())
&& !self.inner.streams.has_streams()
{ self.inner.as_dyn().go_away_now(Reason::NO_ERROR); continue;
}
return Poll::Pending;
}
};
self.inner.as_dyn().handle_poll2_result(result)?
}
State::Closing(reason, initiator) => {
tracing::trace!("connection closing after flush"); // Flush/shutdown the codec
ready!(self.codec.shutdown(cx))?;
// Transition the state to error self.inner.state = State::Closed(reason, initiator);
}
State::Closed(reason, initiator) => { return Poll::Ready(self.take_error(reason, initiator));
}
}
}
}
fn poll2(&mutself, cx: &mut Context) -> Poll<Result<(), Error>> { // This happens outside of the loop to prevent needing to do a clock // check and then comparison of the queue possibly multiple times a // second (and thus, the clock wouldn't have changed enough to matter). self.clear_expired_reset_streams();
loop { // First, ensure that the `Connection` is able to receive a frame // // The order here matters: // - poll_go_away may buffer a graceful shutdown GOAWAY frame // - If it has, we've also added a PING to be sent in poll_ready iflet Some(reason) = ready!(self.poll_go_away(cx)?) { ifself.inner.go_away.should_close_now() { ifself.inner.go_away.is_user_initiated() { // A user initiated abrupt shutdown shouldn't return // the same error back to the user. return Poll::Ready(Ok(()));
} else { return Poll::Ready(Err(Error::library_go_away(reason)));
}
} // Only NO_ERROR should be waiting for idle
debug_assert_eq!(
reason,
Reason::NO_ERROR, "graceful GOAWAY should be NO_ERROR"
);
}
ready!(self.poll_ready(cx))?;
fn go_away_now(&mutself, e: Reason) { let last_processed_id = self.streams.last_processed_id(); let frame = frame::GoAway::new(last_processed_id, e); self.go_away.go_away_now(frame);
}
fn go_away_now_data(&mutself, e: Reason, data: Bytes) { let last_processed_id = self.streams.last_processed_id(); let frame = frame::GoAway::with_debug_data(last_processed_id, e, data); self.go_away.go_away_now(frame);
}
fn go_away_from_user(&mutself, e: Reason) { let last_processed_id = self.streams.last_processed_id(); let frame = frame::GoAway::new(last_processed_id, e); self.go_away.go_away_from_user(frame);
// Notify all streams of reason we're abruptly closing. self.streams.handle_error(Error::user_go_away(e));
}
fn handle_poll2_result(&mutself, result: Result<(), Error>) -> Result<(), Error> { match result { // The connection has shutdown normally
Ok(()) => {
*self.state = State::Closing(Reason::NO_ERROR, Initiator::Library);
Ok(())
} // Attempting to read a frame resulted in a connection level // error. This is handled by setting a GOAWAY frame followed by // terminating the connection.
Err(Error::GoAway(debug_data, reason, initiator)) => { let e = Error::GoAway(debug_data.clone(), reason, initiator);
tracing::debug!(error = ?e, "Connection::poll; connection error");
// We may have already sent a GOAWAY for this error, // if so, don't send another, just flush and close up. ifself
.go_away
.going_away()
.map_or(false, |frame| frame.reason() == reason)
{
tracing::trace!(" -> already going away");
*self.state = State::Closing(reason, initiator); return Ok(());
}
// Reset all active streams self.streams.handle_error(e); self.go_away_now_data(reason, debug_data);
Ok(())
} // Attempting to read a frame resulted in a stream level error. // This is handled by resetting the frame then trying to read // another frame.
Err(Error::Reset(id, reason, initiator)) => {
debug_assert_eq!(initiator, Initiator::Library);
tracing::trace!(?id, ?reason, "stream error"); self.streams.send_reset(id, reason);
Ok(())
} // Attempting to read a frame resulted in an I/O error. All // active streams must be reset. // // TODO: Are I/O errors recoverable?
Err(Error::Io(e, inner)) => {
tracing::debug!(error = ?e, "Connection::poll; IO error"); let e = Error::Io(e, inner);
// Reset all active streams self.streams.handle_error(e.clone());
// Return the error
Err(e)
}
}
}
fn recv_frame(&mutself, frame: Option<Frame>) -> Result<ReceivedFrame, Error> { usecrate::frame::Frame::*; match frame {
Some(Headers(frame)) => {
tracing::trace!(?frame, "recv HEADERS"); self.streams.recv_headers(frame)?;
}
Some(Data(frame)) => {
tracing::trace!(?frame, "recv DATA"); self.streams.recv_data(frame)?;
}
Some(Reset(frame)) => {
tracing::trace!(?frame, "recv RST_STREAM"); self.streams.recv_reset(frame)?;
}
Some(PushPromise(frame)) => {
tracing::trace!(?frame, "recv PUSH_PROMISE"); self.streams.recv_push_promise(frame)?;
}
Some(Settings(frame)) => {
tracing::trace!(?frame, "recv SETTINGS"); return Ok(ReceivedFrame::Settings(frame));
}
Some(GoAway(frame)) => {
tracing::trace!(?frame, "recv GOAWAY"); // This should prevent starting new streams, // but should allow continuing to process current streams // until they are all EOS. Once they are, State should // transition to GoAway. self.streams.recv_go_away(&frame)?;
*self.error = Some(frame);
}
Some(Ping(frame)) => {
tracing::trace!(?frame, "recv PING"); let status = self.ping_pong.recv_ping(frame); if status.is_shutdown() {
assert!( self.go_away.is_going_away(), "received unexpected shutdown ping"
);
// Graceful shutdown only makes sense for server peers. pubfn go_away_gracefully(&mutself) { ifself.inner.go_away.is_going_away() { // No reason to start a new one. return;
}
// According to http://httpwg.org/specs/rfc7540.html#GOAWAY: // // > A server that is attempting to gracefully shut down a connection // > SHOULD send an initial GOAWAY frame with the last stream // > identifier set to 2^31-1 and a NO_ERROR code. This signals to the // > client that a shutdown is imminent and that initiating further // > requests is prohibited. After allowing time for any in-flight // > stream creation (at least one round-trip time), the server can // > send another GOAWAY frame with an updated last stream identifier. // > This ensures that a connection can be cleanly shut down without // > losing requests. self.inner.as_dyn().go_away(StreamId::MAX, Reason::NO_ERROR);
// We take the advice of waiting 1 RTT literally, and wait // for a pong before proceeding. self.inner.ping_pong.ping_shutdown();
}
}
impl<T, P, B> Drop for Connection<T, P, B> where
P: Peer,
B: Buf,
{ fn drop(&mutself) { // Ignore errors as this indicates that the mutex is poisoned. let _ = self.inner.streams.recv_eof(true);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-23)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.