#[cfg(feature = "multipart")]
use crate::multipart::FilePart;
use crate::{
body::HttpBody,
error::Error,
responder::{Form, FromBytes, Json, Query},
serde_request::{from_str_map, from_str_multi_map, from_str_multi_val, from_str_val},
Result,
};
#[cfg(feature = "cookie")]
use cookie::{Cookie, CookieJar};
use headers::{Header, HeaderMapExt};
use http_body_util::BodyExt;
use hyper::{body::Bytes, header::AsHeaderName};
use multer;
use multimap::MultiMap;
use once_cell::sync::OnceCell;
use std::{
any::Any,
collections::HashMap,
net::SocketAddr,
ops::{Deref, DerefMut},
};
#[derive(Debug)]
pub struct Request {
inner: hyper::http::Request<HttpBody>,
params: HashMap<String, String>,
queries: OnceCell<MultiMap<String, String>>,
form_data: tokio::sync::OnceCell<MultiMap<String, String>>,
#[cfg(feature = "multipart")]
file_part: tokio::sync::OnceCell<MultiMap<String, FilePart>>,
#[cfg(feature = "cookie")]
cookies: CookieJar,
addr: SocketAddr,
depot: HashMap<String, Box<dyn Any + Send + Sync>>,
}
impl Request {
#[inline]
pub(crate) fn new(inner: hyper::http::Request<HttpBody>, addr: SocketAddr) -> Self {
#[cfg(feature = "cookie")]
let cookies = if let Some(cookie_iter) = inner
.headers()
.get("Cookie")
.and_then(|cookies| cookies.to_str().ok())
.map(|cookies_str| cookies_str.split(';').map(|s| s.trim()))
.map(|cookie_iter| {
cookie_iter.filter_map(|cookie_s| Cookie::parse_encoded(cookie_s.to_string()).ok())
}) {
let mut jar = CookieJar::new();
cookie_iter.for_each(|c| jar.add_original(c));
jar
} else {
CookieJar::new()
};
Request {
inner,
params: HashMap::new(),
queries: OnceCell::new(),
form_data: tokio::sync::OnceCell::new(),
#[cfg(feature = "multipart")]
file_part: tokio::sync::OnceCell::new(),
addr,
#[cfg(feature = "cookie")]
cookies,
depot: HashMap::new(),
}
}
#[inline]
pub fn request(&mut self) -> &mut hyper::Request<HttpBody> {
&mut self.inner
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookie(&self, name: &str) -> Option<&Cookie<'static>> {
self.cookies.get(name)
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookies(&self) -> &CookieJar {
&self.cookies
}
#[cfg(feature = "cookie")]
#[inline]
pub fn cookies_mut(&mut self) -> &mut CookieJar {
&mut self.cookies
}
#[cfg(feature = "cookie")]
#[inline]
pub fn parse_cookies<'de, T>(&'de self) -> Result<T>
where
T: serde::Deserialize<'de>,
{
let iter = self.cookies().iter().map(|c| c.name_value());
from_str_map(iter).map_err(Error::Deserialize)
}
#[inline]
pub fn set<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: Into<String>,
V: Any + Send + Sync,
{
self.depot.insert(key.into(), Box::new(value));
self
}
#[inline]
pub fn get<V: Any + Send + Sync>(&self, key: &str) -> Option<&V> {
if let Some(value) = self.depot.get(key) {
value.downcast_ref::<V>()
} else {
None
}
}
#[inline]
pub fn get_mut<V: Any + Send + Sync>(&mut self, key: &str) -> Option<&mut V> {
if let Some(value) = self.depot.get_mut(key) {
value.downcast_mut::<V>()
} else {
None
}
}
#[inline]
pub fn remove<V: Any + Send + Sync>(
&mut self,
key: &str,
) -> Result<V, Option<Box<dyn Any + Send + Sync>>> {
if let Some(value) = self.depot.remove(key) {
value.downcast::<V>().map(|b| *b).map_err(Some)
} else {
Err(None)
}
}
#[inline]
pub fn accept(&self) -> Vec<mime::Mime> {
let mut list: Vec<mime::Mime> = vec![];
if let Some(accept) = self
.inner
.headers()
.get("accept")
.and_then(|h| h.to_str().ok())
{
let parts: Vec<&str> = accept.split(',').collect();
for part in parts {
if let Ok(mt) = part.parse() {
list.push(mt);
}
}
}
list
}
#[inline]
pub fn first_accept(&self) -> Option<mime::Mime> {
let mut accept = self.accept();
if !accept.is_empty() {
Some(accept.remove(0))
} else {
None
}
}
#[inline]
pub fn header_typed_get<T: Header>(&self) -> Option<T> {
self.inner.headers().typed_get()
}
#[inline]
pub fn header<'de, T>(&'de self, key: impl AsHeaderName) -> Option<T>
where
T: serde::Deserialize<'de>,
{
let values = self
.inner
.headers()
.get_all(key)
.iter()
.filter_map(|v| v.to_str().ok())
.collect::<Vec<_>>();
from_str_multi_val(values).ok()
}
#[inline]
pub fn parse_header<'de, T>(&'de self) -> Result<T>
where
T: serde::Deserialize<'de>,
{
let iter = self
.inner
.headers()
.iter()
.map(|(k, v)| (k.as_str(), v.to_str().unwrap_or_default()));
from_str_map(iter).map_err(Error::Deserialize)
}
#[inline]
pub fn remote_addr(self) -> SocketAddr {
self.addr
}
#[inline]
pub fn params(&self) -> &HashMap<String, String> {
&self.params
}
#[inline]
pub fn params_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.params
}
#[inline]
pub fn param<'de, T>(&'de self, key: &str) -> Option<T>
where
T: serde::Deserialize<'de>,
{
self.params.get(key).and_then(|v| from_str_val(v).ok())
}
#[inline]
pub fn parse_param<'de, T>(&'de self) -> Result<T>
where
T: serde::Deserialize<'de>,
{
let params = self.params().iter();
from_str_map(params).map_err(Error::Deserialize)
}
#[inline]
fn queries(&self) -> &MultiMap<String, String> {
self.queries.get_or_init(|| {
form_urlencoded::parse(self.inner.uri().query().unwrap_or("").as_bytes())
.into_owned()
.collect()
})
}
#[inline]
pub fn query<'de, T>(&'de self, key: &str) -> Option<T>
where
T: serde::Deserialize<'de>,
{
self.queries()
.get_vec(key)
.and_then(|vs| from_str_multi_val(vs).ok())
}
#[inline]
pub fn parse_query<'de, T>(&'de self) -> Result<T>
where
T: serde::Deserialize<'de>,
{
let query = self.inner.uri().query().unwrap_or("");
serde_urlencoded::from_str::<T>(query).map_err(Error::Deserialize)
}
#[inline]
pub fn content_type(&self) -> Option<mime::Mime> {
self.inner
.headers()
.get("content-type")
.and_then(|h| h.to_str().ok())
.and_then(|v| v.parse().ok())
}
#[inline]
pub async fn body_bytes(&mut self) -> Result<Bytes> {
Ok(self.inner.body_mut().collect().await?.to_bytes())
}
#[inline]
async fn form_data(&mut self) -> Result<&MultiMap<String, String>> {
let body = std::mem::replace(self.body_mut(), HttpBody::Empty);
let ctype = self
.inner
.headers()
.get("content-type")
.and_then(|h| h.to_str().ok());
self.form_data
.get_or_try_init(|| async {
async {
let mut fields = MultiMap::<String, String>::new();
match ctype {
Some(ctype) if ctype == "application/x-www-form-urlencoded" => {
let data = BodyExt::collect(body).await?.to_bytes();
fields = form_urlencoded::parse(&data).into_owned().collect();
}
Some(ctype) if ctype.starts_with("multipart/form-data") => {
let boundary = multer::parse_boundary(ctype)?;
let mut multipart = multer::Multipart::new(body, boundary);
while let Some(field) = multipart.next_field().await? {
if let Some(name) = field.name().map(|s| s.to_owned()) {
if field.headers().get("content-type").is_none() {
fields.insert(name, field.text().await?);
}
}
}
}
_ => {}
}
Ok(fields)
}
.await
})
.await
}
#[inline]
pub async fn form<'de, T>(&'de mut self, key: &str) -> Option<T>
where
T: serde::Deserialize<'de>,
{
self.form_data()
.await
.ok()
.and_then(|ps| ps.get_vec(key))
.and_then(|vs| from_str_multi_val(vs).ok())
}
#[cfg(feature = "multipart")]
#[inline]
async fn file_part(&mut self) -> Result<&MultiMap<String, FilePart>> {
self.file_part
.get_or_try_init(|| async {
async {
if let Some(c_type) = self
.inner
.headers()
.get("content-type")
.and_then(|h| h.to_str().ok())
{
let boundary = multer::parse_boundary(c_type)?;
let mut multipart = multer::Multipart::new(
std::mem::take(self.inner.body_mut()),
&boundary,
);
let mut file_parts = MultiMap::new();
while let Some(mut field) = multipart.next_field().await? {
if let Some(name) = field.name().map(|s| s.to_owned()) {
if field.headers().get("content-type").is_some() {
file_parts.insert(name, FilePart::new(&mut field).await?);
}
}
}
Ok(file_parts)
} else {
Err(Error::Other(String::from("Invalid request Context-Type")))
}
}
.await
})
.await
}
#[cfg(feature = "multipart")]
#[inline]
pub async fn file<'a>(&'a mut self, key: &'a str) -> Option<&'a FilePart> {
self.file_part().await.ok().and_then(|ps| ps.get(key))
}
#[cfg(feature = "multipart")]
#[inline]
pub async fn files<'a>(&'a mut self, key: &'a str) -> Option<&'a Vec<FilePart>> {
self.file_part().await.ok().and_then(|ps| ps.get_vec(key))
}
#[cfg(feature = "multipart")]
#[inline]
pub async fn upload(&mut self, key: &str, save_path: &str) -> Result<u64> {
if let Some(file) = self.file(key).await {
std::fs::create_dir_all(save_path)?;
let dest = format!("{}/{}", save_path, file.name().unwrap());
Ok(std::fs::copy(file.path(), std::path::Path::new(&dest))?)
} else {
Err(Error::Other(String::from(
"File not resolved from current request",
)))
}
}
#[cfg(feature = "multipart")]
#[inline]
pub async fn uploads(&mut self, key: &str, save_path: &str) -> Result<String> {
if let Some(files) = self.files(key).await {
std::fs::create_dir_all(save_path)?;
let mut msgs = Vec::with_capacity(files.len());
for file in files {
let dest = format!("{}/{}", save_path, file.name().unwrap());
if let Err(e) = std::fs::copy(file.path(), std::path::Path::new(&dest)) {
return Ok(format!("file not found in request: {e}"));
} else {
msgs.push(dest);
}
}
Ok(format!("Files uploaded:\n\n{}", msgs.join("\n")))
} else {
Err(Error::Other(String::from(
"Files not resolved from current request",
)))
}
}
#[inline]
pub async fn parse<T>(&mut self) -> Result<T>
where
T: serde::de::DeserializeOwned,
{
let ctype = self
.inner
.headers()
.get("content-type")
.and_then(|h| h.to_str().ok());
match ctype {
Some(ctype) if ctype == "application/json" => {
let body = self.inner.body_mut().collect().await?.to_bytes();
serde_json::from_slice(&body).map_err(Error::SerdeJson)
}
Some(ctype) if ctype == "application/x-www-form-urlencoded" => {
let body = self.inner.body_mut().collect().await?.to_bytes();
serde_urlencoded::from_bytes(&body).map_err(Error::Deserialize)
}
Some(ctype) if ctype.starts_with("multipart/form-data") => {
let mut fields = multimap::MultiMap::new();
let boundary = multer::parse_boundary(ctype)?;
let body = std::mem::replace(self.body_mut(), HttpBody::Empty);
let mut multipart = multer::Multipart::new(body, boundary);
while let Some(field) = multipart.next_field().await? {
if let Some(name) = field.name().map(|s| s.to_owned()) {
if field.headers().get("content-type").is_none() {
fields.insert(name, field.text().await?);
}
}
}
from_str_multi_map(fields.iter_all()).map_err(Error::Deserialize)
}
#[cfg(feature = "cbor")]
Some(ctype) if ctype == "application/cbor" => {
let body = self.inner.body_mut().collect().await?.to_bytes();
ciborium::de::from_reader(&body[..]).map_err(|e| Error::Other(e.to_string()))
}
#[cfg(feature = "msgpack")]
Some(ctype) if ctype == "application/msgpack" => {
let body = self.inner.body_mut().collect().await?.to_bytes();
rmp_serde::from_slice(&body).map_err(Error::MsgpackDe)
}
_ => Err(Error::Other(String::from("Invalid request Context-Type"))),
}
}
#[inline]
pub async fn extract_query<T: FromBytes>(&mut self) -> Result<Query<T::Output>> {
let query = self.inner.uri().query().unwrap_or_default().to_owned();
let bytes = Bytes::from(query);
let value = T::from_bytes(bytes)?;
Ok(Query(value))
}
#[inline]
pub async fn extract_json<T: FromBytes>(&mut self) -> Result<Json<T::Output>> {
let bytes = self.inner.body_mut().collect().await?.to_bytes();
let value = T::from_bytes(bytes)?;
Ok(Json(value))
}
#[inline]
pub async fn extract_form<T: FromBytes>(&mut self) -> Result<Form<T::Output>> {
let bytes = self.inner.body_mut().collect().await?.to_bytes();
let value = T::from_bytes(bytes)?;
Ok(Form(value))
}
}
impl Deref for Request {
type Target = hyper::Request<HttpBody>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for Request {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}