use self::internal::SetOpt;
use crate::auth::{Authentication, Credentials};
use curl::easy::Easy2;
use std::{
iter::FromIterator,
net::{IpAddr, SocketAddr},
time::Duration,
};
pub(crate) mod dns;
pub(crate) mod internal;
pub(crate) mod proxy;
pub(crate) mod redirect;
pub(crate) mod ssl;
pub use dns::{DnsCache, ResolveMap};
pub use redirect::RedirectPolicy;
pub use ssl::{CaCertificate, ClientCertificate, PrivateKey, SslOption};
pub trait Configurable: internal::ConfigurableBase {
fn timeout(self, timeout: Duration) -> Self {
self.configure(Timeout(timeout))
}
fn connect_timeout(self, timeout: Duration) -> Self {
self.configure(ConnectTimeout(timeout))
}
fn version_negotiation(self, negotiation: VersionNegotiation) -> Self {
self.configure(negotiation)
}
fn redirect_policy(self, policy: RedirectPolicy) -> Self {
self.configure(policy)
}
fn auto_referer(self) -> Self {
self.configure(redirect::AutoReferer)
}
fn authentication(self, authentication: Authentication) -> Self {
self.configure(authentication)
}
fn credentials(self, credentials: Credentials) -> Self {
self.configure(credentials)
}
fn tcp_keepalive(self, interval: Duration) -> Self {
self.configure(TcpKeepAlive(interval))
}
fn tcp_nodelay(self) -> Self {
self.configure(TcpNoDelay)
}
fn interface(self, interface: impl Into<NetworkInterface>) -> Self {
self.configure(interface.into())
}
fn proxy(self, proxy: impl Into<Option<http::Uri>>) -> Self {
self.configure(proxy::Proxy(proxy.into()))
}
fn proxy_blacklist<I, T>(self, hosts: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<String>,
{
self.configure(proxy::Blacklist::from_iter(hosts.into_iter().map(T::into)))
}
fn proxy_authentication(self, authentication: Authentication) -> Self {
self.configure(proxy::Proxy(authentication))
}
fn proxy_credentials(self, credentials: Credentials) -> Self {
self.configure(proxy::Proxy(credentials))
}
fn max_upload_speed(self, max: u64) -> Self {
self.configure(MaxUploadSpeed(max))
}
fn max_download_speed(self, max: u64) -> Self {
self.configure(MaxDownloadSpeed(max))
}
fn dns_servers<I, T>(self, servers: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<SocketAddr>,
{
self.configure(dns::Servers::from_iter(servers.into_iter().map(T::into)))
}
fn ssl_client_certificate(self, certificate: ClientCertificate) -> Self {
self.configure(certificate)
}
fn ssl_ca_certificate(self, certificate: CaCertificate) -> Self {
self.configure(certificate)
}
fn ssl_ciphers<I, T>(self, servers: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<String>,
{
self.configure(ssl::Ciphers::from_iter(servers.into_iter().map(T::into)))
}
fn ssl_options(self, options: SslOption) -> Self {
self.configure(options)
}
fn metrics(self, enable: bool) -> Self {
self.configure(EnableMetrics(enable))
}
}
impl SetOpt for http::HeaderMap {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
let mut headers = curl::easy::List::new();
for (name, value) in self.iter() {
let header = format!("{}: {}", name.as_str(), value.to_str().unwrap());
headers.append(&header)?;
}
easy.http_headers(headers)
}
}
#[derive(Clone, Debug)]
pub struct VersionNegotiation {
flag: curl::easy::HttpVersion,
strict: bool,
}
impl Default for VersionNegotiation {
fn default() -> Self {
Self::latest_compatible()
}
}
impl VersionNegotiation {
pub const fn latest_compatible() -> Self {
Self {
flag: curl::easy::HttpVersion::V2TLS,
strict: false,
}
}
pub const fn http10() -> Self {
Self {
flag: curl::easy::HttpVersion::V10,
strict: true,
}
}
pub const fn http11() -> Self {
Self {
flag: curl::easy::HttpVersion::V11,
strict: true,
}
}
pub const fn http2() -> Self {
Self {
flag: curl::easy::HttpVersion::V2PriorKnowledge,
strict: true,
}
}
}
impl SetOpt for VersionNegotiation {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
if let Err(e) = easy.http_version(self.flag) {
if self.strict {
return Err(e);
} else {
log::debug!("failed to set HTTP version: {}", e);
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct NetworkInterface {
interface: Option<String>,
}
impl NetworkInterface {
pub fn any() -> Self {
Self { interface: None }
}
#[cfg(unix)]
pub fn name(name: impl AsRef<str>) -> Self {
Self {
interface: Some(format!("if!{}", name.as_ref())),
}
}
pub fn host(host: impl AsRef<str>) -> Self {
Self {
interface: Some(format!("host!{}", host.as_ref())),
}
}
}
impl Default for NetworkInterface {
fn default() -> Self {
Self::any()
}
}
impl From<IpAddr> for NetworkInterface {
fn from(ip: IpAddr) -> Self {
Self {
interface: Some(format!("host!{}", ip)),
}
}
}
impl SetOpt for NetworkInterface {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
#[allow(unsafe_code)]
match self.interface.as_ref() {
Some(interface) => easy.interface(interface),
None => unsafe {
match curl_sys::curl_easy_setopt(easy.raw(), curl_sys::CURLOPT_INTERFACE, 0) {
curl_sys::CURLE_OK => Ok(()),
code => Err(curl::Error::new(code)),
}
},
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct Timeout(pub(crate) Duration);
impl SetOpt for Timeout {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.timeout(self.0)
}
}
#[derive(Clone, Debug)]
pub(crate) struct ConnectTimeout(pub(crate) Duration);
impl SetOpt for ConnectTimeout {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.connect_timeout(self.0)
}
}
#[derive(Clone, Debug)]
pub(crate) struct TcpKeepAlive(pub(crate) Duration);
impl SetOpt for TcpKeepAlive {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.tcp_keepalive(true)?;
easy.tcp_keepintvl(self.0)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct TcpNoDelay;
impl SetOpt for TcpNoDelay {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.tcp_nodelay(true)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MaxUploadSpeed(pub(crate) u64);
impl SetOpt for MaxUploadSpeed {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.max_send_speed(self.0)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MaxDownloadSpeed(pub(crate) u64);
impl SetOpt for MaxDownloadSpeed {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.max_recv_speed(self.0)
}
}
#[derive(Clone, Debug)]
pub(crate) struct CloseConnection(pub(crate) bool);
impl SetOpt for CloseConnection {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.forbid_reuse(self.0)
}
}
#[derive(Clone, Debug)]
pub(crate) struct EnableMetrics(pub(crate) bool);
impl SetOpt for EnableMetrics {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.progress(self.0)
}
}