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}