Coverage Report

Created: 2026-09-04 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2/src/proto/connection.rs
Line
Count
Source
1
use crate::codec::UserError;
2
use crate::frame::{Reason, StreamId};
3
use crate::{client, server};
4
5
use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
6
use crate::proto::*;
7
8
use bytes::Bytes;
9
use futures_core::Stream;
10
use std::io;
11
use std::marker::PhantomData;
12
use std::pin::Pin;
13
use std::task::{Context, Poll};
14
use std::time::Duration;
15
use tokio::io::AsyncRead;
16
17
/// An H2 connection
18
#[derive(Debug)]
19
pub(crate) struct Connection<T, P, B: Buf = Bytes>
20
where
21
    P: Peer,
22
{
23
    /// Read / write frame values
24
    codec: Codec<T, Prioritized<B>>,
25
26
    inner: ConnectionInner<P, B>,
27
}
28
29
// Extracted part of `Connection` which does not depend on `T`. Reduces the amount of duplicated
30
// method instantiations.
31
#[derive(Debug)]
32
struct ConnectionInner<P, B: Buf = Bytes>
33
where
34
    P: Peer,
35
{
36
    /// Tracks the connection level state transitions.
37
    state: State,
38
39
    /// An error to report back once complete.
40
    ///
41
    /// This exists separately from State in order to support
42
    /// graceful shutdown.
43
    error: Option<frame::GoAway>,
44
45
    /// Pending GOAWAY frames to write.
46
    go_away: GoAway,
47
48
    /// Ping/pong handler
49
    ping_pong: PingPong,
50
51
    /// Connection settings
52
    settings: Settings,
53
54
    /// Stream state handler
55
    streams: Streams<B, P>,
56
57
    /// A `tracing` span tracking the lifetime of the connection.
58
    span: tracing::Span,
59
60
    /// Client or server
61
    _phantom: PhantomData<P>,
62
}
63
64
struct DynConnection<'a, B: Buf = Bytes> {
65
    state: &'a mut State,
66
67
    go_away: &'a mut GoAway,
68
69
    streams: DynStreams<'a, B>,
70
71
    error: &'a mut Option<frame::GoAway>,
72
73
    ping_pong: &'a mut PingPong,
74
}
75
76
#[derive(Debug, Clone)]
77
pub(crate) struct Config {
78
    pub next_stream_id: StreamId,
79
    pub initial_max_send_streams: usize,
80
    pub max_send_buffer_size: usize,
81
    pub reset_stream_duration: Duration,
82
    pub reset_stream_max: usize,
83
    pub remote_reset_stream_max: usize,
84
    pub local_error_reset_streams_max: Option<usize>,
85
    pub settings: frame::Settings,
86
    pub data_frame_budget: usize,
87
}
88
89
#[derive(Clone, Copy, Debug)]
90
pub(crate) enum DataFrameBudget {
91
    Auto,
92
    Configured(usize),
93
}
94
95
impl DataFrameBudget {
96
12.7k
    pub(crate) fn resolve(self, connection_window: Option<WindowSize>) -> usize {
97
12.7k
        match self {
98
0
            Self::Configured(budget) => budget,
99
            Self::Auto => {
100
12.7k
                let window = connection_window.unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE);
101
12.7k
                let budget = window as usize / 2;
102
103
12.7k
                budget.max(DEFAULT_DATA_FRAME_BUDGET)
104
            }
105
        }
106
12.7k
    }
107
}
108
109
#[derive(Debug)]
110
enum State {
111
    /// Currently open in a sane state
112
    Open,
113
114
    /// The codec must be flushed
115
    Closing(Reason, Initiator),
116
117
    /// In a closed state
118
    Closed(Reason, Initiator),
119
}
120
121
impl<T, P, B> Connection<T, P, B>
122
where
123
    T: AsyncRead + AsyncWrite + Unpin,
124
    P: Peer,
125
    B: Buf,
