use std::convert::TryInto;
use reqwest::Identity;
use std::fs::File;
use std::io::Read;
use std::time::Duration;
use thiserror::Error;
use url::Url;
use crate::ddi::poll;
#[derive(Debug, Clone)]
pub struct Client {
base_url: Url,
client: reqwest::Client,
}
#[derive(Debug, Clone)]
pub enum ClientAuthorization {
TargetToken(String),
GatewayToken(String),
None,
}
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum Error {
#[error("Could not parse url")]
ParseUrlError(#[from] url::ParseError),
#[error("Invalid token format")]
InvalidToken(#[from] reqwest::header::InvalidHeaderValue),
#[error(transparent)]
ReqwestError(#[from] reqwest::Error),
#[error("Failed to parse polling sleep")]
InvalidSleep,
#[error("IO error {0}")]
Io(#[from] std::io::Error),
#[cfg(feature = "hash-digest")]
#[error("Invalid Checksum")]
ChecksumError(crate::ddi::deployment_base::ChecksumType),
}
impl Client {
pub fn new(
url: &str,
tenant: &str,
controller_id: &str,
authorization: ClientAuthorization,
server_cert: Option<&str>,
client_cert: Option<&str>,
timeout: Option<Duration>,
) -> Result<Self, Error> {
let host: Url = url.parse()?;
let path = format!("{}/controller/v1/{}", tenant, controller_id);
let base_url = host.join(&path)?;
let mut client_builder = reqwest::Client::builder();
let mut headers = reqwest::header::HeaderMap::new();
match authorization {
ClientAuthorization::TargetToken(key_token) => {
headers.insert(
reqwest::header::AUTHORIZATION,
format!("TargetToken {}", &key_token).try_into()?,
);
}
ClientAuthorization::GatewayToken(key_token) => {
headers.insert(
reqwest::header::AUTHORIZATION,
format!("GatewayToken {}", &key_token).try_into()?,
);
}
ClientAuthorization::None => {
}
}
if let Some(cert_file) = client_cert {
let mut buf = Vec::new();
File::open(cert_file)?.read_to_end(&mut buf)?;
let identity = Identity::from_pem(&buf)?;
client_builder = client_builder.identity(identity);
}
if let Some(cert_file) = server_cert {
let mut buf = Vec::new();
File::open(cert_file)?.read_to_end(&mut buf)?;
if cert_file.ends_with(".crt") {
let certs = reqwest::Certificate::from_pem_bundle(&buf)?;
for cert in certs {
client_builder = client_builder.add_root_certificate(cert);
}
} else {
let cert = reqwest::Certificate::from_pem(&buf)?;
client_builder = client_builder.add_root_certificate(cert);
}
client_builder = client_builder
.tls_built_in_root_certs(false)
.https_only(true);
}
if let Some(timeout) = timeout {
client_builder = client_builder
.connect_timeout(timeout)
.read_timeout(timeout);
}
let client = client_builder
.default_headers(headers)
.connection_verbose(true)
.build()?;
Ok(Self { base_url, client })
}
pub async fn poll(&self) -> Result<poll::Reply, Error> {
let reply = self.client.get(self.base_url.clone()).send().await?;
reply.error_for_status_ref()?;
let reply = reply.json::<poll::ReplyInternal>().await?;
Ok(poll::Reply::new(reply, self.client.clone()))
}
}