use std::sync::Arc;
use std::time::Duration;
use crate::error::Result;
use crate::net::stream::NetStream;
use crate::net::{connector_from_proxy_url, Connector, DirectConnector};
use crate::url::Url;
pub(crate) struct NetConfig {
pub(crate) connector: Arc<dyn Connector>,
pub(crate) connect_timeout: Option<Duration>,
pub(crate) verify: bool,
pub(crate) ftp_use_epsv: bool,
pub(crate) ftp_create_dirs: bool,
pub(crate) ftp_active: bool,
}
impl Default for NetConfig {
fn default() -> Self {
NetConfig {
connector: Arc::new(DirectConnector),
connect_timeout: Some(Duration::from_secs(30)),
verify: true,
ftp_use_epsv: true,
ftp_create_dirs: false,
ftp_active: false,
}
}
}
impl NetConfig {
pub(crate) fn connect(&self, host: &str, port: u16) -> Result<Box<dyn NetStream>> {
self.connector.connect(host, port, self.connect_timeout)
}
}
#[derive(Clone)]
pub struct Client {
connector: Arc<dyn Connector>,
connect_timeout: Option<Duration>,
read_timeout: Option<Duration>,
verify: bool,
idn: bool,
no_proxy: Vec<String>,
ftp_use_epsv: bool,
ftp_create_dirs: bool,
ftp_active: bool,
}
impl Default for Client {
fn default() -> Self {
Client {
connector: Arc::new(DirectConnector),
connect_timeout: Some(Duration::from_secs(30)),
read_timeout: Some(Duration::from_secs(60)),
verify: true,
idn: true,
no_proxy: Vec::new(),
ftp_use_epsv: true,
ftp_create_dirs: false,
ftp_active: false,
}
}
}
impl Client {
pub fn new() -> Self {
Self::default()
}
pub fn proxy(mut self, spec: &str) -> Result<Self> {
self.connector = connector_from_proxy_url(spec)?;
Ok(self)
}
pub fn connector(mut self, connector: Arc<dyn Connector>) -> Self {
self.connector = connector;
self
}
pub fn connect_timeout(mut self, d: Option<Duration>) -> Self {
self.connect_timeout = d;
self
}
pub fn read_timeout(mut self, d: Option<Duration>) -> Self {
self.read_timeout = d;
self
}
pub fn verify_tls(mut self, on: bool) -> Self {
self.verify = on;
self
}
pub fn idn(mut self, on: bool) -> Self {
self.idn = on;
self
}
pub fn ftp_use_epsv(mut self, on: bool) -> Self {
self.ftp_use_epsv = on;
self
}
pub fn ftp_create_dirs(mut self, on: bool) -> Self {
self.ftp_create_dirs = on;
self
}
pub fn ftp_active(mut self, on: bool) -> Self {
self.ftp_active = on;
self
}
pub fn no_proxy<I, S>(mut self, entries: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.no_proxy = entries.into_iter().map(Into::into).collect();
self
}
fn host_bypassed(&self, host: &str) -> bool {
let host = host.to_ascii_lowercase();
self.no_proxy.iter().any(|e| {
let e = e.trim().to_ascii_lowercase();
e == "*" || host == e || host.ends_with(&format!(".{e}"))
})
}
fn effective_connector(&self, host: &str) -> Arc<dyn Connector> {
if self.host_bypassed(host) {
Arc::new(DirectConnector)
} else {
self.connector.clone()
}
}
fn net_config_for(&self, host: &str) -> NetConfig {
NetConfig {
connector: self.effective_connector(host),
connect_timeout: self.connect_timeout,
verify: self.verify,
ftp_use_epsv: self.ftp_use_epsv,
ftp_create_dirs: self.ftp_create_dirs,
ftp_active: self.ftp_active,
}
}
pub fn request(&self, method: &str, url: &str) -> Result<crate::Request> {
let mut r = crate::Request::new(method, url)?
.verify_tls(self.verify)
.idn(self.idn);
let host = r.url().host.clone();
r = r
.connector(self.effective_connector(&host))
.read_timeout(self.read_timeout);
if let Some(t) = self.connect_timeout {
r = r.connect_timeout(t);
}
Ok(r)
}
pub fn get(&self, url: &str) -> Result<crate::Response> {
self.request("GET", url)?.send()
}
pub fn transfer(&self, url_str: &str) -> Result<Vec<u8>> {
let mut url = Url::parse(url_str)?;
url.set_idn(self.idn)?;
self.transfer_url(&url)
}
pub fn transfer_url(&self, url: &Url) -> Result<Vec<u8>> {
crate::transfer::transfer_url_with(url, &self.net_config_for(&url.host))
}
pub fn transfer_url_to(&self, url: &Url, sink: &mut dyn std::io::Write) -> Result<u64> {
crate::transfer::transfer_url_to_with(url, &self.net_config_for(&url.host), sink)
}
pub fn ftp_store(&self, url: &Url, body: &[u8], resume_at: Option<u64>) -> Result<()> {
crate::ftp::store_with(url, body, resume_at, &self.net_config_for(&url.host))
}
pub fn ftp_append(&self, url: &Url, body: &[u8]) -> Result<()> {
crate::ftp::append_with(url, body, &self.net_config_for(&url.host))
}
pub fn smtp_send(
&self,
url: &Url,
body: &[u8],
from: &str,
rcpts: &[String],
user: Option<&str>,
pass: Option<&str>,
) -> Result<()> {
let opts = crate::smtp::SmtpOptions {
from,
rcpts,
user,
pass,
};
crate::smtp::send(url, body, &opts, &self.net_config_for(&url.host))
}
pub fn telnet(&self, url: &Url, input: &[u8]) -> Result<Vec<u8>> {
crate::telnet::run(url, input, &self.net_config_for(&url.host))
}
}
#[cfg(test)]
mod tests {
use super::Client;
#[test]
fn client_is_send_sync_and_clone() {
fn assert_send_sync<T: Send + Sync + Clone>() {}
assert_send_sync::<Client>();
}
}