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