use std::time::Duration;
use reqwest::Certificate;
use crate::error::{Result, TreetopError};
use crate::token::UploadToken;
use super::inner::{BaseUrl, Client, CorrelationId, ResponseSizeLimit};
const DEFAULT_MAX_RESPONSE_BYTES: usize = 16 * 1024 * 1024;
pub struct ClientBuilder {
base_url: String,
connect_timeout: Duration,
request_timeout: Duration,
pool_idle_timeout: Option<Duration>,
pool_max_idle_per_host: Option<usize>,
upload_token: Option<UploadToken>,
correlation_id: Option<String>,
danger_accept_invalid_certs: bool,
root_certificates: Vec<Certificate>,
custom_client: Option<reqwest::Client>,
max_response_bytes: usize,
danger_allow_insecure_uploads: bool,
}
impl ClientBuilder {
pub fn new(base_url: impl Into<String>) -> Self {
Self {
base_url: base_url.into(),
connect_timeout: Duration::from_secs(5),
request_timeout: Duration::from_secs(30),
pool_idle_timeout: Some(Duration::from_secs(90)),
pool_max_idle_per_host: None,
upload_token: None,
correlation_id: None,
danger_accept_invalid_certs: false,
root_certificates: Vec::new(),
custom_client: None,
max_response_bytes: DEFAULT_MAX_RESPONSE_BYTES,
danger_allow_insecure_uploads: false,
}
}
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
pub fn request_timeout(mut self, timeout: Duration) -> Self {
self.request_timeout = timeout;
self
}
pub fn pool_idle_timeout(mut self, timeout: Duration) -> Self {
self.pool_idle_timeout = Some(timeout);
self
}
pub fn pool_max_idle_per_host(mut self, n: usize) -> Self {
self.pool_max_idle_per_host = Some(n);
self
}
pub fn upload_token(mut self, token: UploadToken) -> Self {
self.upload_token = Some(token);
self
}
pub fn correlation_id(mut self, id: impl Into<String>) -> Self {
self.correlation_id = Some(id.into());
self
}
pub fn danger_accept_invalid_certs(mut self, accept: bool) -> Self {
self.danger_accept_invalid_certs = accept;
self
}
pub fn add_root_certificate(mut self, cert: Certificate) -> Self {
self.root_certificates.push(cert);
self
}
pub fn with_reqwest_client(mut self, client: reqwest::Client) -> Self {
self.custom_client = Some(client);
self
}
pub fn max_response_bytes(mut self, max_response_bytes: usize) -> Self {
self.max_response_bytes = max_response_bytes;
self
}
pub fn danger_allow_insecure_uploads(mut self, allow: bool) -> Self {
self.danger_allow_insecure_uploads = allow;
self
}
pub fn build(self) -> Result<Client> {
let base_url = BaseUrl::parse(&self.base_url)?;
let correlation_id = self.correlation_id.map(CorrelationId::parse).transpose()?;
let max_response_bytes = ResponseSizeLimit::new(self.max_response_bytes)?;
if let Some(token) = &self.upload_token {
token.validate()?;
base_url.validate_upload_transport(self.danger_allow_insecure_uploads)?;
}
let http = if let Some(client) = self.custom_client {
client
} else {
let mut builder = reqwest::Client::builder()
.connect_timeout(self.connect_timeout)
.timeout(self.request_timeout)
.danger_accept_invalid_certs(self.danger_accept_invalid_certs)
.redirect(reqwest::redirect::Policy::none());
if let Some(idle_timeout) = self.pool_idle_timeout {
builder = builder.pool_idle_timeout(idle_timeout);
}
if let Some(max_idle) = self.pool_max_idle_per_host {
builder = builder.pool_max_idle_per_host(max_idle);
}
for cert in self.root_certificates {
builder = builder.add_root_certificate(cert);
}
builder.build().map_err(TreetopError::Transport)?
};
Ok(Client::new(
http,
base_url,
self.upload_token,
correlation_id,
max_response_bytes,
))
}
}