126
{
127
12.7k
    pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
128
12.7k
        fn streams_config(config: &Config) -> streams::Config {
129
            streams::Config {
130
12.7k
                initial_max_send_streams: config.initial_max_send_streams,
131
12.7k
                local_max_buffer_size: config.max_send_buffer_size,
132
12.7k
                local_next_stream_id: config.next_stream_id,
133
12.7k
                local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
134
12.7k
                extended_connect_protocol_enabled: config
135
12.7k
                    .settings
136
12.7k
                    .is_extended_connect_protocol_enabled()
137
12.7k
                    .unwrap_or(false),
138
12.7k
                local_reset_duration: config.reset_stream_duration,
139
12.7k
                local_reset_max: config.reset_stream_max,
140
12.7k
                remote_reset_max: config.remote_reset_stream_max,
141
                remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
142
12.7k
                remote_max_initiated: config
143
12.7k
                    .settings
144
12.7k
                    .max_concurrent_streams()
145
12.7k
                    .map(|max| max as usize),
146
12.7k
                local_max_error_reset_streams: config.local_error_reset_streams_max,
147
12.7k
                data_frame_budget: config.data_frame_budget,
148
            }
149
12.7k
        }
150
12.7k
        let streams = Streams::new(streams_config(&config));
151
12.7k
        let span = tracing::debug_span!(parent: None, "Connection", peer = %P::NAME);
152
12.7k
        span.follows_from(tracing::Span::current());
153
12.7k
        Connection {
154
12.7k
            codec,
155
12.7k
            inner: ConnectionInner {
156
12.7k
                state: State::Open,
157
12.7k
                error: None,
158
12.7k
                go_away: GoAway::new(),
159
12.7k
                ping_pong: PingPong::new(),
160
12.7k
                settings: Settings::new(config.settings),
161
12.7k
                streams,
162
12.7k
                span,
163
12.7k
                _phantom: PhantomData,
164
12.7k
            },
165
12.7k
        }
166
12.7k
    }
<h2::proto::connection::Connection<h2_support::mock::Mock, h2::client::Peer>>::new
Line
Count
Source
127
817
    pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
128
        fn streams_config(config: &Config) -> streams::Config {
129
            streams::Config {
130
                initial_max_send_streams: config.initial_max_send_streams,
131
                local_max_buffer_size: config.max_send_buffer_size,
132
                local_next_stream_id: config.next_stream_id,
133
                local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
134
                extended_connect_protocol_enabled: config
135
                    .settings
136
                    .is_extended_connect_protocol_enabled()
137
                    .unwrap_or(false),
138
                local_reset_duration: config.reset_stream_duration,
139
                local_reset_max: config.reset_stream_max,
140
                remote_reset_max: config.remote_reset_stream_max,
141
                remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
142
                remote_max_initiated: config
143
                    .settings
144
                    .max_concurrent_streams()
145
                    .map(|max| max as usize),
146
                local_max_error_reset_streams: config.local_error_reset_streams_max,
147
                data_frame_budget: config.data_frame_budget,
148
            }
149
        }
150
817
        let streams = Streams::new(streams_config(&config));
151
817
        let span = tracing::debug_span!(parent: None, "Connection", peer = %P::NAME);
152
817
        span.follows_from(tracing::Span::current());
153
817
        Connection {
154
817
            codec,
155
817
            inner: ConnectionInner {
156
817
                state: State::Open,
157
817
                error: None,
158
817
                go_away: GoAway::new(),
159
817
                ping_pong: PingPong::new(),
160
817
                settings: Settings::new(config.settings),
161
817
                streams,
162
817
                span,
163
817
                _phantom: PhantomData,
164
817
            },
165
817
        }
166
817
    }
<h2::proto::connection::Connection<fuzz_e2e::MockIo, h2::client::Peer>>::new
Line
Count
Source
127
11.9k
    pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
128
        fn streams_config(config: &Config) -> streams::Config {
129
            streams::Config {
130
                initial_max_send_streams: config.initial_max_send_streams,
131
                local_max_buffer_size: config.max_send_buffer_size,
132
                local_next_stream_id: config.next_stream_id,
133
                local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
134
                extended_connect_protocol_enabled: config
135
                    .settings
136
                    .is_extended_connect_protocol_enabled()
137
                    .unwrap_or(false),
138
                local_reset_duration: config.reset_stream_duration,
139
                local_reset_max: config.reset_stream_max,
140
                remote_reset_max: config.remote_reset_stream_max,
141
                remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
142
                remote_max_initiated: config
143
                    .settings
144
                    .max_concurrent_streams()
145
                    .map(|max| max as usize),
146
                local_max_error_reset_streams: config.local_error_reset_streams_max,
147
                data_frame_budget: config.data_frame_budget,
148
            }
149
        }
