zerortt_api/
lib.rs

1//! Primitive types and traits for `zerortt`
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use std::{
5    borrow::Cow,
6    net::{SocketAddr, ToSocketAddrs},
7    time::Instant,
8};
9
10use quiche::{Config, ConnectionId, RecvInfo, SendInfo};
11
12/// A `poll` error.
13#[derive(Debug, PartialEq, Eq, thiserror::Error)]
14pub enum Error {
15    /// A quic protocol error.
16    #[error(transparent)]
17    Quiche(#[from] quiche::Error),
18
19    /// The operation needs to block to complete, but the blocking operation was
20    /// requested to not occur.
21    ///
22    /// Should retry this operation later.
23    #[error("Retry this operation later.")]
24    Retry,
25
26    /// Resource is busy, should retry this operation later.
27    #[error("Resource is busy.")]
28    Busy,
29
30    /// Resource is not found.
31    #[error("Resource is not found.")]
32    NotFound,
33
34    #[error("Failed to validate client address.")]
35    ValidateAddress,
36
37    #[error("Maximumn currently opened streams.")]
38    MaxStreams,
39}
40
41impl From<Error> for std::io::Error {
42    fn from(value: Error) -> Self {
43        match value {
44            Error::Quiche(error) => std::io::Error::other(error),
45            Error::Retry | Error::Busy => {
46                std::io::Error::new(std::io::ErrorKind::WouldBlock, value)
47            }
48
49            Error::NotFound => std::io::Error::new(std::io::ErrorKind::NotFound, value),
50            _ => std::io::Error::other(value),
51        }
52    }
53}
54
55/// Short for `std::result::Result<T, crate::poll::Error>`
56pub type Result<T> = std::result::Result<T, Error>;
57
58/// A trait to filter `WouldBlock` like errors and convert them into `Poll::Pending`
59pub trait WouldBlock<T> {
60    type Error;
61
62    fn would_block(self) -> std::task::Poll<std::result::Result<T, Self::Error>>;
63}
64
65impl<T> WouldBlock<T> for Result<T> {
66    type Error = Error;
67
68    fn would_block(self) -> std::task::Poll<Result<T>> {
69        match self {
70            Err(Error::Busy) | Err(Error::Retry) => std::task::Poll::Pending,
71            _ => std::task::Poll::Ready(self),
72        }
73    }
74}
75
76impl<T> WouldBlock<T> for std::io::Result<T> {
77    type Error = std::io::Error;
78
79    fn would_block(self) -> std::task::Poll<std::result::Result<T, Self::Error>> {
80        match self {
81            Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => std::task::Poll::Pending,
82            _ => std::task::Poll::Ready(self),
83        }
84    }
85}
86
87/// Generate a random `ConnectionId`
88#[inline]
89pub fn random_conn_id() -> ConnectionId<'static> {
90    let mut buf = vec![0; 20];
91    boring::rand::rand_bytes(&mut buf).unwrap();
92
93    ConnectionId::from_vec(buf)
94}
95
96/// Returns true if the stream was created locally.
97#[inline]
98pub fn is_local(stream_id: u64, is_server: bool) -> bool {
99    (stream_id & 0x1) == (is_server as u64)
100}
101
102/// Returns true if the stream is bidirectional.
103#[inline]
104pub fn is_bidi(stream_id: u64) -> bool {
105    (stream_id & 0x2) == 0
106}
107
108/// Type for readiness events.
109#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
110pub enum EventKind {
111    /// Readiness for `send` operation.
112    Send,
113    /// Readiness for `recv` operation.
114    Recv,
115    /// Client-side connection handshake is completed.
116    Connected,
117    /// Server-side connection handshake is completed.
118    Accept,
119    /// Connection is closed.
120    Closed,
121    /// Readiness for `stream_open` operation.
122    StreamOpenBidi,
123    /// Readiness for `stream_open` operation.
124    StreamOpenUni,
125    /// Readiness for new inbound stream.
126    StreamAccept,
127    /// Readiness for `stream_send` operation.
128    StreamSend,
129    /// Readiness for `stream_recv` operation.
130    StreamRecv,
131    /// Read lock.
132    ReadLock,
133}
134
135/// Associates readiness events with quic connection.
136#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
137pub struct Token(pub u32);
138
139/// Readiness I/O event.
140#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
141pub struct Event {
142    /// Connection token.
143    pub token: Token,
144    /// Type of this event.
145    pub kind: EventKind,
146    /// Event source is a server-side connection.
147    pub is_server: bool,
148    /// Event raised by an I/O error.
149    pub is_error: bool,
150    /// The meaning of this field depends on the [`kind`](Self::kind) field.
151    pub stream_id: u64,
152}
153
154/// Stream type.
155#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
156pub enum StreamKind {
157    Uni,
158    Bidi,
159}
160
161impl From<u64> for StreamKind {
162    fn from(value: u64) -> Self {
163        if is_bidi(value) {
164            StreamKind::Bidi
165        } else {
166            StreamKind::Uni
167        }
168    }
169}
170
171/// A poll api for `QUIC` group.
172pub trait QuicPoll: Send + Sync {
173    type Error;
174    /// Waits for readiness events without blocking current thread
175    /// and returns possible retry time duration.
176    fn poll(&self, events: &mut Vec<Event>) -> std::result::Result<Option<Instant>, Self::Error>;
177
178    /// Wrap and handle a new `quiche::Connection`.
179    ///
180    /// On success, returns a reference handle [`Token`] to the [`Connection`](quiche::Connection).
181    fn register(&self, wrapped: quiche::Connection) -> std::result::Result<Token, Self::Error>;
182
183    /// Unwrap a `quiche::Connection` referenced by the handle [`Token`].
184    fn deregister(&self, token: Token) -> std::result::Result<quiche::Connection, Self::Error>;
185
186    /// Returns the number of `QUIC` connections in the group.
187    fn len(&self) -> usize;
188
189    /// Close a wrapped `QUIC` connection.
190    ///
191    /// This function does not immediately remove the `QUIC` connection
192    /// from memory, but instead waits until the `QUIC` connection has
193    /// completed its closure process before actually removing it from
194    /// memory.
195    ///
196    /// See [`close`](quiche::Connection::close) for more information.
197    fn close(
198        &self,
199        token: Token,
200        app: bool,
201        err: u64,
202        reason: Cow<'static, [u8]>,
203    ) -> std::result::Result<(), Self::Error>;
204
205    /// Open a local stream via a `QUIC` connection.
206    fn stream_open(
207        &self,
208        token: Token,
209        kind: StreamKind,
210        non_blocking: bool,
211    ) -> std::result::Result<Option<u64>, Self::Error>;
212
213    ///Shuts down reading and writing from/to the specified stream.
214    ///
215    /// See [`stream_send`](quiche::Connection::stream_shutdown) for more information.
216    fn stream_shutdown(
217        &self,
218        token: Token,
219        stream_id: u64,
220        err: u64,
221    ) -> std::result::Result<(), Self::Error>;
222
223    /// Writes data to a stream.
224    ///
225    /// See [`stream_send`](quiche::Connection::stream_send) for more information.
226    fn stream_send(
227        &self,
228        token: Token,
229        stream_id: u64,
230        buf: &[u8],
231        fin: bool,
232    ) -> std::result::Result<usize, Self::Error>;
233
234    /// Reads contiguous data from a stream into the provided slice.
235    ///
236    /// See [`stream_recv`](quiche::Connection::stream_recv) for more information.
237    fn stream_recv(
238        &self,
239        token: Token,
240        stream_id: u64,
241        buf: &mut [u8],
242    ) -> std::result::Result<(usize, bool), Self::Error>;
243}
244
245/// `API` for client-side `QUIC`.
246#[cfg(feature = "client")]
247#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
248pub trait QuicClient: QuicPoll {
249    /// Creates a new client-side connection.
250    fn connect(
251        &self,
252        server_name: Option<&str>,
253        local: SocketAddr,
254        peer: SocketAddr,
255        config: &mut Config,
256    ) -> std::result::Result<Token, Self::Error>;
257}
258
259/// Underlying transport layer API for `QUIC` group.
260pub trait QuicTransport {
261    type Error;
262
263    /// Processes QUIC packets received from the peer.
264    fn recv(&self, buf: &mut [u8], info: RecvInfo) -> std::result::Result<usize, Self::Error>;
265
266    /// Writes a single QUIC packet to be sent to the peer.
267    fn send(
268        &self,
269        token: Token,
270        buf: &mut [u8],
271    ) -> std::result::Result<(usize, SendInfo), Self::Error>;
272}
273
274/// Underlying transport layer API for server-side `QUIC` group.
275#[cfg(feature = "server")]
276#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
277pub trait QuicServerTransport: QuicTransport {
278    /// Server-side `recv` function, supports for `QUIC` handshake process.
279    fn recv_with_acceptor(
280        &self,
281        acceptor: &mut Acceptor,
282        buf: &mut [u8],
283        recv_size: usize,
284        recv_info: RecvInfo,
285        unparker: Option<&crossbeam_utils::sync::Unparker>,
286    ) -> std::result::Result<(usize, SendInfo), Self::Error>;
287}
288
289/// A `group` with underlying transport layer.
290pub trait QuicBind: QuicPoll + Sized {
291    /// Create a new `Group` and bind it to `laddrs`.
292    #[cfg(feature = "server")]
293    #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
294    fn bind<S>(laddrs: S, acceptor: Option<Acceptor>) -> std::result::Result<Self, Self::Error>
295    where
296        S: ToSocketAddrs;
297
298    /// Create a new `Group` and bind it to `laddrs`.
299    #[cfg(not(feature = "server"))]
300    #[cfg_attr(docsrs, doc(cfg(not(feature = "server"))))]
301    fn bind<S>(laddrs: S) -> std::result::Result<Self, Self::Error>
302    where
303        S: ToSocketAddrs;
304
305    /// Returns local bound addresses.
306    fn local_addrs(&self) -> impl Iterator<Item = &SocketAddr>;
307}
308
309#[cfg(feature = "server")]
310#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
311mod acceptor;
312#[cfg(feature = "server")]
313pub use acceptor::*;
314
315pub use quiche;