Skip to content

Commit

Permalink
export more internal error types.
Browse files Browse the repository at this point in the history
  • Loading branch information
fakeshadow committed Feb 18, 2024
1 parent 5e3b71b commit be08b7d
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 206 deletions.
6 changes: 6 additions & 0 deletions web/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# unreleased 0.4.0
## Add
- expose `error::HeaderNameNotFound` type for interacting with internal header name error.
- expose `error::InvalidHeaderValue` type for interacting with internal header value error.
- expose `error::ExtensionNotFound` type for interacting with extension typed map error.

## Change
- `error::InvalidHeaderValue` displays it's associated header name in `Debug` and `Display` format.
- update `xitca-http` to `0.4.0`

# 0.3.0
Expand Down
28 changes: 28 additions & 0 deletions web/src/error/extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use core::{any::type_name, fmt};

use std::error;

use super::{error_from_service, forward_blank_bad_request};

/// error type for typed instance can't be found from [`Extensions`]
///
/// [`Extensions`]: crate::http::Extensions
#[derive(Debug)]
pub struct ExtensionNotFound(&'static str);

impl ExtensionNotFound {
pub(crate) fn from_type<T>() -> Self {
Self(type_name::<T>())
}
}

impl fmt::Display for ExtensionNotFound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} type can't be found from Extensions", self.0)
}
}

impl error::Error for ExtensionNotFound {}

error_from_service!(ExtensionNotFound);
forward_blank_bad_request!(ExtensionNotFound);
37 changes: 37 additions & 0 deletions web/src/error/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use core::fmt;

use std::error;

use crate::http::HeaderName;

use super::{error_from_service, forward_blank_bad_request};

/// error type when named header is not found from request.
#[derive(Debug)]
pub struct HeaderNotFound(pub HeaderName);

impl fmt::Display for HeaderNotFound {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HeaderName: {} is not found", self.0.as_str())
}
}

impl error::Error for HeaderNotFound {}

error_from_service!(HeaderNotFound);
forward_blank_bad_request!(HeaderNotFound);

/// error type when named header is not associated with valid header value.
#[derive(Debug)]
pub struct InvalidHeaderValue(pub HeaderName);

impl fmt::Display for InvalidHeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HeaderName: {} associated with invalid HeaderValue", self.0.as_str())
}
}

impl error::Error for InvalidHeaderValue {}

error_from_service!(InvalidHeaderValue);
forward_blank_bad_request!(InvalidHeaderValue);
183 changes: 26 additions & 157 deletions web/src/error.rs → web/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,30 +70,30 @@
//! }
//! ```
mod extension;
mod header;
mod router;
mod status;

pub use extension::*;
pub use header::*;
pub use router::*;
pub use status::*;

use core::{
any::Any,
convert::Infallible,
fmt,
ops::{Deref, DerefMut},
};

use std::{backtrace::Backtrace, error, io, sync::Mutex};
use std::{error, io, sync::Mutex};

pub use xitca_http::{
error::BodyError,
util::service::{
route::MethodNotAllowed,
router::{MatchError, RouterError},
},
};
pub use xitca_http::error::BodyError;

use crate::{
body::ResponseBody,
context::WebContext,
http::{
header::{InvalidHeaderValue, ALLOW},
StatusCode, WebResponse,
},
http::WebResponse,
service::{pipeline::PipelineE, Service},
};

Expand Down Expand Up @@ -155,6 +155,7 @@ use self::service_impl::ErrorService;
pub struct Error<C = ()>(Box<dyn for<'r> ErrorService<WebContext<'r, C>>>);

impl<C> Error<C> {
// construct an error object from given service type.
pub fn from_service<S>(s: S) -> Self
where
S: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible>
Expand Down Expand Up @@ -227,24 +228,26 @@ pub(crate) use error_from_service;

macro_rules! blank_error_service {
($type: ty, $status: path) => {
impl<'r, C, B> Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
type Error = Infallible;
impl<'r, C, B> crate::service::Service<crate::WebContext<'r, C, B>> for $type {
type Response = crate::http::WebResponse;
type Error = ::core::convert::Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());
async fn call(&self, ctx: crate::WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(crate::body::ResponseBody::empty());
*res.status_mut() = $status;
Ok(res)
}
}
};
}

pub(crate) use blank_error_service;