150
11.9k
        let streams = Streams::new(streams_config(&config));
151
11.9k
        let span = tracing::debug_span!(parent: None, "Connection", peer = %P::NAME);
152
11.9k
        span.follows_from(tracing::Span::current());
153
11.9k
        Connection {
154
11.9k
            codec,
155
11.9k
            inner: ConnectionInner {
156
11.9k
                state: State::Open,
157
11.9k
                error: None,
158
11.9k
                go_away: GoAway::new(),
159
11.9k
                ping_pong: PingPong::new(),
160
11.9k
                settings: Settings::new(config.settings),
161
11.9k
                streams,
162
11.9k
                span,
163
11.9k
                _phantom: PhantomData,
164
11.9k
            },
165
11.9k
        }
166
11.9k
    }
167
168
    /// connection flow control
169
0
    pub(crate) fn set_target_window_size(&mut self, size: WindowSize) {
170
0
        let _res = self.inner.streams.set_target_connection_window_size(size);
171
        // TODO: proper error handling
172
0
        debug_assert!(_res.is_ok());
173
0
    }
Unexecuted instantiation: <h2::proto::connection::Connection<h2_support::mock::Mock, h2::client::Peer>>::set_target_window_size
Unexecuted instantiation: <h2::proto::connection::Connection<fuzz_e2e::MockIo, h2::client::Peer>>::set_target_window_size
174
175
    /// Send a new SETTINGS frame with an updated initial window size.
176
    pub(crate) fn set_initial_window_size(&mut self, size: WindowSize) -> Result<(), UserError> {
177
        let mut settings = frame::Settings::default();
178
        settings.set_initial_window_size(Some(size));
179
        self.inner.settings.send_settings(settings)
180
    }
181
182
    /// Send a new SETTINGS frame with extended CONNECT protocol enabled.
183
    pub(crate) fn set_enable_connect_protocol(&mut self) -> Result<(), UserError> {
184
        let mut settings = frame::Settings::default();
185
        settings.set_enable_connect_protocol(Some(1));
186
        self.inner.settings.send_settings(settings)
187
    }
188
189
    /// Returns the maximum number of concurrent streams that may be initiated
190
    /// by this peer.
191
    pub(crate) fn max_send_streams(&self) -> usize {
192
        self.inner.streams.max_send_streams()
193
    }
194
195
    /// Returns the maximum number of concurrent streams that may be initiated
196
    /// by the remote peer.
197
    pub(crate) fn max_recv_streams(&self) -> usize {
198
        self.inner.streams.max_recv_streams()
199
    }
200
201
    #[cfg(feature = "unstable")]
202
    pub fn num_wired_streams(&self) -> usize {
203
        self.inner.streams.num_wired_streams()
204
    }
205
206
    /// Returns `Ready` when the connection is ready to receive a frame.
207
    ///
208
    /// Returns `Error` as this may raise errors that are caused by delayed
209
    /// processing of received frames.
210
1.25M
    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
211
1.25M
        let _e = self.inner.span.enter();
212
1.25M
        let span = tracing::trace_span!("poll_ready");
213
1.25M
        let _e = span.enter();
214
        // The order of these calls don't really matter too much
215
1.25M
        ready!(self.inner.ping_pong.send_pending_pong(cx, &mut self.codec))?;
216
1.24M
        ready!(self.inner.ping_pong.send_pending_ping(cx, &mut self.codec))?;
217
1.24M
        ready!(self
218
1.24M
            .inner
219
1.24M
            .settings
220
1.24M
            .poll_send(cx, &mut self.codec, &mut self.inner.streams))?;
221
1.24M
        ready!(self.inner.streams.send_pending_refusal(cx, &mut self.codec))?;
222
223
1.24M
        Poll::Ready(Ok(()))
224
1.25M
    }
225
226
    /// Send any pending GOAWAY frames.
227
    ///
228
    /// This will return `Some(reason)` if the connection should be closed
