use std::time::Instant;
use reqwest::{self, Method, Version};
use tracing::{error, instrument};
use super::utils::{parse_version, reqwest_to_ferox_error};
use super::{BlockingRequests, HttpClient};
use crate::error::FeroxFuzzError;
use crate::requests::Request;
use crate::responses::BlockingResponse;
use crate::std_ext::convert::{AsInner, IntoInner};
#[derive(Clone, Default, Debug)]
pub struct BlockingClient {
client: reqwest::blocking::Client,
}
impl HttpClient for BlockingClient {
type ClientType = reqwest::blocking::Client;
fn with_client(client: Self::ClientType) -> Self {
Self { client }
}
}
impl BlockingRequests for BlockingClient {
#[instrument(skip_all, level = "trace")]
fn send(&self, request: Request) -> Result<BlockingResponse, FeroxFuzzError> {
let parsed_version = parse_version(request.version())?;
let reqwest_request = self.build_request(parsed_version, request.clone())?;
let now = Instant::now();
let reqwest_response = self
.client
.execute(reqwest_request)
.map_err(reqwest_to_ferox_error)?;
let response =
BlockingResponse::try_from_reqwest_response(request, reqwest_response, now.elapsed())?;
Ok(response)
}
}
impl BlockingClient {
#[instrument(skip_all, level = "trace")]
fn build_request(
&self,
version: Version,
request: Request,
) -> Result<reqwest::blocking::Request, FeroxFuzzError> {
let method = Method::from_bytes(request.method().inner()).map_err(|source| {
error!(
method = format!("{}", request.method()),
%source,
"could not parse the given http method; must be a valid http method when using a reqwest client"
);
FeroxFuzzError::MethodParseError {
method: format!("{}", request.method()),
}
})?;
let mut builder = if request.url_is_fuzzable() {
self.inner().request(method, request.url_to_string()?)
} else {
self.inner().request(method, request.original_url())
};
builder = builder.timeout(request.timeout).version(version);
if let Some(data_body) = request.body {
builder = builder.body(data_body.into_inner());
}
if let Some(headers) = request.headers {
for (key, value) in headers {
builder = builder.header(key.into_inner(), value.into_inner());
}
}
if let Some(user_agent) = request.user_agent {
builder = builder.header("User-Agent", user_agent.into_inner());
}
let reqwest_request = builder.build()?;
Ok(reqwest_request)
}
}
impl AsInner for BlockingClient {
type Type = reqwest::blocking::Client;
fn inner(&self) -> &Self::Type {
&self.client
}
}