Skip to main content

areq_smol/
connect.rs

1use {
2    areq::{Address, Error, HandshakeWith, Session},
3    async_net::TcpStream,
4    url::Host,
5};
6
7/// Extension trait to connect a [TCP stream](TcpStream).
8///
9/// If a connection is successful, it returns an HTTP client and a future
10/// that needs to be polled in background while the client sends requests
11/// and receives responses. To do this, you can spawn the task in an
12/// [executor] or poll it concurrently with the client, for example,
13/// using [`try_zip`].
14///
15/// [executor]: https://docs.rs/async-executor/latest/async_executor/struct.Executor.html
16/// [`try_zip`]: https://docs.rs/futures-lite/latest/futures_lite/future/fn.try_zip.html
17///
18/// # Example
19///
20/// ```
21/// use {
22///     areq_smol::{http::Uri, http1::Http1, prelude::*},
23///     async_executor::Executor,
24///     std::io::Error,
25/// };
26///
27/// async fn get(ex: &Executor<'_>) -> Result<String, Error> {
28///     let uri = Uri::from_static("http://127.0.0.1:3001/hello");
29///
30///     // Establish a connection to the address
31///     let (mut client, conn) = Http1::default().connect(&uri).await?;
32///
33///     // Spawn the task in background
34///     ex.spawn(conn).detach();
35///
36///     // Now you can work with the client
37///     // The background task will complete once the client is dropped
38///     client.get(uri, ()).await?.text().await
39/// }
40/// ```
41///
42/// You can also use an extension method [`handle`](crate::smol::Handle::handle),
43/// which takes an async closure, calls it on the client, and polls the task
44/// in background for the entire duration of the closure execution.
45///
46/// ```
47/// use {
48///     areq_smol::{http::Uri, http1::Http1, prelude::*},
49///     std::io::Error,
50/// };
51///
52/// async fn get() -> Result<String, Error> {
53///     let uri = Uri::from_static("http://127.0.0.1:3001/hello");
54///
55///     Http1::default()
56///         .connect(&uri)
57///         .await?
58///         .handle(async |client| client.get(uri, ()).await?.text().await)
59///         .await
60/// }
61/// ```
62pub trait Connect<A, B>: HandshakeWith<TcpStream, B> {
63    /// Connects to the given address.
64    async fn connect(self, addr: A) -> Result<(Self::Client, Self::Task), Error>;
65}
66
67impl<H, A, B> Connect<A, B> for H
68where
69    A: TryInto<Address, Error: Into<Error>>,
70    H: HandshakeWith<TcpStream, B>,
71{
72    #[inline]
73    async fn connect(self, addr: A) -> Result<(Self::Client, Self::Task), Error> {
74        let addr = addr.try_into().map_err(A::Error::into)?;
75        let io = match &addr.host {
76            Host::Domain(d) => TcpStream::connect((d.as_str(), addr.port)).await?,
77            Host::Ipv4(ip4) => TcpStream::connect((*ip4, addr.port)).await?,
78            Host::Ipv6(ip6) => TcpStream::connect((*ip6, addr.port)).await?,
79        };
80
81        let se = Session { addr, io };
82        self.handshake(se).await
83    }
84}