229
    /// afterwards. If this is a graceful shutdown, this returns `None`.
230
1.25M
    fn poll_go_away(&mut self, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> {
231
1.25M
        self.inner.go_away.send_pending_go_away(cx, &mut self.codec)
232
1.25M
    }
233
234
    pub fn go_away_from_user(&mut self, e: Reason) {
235
        self.inner.as_dyn().go_away_from_user(e)
236
    }
237
238
681
    fn take_error(&mut self, ours: Reason, initiator: Initiator) -> Result<(), Error> {
239
681
        let (debug_data, theirs) = self
240
681
            .inner
241
681
            .error
242
681
            .take()
243
681
            .as_ref()
244
681
            .map_or((Bytes::new(), Reason::NO_ERROR), |frame| {
245
15
                (frame.debug_data().clone(), frame.reason())
246
15
            });
247
248
681
        match (ours, theirs) {
249
481
            (Reason::NO_ERROR, Reason::NO_ERROR) => Ok(()),
250
185
            (ours, Reason::NO_ERROR) => Err(Error::GoAway(Bytes::new(), ours, initiator)),
251
            // If both sides reported an error, give their
252
            // error back to th user. We assume our error
253
            // was a consequence of their error, and less
254
            // important.
255
15
            (_, theirs) => Err(Error::remote_go_away(debug_data, theirs)),
256
        }
257
681
    }
258
259
    /// Closes the connection by transitioning to a GOAWAY state
260
    /// iff there are no streams or references
261
1.20M
    pub fn maybe_close_connection_if_no_streams(&mut self) {
262
        // If we poll() and realize that there are no streams or references
263
        // then we can close the connection by transitioning to GOAWAY
264
1.20M
        if !self.inner.streams.has_streams_or_other_references() {
265
0
            self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
266
1.20M
        }
267
1.20M
    }
268
269
    /// Checks if there are any streams
270
    pub fn has_streams(&self) -> bool {
271
        self.inner.streams.has_streams()
272
    }
273
274
    /// Checks if there are any streams or references left
275
2.39M
    pub fn has_streams_or_other_references(&self) -> bool {
276
        // If we poll() and realize that there are no streams or references
277
        // then we can close the connection by transitioning to GOAWAY
278
2.39M
        self.inner.streams.has_streams_or_other_references()
279
2.39M
    }
280
281
    pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
282
        self.inner.ping_pong.take_user_pings()
283
    }
284
285
    /// Advances the internal state of the connection.
286
1.20M
    pub fn poll(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
287
        // XXX(eliza): cloning the span is unfortunately necessary here in
288
        // order to placate the borrow checker — `self` is mutably borrowed by
289
        // `poll2`, which means that we can't borrow `self.span` to enter it.
290
        // The clone is just an atomic ref bump.
291
1.20M
        let span = self.inner.span.clone();
292
1.20M
        let _e = span.enter();
293
1.20M
        let span = tracing::trace_span!("poll");
294
1.20M
        let _e = span.enter();
295
296
        loop {
297
1.25M
            tracing::trace!(connection.state = ?self.inner.state);
298
            // TODO: probably clean up this glob of code
299
1.25M
            match self.inner.state {
300
                // When open, continue to poll a frame
301
                State::Open => {
302
1.24M
                    let result = match self.poll2(cx) {
303
51.0k
                        Poll::Ready(result) => result,
304
                        // The connection is not ready to make progress
305
                        Poll::Pending => {
306
                            // Ensure all window updates have been sent.
307
                            //
308
                            // This will also handle flushing `self.codec`
309
1.19M
                            ready!(self.inner.streams.poll_complete(cx, &mut self.codec))?;
310
311
250k
                            if (self.inner.error.is_some()
312
250k
                                || self.inner.go_away.should_close_on_idle())
313
6
                                && !self.inner.streams.has_streams()
314
                            {
315
3
                                self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
316
3
                                continue;
317
250k
                            }
318
319
250k
                            return Poll::Pending;
320
                        }
321
                    };
322
323
51.0k
                    self.inner.as_dyn().handle_poll2_result(result)?
324
                }
325
10.1k
                State::Closing(reason, initiator) => {
326
10.1k
                    tracing::trace!("connection closing after flush");
327
                    // Flush/shutdown the codec
328
10.1k
                    ready!(self.codec.shutdown(cx))?;
329
330
                    // Transition the state to error
331
681
                    self.inner.state = State::Closed(reason, initiator);
332
                }
333
681
                State::Closed(reason, initiator) => {
334
681
                    return Poll::Ready(self.take_error(reason, initiator));
335
                }
336
            }
337
        }
338
1.20M
    }
