use std::sync::Arc;
use url::Url;
use crate::pool::ConnectionPool;
use crate::proxy::Proxy;
use crate::request::Request;
use crate::resolve::{ArcResolver, StdResolver};
use std::time::Duration;
#[cfg(feature = "cookies")]
use {
crate::cookies::{CookieStoreGuard, CookieTin},
cookie_store::CookieStore,
};
#[derive(Debug)]
pub struct AgentBuilder {
config: AgentConfig,
max_idle_connections: usize,
max_idle_connections_per_host: usize,
#[cfg(feature = "cookies")]
cookie_store: Option<CookieStore>,
resolver: ArcResolver,
}
#[derive(Debug, Clone)]
pub(crate) struct AgentConfig {
pub proxy: Option<Proxy>,
pub timeout_connect: Option<Duration>,
pub timeout_read: Option<Duration>,
pub timeout_write: Option<Duration>,
pub timeout: Option<Duration>,
pub redirects: u32,
pub user_agent: String,
#[cfg(feature = "tls")]
pub tls_config: Option<TLSClientConfig>,
}
#[derive(Debug, Clone)]
pub struct Agent {
pub(crate) config: Arc<AgentConfig>,
pub(crate) state: Arc<AgentState>,
}
#[derive(Debug)]
pub(crate) struct AgentState {
pub(crate) pool: ConnectionPool,
#[cfg(feature = "cookies")]
pub(crate) cookie_tin: CookieTin,
pub(crate) resolver: ArcResolver,
}
impl Agent {
pub fn new() -> Self {
AgentBuilder::new().build()
}
pub fn request(&self, method: &str, path: &str) -> Request {
Request::new(self.clone(), method.into(), path.into())
}
pub fn request_url(&self, method: &str, url: &Url) -> Request {
Request::with_url(self.clone(), method.into(), url.clone())
}
pub fn get(&self, path: &str) -> Request {
self.request("GET", path)
}
pub fn head(&self, path: &str) -> Request {
self.request("HEAD", path)
}
pub fn post(&self, path: &str) -> Request {
self.request("POST", path)
}
pub fn put(&self, path: &str) -> Request {
self.request("PUT", path)
}
pub fn delete(&self, path: &str) -> Request {
self.request("DELETE", path)
}
#[cfg(feature = "cookies")]
pub fn cookie_store(&self) -> CookieStoreGuard<'_> {
self.state.cookie_tin.read_lock()
}
}
const DEFAULT_MAX_IDLE_CONNECTIONS: usize = 100;
const DEFAULT_MAX_IDLE_CONNECTIONS_PER_HOST: usize = 1;
impl AgentBuilder {
pub fn new() -> Self {
AgentBuilder {
config: AgentConfig {
proxy: None,
timeout_connect: Some(Duration::from_secs(30)),
timeout_read: None,
timeout_write: None,
timeout: None,
redirects: 5,
user_agent: format!("ureq/{}", env!("CARGO_PKG_VERSION")),
#[cfg(feature = "tls")]
tls_config: None,
},
max_idle_connections: DEFAULT_MAX_IDLE_CONNECTIONS,
max_idle_connections_per_host: DEFAULT_MAX_IDLE_CONNECTIONS_PER_HOST,
resolver: StdResolver.into(),
#[cfg(feature = "cookies")]
cookie_store: None,
}
}
pub fn build(self) -> Agent {
Agent {
config: Arc::new(self.config),
state: Arc::new(AgentState {
pool: ConnectionPool::new_with_limits(
self.max_idle_connections,
self.max_idle_connections_per_host,
),
#[cfg(feature = "cookies")]
cookie_tin: CookieTin::new(
self.cookie_store.unwrap_or_else(|| CookieStore::default()),
),
resolver: self.resolver,
}),
}
}
pub fn proxy(mut self, proxy: Proxy) -> Self {
self.config.proxy = Some(proxy);
self
}
pub fn max_idle_connections(mut self, max: usize) -> Self {
self.max_idle_connections = max;
self
}
pub fn max_idle_connections_per_host(mut self, max: usize) -> Self {
self.max_idle_connections_per_host = max;
self
}
pub fn resolver(mut self, resolver: impl crate::Resolver + 'static) -> Self {
self.resolver = resolver.into();
self
}
pub fn timeout_connect(mut self, timeout: Duration) -> Self {
self.config.timeout_connect = Some(timeout);
self
}
pub fn timeout_read(mut self, timeout: Duration) -> Self {
self.config.timeout_read = Some(timeout);
self
}
pub fn timeout_write(mut self, timeout: Duration) -> Self {
self.config.timeout_write = Some(timeout);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.config.timeout = Some(timeout);
self
}
pub fn redirects(mut self, n: u32) -> Self {
self.config.redirects = n;
self
}
pub fn user_agent(mut self, user_agent: &str) -> Self {
self.config.user_agent = user_agent.into();
self
}
#[cfg(feature = "tls")]
pub fn tls_config(mut self, tls_config: Arc<rustls::ClientConfig>) -> Self {
self.config.tls_config = Some(TLSClientConfig(tls_config));
self
}
#[cfg(feature = "cookies")]
pub fn cookie_store(mut self, cookie_store: CookieStore) -> Self {
self.cookie_store = Some(cookie_store);
self
}
}
#[cfg(feature = "tls")]
#[derive(Clone)]
pub(crate) struct TLSClientConfig(pub(crate) Arc<rustls::ClientConfig>);
#[cfg(feature = "tls")]
impl std::fmt::Debug for TLSClientConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TLSClientConfig").finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn agent_implements_send_and_sync() {
let _agent: Box<dyn Send> = Box::new(AgentBuilder::new().build());
let _agent: Box<dyn Sync> = Box::new(AgentBuilder::new().build());
}
}