use std::fs as sfs;
use reqwest::{
Client,
header::{HeaderMap, HeaderName, HeaderValue},
redirect::Policy,
};
use crate::Arg;
pub(crate) fn build_client(arg: &Arg) -> anyhow::Result<Client> {
let mut builder = Client::builder();
let mut headers = HeaderMap::new();
for header in &arg.headers {
let parts = header.trim().split_once(':');
if let Some(parts) = parts {
headers.insert(
HeaderName::from_bytes(parts.0.as_bytes())?,
HeaderValue::from_str(parts.1)?,
);
}
}
if arg.disable_keep_alive {
headers.insert("Connection", HeaderValue::from_static("Close"));
}
builder = builder
.default_headers(headers)
.timeout(arg.timeout)
.connect_timeout(arg.timeout)
.danger_accept_invalid_certs(arg.insecure)
.danger_accept_invalid_hostnames(arg.insecure);
if let Some(cert) = &arg.cert
&& let Some(key) = &arg.key
{
let cert = sfs::read(cert)?;
let key = sfs::read(key)?;
let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?;
builder = builder.identity(pkcs8);
}
builder = builder.redirect(Policy::none());
match builder.build() {
Ok(client) => Ok(client),
Err(e) => Err(Box::new(e).into()),
}
}
#[cfg(test)]
mod tests {
use std::env;
use std::ffi::OsStr;
use std::path::Path;
use clap::{CommandFactory, FromArgMatches};
use super::*;
const URI: &str = "https://localhost/test";
const BINARY: &str = "rsb";
#[test]
fn test_build_client_success() {
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_ok());
}
#[test]
fn test_build_client_with_custom_headers_success() {
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", "--headers=k3:v3", URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_ok());
}
#[test]
fn test_build_client_with_invalid_custom_headers_success() {
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", "--headers=k3", URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_ok());
}
#[test]
fn test_build_client_disable_keep_alive() {
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", "-a", URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_ok());
}
#[test]
fn test_build_client_with_client_cert() {
let root = env::var("CARGO_MANIFEST_DIR").unwrap();
let tests = Path::new(OsStr::new(root.as_str()))
.join("resources")
.join("tests");
let binding = tests.join("client.pem");
let cert = binding.to_str().unwrap();
let binding = tests.join("client-key.pem");
let key = binding.to_str().unwrap();
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", "--cert", cert, "--key", key, URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_ok());
}
#[test]
fn test_build_client_with_invalid_client_cert() {
let cert = file!();
let key = file!();
let mut cmd = Arg::command();
let args = vec![BINARY, "-n", "20", "--cert", cert, "--key", key, URI];
let mut result = cmd.try_get_matches_from_mut(args);
assert!(result.as_ref().is_ok());
let arg = Arg::from_arg_matches_mut(result.as_mut().unwrap()).unwrap();
let client = build_client(&arg);
assert!(client.as_ref().is_err());
}
}