macro_rules! forward_blank_internal {
($type: ty) => {
impl<'r, C, B> crate::service::Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
type Error = Infallible;
type Response = crate::http::WebResponse;
type Error = core::convert::Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
crate::http::StatusCode::INTERNAL_SERVER_ERROR.call(ctx).await
Expand All @@ -257,11 +260,11 @@ pub(crate) use forward_blank_internal;

macro_rules! forward_blank_bad_request {
($type: ty) => {
impl<'r, C, B> crate::service::Service<WebContext<'r, C, B>> for $type {
type Response = WebResponse;
impl<'r, C, B> crate::service::Service<crate::WebContext<'r, C, B>> for $type {
type Response = crate::http::WebResponse;
type Error = ::core::convert::Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
async fn call(&self, ctx: crate::WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
crate::http::StatusCode::BAD_REQUEST.call(ctx).await
}
}
Expand All @@ -285,143 +288,9 @@ impl<'r, C, B> Service<WebContext<'r, C, B>> for Infallible {
}
}

/// error type derive from http status code. produce minimal "StatusCode Reason" response and stack backtrace
/// of the location status code error occurs.
pub struct ErrorStatus {
status: StatusCode,
_back_trace: Backtrace,
}

impl ErrorStatus {
/// construct an ErrorStatus type from [`StatusCode::INTERNAL_SERVER_ERROR`]
pub fn internal() -> Self {
// verbosity of constructor is desired here so back trace capture
// can direct capture the call site.
Self {
status: StatusCode::BAD_REQUEST,
_back_trace: Backtrace::capture(),
}
}

/// construct an ErrorStatus type from [`StatusCode::BAD_REQUEST`]
pub fn bad_request() -> Self {
Self {
status: StatusCode::BAD_REQUEST,
_back_trace: Backtrace::capture(),
}
}
}

impl fmt::Debug for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.status, f)
}
}

impl fmt::Display for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.status, f)
}
}

impl error::Error for ErrorStatus {
#[cfg(feature = "nightly")]
fn provide<'a>(&'a self, request: &mut error::Request<'a>) {
request.provide_ref(&self._back_trace);
}
}

impl From<StatusCode> for ErrorStatus {
fn from(status: StatusCode) -> Self {
Self {
status,
_back_trace: Backtrace::capture(),
}
}
}

impl<C> From<StatusCode> for Error<C> {
fn from(e: StatusCode) -> Self {
Error::from(ErrorStatus::from(e))
}
}

impl<C> From<ErrorStatus> for Error<C> {
fn from(e: ErrorStatus) -> Self {
Error::from_service(e)
}
}

impl<'r, C, B> Service<WebContext<'r, C, B>> for ErrorStatus {
type Response = WebResponse;
type Error = Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
self.status.call(ctx).await
}
}

impl<'r, C, B> Service<WebContext<'r, C, B>> for StatusCode {
type Response = WebResponse;
type Error = Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());
*res.status_mut() = *self;
Ok(res)
}
}

error_from_service!(io::Error);
forward_blank_internal!(io::Error);

error_from_service!(MatchError);
blank_error_service!(MatchError, StatusCode::NOT_FOUND);

error_from_service!(MethodNotAllowed);

error_from_service!(InvalidHeaderValue);
forward_blank_bad_request!(InvalidHeaderValue);

impl<'r, C, B> Service<WebContext<'r, C, B>> for MethodNotAllowed {
type Response = WebResponse;
type Error = Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());

let allowed = self.allowed_methods();

let len = allowed.iter().fold(0, |a, m| a + m.as_str().len() + 1);

let mut methods = String::with_capacity(len);

for method in allowed {
methods.push_str(method.as_str());
methods.push(',');
}
methods.pop();

res.headers_mut().insert(ALLOW, methods.parse().unwrap());
*res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;

Ok(res)
}
}

impl<E, C> From<RouterError<E>> for Error<C>
where
E: Into<Self>,
{
fn from(e: RouterError<E>) -> Self {
match e {
RouterError::Match(e) => e.into(),
RouterError::NotAllowed(e) => e.into(),
RouterError::Service(e) => e.into(),
}
}
}

type StdErr = Box<dyn error::Error + Send + Sync>;

impl<C> From<StdErr> for Error<C> {
Expand Down
59 changes: 59 additions & 0 deletions web/src/error/router.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pub use xitca_http::util::service::{
route::MethodNotAllowed,
router::{MatchError, RouterError},
};

use core::convert::Infallible;

use crate::{
body::ResponseBody,
http::{header::ALLOW, StatusCode, WebResponse},
service::Service,
WebContext,
};

use super::{blank_error_service, error_from_service, Error};

error_from_service!(MatchError);
blank_error_service!(MatchError, StatusCode::NOT_FOUND);

error_from_service!(MethodNotAllowed);

impl<'r, C, B> Service<WebContext<'r, C, B>> for MethodNotAllowed {
type Response = WebResponse;
type Error = Infallible;

async fn call(&self, ctx: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
let mut res = ctx.into_response(ResponseBody::empty());

let allowed = self.allowed_methods();

let len = allowed.iter().fold(0, |a, m| a + m.as_str().len() + 1);

let mut methods = String::with_capacity(len);

for method in allowed {
methods.push_str(method.as_str());
methods.push(',');
}
methods.pop();

res.headers_mut().insert(ALLOW, methods.parse().unwrap());
*res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;

Ok(res)
}
}

impl<E, C> From<RouterError<E>> for Error<C>
where
E: Into<Self>,
{
fn from(e: RouterError<E>) -> Self {
match e {
RouterError::Match(e) => e.into(),
RouterError::NotAllowed(e) => e.into(),
RouterError::Service(e) => e.into(),
}
}
}
Loading

0 comments on commit be08b7d

Please sign in to comment.