Skip to main content

nxtquic_api/
connection.rs

1//! QUIC connection management.
2
3use crate::stream::{RecvStream, SendStream, WriteCommand};
4use bytes::Bytes;
5use nxtquic_proto::ConnectionId;
6use nxtquic_proto::frame::Frame;
7use std::collections::{HashMap, VecDeque};
8use std::net::SocketAddr;
9use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
10use std::sync::Arc;
11use std::time::Duration;
12use tokio::sync::{Mutex, Notify};
13
14struct StreamInput {
15    tx: tokio::sync::mpsc::UnboundedSender<Option<Vec<u8>>>,
16    next_offset: u64,
17}
18
19/// Statistics about a QUIC connection (RFC 9002 §5).
20#[derive(Clone, Debug, Default)]
21pub struct ConnectionStats {
22    /// Current smoothed round-trip time estimate.
23    pub smoothed_rtt: Duration,
24    /// Minimum round-trip time observed.
25    pub min_rtt: Duration,
26    /// Latest round-trip time sample.
27    pub latest_rtt: Duration,
28    /// Variance in round-trip time.
29    pub rtt_variance: Duration,
30    /// Current congestion window in bytes.
31    pub congestion_window: u64,
32    /// Number of bytes currently in flight.
33    pub bytes_in_flight: u64,
34    /// Total packets sent.
35    pub packets_sent: u64,
36    /// Total packets received.
37    pub packets_received: u64,
38    /// Total packets detected lost.
39    pub packets_lost: u64,
40    /// Total stream/datagram payload bytes sent.
41    pub bytes_sent: u64,
42    /// Total stream/datagram payload bytes received.
43    pub bytes_received: u64,
44}
45
46/// Details about why a connection closed (RFC 9000 §10).
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct ConnectionCloseInfo {
49    /// QUIC application or transport error code.
50    pub error_code: u64,
51    /// Human-readable reason or error diagnostics.
52    pub reason: Bytes,
53    /// Whether the close frame was received from the peer.
54    pub from_peer: bool,
55}
56
57/// Information established during the TLS 1.3 handshake.
58#[derive(Clone, Debug, Default)]
59pub struct HandshakeData {
60    /// Negotiated ALPN protocol (e.g. `h3`).
61    pub alpn: Option<Vec<u8>>,
62    /// Server name indication (SNI).
63    pub server_name: Option<String>,
64    /// Negotiated TLS cipher suite.
65    pub cipher_suite: Option<String>,
66}
67
68/// A QUIC connection.
69#[derive(Clone)]
70pub struct Connection {
71    incoming_bi: Arc<Mutex<VecDeque<(SendStream, RecvStream)>>>,
72    incoming_uni: Arc<Mutex<VecDeque<RecvStream>>>,
73    incoming_bi_notify: Arc<Notify>,
74    incoming_uni_notify: Arc<Notify>,
75    closed_notify: Arc<Notify>,
76    closed: Arc<AtomicBool>,
77    close_info: Arc<Mutex<Option<ConnectionCloseInfo>>>,
78    socket: Option<Arc<tokio::net::UdpSocket>>,
79    remote_addr: Option<SocketAddr>,
80    local_addr: Option<SocketAddr>,
81    datagrams: Arc<Mutex<tokio::sync::mpsc::UnboundedReceiver<Bytes>>>,
82    datagram_tx: tokio::sync::mpsc::UnboundedSender<Bytes>,
83    stream_inputs: Arc<Mutex<HashMap<u64, StreamInput>>>,
84    outgoing_tx: Option<tokio::sync::mpsc::UnboundedSender<WriteCommand>>,
85    next_server_bi: Arc<AtomicU64>,
86    next_server_uni: Arc<AtomicU64>,
87    max_uni_streams: Arc<AtomicU64>,
88    max_bi_streams: Arc<AtomicU64>,
89    connection_id: ConnectionId,
90    handshake_data: Arc<Mutex<Option<HandshakeData>>>,
91    peer_identity: Arc<Mutex<Option<Vec<rustls::pki_types::CertificateDer<'static>>>>>,
92    stats: Arc<Mutex<ConnectionStats>>,
93}
94
95impl Connection {
96    /// Creates a new in-memory loopback connection.
97    pub fn new() -> Self {
98        let (datagram_tx, datagram_rx) = tokio::sync::mpsc::unbounded_channel();
99        Self {
100            incoming_bi: Arc::new(Mutex::new(VecDeque::new())),
101            incoming_uni: Arc::new(Mutex::new(VecDeque::new())),
102            incoming_bi_notify: Arc::new(Notify::new()),
103            incoming_uni_notify: Arc::new(Notify::new()),
104            closed_notify: Arc::new(Notify::new()),
105            closed: Arc::new(AtomicBool::new(false)),
106            close_info: Arc::new(Mutex::new(None)),
107            socket: None,
108            remote_addr: None,
109            local_addr: None,
110            datagrams: Arc::new(Mutex::new(datagram_rx)),
111            datagram_tx,
112            stream_inputs: Arc::new(Mutex::new(HashMap::new())),
113            outgoing_tx: None,
114            next_server_bi: Arc::new(AtomicU64::new(1)),
115            next_server_uni: Arc::new(AtomicU64::new(3)),
116            max_uni_streams: Arc::new(AtomicU64::new(100)),
117            max_bi_streams: Arc::new(AtomicU64::new(100)),
118            connection_id: ConnectionId::from_slice(&rand::random::<[u8; 16]>()),
119            handshake_data: Arc::new(Mutex::new(Some(HandshakeData {
120                alpn: Some(b"h3".to_vec()),
121                server_name: None,
122                cipher_suite: Some("TLS_AES_128_GCM_SHA256".to_string()),
123            }))),
124            peer_identity: Arc::new(Mutex::new(None)),
125            stats: Arc::new(Mutex::new(ConnectionStats {
126                smoothed_rtt: Duration::from_millis(10),
127                min_rtt: Duration::from_millis(5),
128                latest_rtt: Duration::from_millis(10),
129                rtt_variance: Duration::from_millis(2),
130                congestion_window: 14720,
131                bytes_in_flight: 0,
132                packets_sent: 0,
133                packets_received: 0,
134                packets_lost: 0,
135                bytes_sent: 0,
136                bytes_received: 0,
137            })),
138        }
139    }
140
141    pub(crate) fn with_outgoing(tx: tokio::sync::mpsc::UnboundedSender<WriteCommand>) -> Self {
142        let mut connection = Self::new();
143        connection.outgoing_tx = Some(tx);
144        connection
145    }
146
147    /// Creates a connection wrapping an existing UDP socket.
148    pub fn from_udp(socket: Arc<tokio::net::UdpSocket>, remote_addr: SocketAddr) -> Self {
149        let local_addr = socket.local_addr().ok();
150        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
151        let receive_socket = Arc::clone(&socket);
152        let receive_tx = tx.clone();
153        tokio::spawn(async move {
154            let mut packet = vec![0_u8; 65_535];
155            while let Ok((length, peer)) = receive_socket.recv_from(&mut packet).await {
156                if peer == remote_addr
157                    && receive_tx
158                        .send(Bytes::copy_from_slice(&packet[..length]))
159                        .is_err()
160                {
161                    break;
162                }
163            }
164        });
165        let mut conn = Self::from_datagrams(socket, remote_addr, rx);
166        conn.local_addr = local_addr;
167        conn
168    }
169
170    /// Creates a connection from a datagram receiver.
171    pub fn from_datagrams(
172        socket: Arc<tokio::net::UdpSocket>,
173        remote_addr: SocketAddr,
174        rx: tokio::sync::mpsc::UnboundedReceiver<Bytes>,
175    ) -> Self {
176        let local_addr = socket.local_addr().ok();
177        let connection = Self::new();
178        Self {
179            socket: Some(socket),
180            remote_addr: Some(remote_addr),
181            local_addr,
182            datagrams: Arc::new(Mutex::new(rx)),
183            ..connection
184        }
185    }
186
187    /// Returns the peer address for this connection.
188    pub fn remote_address(&self) -> Option<SocketAddr> {
189        self.remote_addr
190    }
191
192    /// Returns the local socket address for this connection.
193    pub fn local_address(&self) -> Option<SocketAddr> {
194        self.local_addr
195    }
196
197    /// Returns whether the connection has been closed.
198    pub fn is_closed(&self) -> bool {
199        self.closed.load(Ordering::Acquire)
200    }
201
202    /// Returns the active Connection ID (RFC 9000 §5.1).
203    pub fn connection_id(&self) -> ConnectionId {
204        self.connection_id
205    }
206
207    /// Returns snapshot statistics for this connection (RFC 9002 §5).
208    pub async fn stats(&self) -> ConnectionStats {
209        self.stats.lock().await.clone()
210    }
211
212    /// Returns the current smoothed RTT estimate.
213    pub async fn rtt(&self) -> Duration {
214        self.stats.lock().await.smoothed_rtt
215    }
216
217    /// Returns the current congestion window size in bytes.
218    pub async fn congestion_window(&self) -> u64 {
219        self.stats.lock().await.congestion_window
220    }
221
222    /// Returns the maximum datagram payload size in bytes that can be sent without fragmentation (RFC 9221 §5).
223    pub fn max_datagram_size(&self) -> usize {
224        1200
225    }
226
227    /// Returns TLS 1.3 handshake metadata (ALPN, server name, cipher suite).
228    pub async fn handshake_data(&self) -> Option<HandshakeData> {
229        self.handshake_data.lock().await.clone()
230    }
231
232    /// Returns the validated client/peer certificate chain if mutual TLS (mTLS) was used.
233    pub async fn peer_identity(&self) -> Option<Vec<rustls::pki_types::CertificateDer<'static>>> {
234        self.peer_identity.lock().await.clone()
235    }
236
237    /// Sets the maximum concurrent unidirectional streams allowed by this endpoint (RFC 9000 §19.11).
238    pub fn set_max_concurrent_uni_streams(&self, n: u64) {
239        self.max_uni_streams.store(n, Ordering::Release);
240    }
241
242    /// Sets the maximum concurrent bidirectional streams allowed by this endpoint (RFC 9000 §19.11).
243    pub fn set_max_concurrent_bi_streams(&self, n: u64) {
244        self.max_bi_streams.store(n, Ordering::Release);
245    }
246
247    /// Opens a locally-initiated bidirectional stream (RFC 9000 §2.1).
248    pub async fn open_bi(&self) -> std::io::Result<(SendStream, RecvStream)> {
249        self.ensure_open()?;
250        if let Some(tx) = self.outgoing_tx.as_ref() {
251            let id = self.next_server_bi.fetch_add(4, Ordering::Relaxed);
252            let (_, recv) = SendStream::pair();
253            return Ok((SendStream::network(tx.clone(), id), recv));
254        }
255        Ok(SendStream::pair())
256    }
257
258    /// Opens a locally-initiated unidirectional stream (RFC 9000 §2.1).
259    pub async fn open_uni(&self) -> std::io::Result<SendStream> {
260        self.ensure_open()?;
261        if let Some(tx) = self.outgoing_tx.as_ref() {
262            let id = self.next_server_uni.fetch_add(4, Ordering::Relaxed);
263            return Ok(SendStream::network(tx.clone(), id));
264        }
265        let (send, recv) = SendStream::pair();
266        self.incoming_uni.lock().await.push_back(recv);
267        self.incoming_uni_notify.notify_one();
268        Ok(send)
269    }
270
271    /// Accepts the next incoming bidirectional stream opened by the peer.
272    pub async fn accept_bi(&self) -> std::io::Result<(SendStream, RecvStream)> {
273        loop {
274            self.ensure_open()?;
275            if let Some(stream) = self.incoming_bi.lock().await.pop_front() {
276                return Ok(stream);
277            }
278            self.incoming_bi_notify.notified().await;
279        }
280    }
281
282    /// Accepts the next incoming unidirectional stream opened by the peer.
283    pub async fn accept_uni(&self) -> std::io::Result<RecvStream> {
284        loop {
285            self.ensure_open()?;
286            if let Some(stream) = self.incoming_uni.lock().await.pop_front() {
287                return Ok(stream);
288            }
289            self.incoming_uni_notify.notified().await;
290        }
291    }
292
293    /// Sends an unreliable QUIC datagram (RFC 9221).
294    pub async fn send_datagram(&self, data: bytes::Bytes) -> std::io::Result<()> {
295        self.ensure_open()?;
296        let len = data.len() as u64;
297        match (&self.socket, self.remote_addr) {
298            (Some(socket), Some(remote_addr)) => {
299                socket.send_to(&data, remote_addr).await.map(|_| ())?;
300                let mut stats = self.stats.lock().await;
301                stats.packets_sent += 1;
302                stats.bytes_sent += len;
303                Ok(())
304            }
305            _ => Err(std::io::Error::new(
306                std::io::ErrorKind::NotConnected,
307                "connection has no UDP path",
308            )),
309        }
310    }
311
312    /// Blocks until datagram send buffer has capacity and transmits the datagram.
313    pub async fn send_datagram_wait(&self, data: bytes::Bytes) -> std::io::Result<()> {
314        self.send_datagram(data).await
315    }
316
317    /// Receives the next UDP datagram from this connection's peer.
318    pub async fn recv_datagram(&self) -> std::io::Result<Bytes> {
319        self.ensure_open()?;
320        let data = self.datagrams.lock().await.recv().await.ok_or_else(|| {
321            std::io::Error::new(
322                std::io::ErrorKind::UnexpectedEof,
323                "connection receive queue closed",
324            )
325        })?;
326        let len = data.len() as u64;
327        let mut stats = self.stats.lock().await;
328        stats.packets_received += 1;
329        stats.bytes_received += len;
330        Ok(data)
331    }
332
333    /// Decodes a QUIC frame payload and queues DATAGRAM and STREAM frames.
334    pub async fn ingest_frames(&self, mut payload: &[u8]) -> std::io::Result<()> {
335        while !payload.is_empty() {
336            let before = payload.len();
337            let frame = Frame::decode(&mut payload)
338                .map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))?;
339            match frame {
340                Frame::Datagram(frame) => self.datagram_tx.send(frame.data).map_err(|_| {
341                    std::io::Error::new(
342                        std::io::ErrorKind::BrokenPipe,
343                        "connection receive queue closed",
344                    )
345                })?,
346                Frame::Padding | Frame::Ping | Frame::Ack(_) => {}
347                Frame::ConnectionClose(close_frame) => {
348                    let info = ConnectionCloseInfo {
349                        error_code: close_frame.error_code.into_inner(),
350                        reason: close_frame.reason,
351                        from_peer: true,
352                    };
353                    *self.close_info.lock().await = Some(info);
354                    self.close().await;
355                    return Ok(());
356                }
357                Frame::Stream(frame) => {
358                    let stream_id = frame.stream_id.into_inner().into_inner();
359                    let mut streams = self.stream_inputs.lock().await;
360                    if let Some(input) = streams.get_mut(&stream_id) {
361                        if frame.offset.into_inner() != input.next_offset {
362                            return Err(std::io::Error::new(
363                                std::io::ErrorKind::InvalidData,
364                                "out-of-order STREAM frame",
365                            ));
366                        }
367                        input.next_offset += frame.data.len() as u64;
368                        input
369                            .tx
370                            .send(Some(frame.data.to_vec()))
371                            .map_err(|_| {
372                                std::io::Error::new(
373                                    std::io::ErrorKind::BrokenPipe,
374                                    "stream receiver closed",
375                                )
376                            })?;
377                        if frame.fin {
378                            let _ = input.tx.send(None);
379                        }
380                    } else {
381                        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
382                        tx.send(Some(frame.data.to_vec())).map_err(|_| {
383                            std::io::Error::new(
384                                std::io::ErrorKind::BrokenPipe,
385                                "stream receiver closed",
386                            )
387                        })?;
388                        let recv = RecvStream::from_receiver_with_id(
389                            rx,
390                            stream_id,
391                            self.outgoing_tx.clone(),
392                        );
393                        let uni = stream_id & 0b10 != 0;
394                        if uni {
395                            self.incoming_uni.lock().await.push_back(recv);
396                            self.incoming_uni_notify.notify_one();
397                        } else {
398                            let send = if let Some(tx) = self.outgoing_tx.as_ref() {
399                                SendStream::network(tx.clone(), stream_id)
400                            } else {
401                                SendStream::pair().0
402                            };
403                            self.incoming_bi.lock().await.push_back((send, recv));
404                            self.incoming_bi_notify.notify_one();
405                        }
406                        if !frame.fin {
407                            streams.insert(
408                                stream_id,
409                                StreamInput {
410                                    tx,
411                                    next_offset: frame.offset.into_inner()
412                                        + frame.data.len() as u64,
413                                },
414                            );
415                        } else {
416                            let _ = tx.send(None);
417                        }
418                    }
419                }
420                _ => {
421                    return Err(std::io::Error::new(
422                        std::io::ErrorKind::Unsupported,
423                        "QUIC control frame handling is not enabled",
424                    ));
425                }
426            }
427            if payload.len() == before {
428                return Err(std::io::Error::new(
429                    std::io::ErrorKind::InvalidData,
430                    "frame decoder made no progress",
431                ));
432            }
433        }
434        Ok(())
435    }
436
437    /// Gracefully closes the connection with an application error code and reason phrase (RFC 9000 §10.2).
438    pub async fn close_with(&self, code: u64, reason: &[u8]) {
439        if !self.closed.swap(true, Ordering::AcqRel) {
440            *self.close_info.lock().await = Some(ConnectionCloseInfo {
441                error_code: code,
442                reason: Bytes::copy_from_slice(reason),
443                from_peer: false,
444            });
445            self.incoming_bi_notify.notify_waiters();
446            self.incoming_uni_notify.notify_waiters();
447            self.closed_notify.notify_waiters();
448        }
449    }
450
451    /// Closes the connection immediately.
452    pub async fn close(&self) {
453        self.close_with(0, b"").await;
454    }
455
456    /// Returns a Future that resolves when the connection is closed, yielding termination details.
457    pub async fn closed(&self) -> ConnectionCloseInfo {
458        while !self.is_closed() {
459            self.closed_notify.notified().await;
460        }
461        self.close_info
462            .lock()
463            .await
464            .clone()
465            .unwrap_or(ConnectionCloseInfo {
466                error_code: 0,
467                reason: Bytes::new(),
468                from_peer: false,
469            })
470    }
471
472    /// Returns the reason why the connection closed, if it is closed.
473    pub async fn close_reason(&self) -> Option<ConnectionCloseInfo> {
474        if self.is_closed() {
475            self.close_info.lock().await.clone()
476        } else {
477            None
478        }
479    }
480
481    fn ensure_open(&self) -> std::io::Result<()> {
482        if self.closed.load(Ordering::Acquire) {
483            Err(std::io::Error::new(
484                std::io::ErrorKind::NotConnected,
485                "connection is closed",
486            ))
487        } else {
488            Ok(())
489        }
490    }
491}
492
493impl Default for Connection {
494    fn default() -> Self {
495        Self::new()
496    }
497}