339
340
1.24M
    fn poll2(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
341
        // This happens outside of the loop to prevent needing to do a clock
342
        // check and then comparison of the queue possibly multiple times a
343
        // second (and thus, the clock wouldn't have changed enough to matter).
344
1.24M
        self.clear_expired_reset_streams();
345
346
        loop {
347
            // First, ensure that the `Connection` is able to receive a frame
348
            //
349
            // The order here matters:
350
            // - poll_go_away may buffer a graceful shutdown GOAWAY frame
351
            // - If it has, we've also added a PING to be sent in poll_ready
352
1.25M
            if let Some(reason) = ready!(self.poll_go_away(cx)?) {
353
6.60k
                if self.inner.go_away.should_close_now() {
354
6.60k
                    if self.inner.go_away.is_user_initiated() {
355
                        // A user initiated abrupt shutdown shouldn't return
356
                        // the same error back to the user.
357
0
                        return Poll::Ready(Ok(()));
358
                    } else {
359
6.60k
                        return Poll::Ready(Err(Error::library_go_away(reason)));
360
                    }
361
0
                }
362
                // Only NO_ERROR should be waiting for idle
363
0
                debug_assert_eq!(
364
                    reason,
365
                    Reason::NO_ERROR,
366
0
                    "graceful GOAWAY should be NO_ERROR"
367
                );
368
1.25M
            }
369
1.25M
            ready!(self.poll_ready(cx))?;
370
371
1.24M
            match self
372
1.24M
                .inner
373
1.24M
                .as_dyn()
374
1.24M
                .recv_frame(ready!(Pin::new(&mut self.codec).poll_next(cx)?))?
375
            {
376
5.39k
                ReceivedFrame::Settings(frame) => {
377
5.39k
                    self.inner.settings.recv_settings(
378
5.39k
                        frame,
379
5.39k
                        &mut self.codec,
380
5.39k
                        &mut self.inner.streams,
381
1
                    )?;
382
                }
383
9.47k
                ReceivedFrame::Continue => (),
384
                ReceivedFrame::Done => {
385
3.32k
                    return Poll::Ready(Ok(()));
386
                }
387
            }
388
        }
389
1.24M
    }
390
391
1.24M
    fn clear_expired_reset_streams(&mut self) {
392
1.24M
        self.inner.streams.clear_expired_reset_streams();
393
1.24M
    }
394
}
395
396
impl<P, B> ConnectionInner<P, B>
397
where
398
    P: Peer,
399
    B: Buf,
400
{
401
1.29M
    fn as_dyn(&mut self) -> DynConnection<'_, B> {
402
        let ConnectionInner {
403
1.29M
            state,
404
1.29M
            go_away,
405
1.29M
            streams,
406
1.29M
            error,
407
1.29M
            ping_pong,
408
            ..
409
1.29M
        } = self;
410
1.29M
        let streams = streams.as_dyn();
411
1.29M
        DynConnection {
412
1.29M
            state,
413
1.29M
            go_away,
414
1.29M
            streams,
415
1.29M
            error,
416
1.29M
            ping_pong,
417
1.29M
        }
418
1.29M
    }
419
}
420
421
impl<B> DynConnection<'_, B>
422
where
423
    B: Buf,
