Make some functions less-generic to reduce binary bloat (#503)
* refactor: Extract FramedWrite::buffer to a less generic function Should cut out another 23 KiB (since I see it duplicated) * refactor: Extract some duplicated code to a function * refactor: Extract part of flush into a less generic function * refactor: Extract a less generic part of connection * refactor: Factor out a less generic part of Connection::poll2 * refactor: Extract a non-generic part of handshake2 * refactor: Don't duplicate Streams code on Peer (-3.5%) The `P: Peer` parameter is rarely used and there is already a mechanism for using it dynamically. * refactor: Make recv_frame less generic (-2.3%) * Move out part of Connection::poll * refactor: Extract parts of Connection * refactor: Extract a non-generic part of reclaim_frame * comments
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							6357e3256a
						
					
				
				
					commit
					30ca832790
				
			@@ -17,6 +17,19 @@ use tokio::io::{AsyncRead, AsyncWrite};
 | 
			
		||||
/// An H2 connection
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub(crate) struct Connection<T, P, B: Buf = Bytes>
 | 
			
		||||
where
 | 
			
		||||
    P: Peer,
 | 
			
		||||
{
 | 
			
		||||
    /// Read / write frame values
 | 
			
		||||
    codec: Codec<T, Prioritized<B>>,
 | 
			
		||||
 | 
			
		||||
    inner: ConnectionInner<P, B>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,
 | 
			
		||||
{
 | 
			
		||||
@@ -29,9 +42,6 @@ where
 | 
			
		||||
    /// graceful shutdown.
 | 
			
		||||
    error: Option<Reason>,
 | 
			
		||||
 | 
			
		||||
    /// Read / write frame values
 | 
			
		||||
    codec: Codec<T, Prioritized<B>>,
 | 
			
		||||
 | 
			
		||||
    /// Pending GOAWAY frames to write.
 | 
			
		||||
    go_away: GoAway,
 | 
			
		||||
 | 
			
		||||
@@ -51,6 +61,18 @@ where
 | 
			
		||||
    _phantom: PhantomData<P>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct DynConnection<'a, B: Buf = Bytes> {
 | 
			
		||||
    state: &'a mut State,
 | 
			
		||||
 | 
			
		||||
    go_away: &'a mut GoAway,
 | 
			
		||||
 | 
			
		||||
    streams: DynStreams<'a, B>,
 | 
			
		||||
 | 
			
		||||
    error: &'a mut Option<Reason>,
 | 
			
		||||
 | 
			
		||||
    ping_pong: &'a mut PingPong,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(crate) struct Config {
 | 
			
		||||
    pub next_stream_id: StreamId,
 | 
			
		||||
@@ -79,51 +101,56 @@ where
 | 
			
		||||
    B: Buf,
 | 
			
		||||
{
 | 
			
		||||
    pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
 | 
			
		||||
        let streams = Streams::new(streams::Config {
 | 
			
		||||
            local_init_window_sz: config
 | 
			
		||||
                .settings
 | 
			
		||||
                .initial_window_size()
 | 
			
		||||
                .unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE),
 | 
			
		||||
            initial_max_send_streams: config.initial_max_send_streams,
 | 
			
		||||
            local_next_stream_id: config.next_stream_id,
 | 
			
		||||
            local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
 | 
			
		||||
            local_reset_duration: config.reset_stream_duration,
 | 
			
		||||
            local_reset_max: config.reset_stream_max,
 | 
			
		||||
            remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
 | 
			
		||||
            remote_max_initiated: config
 | 
			
		||||
                .settings
 | 
			
		||||
                .max_concurrent_streams()
 | 
			
		||||
                .map(|max| max as usize),
 | 
			
		||||
        });
 | 
			
		||||
        fn streams_config(config: &Config) -> streams::Config {
 | 
			
		||||
            streams::Config {
 | 
			
		||||
                local_init_window_sz: config
 | 
			
		||||
                    .settings
 | 
			
		||||
                    .initial_window_size()
 | 
			
		||||
                    .unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE),
 | 
			
		||||
                initial_max_send_streams: config.initial_max_send_streams,
 | 
			
		||||
                local_next_stream_id: config.next_stream_id,
 | 
			
		||||
                local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
 | 
			
		||||
                local_reset_duration: config.reset_stream_duration,
 | 
			
		||||
                local_reset_max: config.reset_stream_max,
 | 
			
		||||
                remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
 | 
			
		||||
                remote_max_initiated: config
 | 
			
		||||
                    .settings
 | 
			
		||||
                    .max_concurrent_streams()
 | 
			
		||||
                    .map(|max| max as usize),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let streams = Streams::new(streams_config(&config));
 | 
			
		||||
        Connection {
 | 
			
		||||
            state: State::Open,
 | 
			
		||||
            error: None,
 | 
			
		||||
            codec,
 | 
			
		||||
            go_away: GoAway::new(),
 | 
			
		||||
            ping_pong: PingPong::new(),
 | 
			
		||||
            settings: Settings::new(config.settings),
 | 
			
		||||
            streams,
 | 
			
		||||
            span: tracing::debug_span!("Connection", peer = %P::NAME),
 | 
			
		||||
            _phantom: PhantomData,
 | 
			
		||||
            inner: ConnectionInner {
 | 
			
		||||
                state: State::Open,
 | 
			
		||||
                error: None,
 | 
			
		||||
                go_away: GoAway::new(),
 | 
			
		||||
                ping_pong: PingPong::new(),
 | 
			
		||||
                settings: Settings::new(config.settings),
 | 
			
		||||
                streams,
 | 
			
		||||
                span: tracing::debug_span!("Connection", peer = %P::NAME),
 | 
			
		||||
                _phantom: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// connection flow control
 | 
			
		||||
    pub(crate) fn set_target_window_size(&mut self, size: WindowSize) {
 | 
			
		||||
        self.streams.set_target_connection_window_size(size);
 | 
			
		||||
        self.inner.streams.set_target_connection_window_size(size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Send a new SETTINGS frame with an updated initial window size.
 | 
			
		||||
    pub(crate) fn set_initial_window_size(&mut self, size: WindowSize) -> Result<(), UserError> {
 | 
			
		||||
        let mut settings = frame::Settings::default();
 | 
			
		||||
        settings.set_initial_window_size(Some(size));
 | 
			
		||||
        self.settings.send_settings(settings)
 | 
			
		||||
        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.streams.max_send_streams()
 | 
			
		||||
        self.inner.streams.max_send_streams()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns `Ready` when the connection is ready to receive a frame.
 | 
			
		||||
@@ -131,16 +158,17 @@ where
 | 
			
		||||
    /// Returns `RecvError` as this may raise errors that are caused by delayed
 | 
			
		||||
    /// processing of received frames.
 | 
			
		||||
    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), RecvError>> {
 | 
			
		||||
        let _e = self.span.enter();
 | 
			
		||||
        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.ping_pong.send_pending_pong(cx, &mut self.codec))?;
 | 
			
		||||
        ready!(self.ping_pong.send_pending_ping(cx, &mut self.codec))?;
 | 
			
		||||
        ready!(self.inner.ping_pong.send_pending_pong(cx, &mut self.codec))?;
 | 
			
		||||
        ready!(self.inner.ping_pong.send_pending_ping(cx, &mut self.codec))?;
 | 
			
		||||
        ready!(self
 | 
			
		||||
            .inner
 | 
			
		||||
            .settings
 | 
			
		||||
            .poll_send(cx, &mut self.codec, &mut self.streams))?;
 | 
			
		||||
        ready!(self.streams.send_pending_refusal(cx, &mut self.codec))?;
 | 
			
		||||
            .poll_send(cx, &mut self.codec, &mut self.inner.streams))?;
 | 
			
		||||
        ready!(self.inner.streams.send_pending_refusal(cx, &mut self.codec))?;
 | 
			
		||||
 | 
			
		||||
        Poll::Ready(Ok(()))
 | 
			
		||||
    }
 | 
			
		||||
@@ -150,32 +178,15 @@ where
 | 
			
		||||
    /// 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(&mut self, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> {
 | 
			
		||||
        self.go_away.send_pending_go_away(cx, &mut self.codec)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn go_away(&mut self, id: StreamId, e: Reason) {
 | 
			
		||||
        let frame = frame::GoAway::new(id, e);
 | 
			
		||||
        self.streams.send_go_away(id);
 | 
			
		||||
        self.go_away.go_away(frame);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn go_away_now(&mut self, 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);
 | 
			
		||||
        self.inner.go_away.send_pending_go_away(cx, &mut self.codec)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn go_away_from_user(&mut self, 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.recv_err(&proto::Error::Proto(e));
 | 
			
		||||
        self.inner.as_dyn().go_away_from_user(e)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn take_error(&mut self, ours: Reason) -> Poll<Result<(), proto::Error>> {
 | 
			
		||||
        let reason = if let Some(theirs) = self.error.take() {
 | 
			
		||||
        let reason = if let Some(theirs) = self.inner.error.take() {
 | 
			
		||||
            match (ours, theirs) {
 | 
			
		||||
                // If either side reported an error, return that
 | 
			
		||||
                // to the user.
 | 
			
		||||
@@ -202,13 +213,13 @@ where
 | 
			
		||||
    pub fn maybe_close_connection_if_no_streams(&mut self) {
 | 
			
		||||
        // If we poll() and realize that there are no streams or references
 | 
			
		||||
        // then we can close the connection by transitioning to GOAWAY
 | 
			
		||||
        if !self.streams.has_streams_or_other_references() {
 | 
			
		||||
            self.go_away_now(Reason::NO_ERROR);
 | 
			
		||||
        if !self.inner.streams.has_streams_or_other_references() {
 | 
			
		||||
            self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
 | 
			
		||||
        self.ping_pong.take_user_pings()
 | 
			
		||||
        self.inner.ping_pong.take_user_pings()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advances the internal state of the connection.
 | 
			
		||||
@@ -217,79 +228,39 @@ where
 | 
			
		||||
        // 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.span.clone();
 | 
			
		||||
        let span = self.inner.span.clone();
 | 
			
		||||
        let _e = span.enter();
 | 
			
		||||
        let span = tracing::trace_span!("poll");
 | 
			
		||||
        let _e = span.enter();
 | 
			
		||||
        use crate::codec::RecvError::*;
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            tracing::trace!(connection.state = ?self.state);
 | 
			
		||||
            tracing::trace!(connection.state = ?self.inner.state);
 | 
			
		||||
            // TODO: probably clean up this glob of code
 | 
			
		||||
            match self.state {
 | 
			
		||||
            match self.inner.state {
 | 
			
		||||
                // When open, continue to poll a frame
 | 
			
		||||
                State::Open => {
 | 
			
		||||
                    match self.poll2(cx) {
 | 
			
		||||
                        // The connection has shutdown normally
 | 
			
		||||
                        Poll::Ready(Ok(())) => self.state = State::Closing(Reason::NO_ERROR),
 | 
			
		||||
                    let result = match self.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.streams.poll_complete(cx, &mut self.codec))?;
 | 
			
		||||
                            ready!(self.inner.streams.poll_complete(cx, &mut self.codec))?;
 | 
			
		||||
 | 
			
		||||
                            if (self.error.is_some() || self.go_away.should_close_on_idle())
 | 
			
		||||
                                && !self.streams.has_streams()
 | 
			
		||||
                            if (self.inner.error.is_some()
 | 
			
		||||
                                || self.inner.go_away.should_close_on_idle())
 | 
			
		||||
                                && !self.inner.streams.has_streams()
 | 
			
		||||
                            {
 | 
			
		||||
                                self.go_away_now(Reason::NO_ERROR);
 | 
			
		||||
                                self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
 | 
			
		||||
                                continue;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            return Poll::Pending;
 | 
			
		||||
                        }
 | 
			
		||||
                        // Attempting to read a frame resulted in a connection level
 | 
			
		||||
                        // error. This is handled by setting a GOAWAY frame followed by
 | 
			
		||||
                        // terminating the connection.
 | 
			
		||||
                        Poll::Ready(Err(Connection(e))) => {
 | 
			
		||||
                            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.
 | 
			
		||||
                            if let Some(reason) = self.go_away.going_away_reason() {
 | 
			
		||||
                                if reason == e {
 | 
			
		||||
                                    tracing::trace!("    -> already going away");
 | 
			
		||||
                                    self.state = State::Closing(e);
 | 
			
		||||
                                    continue;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            // Reset all active streams
 | 
			
		||||
                            self.streams.recv_err(&e.into());
 | 
			
		||||
                            self.go_away_now(e);
 | 
			
		||||
                        }
 | 
			
		||||
                        // Attempting to read a frame resulted in a stream level error.
 | 
			
		||||
                        // This is handled by resetting the frame then trying to read
 | 
			
		||||
                        // another frame.
 | 
			
		||||
                        Poll::Ready(Err(Stream { id, reason })) => {
 | 
			
		||||
                            tracing::trace!(?id, ?reason, "stream error");
 | 
			
		||||
                            self.streams.send_reset(id, reason);
 | 
			
		||||
                        }
 | 
			
		||||
                        // Attempting to read a frame resulted in an I/O error. All
 | 
			
		||||
                        // active streams must be reset.
 | 
			
		||||
                        //
 | 
			
		||||
                        // TODO: Are I/O errors recoverable?
 | 
			
		||||
                        Poll::Ready(Err(Io(e))) => {
 | 
			
		||||
                            tracing::debug!(error = ?e, "Connection::poll; IO error");
 | 
			
		||||
                            let e = e.into();
 | 
			
		||||
 | 
			
		||||
                            // Reset all active streams
 | 
			
		||||
                            self.streams.recv_err(&e);
 | 
			
		||||
 | 
			
		||||
                            // Return the error
 | 
			
		||||
                            return Poll::Ready(Err(e));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    self.inner.as_dyn().handle_poll2_result(result)?
 | 
			
		||||
                }
 | 
			
		||||
                State::Closing(reason) => {
 | 
			
		||||
                    tracing::trace!("connection closing after flush");
 | 
			
		||||
@@ -297,7 +268,7 @@ where
 | 
			
		||||
                    ready!(self.codec.shutdown(cx))?;
 | 
			
		||||
 | 
			
		||||
                    // Transition the state to error
 | 
			
		||||
                    self.state = State::Closed(reason);
 | 
			
		||||
                    self.inner.state = State::Closed(reason);
 | 
			
		||||
                }
 | 
			
		||||
                State::Closed(reason) => return self.take_error(reason),
 | 
			
		||||
            }
 | 
			
		||||
@@ -305,8 +276,6 @@ where
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn poll2(&mut self, cx: &mut Context) -> Poll<Result<(), RecvError>> {
 | 
			
		||||
        use crate::frame::Frame::*;
 | 
			
		||||
 | 
			
		||||
        // 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).
 | 
			
		||||
@@ -319,8 +288,8 @@ where
 | 
			
		||||
            // - 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
 | 
			
		||||
            if let Some(reason) = ready!(self.poll_go_away(cx)?) {
 | 
			
		||||
                if self.go_away.should_close_now() {
 | 
			
		||||
                    if self.go_away.is_user_initiated() {
 | 
			
		||||
                if self.inner.go_away.should_close_now() {
 | 
			
		||||
                    if self.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(()));
 | 
			
		||||
@@ -337,61 +306,20 @@ where
 | 
			
		||||
            }
 | 
			
		||||
            ready!(self.poll_ready(cx))?;
 | 
			
		||||
 | 
			
		||||
            match ready!(Pin::new(&mut self.codec).poll_next(cx)?) {
 | 
			
		||||
                Some(Headers(frame)) => {
 | 
			
		||||
                    tracing::trace!(?frame, "recv HEADERS");
 | 
			
		||||
                    self.streams.recv_headers(frame)?;
 | 
			
		||||
            match self
 | 
			
		||||
                .inner
 | 
			
		||||
                .as_dyn()
 | 
			
		||||
                .recv_frame(ready!(Pin::new(&mut self.codec).poll_next(cx)?))?
 | 
			
		||||
            {
 | 
			
		||||
                ReceivedFrame::Settings(frame) => {
 | 
			
		||||
                    self.inner.settings.recv_settings(
 | 
			
		||||
                        frame,
 | 
			
		||||
                        &mut self.codec,
 | 
			
		||||
                        &mut self.inner.streams,
 | 
			
		||||
                    )?;
 | 
			
		||||
                }
 | 
			
		||||
                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");
 | 
			
		||||
                    self.settings
 | 
			
		||||
                        .recv_settings(frame, &mut self.codec, &mut self.streams)?;
 | 
			
		||||
                }
 | 
			
		||||
                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.reason());
 | 
			
		||||
                }
 | 
			
		||||
                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"
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                        let last_processed_id = self.streams.last_processed_id();
 | 
			
		||||
                        self.go_away(last_processed_id, Reason::NO_ERROR);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Some(WindowUpdate(frame)) => {
 | 
			
		||||
                    tracing::trace!(?frame, "recv WINDOW_UPDATE");
 | 
			
		||||
                    self.streams.recv_window_update(frame)?;
 | 
			
		||||
                }
 | 
			
		||||
                Some(Priority(frame)) => {
 | 
			
		||||
                    tracing::trace!(?frame, "recv PRIORITY");
 | 
			
		||||
                    // TODO: handle
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    tracing::trace!("codec closed");
 | 
			
		||||
                    self.streams.recv_eof(false).expect("mutex poisoned");
 | 
			
		||||
                ReceivedFrame::Continue => (),
 | 
			
		||||
                ReceivedFrame::Done => {
 | 
			
		||||
                    return Poll::Ready(Ok(()));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -399,17 +327,190 @@ where
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clear_expired_reset_streams(&mut self) {
 | 
			
		||||
        self.streams.clear_expired_reset_streams();
 | 
			
		||||
        self.inner.streams.clear_expired_reset_streams();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<P, B> ConnectionInner<P, B>
 | 
			
		||||
where
 | 
			
		||||
    P: Peer,
 | 
			
		||||
    B: Buf,
 | 
			
		||||
{
 | 
			
		||||
    fn as_dyn(&mut self) -> DynConnection<'_, B> {
 | 
			
		||||
        let ConnectionInner {
 | 
			
		||||
            state,
 | 
			
		||||
            go_away,
 | 
			
		||||
            streams,
 | 
			
		||||
            error,
 | 
			
		||||
            ping_pong,
 | 
			
		||||
            ..
 | 
			
		||||
        } = self;
 | 
			
		||||
        let streams = streams.as_dyn();
 | 
			
		||||
        DynConnection {
 | 
			
		||||
            state,
 | 
			
		||||
            go_away,
 | 
			
		||||
            streams,
 | 
			
		||||
            error,
 | 
			
		||||
            ping_pong,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<B> DynConnection<'_, B>
 | 
			
		||||
where
 | 
			
		||||
    B: Buf,
 | 
			
		||||
{
 | 
			
		||||
    fn go_away(&mut self, id: StreamId, e: Reason) {
 | 
			
		||||
        let frame = frame::GoAway::new(id, e);
 | 
			
		||||
        self.streams.send_go_away(id);
 | 
			
		||||
        self.go_away.go_away(frame);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn go_away_now(&mut self, 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_from_user(&mut self, 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.recv_err(&proto::Error::Proto(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn handle_poll2_result(&mut self, result: Result<(), RecvError>) -> Result<(), Error> {
 | 
			
		||||
        use crate::codec::RecvError::*;
 | 
			
		||||
        match result {
 | 
			
		||||
            // The connection has shutdown normally
 | 
			
		||||
            Ok(()) => {
 | 
			
		||||
                *self.state = State::Closing(Reason::NO_ERROR);
 | 
			
		||||
                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(Connection(e)) => {
 | 
			
		||||
                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.
 | 
			
		||||
                if let Some(reason) = self.go_away.going_away_reason() {
 | 
			
		||||
                    if reason == e {
 | 
			
		||||
                        tracing::trace!("    -> already going away");
 | 
			
		||||
                        *self.state = State::Closing(e);
 | 
			
		||||
                        return Ok(());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Reset all active streams
 | 
			
		||||
                self.streams.recv_err(&e.into());
 | 
			
		||||
                self.go_away_now(e);
 | 
			
		||||
                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(Stream { id, reason }) => {
 | 
			
		||||
                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(Io(e)) => {
 | 
			
		||||
                tracing::debug!(error = ?e, "Connection::poll; IO error");
 | 
			
		||||
                let e = e.into();
 | 
			
		||||
 | 
			
		||||
                // Reset all active streams
 | 
			
		||||
                self.streams.recv_err(&e);
 | 
			
		||||
 | 
			
		||||
                // Return the error
 | 
			
		||||
                Err(e)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn recv_frame(&mut self, frame: Option<Frame>) -> Result<ReceivedFrame, RecvError> {
 | 
			
		||||
        use crate::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.reason());
 | 
			
		||||
            }
 | 
			
		||||
            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"
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    let last_processed_id = self.streams.last_processed_id();
 | 
			
		||||
                    self.go_away(last_processed_id, Reason::NO_ERROR);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Some(WindowUpdate(frame)) => {
 | 
			
		||||
                tracing::trace!(?frame, "recv WINDOW_UPDATE");
 | 
			
		||||
                self.streams.recv_window_update(frame)?;
 | 
			
		||||
            }
 | 
			
		||||
            Some(Priority(frame)) => {
 | 
			
		||||
                tracing::trace!(?frame, "recv PRIORITY");
 | 
			
		||||
                // TODO: handle
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                tracing::trace!("codec closed");
 | 
			
		||||
                self.streams.recv_eof(false).expect("mutex poisoned");
 | 
			
		||||
                return Ok(ReceivedFrame::Done);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(ReceivedFrame::Continue)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum ReceivedFrame {
 | 
			
		||||
    Settings(frame::Settings),
 | 
			
		||||
    Continue,
 | 
			
		||||
    Done,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T, B> Connection<T, client::Peer, B>
 | 
			
		||||
where
 | 
			
		||||
    T: AsyncRead + AsyncWrite,
 | 
			
		||||
    B: Buf,
 | 
			
		||||
{
 | 
			
		||||
    pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
 | 
			
		||||
        &self.streams
 | 
			
		||||
        &self.inner.streams
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -419,12 +520,12 @@ where
 | 
			
		||||
    B: Buf,
 | 
			
		||||
{
 | 
			
		||||
    pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
 | 
			
		||||
        self.streams.next_incoming()
 | 
			
		||||
        self.inner.streams.next_incoming()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Graceful shutdown only makes sense for server peers.
 | 
			
		||||
    pub fn go_away_gracefully(&mut self) {
 | 
			
		||||
        if self.go_away.is_going_away() {
 | 
			
		||||
        if self.inner.go_away.is_going_away() {
 | 
			
		||||
            // No reason to start a new one.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -440,11 +541,11 @@ where
 | 
			
		||||
        // > send another GOAWAY frame with an updated last stream identifier.
 | 
			
		||||
        // > This ensures that a connection can be cleanly shut down without
 | 
			
		||||
        // > losing requests.
 | 
			
		||||
        self.go_away(StreamId::MAX, Reason::NO_ERROR);
 | 
			
		||||
        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.ping_pong.ping_shutdown();
 | 
			
		||||
        self.inner.ping_pong.ping_shutdown();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -455,6 +556,6 @@ where
 | 
			
		||||
{
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        // Ignore errors as this indicates that the mutex is poisoned.
 | 
			
		||||
        let _ = self.streams.recv_eof(true);
 | 
			
		||||
        let _ = self.inner.streams.recv_eof(true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user