Skip to main content

trillium_quinn/
connection.rs

1use async_compat::Compat;
2use futures_lite::{AsyncRead, AsyncWrite};
3use quinn::VarInt;
4use std::{
5    fmt::{self, Debug, Formatter},
6    future::Future,
7    io,
8    net::SocketAddr,
9    pin::Pin,
10};
11use trillium_macros::{AsyncRead, AsyncWrite};
12use trillium_server_common::{
13    QuicConnectionTrait, QuicTransportBidi, QuicTransportReceive, QuicTransportSend, Transport,
14};
15
16/// A bidirectional QUIC stream, combining quinn's split send/recv
17/// into a single [`Transport`].
18#[derive(AsyncRead, AsyncWrite)]
19pub struct QuinnTransport {
20    #[async_read]
21    recv: Compat<quinn::RecvStream>,
22    #[async_write]
23    send: Compat<quinn::SendStream>,
24}
25
26impl QuinnTransport {
27    fn new(recv: quinn::RecvStream, send: quinn::SendStream) -> Self {
28        Self {
29            recv: Compat::new(recv),
30            send: Compat::new(send),
31        }
32    }
33}
34
35impl QuicTransportReceive for QuinnTransport {
36    fn stop(&mut self, code: u64) {
37        let error_code = VarInt::from_u64(code).unwrap_or_default();
38        let _ = self.recv.get_mut().stop(error_code);
39    }
40}
41
42/// quinn resolves `stopped()` on `STOP_SENDING`, on stream reset, and on connection loss — and
43/// also once the peer has read a *finished* stream to completion. Only the abandonment arms can
44/// be reached through this trait: it is called at stream accept, while the send half is still
45/// open, and trillium-http polls it only for the lifetime of a stream it has not finished.
46fn stopped_future(
47    send: &quinn::SendStream,
48) -> Option<Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>> {
49    let stopped = send.stopped();
50    Some(Box::pin(async move {
51        let _ = stopped.await;
52    }))
53}
54
55impl QuicTransportSend for QuinnTransport {
56    fn reset(&mut self, code: u64) {
57        let error_code = VarInt::from_u64(code).unwrap_or_default();
58        let _ = self.send.get_mut().reset(error_code);
59    }
60
61    fn stopped(&self) -> Option<Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>> {
62        stopped_future(self.send.get_ref())
63    }
64
65    fn set_priority(&mut self, priority: i32) {
66        // Errors only when the stream is already gone, in which case there's nothing to
67        // prioritize.
68        let _ = self.send.get_mut().set_priority(priority);
69    }
70}
71
72impl QuicTransportBidi for QuinnTransport {}
73
74// `negotiated_alpn` is left at the trait default (`None`). trillium-quinn is positioned as the
75// QUIC adapter for trillium-http's HTTP/3 support, where the ALPN value is always `h3`. Nothing
76// in the framework currently needs to read it back per stream, and h1-vs-h2 dispatch only ever
77// runs on a TCP listener.
78impl Transport for QuinnTransport {}
79
80/// A QUIC connection backed by quinn, implementing [`QuicConnectionTrait`].
81#[derive(Clone, Debug)]
82pub struct QuinnConnection(quinn::Connection);
83
84impl QuinnConnection {
85    pub(crate) fn new(connection: quinn::Connection) -> Self {
86        Self(connection)
87    }
88}
89
90#[derive(AsyncRead)]
91pub struct QuinnRecv(Compat<quinn::RecvStream>);
92impl Debug for QuinnRecv {
93    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
94        f.debug_tuple("QuinnRecv").finish_non_exhaustive()
95    }
96}
97impl From<quinn::RecvStream> for QuinnRecv {
98    fn from(value: quinn::RecvStream) -> Self {
99        Self(Compat::new(value))
100    }
101}
102impl QuicTransportReceive for QuinnRecv {
103    fn stop(&mut self, code: u64) {
104        let error_code = VarInt::from_u64(code).unwrap_or_default();
105        let _ = self.0.get_mut().stop(error_code);
106    }
107}
108
109#[derive(AsyncWrite)]
110pub struct QuinnSend(Compat<quinn::SendStream>);
111
112impl Debug for QuinnSend {
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        f.debug_tuple("QuinnSend").finish_non_exhaustive()
115    }
116}
117impl From<quinn::SendStream> for QuinnSend {
118    fn from(value: quinn::SendStream) -> Self {
119        Self(Compat::new(value))
120    }
121}
122impl QuicTransportSend for QuinnSend {
123    fn reset(&mut self, code: u64) {
124        let error_code = VarInt::from_u64(code).unwrap_or_default();
125        let _ = self.0.get_mut().reset(error_code);
126    }
127
128    fn stopped(&self) -> Option<Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>> {
129        stopped_future(self.0.get_ref())
130    }
131
132    fn set_priority(&mut self, priority: i32) {
133        let _ = self.0.get_mut().set_priority(priority);
134    }
135}
136
137impl QuicConnectionTrait for QuinnConnection {
138    type BidiStream = QuinnTransport;
139    type RecvStream = QuinnRecv;
140    type SendStream = QuinnSend;
141
142    async fn accept_bidi(&self) -> io::Result<(u64, Self::BidiStream)> {
143        let (send, recv) = self.0.accept_bi().await.map_err(conn_err)?;
144        let stream_id = VarInt::from(recv.id()).into_inner();
145        Ok((stream_id, QuinnTransport::new(recv, send)))
146    }
147
148    async fn accept_uni(&self) -> io::Result<(u64, Self::RecvStream)> {
149        let recv = self.0.accept_uni().await.map_err(conn_err)?;
150        let stream_id = VarInt::from(recv.id()).into_inner();
151        Ok((stream_id, recv.into()))
152    }
153
154    async fn open_uni(&self) -> io::Result<(u64, Self::SendStream)> {
155        let send = self.0.open_uni().await.map_err(conn_err)?;
156        let stream_id = VarInt::from(send.id()).into_inner();
157        Ok((stream_id, send.into()))
158    }
159
160    async fn open_bidi(&self) -> io::Result<(u64, Self::BidiStream)> {
161        let (send, recv) = self.0.open_bi().await.map_err(conn_err)?;
162        let stream_id = VarInt::from(recv.id()).into_inner();
163        Ok((stream_id, QuinnTransport::new(recv, send)))
164    }
165
166    fn remote_address(&self) -> SocketAddr {
167        self.0.remote_address()
168    }
169
170    fn close(&self, error_code: u64, reason: &[u8]) {
171        self.0
172            .close(VarInt::from_u64(error_code).unwrap_or(VarInt::MAX), reason);
173    }
174
175    fn send_datagram(&self, data: &[u8]) -> io::Result<()> {
176        self.0
177            .send_datagram(data.to_vec().into())
178            .map_err(io::Error::other)
179    }
180
181    async fn recv_datagram<F: FnOnce(&[u8]) + Send>(&self, callback: F) -> io::Result<()> {
182        self.0
183            .read_datagram()
184            .await
185            .map(|d| callback(&d))
186            .map_err(conn_err)
187    }
188
189    fn max_datagram_size(&self) -> Option<usize> {
190        self.0.max_datagram_size()
191    }
192}
193
194fn conn_err(e: quinn::ConnectionError) -> io::Error {
195    io::Error::new(io::ErrorKind::ConnectionReset, e)
196}