use http::Method;
use http::header::{HeaderMap, HeaderName, HeaderValue};
use std::path::PathBuf;
use std::time::Duration;
use crate::request::{Auth, HttpVersion, Request};
#[derive(Debug, Clone)]
pub struct Client {
headers: HeaderMap,
timeout: Duration,
connect_timeout: Duration,
stall_timeout: Duration,
follow_redirects: bool,
max_redirects: u32,
http_version: Option<HttpVersion>,
proxy: Option<String>,
unix_socket: Option<PathBuf>,
ca_bundle: Option<PathBuf>,
verify_tls: bool,
compression: bool,
auth: Option<Auth>,
netrc: bool,
proxy_auth: Option<Auth>,
client_cert: Option<(PathBuf, PathBuf)>,
max_body_size: Option<u64>,
}
impl Default for Client {
fn default() -> Self {
let defaults = Request::new(Method::GET, "");
Client {
headers: HeaderMap::new(),
timeout: defaults.timeout,
connect_timeout: defaults.connect_timeout,
stall_timeout: defaults.stall_timeout,
follow_redirects: defaults.follow_redirects,
max_redirects: defaults.max_redirects,
http_version: defaults.http_version,
proxy: defaults.proxy,
unix_socket: defaults.unix_socket,
ca_bundle: defaults.ca_bundle,
verify_tls: defaults.verify_tls,
compression: defaults.compression,
auth: defaults.auth,
netrc: defaults.netrc,
proxy_auth: defaults.proxy_auth,
client_cert: defaults.client_cert,
max_body_size: defaults.max_body_size,
}
}
}
impl Client {
pub fn new() -> Self {
Self::default()
}
pub fn header(mut self, name: HeaderName, value: HeaderValue) -> Self {
self.headers.insert(name, value);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
pub fn stall_timeout(mut self, timeout: Duration) -> Self {
self.stall_timeout = timeout;
self
}
pub fn follow_redirects(mut self, follow: bool) -> Self {
self.follow_redirects = follow;
self
}
pub fn max_redirects(mut self, max: u32) -> Self {
self.max_redirects = max;
self
}
pub fn http_version(mut self, version: HttpVersion) -> Self {
self.http_version = Some(version);
self
}
pub fn proxy(mut self, proxy: impl Into<String>) -> Self {
self.proxy = Some(proxy.into());
self
}
pub fn unix_socket(mut self, path: impl Into<PathBuf>) -> Self {
self.unix_socket = Some(path.into());
self
}
pub fn ca_bundle(mut self, path: impl Into<PathBuf>) -> Self {
self.ca_bundle = Some(path.into());
self
}
pub fn danger_accept_invalid_certs(mut self) -> Self {
self.verify_tls = false;
self
}
pub fn compression(mut self, enabled: bool) -> Self {
self.compression = enabled;
self
}
pub fn auth(mut self, auth: Auth) -> Self {
self.auth = Some(auth);
self
}
pub fn basic_auth(self, user: impl Into<String>, password: impl Into<String>) -> Self {
self.auth(Auth::Basic {
user: user.into(),
password: password.into(),
})
}
pub fn bearer_auth(self, token: impl Into<String>) -> Self {
self.auth(Auth::Bearer(token.into()))
}
pub fn netrc(mut self) -> Self {
self.netrc = true;
self
}
pub fn proxy_auth(mut self, auth: Auth) -> Self {
self.proxy_auth = Some(auth);
self
}
pub fn client_cert(mut self, cert: impl Into<PathBuf>, key: impl Into<PathBuf>) -> Self {
self.client_cert = Some((cert.into(), key.into()));
self
}
pub fn max_body_size(mut self, limit: Option<u64>) -> Self {
self.max_body_size = limit;
self
}
pub fn request(&self, method: Method, url: &str) -> Request {
let mut request = Request::new(method, url);
request.headers = self.headers.clone();
request.timeout = self.timeout;
request.connect_timeout = self.connect_timeout;
request.stall_timeout = self.stall_timeout;
request.follow_redirects = self.follow_redirects;
request.max_redirects = self.max_redirects;
request.http_version = self.http_version;
request.proxy = self.proxy.clone();
request.unix_socket = self.unix_socket.clone();
request.ca_bundle = self.ca_bundle.clone();
request.verify_tls = self.verify_tls;
request.compression = self.compression;
request.auth = self.auth.clone();
request.netrc = self.netrc;
request.proxy_auth = self.proxy_auth.clone();
request.client_cert = self.client_cert.clone();
request.max_body_size = self.max_body_size;
request
}
pub fn get(&self, url: &str) -> Request {
self.request(Method::GET, url)
}
pub fn post(&self, url: &str) -> Request {
self.request(Method::POST, url)
}
pub fn put(&self, url: &str) -> Request {
self.request(Method::PUT, url)
}
pub fn patch(&self, url: &str) -> Request {
self.request(Method::PATCH, url)
}
pub fn delete(&self, url: &str) -> Request {
self.request(Method::DELETE, url)
}
pub fn head(&self, url: &str) -> Request {
self.request(Method::HEAD, url)
}
}
#[cfg(test)]
mod tests {
use super::*;
use http::header::USER_AGENT;
#[test]
fn requests_inherit_and_override() {
let client = Client::new()
.header(USER_AGENT, HeaderValue::from_static("client"))
.header(
HeaderName::from_static("x-a"),
HeaderValue::from_static("1"),
)
.timeout(Duration::from_secs(3))
.connect_timeout(Duration::from_secs(1))
.stall_timeout(Duration::from_secs(2))
.follow_redirects(false)
.max_redirects(2)
.http_version(HttpVersion::Http11)
.proxy("http://proxy:3128")
.unix_socket("/run/x.sock")
.ca_bundle("/etc/ca.pem")
.danger_accept_invalid_certs()
.compression(false)
.bearer_auth("t")
.netrc()
.client_cert("/c.pem", "/k.pem")
.max_body_size(None);
let req = client.post("http://localhost").header("X-A", "2");
assert_eq!(req.method, Method::POST);
assert_eq!(req.headers[USER_AGENT], "client");
assert_eq!(req.headers["x-a"], "2");
assert_eq!(req.timeout, Duration::from_secs(3));
assert_eq!(req.connect_timeout, Duration::from_secs(1));
assert_eq!(req.stall_timeout, Duration::from_secs(2));
assert!(!req.follow_redirects);
assert_eq!(req.max_redirects, 2);
assert_eq!(req.http_version, Some(HttpVersion::Http11));
assert_eq!(req.proxy.as_deref(), Some("http://proxy:3128"));
assert_eq!(
req.unix_socket.as_deref(),
Some(std::path::Path::new("/run/x.sock"))
);
assert_eq!(
req.ca_bundle.as_deref(),
Some(std::path::Path::new("/etc/ca.pem"))
);
assert!(!req.verify_tls);
assert!(!req.compression);
assert_eq!(req.auth, Some(Auth::Bearer("t".into())));
assert!(req.netrc);
assert_eq!(req.client_cert, Some(("/c.pem".into(), "/k.pem".into())));
assert_eq!(req.max_body_size, None);
}
}