s2n_quic/
client.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    connection::{self, Connection},
6    provider::*,
7};
8use core::{
9    fmt,
10    future::Future,
11    pin::Pin,
12    task::{Context, Poll},
13};
14use s2n_quic_transport::endpoint::{connect, handle::Connector};
15
16mod builder;
17mod providers;
18
19pub use builder::*;
20pub use connect::Connect;
21pub use providers::*;
22
23/// A QUIC client endpoint, capable of opening connections
24#[derive(Clone)]
25pub struct Client {
26    connector: Connector,
27    local_addr: s2n_quic_core::inet::SocketAddress,
28}
29
30impl fmt::Debug for Client {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        f.debug_struct("Client")
33            .field("local_addr", &self.local_addr().ok())
34            .finish()
35    }
36}
37
38impl Client {
39    /// Starts listening on the provided socket
40    ///
41    /// # Examples
42    ///
43    /// ```rust,no_run
44    /// # use std::error::Error;
45    /// # use s2n_quic::Client;
46    /// #
47    /// # fn main() -> Result<(), Box<dyn Error>> {
48    /// let Client = Client::bind("0.0.0.0:0")?;
49    /// #
50    /// #    Ok(())
51    /// # }
52    /// ```
53    pub fn bind<T>(socket: T) -> Result<Self, StartError>
54    where
55        T: io::TryInto,
56    {
57        let client = Self::builder()
58            .with_io(socket)
59            .map_err(StartError::new)?
60            .start()?;
61        Ok(client)
62    }
63
64    /// Returns a [`Builder`] which is able to configure the [`Client`] components.
65    ///
66    /// # Examples
67    ///
68    /// ```rust,no_run
69    /// # use std::error::Error;
70    /// use std::path::Path;
71    /// use s2n_quic::Client;
72    ///
73    /// #
74    /// # fn main() -> Result<(), Box<dyn Error>> {
75    /// let client = Client::builder()
76    ///     .with_tls(Path::new("./certs/cert.pem"))?
77    ///     .with_io("0.0.0.0:0")?
78    ///     .start()?;
79    /// #
80    /// #    Ok(())
81    /// # }
82    /// ```
83    pub fn builder() -> Builder<impl ClientProviders> {
84        Builder::default()
85    }
86
87    /// Establishes a connection to the specified endpoint
88    ///
89    /// # Examples
90    ///
91    /// ```rust,no_run
92    /// # use std::error::Error;
93    /// use s2n_quic::Client;
94    /// use std::{net::SocketAddr, path::Path};
95    ///
96    /// # async fn connect() -> Result<(), Box<dyn Error>> {
97    /// let client = Client::builder()
98    ///     .with_tls(Path::new("./certs/cert.pem"))?
99    ///     .with_io("0.0.0.0:0")?
100    ///     .start()?;
101    ///
102    /// let addr: SocketAddr = "127.0.0.1:443".parse()?;
103    /// let connection = client.connect(addr.into()).await?;
104    /// #
105    /// #    Ok(())
106    /// # }
107    /// ```
108    pub fn connect(&self, connect: Connect) -> ConnectionAttempt {
109        let attempt = self.connector.connect(connect);
110        ConnectionAttempt(attempt)
111    }
112
113    /// Wait for the client endpoint to finish handling all outstanding connections
114    ///
115    /// Notifies the endpoint of application interest in closing the endpoint. The
116    /// call waits for **all** outstanding connections to finish before returning.
117    ///
118    /// Note: The endpoint will continue to accept new connection attempts. If there
119    /// are other client handles with active connections, then this call will never
120    /// return.
121    ///
122    /// # Examples
123    ///
124    /// ```rust,no_run
125    /// # use std::error::Error;
126    /// use s2n_quic::Client;
127    /// use std::{net::SocketAddr, path::Path};
128    ///
129    /// # async fn connect() -> Result<(), Box<dyn Error>> {
130    /// let mut client = Client::builder()
131    ///     .with_tls(Path::new("./certs/cert.pem"))?
132    ///     .with_io("0.0.0.0:0")?
133    ///     .start()?;
134    ///
135    /// let addr: SocketAddr = "127.0.0.1:443".parse()?;
136    /// let connection = client.connect(addr.into()).await?;
137    ///
138    /// client.wait_idle().await?;
139    /// #
140    /// #    Ok(())
141    /// # }
142    /// ```
143    pub async fn wait_idle(&mut self) -> Result<(), connection::Error> {
144        futures::future::poll_fn(|cx| self.connector.poll_close(cx)).await
145    }
146
147    /// Returns the local address that this listener is bound to.
148    ///
149    /// This can be useful, for example, when binding to port `0` to figure out which
150    /// port was actually bound.
151    ///
152    /// # Examples
153    ///
154    /// ```rust,no_run
155    /// # use std::error::Error;
156    /// # use s2n_quic::Client;
157    /// #
158    /// # fn main() -> Result<(), Box<dyn Error>> {
159    /// let client = Client::bind("0.0.0.0:0")?;
160    ///
161    /// let local_addr = client.local_addr()?;
162    /// assert_ne!(local_addr.port(), 0);
163    /// #    Ok(())
164    /// # }
165    /// ```
166    pub fn local_addr(&self) -> Result<std::net::SocketAddr, std::io::Error> {
167        Ok(self.local_addr.into())
168    }
169}
170
171#[must_use = "futures do nothing unless you `.await` or poll them"]
172pub struct ConnectionAttempt(connect::Attempt);
173
174impl Future for ConnectionAttempt {
175    type Output = Result<Connection, connection::Error>;
176
177    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
178        match Pin::new(&mut self.0).poll(cx) {
179            Poll::Ready(Ok(conn)) => Poll::Ready(Ok(Connection::new(conn))),
180            Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
181            Poll::Pending => Poll::Pending,
182        }
183    }
184}