use crate::{Error, ErrorKind};
use http::header::{AsHeaderName, HeaderValue};
use http::Request as HttpRequest;
use http_body_util::BodyExt;
use hyper::body::Incoming;
#[derive(Debug)]
pub struct Request {
inner: HttpRequest<Incoming>,
body: Option<Vec<u8>>,
}
impl Request {
pub(crate) fn new(request: HttpRequest<Incoming>) -> Self {
Request {
inner: request,
body: None,
}
}
pub fn method(&self) -> &str {
self.inner.method().as_ref()
}
pub fn path(&self) -> &str {
self.inner.uri().path()
}
pub fn path_and_query(&self) -> &str {
self.inner
.uri()
.path_and_query()
.map(|pq| pq.as_str())
.unwrap_or("")
}
pub fn header<T: AsHeaderName>(&self, header_name: T) -> Vec<&HeaderValue> {
self.inner.headers().get_all(header_name).iter().collect()
}
pub fn has_header<T: AsHeaderName>(&self, header_name: T) -> bool {
self.inner.headers().contains_key(header_name)
}
pub fn body(&self) -> Result<&Vec<u8>, Error> {
self.body
.as_ref()
.ok_or_else(|| Error::new(ErrorKind::RequestBodyFailure))
}
pub(crate) async fn read_body(&mut self) -> &Vec<u8> {
if self.body.is_none() {
let raw_body = self.inner.body_mut();
let bytes = raw_body
.collect()
.await
.map_err(|err| Error::new_with_context(ErrorKind::RequestBodyFailure, err))
.unwrap()
.to_bytes();
self.body = Some(bytes.to_vec());
}
self.body.as_ref().unwrap()
}
pub(crate) fn formatted(&self) -> String {
let mut formatted = format!(
"\r\n{} {}\r\n",
&self.inner.method(),
&self
.inner
.uri()
.path_and_query()
.map(|pq| pq.as_str())
.unwrap_or("")
);
for (key, value) in self.inner.headers() {
formatted.push_str(&format!(
"{}: {}\r\n",
key,
value.to_str().unwrap_or("<invalid>")
));
}
if let Some(body) = &self.body {
if !body.is_empty() {
formatted.push_str(&format!("{}\r\n", &String::from_utf8_lossy(body)));
}
}
formatted
}
}