424
{
425
0
    fn go_away(&mut self, id: StreamId, e: Reason) {
426
0
        let frame = frame::GoAway::new(id, e);
427
0
        self.streams.send_go_away(id);
428
0
        self.go_away.go_away(frame);
429
0
    }
430
431
3
    fn go_away_now(&mut self, e: Reason) {
432
3
        let last_processed_id = self.streams.last_processed_id();
433
3
        let frame = frame::GoAway::new(last_processed_id, e);
434
3
        self.go_away.go_away_now(frame);
435
3
    }
436
437
6.86k
    fn go_away_now_data(&mut self, e: Reason, data: Bytes) {
438
6.86k
        let last_processed_id = self.streams.last_processed_id();
439
6.86k
        let frame = frame::GoAway::with_debug_data(last_processed_id, e, data);
440
6.86k
        self.go_away.go_away_now(frame);
441
6.86k
    }
442
443
    fn go_away_from_user(&mut self, e: Reason) {
444
        let last_processed_id = self.streams.last_processed_id();
445
        let frame = frame::GoAway::new(last_processed_id, e);
446
        self.go_away.go_away_from_user(frame);
447
448
        // Notify all streams of reason we're abruptly closing.
449
        self.streams.handle_error(Error::user_go_away(e));
450
    }
451
452
51.0k
    fn handle_poll2_result(&mut self, result: Result<(), Error>) -> Result<(), Error> {
453
47.6k
        match result {
454
            // The connection has shutdown normally
455
            Ok(()) => {
456
3.32k
                *self.state = State::Closing(Reason::NO_ERROR, Initiator::Library);
457
3.32k
                Ok(())
458
            }
459
            // Attempting to read a frame resulted in a connection level
460
            // error. This is handled by setting a GOAWAY frame followed by
461
            // terminating the connection.
462
13.4k
            Err(Error::GoAway(debug_data, reason, initiator)) => {
463
13.4k
                self.handle_go_away(reason, debug_data, initiator);
464
13.4k
                Ok(())
465
            }
466
            // Attempting to read a frame resulted in a stream level error.
467
            // Locally detected stream errors are reported to the peer with
468
            // RST_STREAM. Remotely initiated resets have already been applied
469
            // by the streams state machine and must not be echoed back.
470
32.8k
            Err(Error::Reset(id, reason, initiator)) => {
471
32.8k
                if initiator == Initiator::Remote {
472
0
                    tracing::trace!(?id, ?reason, ?initiator, "stream reset");
473
0
                    return Ok(());
474
32.8k
                }
475
476
32.8k
                debug_assert_eq!(initiator, Initiator::Library);
477
32.8k
                tracing::trace!(?id, ?reason, ?initiator, "stream error");
478
32.8k
                match self.streams.send_reset(id, reason) {
479
32.8k
                    Ok(()) => (),
480
0
                    Err(crate::proto::error::GoAway { debug_data, reason }) => {
481
0
                        self.handle_go_away(reason, debug_data, Initiator::Library);
482
0
                    }
483
                }
484
32.8k
                Ok(())
485
            }
486
            // Attempting to read a frame resulted in an I/O error. All
487
            // active streams must be reset.
488
            //
489
            // TODO: Are I/O errors recoverable?
490
1.34k
            Err(Error::Io(kind, inner)) => {
491
1.34k
                tracing::debug!(error = ?kind, "Connection::poll; IO error");
492
1.34k
                let e = Error::Io(kind, inner);
493
494
                // Reset all active streams
495
1.34k
                self.streams.handle_error(e.clone());
496
497
                // Some client implementations drop the connections without notifying its peer
498
                // Attempting to read after the client dropped the connection results in UnexpectedEof
499
                // If as a server, we don't have anything more to send, just close the connection
500
                // without error
501
                //
502
                // See https://github.com/hyperium/hyper/issues/3427
503
1.34k
                if self.streams.is_buffer_empty()
504
1.34k
                    && matches!(kind, io::ErrorKind::UnexpectedEof)
505
0
                    && (self.streams.is_server()
506
0
                        || self.error.as_ref().map(|f| f.reason() == Reason::NO_ERROR)
507
0
                            == Some(true))
508
                {
509
0
                    *self.state = State::Closed(Reason::NO_ERROR, Initiator::Library);
510
0
                    return Ok(());
511
1.34k
                }
512
513
                // Return the error
514
1.34k
                Err(e)
515
            }
516
        }
517
51.0k
    }
518
519
13.4k
    fn handle_go_away(&mut self, reason: Reason, debug_data: Bytes, initiator: Initiator) {
520
13.4k
        let e = Error::GoAway(debug_data.clone(), reason, initiator);
521
13.4k
        tracing::debug!(error = ?e, "Connection::poll; connection error");
522
523
        // We may have already sent a GOAWAY for this error,
524
        // if so, don't send another, just flush and close up.
525
13.4k
        if self
526
13.4k
            .go_away
527
13.4k
            .going_away()
528
13.4k
            .map_or(false, |frame| frame.reason() == reason)
529
        {
530
6.60k
            tracing::trace!("    -> already going away");
531
6.60k
            *self.state = State::Closing(reason, initiator);
532
6.60k
            return;
533
6.86k
        }
534
535
        // Reset all active streams
536
6.86k
        self.streams.handle_error(e);
537
6.86k
        self.go_away_now_data(reason, debug_data);
538
13.4k
    }
539
540
25.0k
    fn recv_frame(&mut self, frame: Option<Frame>) -> Result<ReceivedFrame, Error> {
541
        use crate::frame::Frame::*;
542
21.6k
        match frame {
543
5.52k
            Some(Headers(frame)) => {
544
5.52k
                tracing::trace!(?frame, "recv HEADERS");
545
5.52k
                self.streams.recv_headers(frame)?;
546
            }
547
6.68k
            Some(Data(frame)) => {
548
6.68k
                tracing::trace!(?frame, "recv DATA");
549
6.68k
                self.streams.recv_data(frame)?;
550
            }
551
1.08k
            Some(Reset(frame)) => {
552
1.08k
                tracing::trace!(?frame, "recv RST_STREAM");
553
1.08k
                self.streams.recv_reset(frame)?;
554
            }
555
1.02k
            Some(PushPromise(frame)) => {
556
1.02k
                tracing::trace!(?frame, "recv PUSH_PROMISE");
557
1.02k
                self.streams.recv_push_promise(frame)?;
558
            }
559
5.39k
            Some(Settings(frame)) => {
560
5.39k
                tracing::trace!(?frame, "recv SETTINGS");
561
5.39k
                return Ok(ReceivedFrame::Settings(frame));
562
            }
563
139
            Some(GoAway(frame)) => {
564
139
                tracing::trace!(?frame, "recv GOAWAY");
565
                // This should prevent starting new streams,
566
                // but should allow continuing to process current streams
567
                // until they are all EOS. Once they are, State should
568
                // transition to GoAway.
569
139
                self.streams.recv_go_away(&frame)?;
570
139
                *self.error = Some(frame);
571
            }
572
443
            Some(Ping(frame)) => {
573
443
                tracing::trace!(?frame, "recv PING");
574
443
                let status = self.ping_pong.recv_ping(frame);
575
443
                if status.is_shutdown() {
576
0
                    assert!(
577
0
                        self.go_away.is_going_away(),
578
0
                        "received unexpected shutdown ping"
579
                    );
580
581
0
                    let last_processed_id = self.streams.last_processed_id();
582
0
                    self.go_away(last_processed_id, Reason::NO_ERROR);
583
443
                }
584
            }
585
1.29k
            Some(WindowUpdate(frame)) => {
586
1.29k
                tracing::trace!(?frame, "recv WINDOW_UPDATE");
587
1.29k
                self.streams.recv_window_update(frame)?;
588
            }
589
101
            Some(Priority(frame)) => {
590
101
                tracing::trace!(?frame, "recv PRIORITY");
591
                // TODO: handle
592
            }
593
            None => {
594
3.32k
                tracing::trace!("codec closed");
595
3.32k
                self.streams.recv_eof(false).expect("mutex poisoned");
596
3.32k
                return Ok(ReceivedFrame::Done);
597
            }
598
        }
599
9.47k
        Ok(ReceivedFrame::Continue)
600
25.0k
    }
601
}
602
603
enum ReceivedFrame {
604
    Settings(frame::Settings),
605
    Continue,
606
    Done,
607
}
608
609
impl<T, B> Connection<T, client::Peer, B>
610
where
611
    T: AsyncRead + AsyncWrite,
