quinn/connection.rs
1use std::{
2 any::Any,
3 fmt,
4 future::Future,
5 io,
6 net::{IpAddr, SocketAddr},
7 pin::Pin,
8 sync::{
9 Arc,
10 atomic::{AtomicUsize, Ordering},
11 },
12 task::{Context, Poll, Waker, ready},
13};
14
15use bytes::Bytes;
16use pin_project_lite::pin_project;
17use rustc_hash::FxHashMap;
18use thiserror::Error;
19use tokio::sync::{Notify, futures::Notified, mpsc, oneshot};
20use tracing::{Instrument, Span, debug_span};
21
22use crate::{
23 ConnectionEvent, Duration, Instant, VarInt,
24 mutex::Mutex,
25 recv_stream::RecvStream,
26 runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpPoller},
27 send_stream::SendStream,
28 udp_transmit,
29};
30use proto::{
31 ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, Side, StreamEvent,
32 StreamId, congestion::Controller,
33};
34
35/// In-progress connection attempt future
36#[derive(Debug)]
37pub struct Connecting {
38 conn: Option<ConnectionRef>,
39 connected: oneshot::Receiver<bool>,
40 handshake_data_ready: Option<oneshot::Receiver<()>>,
41}
42
43impl Connecting {
44 pub(crate) fn new(
45 handle: ConnectionHandle,
46 conn: proto::Connection,
47 endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
48 conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
49 socket: Arc<dyn AsyncUdpSocket>,
50 runtime: Arc<dyn Runtime>,
51 ) -> Self {
52 let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel();
53 let (on_connected_send, on_connected_recv) = oneshot::channel();
54 let conn = ConnectionRef::new(
55 handle,
56 conn,
57 endpoint_events,
58 conn_events,
59 on_handshake_data_send,
60 on_connected_send,
61 socket,
62 runtime.clone(),
63 );
64
65 let driver = ConnectionDriver(conn.clone());
66 runtime.spawn(Box::pin(
67 async {
68 if let Err(e) = driver.await {
69 tracing::error!("I/O error: {e}");
70 }
71 }
72 .instrument(Span::current()),
73 ));
74
75 Self {
76 conn: Some(conn),
77 connected: on_connected_recv,
78 handshake_data_ready: Some(on_handshake_data_recv),
79 }
80 }
81
82 /// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
83 ///
84 /// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
85 /// If so, the returned [`Connection`] can be used to send application data without waiting for
86 /// the rest of the handshake to complete, at the cost of weakened cryptographic security
87 /// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
88 /// complete, at which point subsequently opened streams and written data will have full
89 /// cryptographic protection.
90 ///
91 /// ## Outgoing
92 ///
93 /// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
94 /// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
95 /// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
96 /// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
97 /// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
98 /// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
99 /// If it was rejected, the existence of streams opened and other application data sent prior
100 /// to the handshake completing will not be conveyed to the remote application, and local
101 /// operations on them will return `ZeroRttRejected` errors.
102 ///
103 /// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
104 /// relevant resumption state to be stored in the server, which servers may limit or lose for
105 /// various reasons including not persisting resumption state across server restarts.
106 ///
107 /// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
108 /// implementation's docs for 0-RTT pitfalls.
109 ///
110 /// ## Incoming
111 ///
112 /// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
113 /// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
114 ///
115 /// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
116 /// implementation's docs for 0-RTT pitfalls.
117 ///
118 /// ## Security
119 ///
120 /// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
121 /// replay attacks, and should therefore never invoke non-idempotent operations.
122 ///
123 /// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
124 /// before TLS client authentication has occurred, and should therefore not be used to send
125 /// data for which client authentication is being used.
126 pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
127 // This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
128 // have to release it explicitly before returning `self` by value.
129 let conn = (self.conn.as_mut().unwrap()).state.lock("into_0rtt");
130
131 let is_ok = conn.inner.has_0rtt() || conn.inner.side().is_server();
132 drop(conn);
133
134 if is_ok {
135 let conn = self.conn.take().unwrap();
136 Ok((Connection(conn), ZeroRttAccepted(self.connected)))
137 } else {
138 Err(self)
139 }
140 }
141
142 /// Parameters negotiated during the handshake
143 ///
144 /// The dynamic type returned is determined by the configured
145 /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
146 /// be [`downcast`](Box::downcast) to a
147 /// [`crypto::rustls::HandshakeData`](crate::crypto::rustls::HandshakeData).
148 pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError> {
149 // Taking &mut self allows us to use a single oneshot channel rather than dealing with
150 // potentially many tasks waiting on the same event. It's a bit of a hack, but keeps things
151 // simple.
152 if let Some(x) = self.handshake_data_ready.take() {
153 let _ = x.await;
154 }
155 let conn = self.conn.as_ref().unwrap();
156 let inner = conn.state.lock("handshake");
157 inner
158 .inner
159 .crypto_session()
160 .handshake_data()
161 .ok_or_else(|| {
162 inner
163 .error
164 .clone()
165 .expect("spurious handshake data ready notification")
166 })
167 }
168
169 /// The local IP address which was used when the peer established
170 /// the connection
171 ///
172 /// This can be different from the address the endpoint is bound to, in case
173 /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
174 ///
175 /// This will return `None` for clients, or when the platform does not expose this
176 /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
177 /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
178 ///
179 /// Will panic if called after `poll` has returned `Ready`.
180 pub fn local_ip(&self) -> Option<IpAddr> {
181 let conn = self.conn.as_ref().unwrap();
182 let inner = conn.state.lock("local_ip");
183
184 inner.inner.local_ip()
185 }
186
187 /// The peer's UDP address
188 ///
189 /// Will panic if called after `poll` has returned `Ready`.
190 pub fn remote_address(&self) -> SocketAddr {
191 let conn_ref: &ConnectionRef = self.conn.as_ref().expect("used after yielding Ready");
192 conn_ref.state.lock("remote_address").inner.remote_address()
193 }
194}
195
196impl Future for Connecting {
197 type Output = Result<Connection, ConnectionError>;
198 fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
199 Pin::new(&mut self.connected).poll(cx).map(|_| {
200 let conn = self.conn.take().unwrap();
201 let inner = conn.state.lock("connecting");
202 if inner.connected {
203 drop(inner);
204 Ok(Connection(conn))
205 } else {
206 Err(inner
207 .error
208 .clone()
209 .expect("connected signaled without connection success or error"))
210 }
211 })
212 }
213}
214
215/// Future that completes when a connection is fully established
216///
217/// For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting
218/// value is meaningless.
219pub struct ZeroRttAccepted(oneshot::Receiver<bool>);
220
221impl Future for ZeroRttAccepted {
222 type Output = bool;
223 fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
224 Pin::new(&mut self.0).poll(cx).map(|x| x.unwrap_or(false))
225 }
226}
227
228/// A future that drives protocol logic for a connection
229///
230/// This future handles the protocol logic for a single connection, routing events from the
231/// `Connection` API object to the `Endpoint` task and the related stream-related interfaces.
232/// It also keeps track of outstanding timeouts for the `Connection`.
233///
234/// If the connection encounters an error condition, this future will yield an error. It will
235/// terminate (yielding `Ok(())`) if the connection was closed without error. Unlike other
236/// connection-related futures, this waits for the draining period to complete to ensure that
237/// packets still in flight from the peer are handled gracefully.
238#[must_use = "connection drivers must be spawned for their connections to function"]
239#[derive(Debug)]
240struct ConnectionDriver(ConnectionRef);
241
242impl Future for ConnectionDriver {
243 type Output = Result<(), io::Error>;
244
245 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
246 let conn = &mut *self.0.state.lock("poll");
247
248 let span = debug_span!("drive", id = conn.handle.0);
249 let _guard = span.enter();
250
251 if let Err(e) = conn.process_conn_events(&self.0.shared, cx) {
252 conn.terminate(e, &self.0.shared);
253 return Poll::Ready(Ok(()));
254 }
255 let mut keep_going = conn.drive_transmit(cx)?;
256 // If a timer expires, there might be more to transmit. When we transmit something, we
257 // might need to reset a timer. Hence, we must loop until neither happens.
258 keep_going |= conn.drive_timer(cx);
259 conn.forward_endpoint_events();
260 conn.forward_app_events(&self.0.shared);
261
262 if !conn.inner.is_drained() {
263 if keep_going {
264 // If the connection hasn't processed all tasks, schedule it again
265 cx.waker().wake_by_ref();
266 } else {
267 conn.driver = Some(cx.waker().clone());
268 }
269 return Poll::Pending;
270 }
271 if conn.error.is_none() {
272 unreachable!("drained connections always have an error");
273 }
274 Poll::Ready(Ok(()))
275 }
276}
277
278/// A QUIC connection.
279///
280/// If all references to a connection (including every clone of the `Connection` handle, streams of
281/// incoming streams, and the various stream types) have been dropped, then the connection will be
282/// automatically closed with an `error_code` of 0 and an empty `reason`. You can also close the
283/// connection explicitly by calling [`Connection::close()`].
284///
285/// Closing the connection immediately abandons efforts to deliver data to the peer. Upon
286/// receiving CONNECTION_CLOSE the peer *may* drop any stream data not yet delivered to the
287/// application. [`Connection::close()`] describes in more detail how to gracefully close a
288/// connection without losing application data.
289///
290/// May be cloned to obtain another handle to the same connection.
291///
292/// [`Connection::close()`]: Connection::close
293#[derive(Debug, Clone)]
294pub struct Connection(ConnectionRef);
295
296impl Connection {
297 /// Initiate a new outgoing unidirectional stream.
298 ///
299 /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
300 /// consequence, the peer won't be notified that a stream has been opened until the stream is
301 /// actually used.
302 pub fn open_uni(&self) -> OpenUni<'_> {
303 OpenUni {
304 conn: &self.0,
305 notify: self.0.shared.stream_budget_available[Dir::Uni as usize].notified(),
306 }
307 }
308
309 /// Initiate a new outgoing bidirectional stream.
310 ///
311 /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
312 /// consequence, the peer won't be notified that a stream has been opened until the stream is
313 /// actually used. Calling [`open_bi()`] then waiting on the [`RecvStream`] without writing
314 /// anything to [`SendStream`] will never succeed.
315 ///
316 /// [`open_bi()`]: crate::Connection::open_bi
317 /// [`SendStream`]: crate::SendStream
318 /// [`RecvStream`]: crate::RecvStream
319 pub fn open_bi(&self) -> OpenBi<'_> {
320 OpenBi {
321 conn: &self.0,
322 notify: self.0.shared.stream_budget_available[Dir::Bi as usize].notified(),
323 }
324 }
325
326 /// Accept the next incoming uni-directional stream
327 pub fn accept_uni(&self) -> AcceptUni<'_> {
328 AcceptUni {
329 conn: &self.0,
330 notify: self.0.shared.stream_incoming[Dir::Uni as usize].notified(),
331 }
332 }
333
334 /// Accept the next incoming bidirectional stream
335 ///
336 /// **Important Note**: The `Connection` that calls [`open_bi()`] must write to its [`SendStream`]
337 /// before the other `Connection` is able to `accept_bi()`. Calling [`open_bi()`] then
338 /// waiting on the [`RecvStream`] without writing anything to [`SendStream`] will never succeed.
339 ///
340 /// [`accept_bi()`]: crate::Connection::accept_bi
341 /// [`open_bi()`]: crate::Connection::open_bi
342 /// [`SendStream`]: crate::SendStream
343 /// [`RecvStream`]: crate::RecvStream
344 pub fn accept_bi(&self) -> AcceptBi<'_> {
345 AcceptBi {
346 conn: &self.0,
347 notify: self.0.shared.stream_incoming[Dir::Bi as usize].notified(),
348 }
349 }
350
351 /// Receive an application datagram
352 pub fn read_datagram(&self) -> ReadDatagram<'_> {
353 ReadDatagram {
354 conn: &self.0,
355 notify: self.0.shared.datagram_received.notified(),
356 }
357 }
358
359 /// Wait for the connection to be closed for any reason
360 ///
361 /// Despite the return type's name, closed connections are often not an error condition at the
362 /// application layer. Cases that might be routine include [`ConnectionError::LocallyClosed`]
363 /// and [`ConnectionError::ApplicationClosed`].
364 pub async fn closed(&self) -> ConnectionError {
365 {
366 let conn = self.0.state.lock("closed");
367 if let Some(error) = conn.error.as_ref() {
368 return error.clone();
369 }
370 // Construct the future while the lock is held to ensure we can't miss a wakeup if
371 // the `Notify` is signaled immediately after we release the lock. `await` it after
372 // the lock guard is out of scope.
373 self.0.shared.closed.notified()
374 }
375 .await;
376 self.0
377 .state
378 .lock("closed")
379 .error
380 .as_ref()
381 .expect("closed without an error")
382 .clone()
383 }
384
385 /// If the connection is closed, the reason why.
386 ///
387 /// Returns `None` if the connection is still open.
388 pub fn close_reason(&self) -> Option<ConnectionError> {
389 self.0.state.lock("close_reason").error.clone()
390 }
391
392 /// Close the connection immediately.
393 ///
394 /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
395 /// more data is sent to the peer and the peer may drop buffered data upon receiving
396 /// the CONNECTION_CLOSE frame.
397 ///
398 /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
399 ///
400 /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
401 /// is preserved in full, it should be kept under 1KiB.
402 ///
403 /// # Gracefully closing a connection
404 ///
405 /// Only the peer last receiving application data can be certain that all data is
406 /// delivered. The only reliable action it can then take is to close the connection,
407 /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
408 /// frame is very likely if both endpoints stay online long enough, and
409 /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
410 /// remote peer will time out the connection, provided that the idle timeout is not
411 /// disabled.
412 ///
413 /// The sending side can not guarantee all stream data is delivered to the remote
414 /// application. It only knows the data is delivered to the QUIC stack of the remote
415 /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
416 /// [`close()`] the remote endpoint may drop any data it received but is as yet
417 /// undelivered to the application, including data that was acknowledged as received to
418 /// the local endpoint.
419 ///
420 /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
421 /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
422 /// [`close()`]: Connection::close
423 pub fn close(&self, error_code: VarInt, reason: &[u8]) {
424 let conn = &mut *self.0.state.lock("close");
425 conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
426 }
427
428 /// Transmit `data` as an unreliable, unordered application datagram
429 ///
430 /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
431 /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
432 /// dictated by the peer.
433 ///
434 /// Previously queued datagrams which are still unsent may be discarded to make space for this
435 /// datagram, in order of oldest to newest.
436 pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
437 let conn = &mut *self.0.state.lock("send_datagram");
438 if let Some(ref x) = conn.error {
439 return Err(SendDatagramError::ConnectionLost(x.clone()));
440 }
441 use proto::SendDatagramError::*;
442 match conn.inner.datagrams().send(data, true) {
443 Ok(()) => {
444 conn.wake();
445 Ok(())
446 }
447 Err(e) => Err(match e {
448 Blocked(..) => unreachable!(),
449 UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
450 Disabled => SendDatagramError::Disabled,
451 TooLarge => SendDatagramError::TooLarge,
452 }),
453 }
454 }
455
456 /// Transmit `data` as an unreliable, unordered application datagram
457 ///
458 /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
459 /// conditions, which effectively prioritizes old datagrams over new datagrams.
460 ///
461 /// See [`send_datagram()`] for details.
462 ///
463 /// [`send_datagram()`]: Connection::send_datagram
464 pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
465 SendDatagram {
466 conn: &self.0,
467 data: Some(data),
468 notify: self.0.shared.datagrams_unblocked.notified(),
469 }
470 }
471
472 /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
473 ///
474 /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
475 ///
476 /// This may change over the lifetime of a connection according to variation in the path MTU
477 /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
478 /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
479 ///
480 /// Not necessarily the maximum size of received datagrams.
481 ///
482 /// [`send_datagram()`]: Connection::send_datagram
483 pub fn max_datagram_size(&self) -> Option<usize> {
484 self.0
485 .state
486 .lock("max_datagram_size")
487 .inner
488 .datagrams()
489 .max_size()
490 }
491
492 /// Bytes available in the outgoing datagram buffer
493 ///
494 /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
495 /// at most this size is guaranteed not to cause older datagrams to be dropped.
496 pub fn datagram_send_buffer_space(&self) -> usize {
497 self.0
498 .state
499 .lock("datagram_send_buffer_space")
500 .inner
501 .datagrams()
502 .send_buffer_space()
503 }
504
505 /// The side of the connection (client or server)
506 pub fn side(&self) -> Side {
507 self.0.state.lock("side").inner.side()
508 }
509
510 /// The peer's UDP address
511 ///
512 /// If `ServerConfig::migration` is `true`, clients may change addresses at will, e.g. when
513 /// switching to a cellular internet connection.
514 pub fn remote_address(&self) -> SocketAddr {
515 self.0.state.lock("remote_address").inner.remote_address()
516 }
517
518 /// The local IP address which was used when the peer established
519 /// the connection
520 ///
521 /// This can be different from the address the endpoint is bound to, in case
522 /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
523 ///
524 /// This will return `None` for clients, or when the platform does not expose this
525 /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
526 /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
527 pub fn local_ip(&self) -> Option<IpAddr> {
528 self.0.state.lock("local_ip").inner.local_ip()
529 }
530
531 /// Current best estimate of this connection's latency (round-trip-time)
532 pub fn rtt(&self) -> Duration {
533 self.0.state.lock("rtt").inner.rtt()
534 }
535
536 /// Returns connection statistics
537 pub fn stats(&self) -> ConnectionStats {
538 self.0.state.lock("stats").inner.stats()
539 }
540
541 /// Current state of the congestion control algorithm, for debugging purposes
542 pub fn congestion_state(&self) -> Box<dyn Controller> {
543 self.0
544 .state
545 .lock("congestion_state")
546 .inner
547 .congestion_state()
548 .clone_box()
549 }
550
551 /// Parameters negotiated during the handshake
552 ///
553 /// Guaranteed to return `Some` on fully established connections or after
554 /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
555 /// the returned value.
556 ///
557 /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
558 pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
559 self.0
560 .state
561 .lock("handshake_data")
562 .inner
563 .crypto_session()
564 .handshake_data()
565 }
566
567 /// Cryptographic identity of the peer
568 ///
569 /// The dynamic type returned is determined by the configured
570 /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
571 /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
572 pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
573 self.0
574 .state
575 .lock("peer_identity")
576 .inner
577 .crypto_session()
578 .peer_identity()
579 }
580
581 /// A stable identifier for this connection
582 ///
583 /// Peer addresses and connection IDs can change, but this value will remain
584 /// fixed for the lifetime of the connection.
585 pub fn stable_id(&self) -> usize {
586 self.0.stable_id()
587 }
588
589 /// Update traffic keys spontaneously
590 ///
591 /// This primarily exists for testing purposes.
592 pub fn force_key_update(&self) {
593 self.0
594 .state
595 .lock("force_key_update")
596 .inner
597 .force_key_update()
598 }
599
600 /// Derive keying material from this connection's TLS session secrets.
601 ///
602 /// When both peers call this method with the same `label` and `context`
603 /// arguments and `output` buffers of equal length, they will get the
604 /// same sequence of bytes in `output`. These bytes are cryptographically
605 /// strong and pseudorandom, and are suitable for use as keying material.
606 ///
607 /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
608 pub fn export_keying_material(
609 &self,
610 output: &mut [u8],
611 label: &[u8],
612 context: &[u8],
613 ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
614 self.0
615 .state
616 .lock("export_keying_material")
617 .inner
618 .crypto_session()
619 .export_keying_material(output, label, context)
620 }
621
622 /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
623 ///
624 /// No streams may be opened by the peer unless fewer than `count` are already open. Large
625 /// `count`s increase both minimum and worst-case memory consumption.
626 pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
627 let mut conn = self.0.state.lock("set_max_concurrent_uni_streams");
628 conn.inner.set_max_concurrent_streams(Dir::Uni, count);
629 // May need to send MAX_STREAMS to make progress
630 conn.wake();
631 }
632
633 /// See [`proto::TransportConfig::send_window()`]
634 pub fn set_send_window(&self, send_window: u64) {
635 let mut conn = self.0.state.lock("set_send_window");
636 conn.inner.set_send_window(send_window);
637 conn.wake();
638 }
639
640 /// See [`proto::TransportConfig::receive_window()`]
641 pub fn set_receive_window(&self, receive_window: VarInt) {
642 let mut conn = self.0.state.lock("set_receive_window");
643 conn.inner.set_receive_window(receive_window);
644 conn.wake();
645 }
646
647 /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
648 ///
649 /// No streams may be opened by the peer unless fewer than `count` are already open. Large
650 /// `count`s increase both minimum and worst-case memory consumption.
651 pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
652 let mut conn = self.0.state.lock("set_max_concurrent_bi_streams");
653 conn.inner.set_max_concurrent_streams(Dir::Bi, count);
654 // May need to send MAX_STREAMS to make progress
655 conn.wake();
656 }
657}
658
659pin_project! {
660 /// Future produced by [`Connection::open_uni`]
661 pub struct OpenUni<'a> {
662 conn: &'a ConnectionRef,
663 #[pin]
664 notify: Notified<'a>,
665 }
666}
667
668impl Future for OpenUni<'_> {
669 type Output = Result<SendStream, ConnectionError>;
670 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
671 let this = self.project();
672 let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
673 Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
674 }
675}
676
677pin_project! {
678 /// Future produced by [`Connection::open_bi`]
679 pub struct OpenBi<'a> {
680 conn: &'a ConnectionRef,
681 #[pin]
682 notify: Notified<'a>,
683 }
684}
685
686impl Future for OpenBi<'_> {
687 type Output = Result<(SendStream, RecvStream), ConnectionError>;
688 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
689 let this = self.project();
690 let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
691
692 Poll::Ready(Ok((
693 SendStream::new(conn.clone(), id, is_0rtt),
694 RecvStream::new(conn, id, is_0rtt),
695 )))
696 }
697}
698
699fn poll_open<'a>(
700 ctx: &mut Context<'_>,
701 conn: &'a ConnectionRef,
702 mut notify: Pin<&mut Notified<'a>>,
703 dir: Dir,
704) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
705 let mut state = conn.state.lock("poll_open");
706 if let Some(ref e) = state.error {
707 return Poll::Ready(Err(e.clone()));
708 } else if let Some(id) = state.inner.streams().open(dir) {
709 let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
710 drop(state); // Release the lock so clone can take it
711 return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
712 }
713 loop {
714 match notify.as_mut().poll(ctx) {
715 // `state` lock ensures we didn't race with readiness
716 Poll::Pending => return Poll::Pending,
717 // Spurious wakeup, get a new future
718 Poll::Ready(()) => {
719 notify.set(conn.shared.stream_budget_available[dir as usize].notified())
720 }
721 }
722 }
723}
724
725pin_project! {
726 /// Future produced by [`Connection::accept_uni`]
727 pub struct AcceptUni<'a> {
728 conn: &'a ConnectionRef,
729 #[pin]
730 notify: Notified<'a>,
731 }
732}
733
734impl Future for AcceptUni<'_> {
735 type Output = Result<RecvStream, ConnectionError>;
736
737 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
738 let this = self.project();
739 let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
740 Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
741 }
742}
743
744pin_project! {
745 /// Future produced by [`Connection::accept_bi`]
746 pub struct AcceptBi<'a> {
747 conn: &'a ConnectionRef,
748 #[pin]
749 notify: Notified<'a>,
750 }
751}
752
753impl Future for AcceptBi<'_> {
754 type Output = Result<(SendStream, RecvStream), ConnectionError>;
755
756 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
757 let this = self.project();
758 let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
759 Poll::Ready(Ok((
760 SendStream::new(conn.clone(), id, is_0rtt),
761 RecvStream::new(conn, id, is_0rtt),
762 )))
763 }
764}
765
766fn poll_accept<'a>(
767 ctx: &mut Context<'_>,
768 conn: &'a ConnectionRef,
769 mut notify: Pin<&mut Notified<'a>>,
770 dir: Dir,
771) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
772 let mut state = conn.state.lock("poll_accept");
773 // Check for incoming streams before checking `state.error` so that already-received streams,
774 // which are necessarily finite, can be drained from a closed connection.
775 if let Some(id) = state.inner.streams().accept(dir) {
776 let is_0rtt = state.inner.is_handshaking();
777 state.wake(); // To send additional stream ID credit
778 drop(state); // Release the lock so clone can take it
779 return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
780 } else if let Some(ref e) = state.error {
781 return Poll::Ready(Err(e.clone()));
782 }
783 loop {
784 match notify.as_mut().poll(ctx) {
785 // `state` lock ensures we didn't race with readiness
786 Poll::Pending => return Poll::Pending,
787 // Spurious wakeup, get a new future
788 Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
789 }
790 }
791}
792
793pin_project! {
794 /// Future produced by [`Connection::read_datagram`]
795 pub struct ReadDatagram<'a> {
796 conn: &'a ConnectionRef,
797 #[pin]
798 notify: Notified<'a>,
799 }
800}
801
802impl Future for ReadDatagram<'_> {
803 type Output = Result<Bytes, ConnectionError>;
804 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
805 let mut this = self.project();
806 let mut state = this.conn.state.lock("ReadDatagram::poll");
807 // Check for buffered datagrams before checking `state.error` so that already-received
808 // datagrams, which are necessarily finite, can be drained from a closed connection.
809 if let Some(x) = state.inner.datagrams().recv() {
810 return Poll::Ready(Ok(x));
811 } else if let Some(ref e) = state.error {
812 return Poll::Ready(Err(e.clone()));
813 }
814 loop {
815 match this.notify.as_mut().poll(ctx) {
816 // `state` lock ensures we didn't race with readiness
817 Poll::Pending => return Poll::Pending,
818 // Spurious wakeup, get a new future
819 Poll::Ready(()) => this
820 .notify
821 .set(this.conn.shared.datagram_received.notified()),
822 }
823 }
824 }
825}
826
827pin_project! {
828 /// Future produced by [`Connection::send_datagram_wait`]
829 pub struct SendDatagram<'a> {
830 conn: &'a ConnectionRef,
831 data: Option<Bytes>,
832 #[pin]
833 notify: Notified<'a>,
834 }
835}
836
837impl Future for SendDatagram<'_> {
838 type Output = Result<(), SendDatagramError>;
839 fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
840 let mut this = self.project();
841 let mut state = this.conn.state.lock("SendDatagram::poll");
842 if let Some(ref e) = state.error {
843 return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
844 }
845 use proto::SendDatagramError::*;
846 match state
847 .inner
848 .datagrams()
849 .send(this.data.take().unwrap(), false)
850 {
851 Ok(()) => {
852 state.wake();
853 Poll::Ready(Ok(()))
854 }
855 Err(e) => Poll::Ready(Err(match e {
856 Blocked(data) => {
857 this.data.replace(data);
858 loop {
859 match this.notify.as_mut().poll(ctx) {
860 Poll::Pending => return Poll::Pending,
861 // Spurious wakeup, get a new future
862 Poll::Ready(()) => this
863 .notify
864 .set(this.conn.shared.datagrams_unblocked.notified()),
865 }
866 }
867 }
868 UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
869 Disabled => SendDatagramError::Disabled,
870 TooLarge => SendDatagramError::TooLarge,
871 })),
872 }
873 }
874}
875
876#[derive(Debug)]
877pub(crate) struct ConnectionRef(Arc<ConnectionInner>);
878
879impl ConnectionRef {
880 #[allow(clippy::too_many_arguments)]
881 fn new(
882 handle: ConnectionHandle,
883 conn: proto::Connection,
884 endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
885 conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
886 on_handshake_data: oneshot::Sender<()>,
887 on_connected: oneshot::Sender<bool>,
888 socket: Arc<dyn AsyncUdpSocket>,
889 runtime: Arc<dyn Runtime>,
890 ) -> Self {
891 Self(Arc::new(ConnectionInner {
892 state: Mutex::new(State {
893 inner: conn,
894 driver: None,
895 handle,
896 on_handshake_data: Some(on_handshake_data),
897 on_connected: Some(on_connected),
898 connected: false,
899 timer: None,
900 timer_deadline: None,
901 conn_events,
902 endpoint_events,
903 blocked_writers: FxHashMap::default(),
904 blocked_readers: FxHashMap::default(),
905 stopped: FxHashMap::default(),
906 error: None,
907 io_poller: socket.clone().create_io_poller(),
908 socket,
909 runtime,
910 send_buffer: Vec::new(),
911 buffered_transmit: None,
912 }),
913 shared: Shared::default(),
914 }))
915 }
916
917 fn stable_id(&self) -> usize {
918 &*self.0 as *const _ as usize
919 }
920}
921
922impl Clone for ConnectionRef {
923 fn clone(&self) -> Self {
924 self.shared.ref_count.fetch_add(1, Ordering::Relaxed);
925 Self(self.0.clone())
926 }
927}
928
929impl Drop for ConnectionRef {
930 fn drop(&mut self) {
931 if self.shared.ref_count.fetch_sub(1, Ordering::Relaxed) > 1 {
932 return;
933 }
934
935 let conn = &mut *self.state.lock("drop");
936
937 if !conn.inner.is_closed() {
938 // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
939 // not, we can't do any harm. If there were any streams being opened, then either
940 // the connection will be closed for an unrelated reason or a fresh reference will
941 // be constructed for the newly opened stream.
942 conn.implicit_close(&self.shared);
943 }
944 }
945}
946
947impl std::ops::Deref for ConnectionRef {
948 type Target = ConnectionInner;
949 fn deref(&self) -> &Self::Target {
950 &self.0
951 }
952}
953
954#[derive(Debug)]
955pub(crate) struct ConnectionInner {
956 pub(crate) state: Mutex<State>,
957 pub(crate) shared: Shared,
958}
959
960#[derive(Debug, Default)]
961pub(crate) struct Shared {
962 /// Notified when new streams may be locally initiated due to an increase in stream ID flow
963 /// control budget
964 stream_budget_available: [Notify; 2],
965 /// Notified when the peer has initiated a new stream
966 stream_incoming: [Notify; 2],
967 datagram_received: Notify,
968 datagrams_unblocked: Notify,
969 closed: Notify,
970 /// Number of live handles that can used to initiate or handle I/O; excludes the driver
971 ref_count: AtomicUsize,
972}
973
974pub(crate) struct State {
975 pub(crate) inner: proto::Connection,
976 driver: Option<Waker>,
977 handle: ConnectionHandle,
978 on_handshake_data: Option<oneshot::Sender<()>>,
979 on_connected: Option<oneshot::Sender<bool>>,
980 connected: bool,
981 timer: Option<Pin<Box<dyn AsyncTimer>>>,
982 timer_deadline: Option<Instant>,
983 conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
984 endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
985 pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
986 pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
987 pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
988 /// Always set to Some before the connection becomes drained
989 pub(crate) error: Option<ConnectionError>,
990 socket: Arc<dyn AsyncUdpSocket>,
991 io_poller: Pin<Box<dyn UdpPoller>>,
992 runtime: Arc<dyn Runtime>,
993 send_buffer: Vec<u8>,
994 /// We buffer a transmit when the underlying I/O would block
995 buffered_transmit: Option<proto::Transmit>,
996}
997
998impl State {
999 fn drive_transmit(&mut self, cx: &mut Context) -> io::Result<bool> {
1000 let now = self.runtime.now();
1001 let mut transmits = 0;
1002
1003 let max_datagrams = self
1004 .socket
1005 .max_transmit_segments()
1006 .min(MAX_TRANSMIT_SEGMENTS);
1007
1008 loop {
1009 // Retry the last transmit, or get a new one.
1010 let t = match self.buffered_transmit.take() {
1011 Some(t) => t,
1012 None => {
1013 self.send_buffer.clear();
1014 self.send_buffer.reserve(self.inner.current_mtu() as usize);
1015 match self
1016 .inner
1017 .poll_transmit(now, max_datagrams, &mut self.send_buffer)
1018 {
1019 Some(t) => {
1020 transmits += match t.segment_size {
1021 None => 1,
1022 Some(s) => t.size.div_ceil(s), // round up
1023 };
1024 t
1025 }
1026 None => break,
1027 }
1028 }
1029 };
1030
1031 if self.io_poller.as_mut().poll_writable(cx)?.is_pending() {
1032 // Retry after a future wakeup
1033 self.buffered_transmit = Some(t);
1034 return Ok(false);
1035 }
1036
1037 let len = t.size;
1038 let retry = match self
1039 .socket
1040 .try_send(&udp_transmit(&t, &self.send_buffer[..len]))
1041 {
1042 Ok(()) => false,
1043 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => true,
1044 Err(e) => return Err(e),
1045 };
1046 if retry {
1047 // We thought the socket was writable, but it wasn't. Retry so that either another
1048 // `poll_writable` call determines that the socket is indeed not writable and
1049 // registers us for a wakeup, or the send succeeds if this really was just a
1050 // transient failure.
1051 self.buffered_transmit = Some(t);
1052 continue;
1053 }
1054
1055 if transmits >= MAX_TRANSMIT_DATAGRAMS {
1056 // TODO: What isn't ideal here yet is that if we don't poll all
1057 // datagrams that could be sent we don't go into the `app_limited`
1058 // state and CWND continues to grow until we get here the next time.
1059 // See https://github.com/quinn-rs/quinn/issues/1126
1060 return Ok(true);
1061 }
1062 }
1063
1064 Ok(false)
1065 }
1066
1067 fn forward_endpoint_events(&mut self) {
1068 while let Some(event) = self.inner.poll_endpoint_events() {
1069 // If the endpoint driver is gone, noop.
1070 let _ = self.endpoint_events.send((self.handle, event));
1071 }
1072 }
1073
1074 /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
1075 fn process_conn_events(
1076 &mut self,
1077 shared: &Shared,
1078 cx: &mut Context,
1079 ) -> Result<(), ConnectionError> {
1080 loop {
1081 match self.conn_events.poll_recv(cx) {
1082 Poll::Ready(Some(ConnectionEvent::Rebind(socket))) => {
1083 self.socket = socket;
1084 self.io_poller = self.socket.clone().create_io_poller();
1085 self.inner.local_address_changed();
1086 }
1087 Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
1088 self.inner.handle_event(event);
1089 }
1090 Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
1091 self.close(error_code, reason, shared);
1092 }
1093 Poll::Ready(None) => {
1094 return Err(ConnectionError::TransportError(proto::TransportError {
1095 code: proto::TransportErrorCode::INTERNAL_ERROR,
1096 frame: None,
1097 reason: "endpoint driver future was dropped".to_string(),
1098 }));
1099 }
1100 Poll::Pending => {
1101 return Ok(());
1102 }
1103 }
1104 }
1105 }
1106
1107 fn forward_app_events(&mut self, shared: &Shared) {
1108 while let Some(event) = self.inner.poll() {
1109 use proto::Event::*;
1110 match event {
1111 HandshakeDataReady => {
1112 if let Some(x) = self.on_handshake_data.take() {
1113 let _ = x.send(());
1114 }
1115 }
1116 Connected => {
1117 self.connected = true;
1118 if let Some(x) = self.on_connected.take() {
1119 // We don't care if the on-connected future was dropped
1120 let _ = x.send(self.inner.accepted_0rtt());
1121 }
1122 if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
1123 // Wake up rejected 0-RTT streams so they can fail immediately with
1124 // `ZeroRttRejected` errors.
1125 wake_all(&mut self.blocked_writers);
1126 wake_all(&mut self.blocked_readers);
1127 wake_all_notify(&mut self.stopped);
1128 }
1129 }
1130 ConnectionLost { reason } => {
1131 self.terminate(reason, shared);
1132 }
1133 Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
1134 Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
1135 shared.stream_incoming[Dir::Uni as usize].notify_waiters();
1136 }
1137 Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
1138 shared.stream_incoming[Dir::Bi as usize].notify_waiters();
1139 }
1140 DatagramReceived => {
1141 shared.datagram_received.notify_waiters();
1142 }
1143 DatagramsUnblocked => {
1144 shared.datagrams_unblocked.notify_waiters();
1145 }
1146 Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
1147 Stream(StreamEvent::Available { dir }) => {
1148 // Might mean any number of streams are ready, so we wake up everyone
1149 shared.stream_budget_available[dir as usize].notify_waiters();
1150 }
1151 Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
1152 Stream(StreamEvent::Stopped { id, .. }) => {
1153 wake_stream_notify(id, &mut self.stopped);
1154 wake_stream(id, &mut self.blocked_writers);
1155 }
1156 }
1157 }
1158 }
1159
1160 fn drive_timer(&mut self, cx: &mut Context<'_>) -> bool {
1161 let Some(deadline) = self.inner.poll_timeout() else {
1162 self.timer_deadline = None;
1163 return false;
1164 };
1165
1166 // Use the clock rather than the async timer to detect expiry: Sleep::poll
1167 // respects Tokio's cooperative budget and can return Pending for elapsed
1168 // deadlines.
1169 let now = self.runtime.now();
1170 if now >= deadline {
1171 self.inner.handle_timeout(now);
1172 self.timer_deadline = None;
1173 return true;
1174 }
1175
1176 match &mut self.timer {
1177 // Avoid resetting the timer when the deadline is unchanged.
1178 Some(delay) if self.timer_deadline != Some(deadline) => {
1179 delay.as_mut().reset(deadline);
1180 }
1181 None => {
1182 self.timer = Some(self.runtime.new_timer(deadline));
1183 }
1184 _ => {}
1185 }
1186 self.timer_deadline = Some(deadline);
1187
1188 let delay = self
1189 .timer
1190 .as_mut()
1191 .expect("timer must exist in this state")
1192 .as_mut();
1193 if delay.poll(cx).is_pending() {
1194 return false;
1195 }
1196
1197 // The deadline elapsed in the window between the clock check and poll.
1198 self.inner.handle_timeout(self.runtime.now());
1199 self.timer_deadline = None;
1200 true
1201 }
1202
1203 /// Wake up a blocked `Driver` task to process I/O
1204 pub(crate) fn wake(&mut self) {
1205 if let Some(x) = self.driver.take() {
1206 x.wake();
1207 }
1208 }
1209
1210 /// Used to wake up all blocked futures when the connection becomes closed for any reason
1211 fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
1212 self.error = Some(reason.clone());
1213 if let Some(x) = self.on_handshake_data.take() {
1214 let _ = x.send(());
1215 }
1216 wake_all(&mut self.blocked_writers);
1217 wake_all(&mut self.blocked_readers);
1218 shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
1219 shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
1220 shared.stream_incoming[Dir::Uni as usize].notify_waiters();
1221 shared.stream_incoming[Dir::Bi as usize].notify_waiters();
1222 shared.datagram_received.notify_waiters();
1223 shared.datagrams_unblocked.notify_waiters();
1224 if let Some(x) = self.on_connected.take() {
1225 let _ = x.send(false);
1226 }
1227 wake_all_notify(&mut self.stopped);
1228 shared.closed.notify_waiters();
1229 }
1230
1231 fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
1232 self.inner.close(self.runtime.now(), error_code, reason);
1233 self.terminate(ConnectionError::LocallyClosed, shared);
1234 self.wake();
1235 }
1236
1237 /// Close for a reason other than the application's explicit request
1238 pub(crate) fn implicit_close(&mut self, shared: &Shared) {
1239 self.close(0u32.into(), Bytes::new(), shared);
1240 }
1241
1242 pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
1243 if self.inner.is_handshaking()
1244 || self.inner.accepted_0rtt()
1245 || self.inner.side().is_server()
1246 {
1247 Ok(())
1248 } else {
1249 Err(())
1250 }
1251 }
1252}
1253
1254impl Drop for State {
1255 fn drop(&mut self) {
1256 if !self.inner.is_drained() {
1257 // Ensure the endpoint can tidy up
1258 let _ = self
1259 .endpoint_events
1260 .send((self.handle, proto::EndpointEvent::drained()));
1261 }
1262 }
1263}
1264
1265impl fmt::Debug for State {
1266 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1267 f.debug_struct("State").field("inner", &self.inner).finish()
1268 }
1269}
1270
1271fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
1272 if let Some(waker) = wakers.remove(&stream_id) {
1273 waker.wake();
1274 }
1275}
1276
1277fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
1278 wakers.drain().for_each(|(_, waker)| waker.wake())
1279}
1280
1281fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
1282 if let Some(notify) = wakers.remove(&stream_id) {
1283 notify.notify_waiters()
1284 }
1285}
1286
1287fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
1288 wakers
1289 .drain()
1290 .for_each(|(_, notify)| notify.notify_waiters())
1291}
1292
1293/// Errors that can arise when sending a datagram
1294#[derive(Debug, Error, Clone, Eq, PartialEq)]
1295pub enum SendDatagramError {
1296 /// The peer does not support receiving datagram frames
1297 #[error("datagrams not supported by peer")]
1298 UnsupportedByPeer,
1299 /// Datagram support is disabled locally
1300 #[error("datagram support disabled")]
1301 Disabled,
1302 /// The datagram is larger than the connection can currently accommodate
1303 ///
1304 /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
1305 /// exceeded.
1306 #[error("datagram too large")]
1307 TooLarge,
1308 /// The connection was lost
1309 #[error("connection lost")]
1310 ConnectionLost(#[from] ConnectionError),
1311}
1312
1313/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
1314///
1315/// This limits the amount of CPU resources consumed by datagram generation,
1316/// and allows other tasks (like receiving ACKs) to run in between.
1317const MAX_TRANSMIT_DATAGRAMS: usize = 20;
1318
1319/// The maximum amount of datagrams that are sent in a single transmit
1320///
1321/// This can be lower than the maximum platform capabilities, to avoid excessive
1322/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
1323/// that numbers around 10 are a good compromise.
1324const MAX_TRANSMIT_SEGMENTS: usize = 10;