Skip to main content

trillium_smol/
client.rs

1use crate::{SmolRuntime, SmolTransport};
2use async_net::TcpStream;
3use std::{
4    io::{Error, ErrorKind, Result},
5    net::SocketAddr,
6};
7use trillium_server_common::{
8    Connector, Transport,
9    url::{Host, Url},
10};
11
12/// configuration for the tcp Connector
13#[derive(Default, Debug, Clone, Copy)]
14pub struct ClientConfig {
15    /// disable [nagle's algorithm](https://en.wikipedia.org/wiki/Nagle%27s_algorithm)
16    pub nodelay: Option<bool>,
17
18    /// set a time to live for the tcp protocol
19    pub ttl: Option<u32>,
20}
21
22impl ClientConfig {
23    /// constructs a default ClientConfig
24    pub const fn new() -> Self {
25        Self {
26            nodelay: None,
27            ttl: None,
28        }
29    }
30
31    /// chainable setter to set default nodelay
32    pub const fn with_nodelay(mut self, nodelay: bool) -> Self {
33        self.nodelay = Some(nodelay);
34        self
35    }
36
37    /// chainable setter for ip ttl
38    pub const fn with_ttl(mut self, ttl: u32) -> Self {
39        self.ttl = Some(ttl);
40        self
41    }
42}
43
44impl Connector for ClientConfig {
45    type Runtime = SmolRuntime;
46    type Transport = SmolTransport<TcpStream>;
47    type Udp = crate::SmolUdpSocket;
48
49    fn runtime(&self) -> Self::Runtime {
50        SmolRuntime::default()
51    }
52
53    async fn connect(&self, url: &Url) -> Result<Self::Transport> {
54        if url.scheme() != "http" {
55            return Err(Error::new(
56                ErrorKind::InvalidInput,
57                format!("unknown scheme {}", url.scheme()),
58            ));
59        }
60
61        let port = url
62            .port_or_known_default()
63            // this should be ok because we already checked that the scheme is http, which has a
64            // default port
65            .ok_or_else(|| Error::new(ErrorKind::InvalidInput, format!("{url} missing port")))?;
66
67        let host = url
68            .host()
69            .ok_or_else(|| Error::new(ErrorKind::InvalidInput, format!("{url} missing host")))?;
70
71        let mut tcp = match host {
72            Host::Domain(domain) => Self::Transport::connect((domain, port)).await?,
73            Host::Ipv4(ip) => Self::Transport::connect((ip, port)).await?,
74            Host::Ipv6(ip) => Self::Transport::connect((ip, port)).await?,
75        };
76
77        if let Some(nodelay) = self.nodelay {
78            tcp.set_nodelay(nodelay)?;
79        }
80
81        if let Some(ttl) = self.ttl {
82            tcp.set_ip_ttl(ttl)?;
83        }
84
85        Ok(tcp)
86    }
87
88    async fn resolve(&self, host: &str, port: u16) -> Result<Vec<SocketAddr>> {
89        async_net::resolve((host, port)).await
90    }
91}
92
93#[cfg(unix)]
94impl Connector for SmolTransport<async_net::unix::UnixStream> {
95    type Runtime = SmolRuntime;
96    type Transport = Self;
97    type Udp = crate::SmolUdpSocket;
98
99    async fn connect(&self, _url: &Url) -> Result<Self::Transport> {
100        Ok(self.clone())
101    }
102
103    fn runtime(&self) -> Self::Runtime {
104        SmolRuntime::default()
105    }
106
107    async fn resolve(&self, _host: &str, _port: u16) -> Result<Vec<SocketAddr>> {
108        Ok(vec![])
109    }
110}