[go: up one dir, main page]

surf/
config.rs

1//! Configuration for `HttpClient`s.
2
3use std::sync::Arc;
4use std::{collections::HashMap, fmt::Debug, time::Duration};
5
6use http_client::{Config as HttpConfig, HttpClient};
7use http_types::headers::{HeaderName, HeaderValues, ToHeaderValues};
8
9use crate::http::Url;
10use crate::Result;
11
12/// Configuration for `surf::Client`s and their underlying HTTP clients.
13///
14/// ```
15/// use std::convert::TryInto;
16/// use surf::{Client, Config, Url};
17///
18/// # #[async_std::main]
19/// # async fn main() -> surf::Result<()> {
20/// let client: Client = Config::new()
21///     .set_base_url(Url::parse("https://example.org")?)
22///     .try_into()?;
23///
24/// let mut response = client.get("/").await?;
25///
26/// println!("{}", response.body_string().await?);
27/// # Ok(())
28/// # }
29/// ```
30#[non_exhaustive]
31#[derive(Clone, Debug)]
32pub struct Config {
33    /// The base URL for a client. All request URLs will be relative to this URL.
34    ///
35    /// Note: a trailing slash is significant.
36    /// Without it, the last path component is considered to be a “file” name
37    /// to be removed to get at the “directory” that is used as the base.
38    pub base_url: Option<Url>,
39    /// Headers to be applied to every request made by this client.
40    pub headers: HashMap<HeaderName, HeaderValues>,
41    /// Underlying HTTP client config.
42    pub http_config: HttpConfig,
43    /// Optional custom http client.
44    pub http_client: Option<Arc<dyn HttpClient>>,
45}
46
47impl Config {
48    /// Construct new empty config.
49    pub fn new() -> Self {
50        Self::default()
51    }
52}
53
54impl Default for Config {
55    fn default() -> Self {
56        HttpConfig::default().into()
57    }
58}
59
60impl Config {
61    /// Adds a header to be added to every request by this client.
62    ///
63    /// Default: No extra headers.
64    ///
65    /// ```
66    /// use std::convert::TryInto;
67    /// use surf::{Client, Config};
68    /// use surf::http::auth::BasicAuth;
69    ///
70    /// # fn main() -> surf::Result<()> {
71    /// let auth = BasicAuth::new("Username", "Password");
72    ///
73    /// let client: Client = Config::new()
74    ///     .add_header(auth.name(), auth.value())?
75    ///     .try_into()?;
76    /// # Ok(())
77    /// # }
78    /// ```
79    pub fn add_header(
80        mut self,
81        name: impl Into<HeaderName>,
82        values: impl ToHeaderValues,
83    ) -> Result<Self> {
84        self.headers
85            .insert(name.into(), values.to_header_values()?.collect());
86        Ok(self)
87    }
88
89    /// Sets the base URL for this client. All request URLs will be relative to this URL.
90    ///
91    /// Note: a trailing slash is significant.
92    /// Without it, the last path component is considered to be a “file” name
93    /// to be removed to get at the “directory” that is used as the base.
94    ///
95    /// Default: `None` (internally).
96    ///
97    /// ```
98    /// use std::convert::TryInto;
99    /// use surf::{Client, Config, Url};
100    ///
101    /// # fn main() -> surf::Result<()> {
102    /// let client: Client = Config::new()
103    ///     .set_base_url(Url::parse("https://example.org")?)
104    ///     .try_into()?;
105    /// # Ok(())
106    /// # }
107    /// ```
108    pub fn set_base_url(mut self, base: Url) -> Self {
109        self.base_url = Some(base);
110        self
111    }
112
113    /// Set HTTP/1.1 `keep-alive` (connection pooling).
114    ///
115    /// Default: `true`.
116    ///
117    /// Note: Does nothing on `wasm-client` (or `native-client` on `wasm32`).
118    pub fn set_http_keep_alive(mut self, keep_alive: bool) -> Self {
119        self.http_config.http_keep_alive = keep_alive;
120        self
121    }
122
123    /// Set TCP `NO_DELAY`.
124    ///
125    /// Default: `false`.
126    ///
127    /// Note: Does nothing on `wasm-client` (or `native-client` on `wasm32`).
128    pub fn set_tcp_no_delay(mut self, no_delay: bool) -> Self {
129        self.http_config.tcp_no_delay = no_delay;
130        self
131    }
132
133    /// Set connection timeout duration.
134    ///
135    /// Passing `None` will remove the timeout.
136    ///
137    /// Default: `Some(Duration::from_secs(60))`.
138    ///
139    /// ```
140    /// use std::convert::TryInto;
141    /// use std::time::Duration;
142    /// use surf::{Client, Config};
143    ///
144    /// # fn main() -> surf::Result<()> {
145    /// let client: Client = Config::new()
146    ///     .set_timeout(Some(Duration::from_secs(5)))
147    ///     .try_into()?;
148    /// # Ok(())
149    /// # }
150    /// ```
151    pub fn set_timeout(mut self, timeout: Option<Duration>) -> Self {
152        self.http_config.timeout = timeout;
153        self
154    }
155
156    /// Set the maximum number of simultaneous connections that this client is allowed to keep open to individual hosts at one time.
157    ///
158    /// Default: `50`.
159    /// This number is based on a few random benchmarks and see whatever gave decent perf vs resource use in Orogene.
160    ///
161    /// Note: The behavior of this is different depending on the backend in use.
162    /// - `h1-client`: `0` is disallowed and asserts as otherwise it would cause a semaphore deadlock.
163    /// - `curl-client`: `0` allows for limitless connections per host.
164    /// - `hyper-client`: No effect. Hyper does not support such an option.
165    /// - `wasm-client`: No effect. Web browsers do not support such an option.
166    pub fn set_max_connections_per_host(mut self, max_connections_per_host: usize) -> Self {
167        self.http_config.max_connections_per_host = max_connections_per_host;
168        self
169    }
170
171    /// Override the http client entirely.
172    ///
173    /// When using this, any underlying `http_client::Config` http configuration will be ignored.
174    ///
175    /// ```
176    /// use std::convert::TryInto;
177    /// use surf::{Client, Config};
178    ///
179    /// # fn main() -> surf::Result<()> {
180    /// // Connect directly to a Tide server, e.g. for testing.
181    /// let server = tide::new();
182    ///
183    /// let client: Client = Config::new()
184    ///     .set_http_client(server)
185    ///     .try_into()?;
186    /// # Ok(())
187    /// # }
188    /// ```
189    pub fn set_http_client(mut self, http_client: impl HttpClient) -> Self {
190        self.http_client = Some(Arc::new(http_client));
191        self
192    }
193
194    /// Set TLS Configuration (Rustls)
195    #[cfg_attr(feature = "docs", doc(cfg(feature = "h1-client-rustls")))]
196    #[cfg(feature = "h1-client-rustls")]
197    pub fn set_tls_config(
198        mut self,
199        tls_config: Option<std::sync::Arc<rustls_crate::ClientConfig>>,
200    ) -> Self {
201        self.http_config.tls_config = tls_config;
202        self
203    }
204    /// Set TLS Configuration (Native TLS)
205    #[cfg_attr(feature = "docs", doc(cfg(feature = "h1-client")))]
206    #[cfg(feature = "h1-client")]
207    pub fn set_tls_config(
208        mut self,
209        tls_config: Option<std::sync::Arc<async_native_tls::TlsConnector>>,
210    ) -> Self {
211        self.http_config.tls_config = tls_config;
212        self
213    }
214}
215
216impl AsRef<HttpConfig> for Config {
217    fn as_ref(&self) -> &HttpConfig {
218        &self.http_config
219    }
220}
221
222impl From<HttpConfig> for Config {
223    fn from(http_config: HttpConfig) -> Self {
224        Self {
225            base_url: None,
226            headers: HashMap::new(),
227            http_config,
228            http_client: None,
229        }
230    }
231}