612
    B: Buf,
613
{
614
12.7k
    pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
615
12.7k
        &self.inner.streams
616
12.7k
    }
<h2::proto::connection::Connection<h2_support::mock::Mock, h2::client::Peer>>::streams
Line
Count
Source
614
817
    pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
615
817
        &self.inner.streams
616
817
    }
<h2::proto::connection::Connection<fuzz_e2e::MockIo, h2::client::Peer>>::streams
Line
Count
Source
614
11.9k
    pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
615
11.9k
        &self.inner.streams
616
11.9k
    }
617
}
618
619
impl<T, B> Connection<T, server::Peer, B>
620
where
621
    T: AsyncRead + AsyncWrite + Unpin,
622
    B: Buf,
623
{
624
    pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
625
        self.inner.streams.next_incoming()
626
    }
627
628
    // Graceful shutdown only makes sense for server peers.
629
    pub fn go_away_gracefully(&mut self) {
630
        if self.inner.go_away.is_going_away() {
631
            // No reason to start a new one.
632
            return;
633
        }
634
635
        // According to http://httpwg.org/specs/rfc7540.html#GOAWAY:
636
        //
637
        // > A server that is attempting to gracefully shut down a connection
638
        // > SHOULD send an initial GOAWAY frame with the last stream
639
        // > identifier set to 2^31-1 and a NO_ERROR code. This signals to the
640
        // > client that a shutdown is imminent and that initiating further
641
        // > requests is prohibited. After allowing time for any in-flight
642
        // > stream creation (at least one round-trip time), the server can
643
        // > send another GOAWAY frame with an updated last stream identifier.
644
        // > This ensures that a connection can be cleanly shut down without
645
        // > losing requests.
646
        self.inner.as_dyn().go_away(StreamId::MAX, Reason::NO_ERROR);
647
648
        // We take the advice of waiting 1 RTT literally, and wait
649
        // for a pong before proceeding.
650
        self.inner.ping_pong.ping_shutdown();
651
    }
