use httparse::Status;
use std::borrow::Cow;
use std::fmt;
use std::str::{self, FromStr};
use super::component::{Header, Method};
mod error;
mod headers;
mod uri;
pub use error::{Error, Result};
pub use headers::Headers;
pub use uri::{Query, Uri};
#[derive(Clone, Debug)]
pub struct Request<'a> {
pub method: Method,
pub uri: Uri<'a>,
pub headers: Headers<'a>,
pub body: Cow<'a, [u8]>,
}
impl<'a> Request<'a> {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[allow(clippy::missing_panics_doc)]
pub fn from_bytes(bytes: &'a [u8]) -> Result<Self> {
let mut headers = [httparse::EMPTY_HEADER; 64];
let mut req = httparse::Request::new(&mut headers);
match req.parse(bytes).map_err(Error::from)? {
Status::Partial => Err(Error::Incomplete),
Status::Complete(n) => {
let body = Cow::Borrowed(&bytes[n..]);
let method = req.method.expect("invariant").parse()?;
let uri = Uri::from(req.path.expect("invariant"));
let iter = req.headers.iter();
let headers = iter
.take_while(|header| !header.name.is_empty())
.filter_map(|header| {
str::from_utf8(header.value).ok().and_then(|value| {
Header::from_str(header.name)
.map(|name| (name, value))
.ok()
})
})
.collect();
if uri.path.len() > 4 * 1024 {
return Err(Error::Security("exceeds size of 4kb"));
}
if uri.path.contains("..") {
return Err(Error::Security("path traversal"));
}
Ok(Request { method, uri, headers, body })
}
}
}
}
impl<'a> Request<'a> {
#[inline]
#[must_use]
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}
#[inline]
#[must_use]
pub fn uri<U>(mut self, uri: U) -> Self
where
U: Into<Uri<'a>>,
{
self.uri = uri.into();
self
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
#[must_use]
pub fn header<V>(mut self, header: Header, value: V) -> Self
where
V: ToString,
{
self.headers.put(header, value.to_string());
self
}
#[inline]
#[must_use]
pub fn body<B>(mut self, body: B) -> Self
where
B: Into<Vec<u8>>,
{
self.body = Cow::Owned(body.into());
self
}
}
impl Default for Request<'_> {
#[inline]
fn default() -> Self {
Self {
method: Method::Get,
uri: Uri::default(),
headers: Headers::default(),
body: Cow::Borrowed(&[]),
}
}
}
impl fmt::Display for Request<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} HTTP/1.1\r\n", self.method, self.uri)?;
write!(f, "{}\r\n", self.headers)?;
write!(f, "[Body: {} bytes]\r\n", self.body.len())
}
}