1use crate::codec::UserError;
2use crate::frame::{Reason, StreamId};
3use crate::{client, server};
4
5use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
6use crate::proto::*;
7
8use bytes::Bytes;
9use futures_core::Stream;
10use std::io;
11use std::marker::PhantomData;
12use std::pin::Pin;
13use std::task::{Context, Poll};
14use std::time::Duration;
15use tokio::io::AsyncRead;
16
17#[derive(Debug)]
19pub(crate) struct Connection<T, P, B: Buf = Bytes>
20where
21 P: Peer,
22{
23 codec: Codec<T, Prioritized<B>>,
25
26 inner: ConnectionInner<P, B>,
27}
28
29#[derive(Debug)]
32struct ConnectionInner<P, B: Buf = Bytes>
33where
34 P: Peer,
35{
36 state: State,
38
39 error: Option<frame::GoAway>,
44
45 go_away: GoAway,
47
48 pending_refusal: Option<StreamId>,
50
51 ping_pong: PingPong,
53
54 settings: Settings,
56
57 streams: Streams<B, P>,
59
60 span: tracing::Span,
62
63 _phantom: PhantomData<P>,
65}
66
67struct DynConnection<'a, B: Buf = Bytes> {
68 state: &'a mut State,
69
70 go_away: &'a mut GoAway,
71
72 streams: DynStreams<'a, B>,
73
74 error: &'a mut Option<frame::GoAway>,
75
76 ping_pong: &'a mut PingPong,
77}
78
79#[derive(Debug, Clone)]
80pub(crate) struct Config {
81 pub next_stream_id: StreamId,
82 pub initial_max_send_streams: usize,
83 pub max_send_buffer_size: usize,
84 pub reset_stream_duration: Duration,
85 pub reset_stream_max: usize,
86 pub remote_reset_stream_max: usize,
87 pub local_error_reset_streams_max: Option<usize>,
88 pub settings: frame::Settings,
89 pub data_frame_budget: DataFrameBudget,
90}
91
92#[derive(Clone, Copy, Debug)]
93pub(crate) enum DataFrameBudget {
94 Auto,
95 Configured(usize),
96}
97
98impl DataFrameBudget {
99 pub(crate) fn resolve(self, connection_window: Option<WindowSize>) -> usize {
100 match self {
101 Self::Configured(budget) => budget,
102 Self::Auto => {
103 let window = connection_window.unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE);
104 let budget = window as usize / 2;
105
106 budget.max(DEFAULT_DATA_FRAME_BUDGET)
107 }
108 }
109 }
110
111 pub(crate) fn is_auto(self) -> bool {
112 matches!(self, Self::Auto)
113 }
114}
115
116#[derive(Debug)]
117enum State {
118 Open,
120
121 Closing(Reason, Initiator),
123
124 Closed(Reason, Initiator),
126}
127
128impl<T, P, B> Connection<T, P, B>
129where
130 T: AsyncRead + AsyncWrite + Unpin,
131 P: Peer,
132 B: Buf,
133{
134 pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
135 fn streams_config(config: &Config) -> streams::Config {
136 streams::Config {
137 initial_max_send_streams: config.initial_max_send_streams,
138 local_max_buffer_size: config.max_send_buffer_size,
139 local_next_stream_id: config.next_stream_id,
140 local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
141 extended_connect_protocol_enabled: config
142 .settings
143 .is_extended_connect_protocol_enabled()
144 .unwrap_or(false),
145 local_reset_duration: config.reset_stream_duration,
146 local_reset_max: config.reset_stream_max,
147 remote_reset_max: config.remote_reset_stream_max,
148 remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
149 remote_max_initiated: config
150 .settings
151 .max_concurrent_streams()
152 .map(|max| max as usize),
153 local_max_error_reset_streams: config.local_error_reset_streams_max,
154 data_frame_budget: config.data_frame_budget,
155 }
156 }
157 let streams = Streams::new(streams_config(&config));
158 let span = tracing::debug_span!(parent: None, "Connection", peer = %P::NAME);
159 span.follows_from(tracing::Span::current());
160 Connection {
161 codec,
162 inner: ConnectionInner {
163 state: State::Open,
164 error: None,
165 go_away: GoAway::new(),
166 pending_refusal: None,
167 ping_pong: PingPong::new(),
168 settings: Settings::new(config.settings),
169 streams,
170 span,
171 _phantom: PhantomData,
172 },
173 }
174 }
175
176 pub(crate) fn set_target_window_size(&mut self, size: WindowSize) {
178 let _res = self.inner.streams.set_target_connection_window_size(size);
179 debug_assert!(_res.is_ok());
181 }
182
183 pub(crate) fn set_initial_window_size(&mut self, size: WindowSize) -> Result<(), UserError> {
185 let mut settings = frame::Settings::default();
186 settings.set_initial_window_size(Some(size));
187 self.inner.settings.send_settings(settings)
188 }
189
190 pub(crate) fn set_enable_connect_protocol(&mut self) -> Result<(), UserError> {
192 let mut settings = frame::Settings::default();
193 settings.set_enable_connect_protocol(Some(1));
194 self.inner.settings.send_settings(settings)
195 }
196
197 pub(crate) fn max_send_streams(&self) -> usize {
200 self.inner.streams.max_send_streams()
201 }
202
203 pub(crate) fn max_recv_streams(&self) -> usize {
206 self.inner.streams.max_recv_streams()
207 }
208
209 #[cfg(feature = "unstable")]
210 pub fn num_wired_streams(&self) -> usize {
211 self.inner.streams.num_wired_streams()
212 }
213
214 fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
219 let _e = self.inner.span.enter();
220 let span = tracing::trace_span!("poll_ready");
221 let _e = span.enter();
222 ready!(self.inner.ping_pong.send_pending_pong(cx, &mut self.codec))?;
224 ready!(self.inner.ping_pong.send_pending_ping(cx, &mut self.codec))?;
225 ready!(self
226 .inner
227 .settings
228 .poll_send(cx, &mut self.codec, &mut self.inner.streams))?;
229 if let Some(id) = self.inner.pending_refusal {
230 ready!(self.codec.poll_ready(cx))?;
232 self.inner.streams.count_pending_refusal()?;
233 self.codec
234 .buffer(frame::Reset::new(id, Reason::REFUSED_STREAM).into())
235 .expect("invalid RST_STREAM frame");
236 self.inner.pending_refusal = None;
237 }
238
239 Poll::Ready(Ok(()))
240 }
241
242 fn poll_go_away(&mut self, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> {
247 self.inner.go_away.send_pending_go_away(cx, &mut self.codec)
248 }
249
250 pub fn go_away_from_user(&mut self, e: Reason) {
251 self.inner.as_dyn().go_away_from_user(e)
252 }
253
254 fn take_error(&mut self, ours: Reason, initiator: Initiator) -> Result<(), Error> {
255 let (debug_data, theirs) = self
256 .inner
257 .error
258 .take()
259 .as_ref()
260 .map_or((Bytes::new(), Reason::NO_ERROR), |frame| {
261 (frame.debug_data().clone(), frame.reason())
262 });
263
264 match (ours, theirs) {
265 (Reason::NO_ERROR, Reason::NO_ERROR) => Ok(()),
266 (ours, Reason::NO_ERROR) => Err(Error::GoAway(Bytes::new(), ours, initiator)),
267 (_, theirs) => Err(Error::remote_go_away(debug_data, theirs)),
272 }
273 }
274
275 pub fn maybe_close_connection_if_no_streams(&mut self) {
278 if !self.inner.streams.has_streams_or_other_references() {
281 self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
282 }
283 }
284
285 pub fn has_streams(&self) -> bool {
287 self.inner.streams.has_streams()
288 }
289
290 pub fn has_streams_or_other_references(&self) -> bool {
292 self.inner.streams.has_streams_or_other_references()
295 }
296
297 pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
298 self.inner.ping_pong.take_user_pings()
299 }
300
301 pub fn poll(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
303 let span = self.inner.span.clone();
308 let _e = span.enter();
309 let span = tracing::trace_span!("poll");
310 let _e = span.enter();
311
312 loop {
313 tracing::trace!(connection.state = ?self.inner.state);
314 match self.inner.state {
316 State::Open => {
318 let result = match self.poll2(cx) {
319 Poll::Ready(result) => result,
320 Poll::Pending => {
322 ready!(self.inner.streams.poll_complete(cx, &mut self.codec))?;
326
327 if (self.inner.error.is_some()
328 || self.inner.go_away.should_close_on_idle())
329 && !self.inner.streams.has_streams()
330 {
331 self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
332 continue;
333 }
334
335 return Poll::Pending;
336 }
337 };
338
339 self.inner.as_dyn().handle_poll2_result(result)?
340 }
341 State::Closing(reason, initiator) => {
342 tracing::trace!("connection closing after flush");
343 ready!(self.codec.shutdown(cx))?;
345
346 self.inner.state = State::Closed(reason, initiator);
348 }
349 State::Closed(reason, initiator) => {
350 return Poll::Ready(self.take_error(reason, initiator));
351 }
352 }
353 }
354 }
355
356 fn poll2(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
357 self.clear_expired_reset_streams();
361
362 loop {
363 if let Some(reason) = ready!(self.poll_go_away(cx)?) {
369 if self.inner.go_away.should_close_now() {
370 if self.inner.go_away.is_user_initiated() {
371 return Poll::Ready(Ok(()));
374 } else {
375 return Poll::Ready(Err(Error::library_go_away(reason)));
376 }
377 }
378 debug_assert_eq!(
380 reason,
381 Reason::NO_ERROR,
382 "graceful GOAWAY should be NO_ERROR"
383 );
384 }
385 ready!(self.poll_ready(cx))?;
386
387 match self
388 .inner
389 .as_dyn()
390 .recv_frame(ready!(Pin::new(&mut self.codec).poll_next(cx)?))?
391 {
392 ReceivedFrame::Settings(frame) => {
393 self.inner.settings.recv_settings(
394 frame,
395 &mut self.codec,
396 &mut self.inner.streams,
397 )?;
398 }
399 ReceivedFrame::Refused(id) => {
400 debug_assert!(self.inner.pending_refusal.is_none());
401 self.inner.pending_refusal = Some(id);
402 }
403 ReceivedFrame::Continue => (),
404 ReceivedFrame::Done => {
405 return Poll::Ready(Ok(()));
406 }
407 }
408 }
409 }
410
411 fn clear_expired_reset_streams(&mut self) {
412 self.inner.streams.clear_expired_reset_streams();
413 }
414}
415
416impl<P, B> ConnectionInner<P, B>
417where
418 P: Peer,
419 B: Buf,
420{
421 fn as_dyn(&mut self) -> DynConnection<'_, B> {
422 let ConnectionInner {
423 state,
424 go_away,
425 streams,
426 error,
427 ping_pong,
428 ..
429 } = self;
430 let streams = streams.as_dyn();
431 DynConnection {
432 state,
433 go_away,
434 streams,
435 error,
436 ping_pong,
437 }
438 }
439}
440
441impl<B> DynConnection<'_, B>
442where
443 B: Buf,
444{
445 fn go_away(&mut self, id: StreamId, e: Reason) {
446 let frame = frame::GoAway::new(id, e);
447 self.streams.send_go_away(id);
448 self.go_away.go_away(frame);
449 }
450
451 fn go_away_now(&mut self, e: Reason) {
452 let last_processed_id = self.streams.last_processed_id();
453 let frame = frame::GoAway::new(last_processed_id, e);
454 self.go_away.go_away_now(frame);
455 }
456
457 fn go_away_now_data(&mut self, e: Reason, data: Bytes) {
458 let last_processed_id = self.streams.last_processed_id();
459 let frame = frame::GoAway::with_debug_data(last_processed_id, e, data);
460 self.go_away.go_away_now(frame);
461 }
462
463 fn go_away_from_user(&mut self, e: Reason) {
464 let last_processed_id = self.streams.last_processed_id();
465 let frame = frame::GoAway::new(last_processed_id, e);
466 self.go_away.go_away_from_user(frame);
467
468 self.streams.handle_error(Error::user_go_away(e));
470 }
471
472 fn handle_poll2_result(&mut self, result: Result<(), Error>) -> Result<(), Error> {
473 match result {
474 Ok(()) => {
476 *self.state = State::Closing(Reason::NO_ERROR, Initiator::Library);
477 Ok(())
478 }
479 Err(Error::GoAway(debug_data, reason, initiator)) => {
483 self.handle_go_away(reason, debug_data, initiator);
484 Ok(())
485 }
486 Err(Error::Reset(id, reason, initiator)) => {
491 if initiator == Initiator::Remote {
492 tracing::trace!(?id, ?reason, ?initiator, "stream reset");
493 return Ok(());
494 }
495
496 debug_assert_eq!(initiator, Initiator::Library);
497 tracing::trace!(?id, ?reason, ?initiator, "stream error");
498 match self.streams.send_reset(id, reason) {
499 Ok(()) => (),
500 Err(crate::proto::error::GoAway { debug_data, reason }) => {
501 self.handle_go_away(reason, debug_data, Initiator::Library);
502 }
503 }
504 Ok(())
505 }
506 Err(Error::Io(kind, inner)) => {
511 tracing::debug!(error = ?kind, "Connection::poll; IO error");
512 let e = Error::Io(kind, inner);
513
514 self.streams.handle_error(e.clone());
516
517 if self.streams.is_buffer_empty()
524 && matches!(kind, io::ErrorKind::UnexpectedEof)
525 && (self.streams.is_server()
526 || self.error.as_ref().map(|f| f.reason() == Reason::NO_ERROR)
527 == Some(true))
528 {
529 *self.state = State::Closed(Reason::NO_ERROR, Initiator::Library);
530 return Ok(());
531 }
532
533 Err(e)
535 }
536 }
537 }
538
539 fn handle_go_away(&mut self, reason: Reason, debug_data: Bytes, initiator: Initiator) {
540 let e = Error::GoAway(debug_data.clone(), reason, initiator);
541 tracing::debug!(error = ?e, "Connection::poll; connection error");
542
543 if self
546 .go_away
547 .going_away()
548 .map_or(false, |frame| frame.reason() == reason)
549 {
550 tracing::trace!(" -> already going away");
551 *self.state = State::Closing(reason, initiator);
552 return;
553 }
554
555 self.streams.handle_error(e);
557 self.go_away_now_data(reason, debug_data);
558 }
559
560 fn recv_frame(&mut self, frame: Option<Frame>) -> Result<ReceivedFrame, Error> {
561 use crate::frame::Frame::*;
562 match frame {
563 Some(Headers(frame)) => {
564 tracing::trace!(?frame, "recv HEADERS");
565 match self.streams.recv_headers(frame)? {
566 streams::RecvOutcome::Processed => (),
567 streams::RecvOutcome::Refused(id) => return Ok(ReceivedFrame::Refused(id)),
568 }
569 }
570 Some(Data(frame)) => {
571 tracing::trace!(?frame, "recv DATA");
572 self.streams.recv_data(frame)?;
573 }
574 Some(Reset(frame)) => {
575 tracing::trace!(?frame, "recv RST_STREAM");
576 self.streams.recv_reset(frame)?;
577 }
578 Some(PushPromise(frame)) => {
579 tracing::trace!(?frame, "recv PUSH_PROMISE");
580 match self.streams.recv_push_promise(frame)? {
581 streams::RecvOutcome::Processed => (),
582 streams::RecvOutcome::Refused(id) => return Ok(ReceivedFrame::Refused(id)),
583 }
584 }
585 Some(Settings(frame)) => {
586 tracing::trace!(?frame, "recv SETTINGS");
587 return Ok(ReceivedFrame::Settings(frame));
588 }
589 Some(GoAway(frame)) => {
590 tracing::trace!(?frame, "recv GOAWAY");
591 self.streams.recv_go_away(&frame)?;
596 *self.error = Some(frame);
597 }
598 Some(Ping(frame)) => {
599 tracing::trace!(?frame, "recv PING");
600 let status = self.ping_pong.recv_ping(frame);
601 if status.is_shutdown() {
602 assert!(
603 self.go_away.is_going_away(),
604 "received unexpected shutdown ping"
605 );
606
607 let last_processed_id = self.streams.last_processed_id();
608 self.go_away(last_processed_id, Reason::NO_ERROR);
609 }
610 }
611 Some(WindowUpdate(frame)) => {
612 tracing::trace!(?frame, "recv WINDOW_UPDATE");
613 self.streams.recv_window_update(frame)?;
614 }
615 Some(Priority(frame)) => {
616 tracing::trace!(?frame, "recv PRIORITY");
617 }
619 None => {
620 tracing::trace!("codec closed");
621 self.streams.recv_eof(false).expect("mutex poisoned");
622 return Ok(ReceivedFrame::Done);
623 }
624 }
625 Ok(ReceivedFrame::Continue)
626 }
627}
628
629enum ReceivedFrame {
630 Refused(StreamId),
631 Settings(frame::Settings),
632 Continue,
633 Done,
634}
635
636impl<T, B> Connection<T, client::Peer, B>
637where
638 T: AsyncRead + AsyncWrite,
639 B: Buf,
640{
641 pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
642 &self.inner.streams
643 }
644}
645
646impl<T, B> Connection<T, server::Peer, B>
647where
648 T: AsyncRead + AsyncWrite + Unpin,
649 B: Buf,
650{
651 pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
652 self.inner.streams.next_incoming()
653 }
654
655 pub fn go_away_gracefully(&mut self) {
657 if self.inner.go_away.is_going_away() {
658 return;
660 }
661
662 self.inner.as_dyn().go_away(StreamId::MAX, Reason::NO_ERROR);
674
675 self.inner.ping_pong.ping_shutdown();
678 }
679}
680
681impl<T, P, B> Drop for Connection<T, P, B>
682where
683 P: Peer,
684 B: Buf,
685{
686 fn drop(&mut self) {
687 let _ = self.inner.streams.recv_eof(true);
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695
696 #[test]
697 fn auto_data_frame_budget_scales_with_connection_window() {
698 assert_eq!(
699 DataFrameBudget::Auto.resolve(None),
700 DEFAULT_INITIAL_WINDOW_SIZE as usize / 2
701 );
702 assert_eq!(
703 DataFrameBudget::Auto.resolve(Some(DEFAULT_INITIAL_WINDOW_SIZE)),
704 DEFAULT_INITIAL_WINDOW_SIZE as usize / 2
705 );
706 assert_eq!(DataFrameBudget::Auto.resolve(Some(1024 * 1024)), 512 * 1024);
707 }
708
709 #[test]
710 fn auto_data_frame_budget_has_minimum() {
711 assert_eq!(
712 DataFrameBudget::Auto.resolve(Some(1)),
713 DEFAULT_DATA_FRAME_BUDGET
714 );
715 assert_eq!(
716 DataFrameBudget::Auto.resolve(Some(MAX_WINDOW_SIZE)),
717 MAX_WINDOW_SIZE as usize / 2
718 );
719 }
720
721 #[test]
722 fn configured_data_frame_budget_is_unchanged() {
723 assert_eq!(
724 DataFrameBudget::Configured(123).resolve(Some(MAX_WINDOW_SIZE)),
725 123
726 );
727 }
728}