652
}
653
654
impl<T, P, B> Drop for Connection<T, P, B>
655
where
656
    P: Peer,
657
    B: Buf,
658
{
659
12.7k
    fn drop(&mut self) {
660
        // Ignore errors as this indicates that the mutex is poisoned.
661
12.7k
        let _ = self.inner.streams.recv_eof(true);
662
12.7k
    }
<h2::proto::connection::Connection<h2_support::mock::Mock, h2::client::Peer> as core::ops::drop::Drop>::drop
Line
Count
Source
659
817
    fn drop(&mut self) {
660
        // Ignore errors as this indicates that the mutex is poisoned.
661
817
        let _ = self.inner.streams.recv_eof(true);
662
817
    }
<h2::proto::connection::Connection<fuzz_e2e::MockIo, h2::client::Peer> as core::ops::drop::Drop>::drop
Line
Count
Source
659
11.9k
    fn drop(&mut self) {
660
        // Ignore errors as this indicates that the mutex is poisoned.
661
11.9k
        let _ = self.inner.streams.recv_eof(true);
662
11.9k
    }
663
}
664
665
#[cfg(test)]
666
mod tests {
667
    use super::*;
668
669
    #[test]
670
    fn auto_data_frame_budget_scales_with_connection_window() {
671
        assert_eq!(
672
            DataFrameBudget::Auto.resolve(None),
673
            DEFAULT_INITIAL_WINDOW_SIZE as usize / 2
674
        );
675
        assert_eq!(
676
            DataFrameBudget::Auto.resolve(Some(DEFAULT_INITIAL_WINDOW_SIZE)),
677
            DEFAULT_INITIAL_WINDOW_SIZE as usize / 2
678
        );
679
        assert_eq!(DataFrameBudget::Auto.resolve(Some(1024 * 1024)), 512 * 1024);
680
    }
681
682
    #[test]
683
    fn auto_data_frame_budget_has_minimum() {
684
        assert_eq!(
685
            DataFrameBudget::Auto.resolve(Some(1)),
686
            DEFAULT_DATA_FRAME_BUDGET
687
        );
688
        assert_eq!(
689
            DataFrameBudget::Auto.resolve(Some(MAX_WINDOW_SIZE)),
690
            MAX_WINDOW_SIZE as usize / 2
691
        );
692
    }
693
694
    #[test]
695
    fn configured_data_frame_budget_is_unchanged() {
696
        assert_eq!(
697
            DataFrameBudget::Configured(123).resolve(Some(MAX_WINDOW_SIZE)),
698
            123
699
        );
700
    }
701
}