pub mod background;
pub mod download;
pub mod upload;
use crate::backend::Backend;
use http::{HeaderMap, HeaderName, HeaderValue};
pub use background::BackgroundDownloadBuilder;
pub use download::{DownloadBuilder, DownloadResponse};
pub use upload::UploadBuilder;
use url::Url;
#[derive(Clone, Debug)]
pub struct ProxyConfig {
pub host: String,
pub port: u16,
pub username: Option<String>,
pub password: Option<String>,
}
pub struct Client {
backend: Backend,
}
impl Client {
pub fn new() -> crate::Result<Self> {
Ok(Self {
backend: Backend::default_for_platform()?,
})
}
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
pub fn get(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::GET,
url,
self.backend.clone(),
))
}
pub fn post(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::POST,
url,
self.backend.clone(),
))
}
pub fn put(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::PUT,
url,
self.backend.clone(),
))
}
pub fn delete(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::DELETE,
url,
self.backend.clone(),
))
}
pub fn head(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::HEAD,
url,
self.backend.clone(),
))
}
pub fn patch(&self, url: impl TryInto<Url>) -> crate::Result<crate::RequestBuilder> {
let url = url.try_into().map_err(|_| crate::Error::InvalidUrl)?;
Ok(crate::RequestBuilder::new(
http::Method::PATCH,
url,
self.backend.clone(),
))
}
pub fn download(&self, url: impl TryInto<Url>) -> crate::Result<DownloadBuilder> {
Ok(DownloadBuilder::new(
self.backend.clone(),
url.try_into().map_err(|_| crate::Error::InvalidUrl)?,
))
}
pub fn download_background(&self, url: impl TryInto<Url>) -> BackgroundDownloadBuilder {
BackgroundDownloadBuilder::new(
self.backend.clone(),
url.try_into()
.map_err(|_| crate::Error::InvalidUrl)
.unwrap(),
)
}
pub fn upload(&self, url: impl TryInto<Url>) -> crate::Result<UploadBuilder> {
Ok(UploadBuilder::new(
self.backend.clone(),
url.try_into().map_err(|_| crate::Error::InvalidUrl)?,
))
}
pub fn websocket(&self) -> crate::websocket::WebSocketBuilder {
match &self.backend {
#[cfg(target_vendor = "apple")]
Backend::Foundation(foundation_backend) => {
crate::websocket::WebSocketBuilder::Foundation(
crate::backend::foundation::FoundationWebSocketBuilder::new(
foundation_backend.session().clone(),
),
)
}
#[cfg(windows)]
Backend::Windows(_) => crate::websocket::WebSocketBuilder::Windows(
crate::backend::windows::WindowsWebSocketBuilder::new(),
),
Backend::Reqwest(_) => {
crate::websocket::WebSocketBuilder::Reqwest(
crate::backend::reqwest::ReqwestWebSocketBuilder::new(),
)
}
}
}
pub fn cookie_jar(&self) -> Option<&crate::CookieJar> {
None
}
}
pub struct ClientBuilder {
config: crate::backend::BackendConfig,
backend_type: Option<BackendType>,
}
#[derive(Clone, Copy, Debug)]
pub enum BackendType {
#[cfg(target_vendor = "apple")]
Foundation,
Reqwest,
#[cfg(windows)]
Windows,
}
impl ClientBuilder {
pub fn new() -> Self {
Self {
config: crate::backend::BackendConfig::default(),
backend_type: None,
}
}
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
self.config.timeout = Some(timeout);
self
}
pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.config.user_agent = Some(user_agent.into());
self
}
pub fn ignore_certificate_errors(mut self, ignore: bool) -> Self {
self.config.ignore_certificate_errors = Some(ignore);
self
}
pub fn header(
mut self,
name: impl TryInto<HeaderName>,
value: impl Into<String>,
) -> crate::Result<Self> {
let header_name = name.try_into().map_err(|_| crate::Error::InvalidHeader)?;
let header_value =
HeaderValue::from_str(&value.into()).map_err(|_| crate::Error::InvalidHeader)?;
if self.config.default_headers.is_none() {
self.config.default_headers = Some(HeaderMap::new());
}
self.config
.default_headers
.as_mut()
.unwrap()
.insert(header_name, header_value);
Ok(self)
}
pub fn use_cookies(mut self, use_cookies: bool) -> Self {
self.config.use_cookies = Some(use_cookies);
self
}
pub fn cookie_jar(mut self, jar: crate::CookieJar) -> Self {
self.config.cookie_jar = Some(jar);
self
}
pub fn http_proxy(mut self, host: impl Into<String>, port: u16) -> Self {
self.config.http_proxy = Some(ProxyConfig {
host: host.into(),
port,
username: None,
password: None,
});
self
}
pub fn proxy_auth(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
let username = Some(username.into());
let password = Some(password.into());
if let Some(ref mut proxy) = self.config.socks_proxy {
proxy.username = username.clone();
proxy.password = password.clone();
} else if let Some(ref mut proxy) = self.config.https_proxy {
proxy.username = username.clone();
proxy.password = password.clone();
} else if let Some(ref mut proxy) = self.config.http_proxy {
proxy.username = username;
proxy.password = password;
}
self
}
pub fn https_proxy(mut self, host: impl Into<String>, port: u16) -> Self {
self.config.https_proxy = Some(ProxyConfig {
host: host.into(),
port,
username: None,
password: None,
});
self
}
pub fn socks_proxy(mut self, host: impl Into<String>, port: u16) -> Self {
self.config.socks_proxy = Some(ProxyConfig {
host: host.into(),
port,
username: None,
password: None,
});
self
}
pub fn backend(mut self, backend_type: BackendType) -> Self {
self.backend_type = Some(backend_type);
self
}
pub fn build(self) -> crate::Result<Client> {
let backend = match self.backend_type {
Some(BackendType::Reqwest) => Backend::reqwest_with_config(self.config)?,
#[cfg(target_vendor = "apple")]
Some(BackendType::Foundation) => Backend::foundation_with_config(self.config)?,
#[cfg(windows)]
Some(BackendType::Windows) => Backend::windows_with_config(self.config)?,
None => {
#[cfg(target_vendor = "apple")]
{
Backend::foundation_with_config(self.config)?
}
#[cfg(not(target_vendor = "apple"))]
{
Backend::reqwest_with_config(self.config)?
}
}
};
Ok(Client { backend })
}
}
impl Default for ClientBuilder {
fn default() -> Self {
Self::new()
}
}