use core::{mem, fmt};
use core::convert::TryFrom;
use std::io::Write;
use crate::{header, utils};
use http::header::HeaderValue;
use bytes::BufMut;
pub mod tags;
pub mod multipart;
pub(crate) type HyperRequest = hyper::Request<hyper::Body>;
#[derive(Debug)]
pub struct Request {
pub(crate) parts: http::request::Parts,
pub(crate) body: Option<bytes::Bytes>,
}
impl Request {
pub fn new<U: AsRef<str>>(method: hyper::Method, uri: U) -> Result<Builder, http::uri::InvalidUri> {
let uri = uri.as_ref().parse::<hyper::Uri>()?;
Ok(Builder::new(uri, method))
}
pub fn head<U: AsRef<str>>(uri: U) -> Result<Builder, http::uri::InvalidUri> {
Self::new(hyper::Method::HEAD, uri)
}
pub fn get<U: AsRef<str>>(uri: U) -> Result<Builder, http::uri::InvalidUri> {
Self::new(hyper::Method::GET, uri)
}
pub fn post<U: AsRef<str>>(uri: U) -> Result<Builder, http::uri::InvalidUri> {
Self::new(hyper::Method::POST, uri)
}
pub fn put<U: AsRef<str>>(uri: U) -> Result<Builder, http::uri::InvalidUri> {
Self::new(hyper::Method::PUT, uri)
}
pub fn delete<U: AsRef<str>>(uri: U) -> Result<Builder, http::uri::InvalidUri> {
Self::new(hyper::Method::DELETE, uri)
}
#[inline]
pub fn method(&self) -> &http::Method {
&self.parts.method
}
#[inline]
pub fn method_mut(&mut self) -> &mut http::Method {
&mut self.parts.method
}
#[inline]
pub fn headers(&self) -> &http::HeaderMap {
&self.parts.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut http::HeaderMap {
&mut self.parts.headers
}
#[inline]
pub fn uri(&self) -> &http::Uri {
&self.parts.uri
}
#[inline]
pub fn uri_mut(&mut self) -> &mut http::Uri {
&mut self.parts.uri
}
#[inline]
pub fn extensions(&self) -> &http::Extensions {
&self.parts.extensions
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut http::Extensions {
&mut self.parts.extensions
}
#[inline]
pub fn extract_extensions(&mut self) -> http::Extensions {
let mut extensions = http::Extensions::new();
mem::swap(&mut extensions, self.extensions_mut());
extensions
}
}
impl Into<HyperRequest> for Request {
fn into(self) -> HyperRequest {
let body = self.body.map(|body| body.into()).unwrap_or_else(hyper::Body::empty);
HyperRequest::from_parts(self.parts, body)
}
}
pub struct Builder {
parts: http::request::Parts,
cookies: Option<cookie::CookieJar>,
}
impl Builder {
#[inline]
pub fn new(uri: hyper::Uri, method: hyper::Method) -> Self {
let mut temp = hyper::Request::<()>::new(());
mem::replace(temp.method_mut(), method);
mem::replace(temp.uri_mut(), uri);
let (parts, _) = temp.into_parts();
Self {
parts,
cookies: None
}
}
#[inline]
pub fn extensions(&self) -> &http::Extensions {
&self.parts.extensions
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut http::Extensions {
&mut self.parts.extensions
}
#[inline]
pub fn headers(&mut self) -> &mut http::HeaderMap {
&mut self.parts.headers
}
#[inline]
pub fn if_some<T, F: FnOnce(T, Self) -> Self>(self, value: Option<T>, cb: F) -> Self {
match value {
Some(value) => cb(value, self),
None => self,
}
}
#[inline]
pub fn set_header<K: header::IntoHeaderName, V>(mut self, key: K, value: V) -> Self where HeaderValue: TryFrom<V> {
let value = match HeaderValue::try_from(value) {
Ok(value) => value,
Err(_) => panic!("Attempt to set invalid header"),
};
let _ = self.headers().insert(key, value);
self
}
#[inline]
pub fn set_header_if_none<K: header::IntoHeaderName, V>(mut self, key: K, value: V) -> Self where HeaderValue: TryFrom<V> {
match self.headers().entry(key) {
http::header::Entry::Vacant(entry) => match HeaderValue::try_from(value) {
Ok(value) => {
entry.insert(value);
},
Err(_) => panic!("Attempt to set invalid header value")
},
_ => (),
}
self
}
pub fn set_etag<E: tags::EtagMode>(mut self, etag: &etag::EntityTag, _: E) -> Self {
let mut buffer = utils::BytesWriter::with_smol_capacity();
let _ = match self.headers().remove(E::header_name()) {
Some(old) => write!(&mut buffer, "{}, {}", old.to_str().expect("Invalid ETag!"), etag),
None => write!(&mut buffer, "{}", etag),
};
let value = unsafe { http::header::HeaderValue::from_maybe_shared_unchecked(buffer.freeze()) };
self.headers().insert(E::header_name(), value);
self
}
pub fn set_date<E: tags::DateMode>(mut self, date: httpdate::HttpDate, _: E) -> Self {
let mut buffer = utils::BytesWriter::with_smol_capacity();
let _ = write!(&mut buffer, "{}", date);
let value = unsafe { http::header::HeaderValue::from_maybe_shared_unchecked(buffer.freeze()) };
self.headers().insert(E::header_name(), value);
self
}
pub fn set_cookie_jar(mut self, jar: cookie::CookieJar) -> Self {
if self.cookies.is_none() {
self.cookies = Some(jar);
} else {
let self_jar = self.cookies.as_mut().unwrap();
for cookie in jar.iter().cloned() {
self_jar.add(cookie.into_owned());
}
}
self
}
pub fn add_cookie(mut self, cookie: cookie::Cookie<'static>) -> Self {
if self.cookies.is_none() {
let mut jar = cookie::CookieJar::new();
jar.add(cookie);
self.cookies = Some(jar);
} else {
self.cookies.as_mut().unwrap().add(cookie.into_owned());
}
self
}
#[inline]
pub fn content_len(self, len: u64) -> Self {
self.set_header(http::header::CONTENT_LENGTH, len)
}
#[inline]
pub fn accept_encoding(self, encoding: header::ContentEncoding) -> Self {
self.set_header(header::ACCEPT_ENCODING, encoding.as_str())
}
pub fn content_disposition(mut self, disp: &header::ContentDisposition) -> Self {
let mut buffer = utils::BytesWriter::with_smol_capacity();
let _ = write!(&mut buffer, "{}", disp);
let value = unsafe { http::header::HeaderValue::from_maybe_shared_unchecked(buffer.freeze()) };
self.headers().insert(header::CONTENT_DISPOSITION, value);
self
}
pub fn basic_auth<U: fmt::Display, P: fmt::Display>(mut self, username: U, password: Option<P>) -> Self {
const BASIC: &'static str = "Basic ";
let auth = match password {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username)
};
let encode_len = data_encoding::BASE64.encode_len(auth.as_bytes().len());
let header_value = unsafe {
let mut header_value = bytes::BytesMut::with_capacity(encode_len + BASIC.as_bytes().len());
header_value.put_slice(BASIC.as_bytes());
{
let dest = &mut *(&mut header_value.bytes_mut()[..encode_len] as *mut [core::mem::MaybeUninit<u8>] as *mut [u8]);
data_encoding::BASE64.encode_mut(auth.as_bytes(), dest);
}
header_value.advance_mut(encode_len);
http::header::HeaderValue::from_maybe_shared_unchecked(header_value.freeze())
};
let _ = self.headers().insert(http::header::AUTHORIZATION, header_value);
self
}
pub fn bearer_auth(mut self, token: &str) -> Self {
const TYPE: &'static str = "Bearer ";
let header_value = unsafe {
let mut header_value = bytes::BytesMut::with_capacity(token.as_bytes().len() + TYPE.as_bytes().len());
header_value.put_slice(TYPE.as_bytes());
header_value.put_slice(token.as_bytes());
http::header::HeaderValue::from_maybe_shared_unchecked(header_value.freeze())
};
let _ = self.headers().insert(http::header::AUTHORIZATION, header_value);
self
}
pub fn query<Q: serde::Serialize>(mut self, query: &Q) -> Self {
let mut uri_parts = self.parts.uri.into_parts();
let path = uri_parts.path_and_query;
let mut buffer = utils::BytesWriter::with_smol_capacity();
let query = serde_urlencoded::to_string(&query).expect("To url-encode");
let _ = match path {
Some(path) => write!(buffer, "{}?{}", path.path(), query),
None => write!(buffer, "?{}", query),
};
uri_parts.path_and_query = Some(http::uri::PathAndQuery::from_maybe_shared(buffer.into_inner().freeze()).expect("To create path and query"));
self.parts.uri = match http::Uri::from_parts(uri_parts) {
Ok(uri) => uri,
Err(error) => panic!("Unable to set query for URI: {}", error)
};
self
}
pub fn upgrade<U: crate::upgrade::Upgrade>(mut self, _: U, options: U::Options) -> Request {
U::prepare_request(&mut self.parts.headers, &mut self.parts.extensions, options);
self.empty()
}
pub fn body<B: Into<bytes::Bytes>>(mut self, body: Option<B>) -> Request {
use bytes::Buf;
use crate::utils::enc::USER_INFO_ENCODE_SET;
use percent_encoding::{utf8_percent_encode};
if let Some(jar) = self.cookies.take() {
let mut buffer = utils::BytesWriter::new();
for cook in jar.delta() {
let name = utf8_percent_encode(cook.name(), USER_INFO_ENCODE_SET);
let value = utf8_percent_encode(cook.value(), USER_INFO_ENCODE_SET);
let _ = write!(&mut buffer, "; {}={}", name, value);
}
let mut buffer = buffer.into_inner();
buffer.advance(2);
let cookie = unsafe { http::header::HeaderValue::from_maybe_shared_unchecked(buffer.freeze()) };
let _ = self.headers().insert(http::header::COOKIE, cookie);
}
let body = body.map(|body| body.into());
match body.as_ref() {
None => match self.parts.method {
hyper::Method::PUT | hyper::Method::POST => match self.parts.headers.entry(http::header::CONTENT_LENGTH) {
http::header::Entry::Vacant(value) => {
value.insert(utils::content_len_value(0));
},
_ => (),
},
_ => {
self.parts.headers.remove(http::header::CONTENT_LENGTH);
},
},
Some(body) => match self.parts.headers.entry(http::header::CONTENT_LENGTH) {
http::header::Entry::Vacant(value) => {
value.insert(utils::content_len_value(body.len() as u64));
},
_ => (),
},
}
Request {
parts: self.parts,
body,
}
}
pub fn form<F: serde::Serialize>(self, body: &F) -> Result<Request, serde_urlencoded::ser::Error> {
let body = serde_urlencoded::to_string(&body)?;
Ok(self.set_header_if_none(header::CONTENT_TYPE, "application/x-www-form-urlencoded").body(Some(body)))
}
pub fn json<J: serde::Serialize>(self, body: &J) -> serde_json::Result<Request> {
let mut buffer = utils::BytesWriter::new();
let _ = serde_json::to_writer(&mut buffer, &body)?;
let body = buffer.into_inner().freeze();
Ok(self.set_header_if_none(header::CONTENT_TYPE, "application/json").body(Some(body)))
}
pub fn multipart(self, body: multipart::Form) -> Request {
let mut content_type = utils::BytesWriter::with_capacity(30 + body.boundary.len());
let _ = write!(&mut content_type, "multipart/form-data; boundary={}", body.boundary);
let content_type = unsafe { http::header::HeaderValue::from_maybe_shared_unchecked(content_type.freeze()) };
let (_, body) = body.finish();
self.set_header_if_none(header::CONTENT_TYPE, content_type).body(Some(body))
}
pub fn empty(self) -> Request {
self.body::<bytes::Bytes>(None)
}
}