diff options
Diffstat (limited to 'src/request.rs')
-rw-r--r-- | src/request.rs | 82 |
1 files changed, 70 insertions, 12 deletions
diff --git a/src/request.rs b/src/request.rs index 7846367..79ca2ae 100644 --- a/src/request.rs +++ b/src/request.rs @@ -8,7 +8,9 @@ use hyper::{body::Bytes, header}; use hyper::http::request::Parts as ReqParts; use std::collections::HashMap; -use crate::{Error, security}; +use crate::security; + +use error::*; type HyperRequest = hyper::Request<hyper::Body>; @@ -72,14 +74,14 @@ impl Parts { } /// Parses the query string of the request into a given struct. - pub fn query<T: DeserializeOwned>(&self) -> Result<T,Error> { - serde_urlencoded::from_str::<T>(self.parts.uri.query().unwrap_or("")).map_err(|e|Error::bad_request(e.to_string())) + pub fn query<T: DeserializeOwned>(&self) -> Result<T,QueryError> { + serde_urlencoded::from_str::<T>(self.parts.uri.query().unwrap_or("")).map_err(QueryError) } } impl Body { - pub async fn into_bytes(self) -> Result<Bytes,Error> { - hyper::body::to_bytes(self.body).await.map_err(|_|Error::internal("failed to read body".to_string())) + pub async fn into_bytes(self) -> Result<Bytes, BodyError> { + hyper::body::to_bytes(self.body).await.map_err(BodyError) } /// Parses a `application/x-www-form-urlencoded` request body into a given struct. @@ -102,10 +104,10 @@ impl Body { /// Ok(Response::new(format!("hello {}", msg.text).into())) /// } /// ``` - pub async fn into_form<T: DeserializeOwned>(self) -> Result<T,Error> { + pub async fn into_form<T: DeserializeOwned>(self) -> Result<T, FormError> { self.enforce_content_type(APPLICATION_WWW_FORM_URLENCODED)?; let full_body = self.into_bytes().await?; - serde_urlencoded::from_bytes::<T>(&full_body).map_err(|e|Error::bad_request(e.to_string())) + serde_urlencoded::from_bytes::<T>(&full_body).map_err(FormError::Deserialize) } /// Parses a `application/x-www-form-urlencoded` request body into a given struct. @@ -140,20 +142,76 @@ impl Body { /// Ok(response) /// } /// ``` - pub async fn into_form_csrf<T: DeserializeOwned>(self, csrf_token: &security::CsrfToken) -> Result<T,Error> { + pub async fn into_form_csrf<T: DeserializeOwned>(self, csrf_token: &security::CsrfToken) -> Result<T, CsrfProtectedFormError> { self.enforce_content_type(APPLICATION_WWW_FORM_URLENCODED)?; let full_body = self.into_bytes().await?; - let csrf_data = serde_urlencoded::from_bytes::<CsrfData>(&full_body).map_err(|_|Error::bad_request("no csrf token".to_string()))?; + let csrf_data = serde_urlencoded::from_bytes::<CsrfData>(&full_body).map_err(|_| CsrfProtectedFormError::NoCsrf)?; csrf_token.matches(csrf_data.csrf)?; - serde_urlencoded::from_bytes::<T>(&full_body).map_err(|e|Error::bad_request(e.to_string())) + serde_urlencoded::from_bytes::<T>(&full_body).map_err(CsrfProtectedFormError::Deserialize) } - fn enforce_content_type(&self, mime: Mime) -> Result<(),Error> { + fn enforce_content_type(&self, mime: Mime) -> Result<(), WrongContentTypeError> { if let Some(content_type) = &self.content_type { if *content_type == mime.to_string() { return Ok(()) } } - Err(Error::bad_request(format!("expected content-type: {}", mime))) + Err(WrongContentTypeError{expected: mime, received: self.content_type.as_ref().and_then(|h| h.to_str().ok().map(|s| s.to_owned()))}) + } +} + +pub mod error { + use mime::Mime; + use thiserror::Error; + use hyper::StatusCode; + + use crate::security::CsrfError; + #[derive(Error, Debug)] + #[error("query deserialize error: {0}")] + pub struct QueryError(pub serde_urlencoded::de::Error); + impl_into_http_error!(QueryError, StatusCode::BAD_REQUEST); + + #[derive(Error, Debug)] + #[error("failed to read body")] + pub struct BodyError(pub hyper::Error); + impl_into_http_error!(BodyError, StatusCode::BAD_REQUEST); + + #[derive(Error, Debug)] + #[error("expected Content-Type {expected} but received {}", received.as_ref().unwrap_or(&"nothing".to_owned()))] + pub struct WrongContentTypeError { + pub expected: Mime, + pub received: Option<String>, + } + + #[derive(Error, Debug)] + pub enum FormError { + #[error("{0}")] + ContentType(#[from] WrongContentTypeError), + + #[error("{0}")] + Body(#[from] BodyError), + + #[error("form deserialize error: {0}")] + Deserialize(#[from] serde_urlencoded::de::Error), + } + impl_into_http_error!(FormError, StatusCode::BAD_REQUEST); + + #[derive(Error, Debug)] + pub enum CsrfProtectedFormError { + #[error("{0}")] + ContentType(#[from] WrongContentTypeError), + + #[error("{0}")] + Body(#[from] BodyError), + + #[error("form deserialize error: {0}")] + Deserialize(#[from] serde_urlencoded::de::Error), + + #[error("no csrf token in form data")] + NoCsrf, + + #[error("{0}")] + Csrf(#[from] CsrfError), } + impl_into_http_error!(CsrfProtectedFormError, StatusCode::BAD_REQUEST); }
\ No newline at end of file |