use crate::{client, debug, error, response, types, utils};
use http_types::headers;
use http_types::headers::ToHeaderValues;
use std::{collections, fmt, time};
#[cfg(not(feature = "picodata_tarantool"))]
use tarantool::fiber::{self};
#[cfg(feature = "picodata_tarantool")]
use picodata_tarantool::system::tarantool::fiber::{self};
#[derive(Debug)]
#[allow(clippy::struct_field_names)]
pub struct Request {
body: Option<http_types::Body>,
headers: types::Headers,
inner: http_types::Request,
tls_timeout: Option<time::Duration>,
request_timeout: Option<time::Duration>,
response_timeout: Option<time::Duration>,
}
impl debug::BodyWrapper for Request {
fn body_back(&mut self, body: Vec<u8>) {
self.body = Some(body.into());
}
}
impl Request {
pub fn new(method: http_types::Method, url: http_types::Url) -> Self {
let headers = collections::HashMap::from([
(
headers::HOST,
url.host()
.unwrap()
.to_string()
.to_header_values()
.unwrap()
.collect::<headers::HeaderValues>(),
),
(
headers::USER_AGENT,
"fibreq/1.0"
.to_header_values()
.unwrap()
.collect::<headers::HeaderValues>(),
),
(
headers::ACCEPT,
"*/*"
.to_header_values()
.unwrap()
.collect::<headers::HeaderValues>(),
),
(
headers::CONNECTION,
"Keep-Alive"
.to_header_values()
.unwrap()
.collect::<headers::HeaderValues>(),
),
]);
Self {
headers,
body: None,
tls_timeout: None,
request_timeout: None,
response_timeout: None,
inner: http_types::Request::new(method, url),
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn pieces(
self,
) -> (
http_types::Request,
types::Headers,
Option<http_types::Body>,
time::Duration,
time::Duration,
time::Duration,
) {
(
self.inner,
self.headers,
self.body,
self.tls_timeout.unwrap_or(time::Duration::from_secs(30)),
self.request_timeout
.unwrap_or(time::Duration::from_secs(30)),
self.response_timeout
.unwrap_or(time::Duration::from_secs(30)),
)
}
pub fn url(&self) -> &http_types::Url {
self.inner.url()
}
#[inline]
pub fn headers(&self) -> &types::Headers {
&self.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut types::Headers {
&mut self.headers
}
#[inline]
pub fn body(&self) -> Option<&http_types::Body> {
self.body.as_ref()
}
#[inline]
pub fn body_mut(&mut self) -> Option<&mut http_types::Body> {
self.body.as_mut()
}
#[inline]
pub fn wrapped_body(&mut self) -> debug::WrappedBody<'_, Request> {
match self.body.take() {
Some(body) => {
let b =
fiber::block_on(body.into_bytes()).map_err(|e| Box::new(error::Error::HTTP(e)));
debug::WrappedBody::new(self, Some(b))
}
None => debug::WrappedBody::new(self, None),
}
}
}
#[derive(Debug)]
pub struct Builder {
request: Request,
client: client::Client,
}
impl Builder {
pub(crate) fn new(request: Request, client: client::Client) -> Self {
Self { request, client }
}
#[allow(clippy::needless_pass_by_value)]
pub fn header<K, V>(self, key: K, value: V) -> Result<Self, Box<error::Error>>
where
K: Into<headers::HeaderName>,
V: ToHeaderValues,
{
let Self {
mut request,
client,
} = self;
let value = value
.to_header_values()
.map_err(Box::new(error::Error::HTTP))?
.collect::<headers::HeaderValues>();
request.headers.insert(key.into(), value);
Ok(Self { request, client })
}
pub fn headers<K, V>(
self,
headers: collections::HashMap<K, V>,
) -> Result<Self, Box<error::Error>>
where
K: Into<headers::HeaderName>,
V: ToHeaderValues,
{
let Self {
mut request,
client,
} = self;
for (key, value) in headers {
request.headers.insert(
key.into(),
value
.to_header_values()
.map_err(Box::new(error::Error::HTTP))?
.collect::<headers::HeaderValues>(),
);
}
Ok(Self { request, client })
}
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
where
U: fmt::Display,
P: fmt::Display,
{
self.header(
headers::AUTHORIZATION,
utils::basic_auth(username, password),
)
.expect("Basic auth header cannot fail.")
}
pub fn bearer_auth<T>(self, token: T) -> Self
where
T: fmt::Display,
{
self.header(headers::AUTHORIZATION, format!("Bearer {token}"))
.expect("Bearer auth header cannot fail.")
}
pub fn body<T: Into<http_types::Body>>(self, body: T) -> Self {
let Self {
mut request,
client,
} = self;
request.body = Some(body.into());
Self { request, client }
}
pub fn query<T: serde::Serialize>(self, query: &T) -> Result<Self, Box<error::Error>> {
let Self {
mut request,
client,
} = self;
request
.inner
.set_query(query)
.map_err(Box::new(error::Error::HTTP))?;
Ok(Self { request, client })
}
pub fn json<T: serde::Serialize>(self, json: &T) -> Result<Self, Box<error::Error>> {
let builder = self
.header(headers::CONTENT_TYPE, "application/json")
.expect("Json header cannot fail.");
Ok(builder.body(serde_json::to_vec(json).map_err(|e| Box::new(error::Error::JSON(e)))?))
}
pub fn tls_timeout(self, timeout: time::Duration) -> Self {
let Self {
mut request,
client,
} = self;
request.tls_timeout = Some(timeout);
Self { request, client }
}
pub fn request_timeout(self, timeout: time::Duration) -> Self {
let Self {
mut request,
client,
} = self;
request.request_timeout = Some(timeout);
Self { request, client }
}
pub fn response_timeout(self, timeout: time::Duration) -> Self {
let Self {
mut request,
client,
} = self;
request.response_timeout = Some(timeout);
Self { request, client }
}
pub fn build(self) -> Request {
self.request
}
pub fn send(self) -> Result<response::Response, Box<error::Error>> {
self.client.execute(self.request)
}
}