1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-01 16:55:08 +02:00

move internalerror to actix web (#2215)

This commit is contained in:
Rob Ede
2021-05-14 16:40:00 +01:00
committed by GitHub
parent f277b128b6
commit 2a8c650f2c
18 changed files with 456 additions and 397 deletions

View File

@ -1,16 +1,13 @@
use std::any::type_name;
use std::ops::Deref;
use std::sync::Arc;
use std::{any::type_name, ops::Deref, sync::Arc};
use actix_http::error::{Error, ErrorInternalServerError};
use actix_http::Extensions;
use actix_http::{error::Error, Extensions};
use actix_utils::future::{err, ok, Ready};
use futures_core::future::LocalBoxFuture;
use serde::Serialize;
use crate::dev::Payload;
use crate::extract::FromRequest;
use crate::request::HttpRequest;
use crate::{
dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
};
/// Data factory.
pub(crate) trait DataFactory {

304
src/error/internal.rs Normal file
View File

@ -0,0 +1,304 @@
use std::{cell::RefCell, fmt, io::Write as _};
use actix_http::{body::Body, header, Response, StatusCode};
use bytes::{BufMut as _, BytesMut};
use crate::{Error, HttpResponse, ResponseError};
/// Wraps errors to alter the generated response status code.
///
/// In following example, the `io::Error` is wrapped into `ErrorBadRequest` which will generate a
/// response with the 400 Bad Request status code instead of the usual status code generated by
/// an `io::Error`.
///
/// # Examples
/// ```
/// # use std::io;
/// # use actix_web::{error, HttpRequest};
/// async fn handler_error() -> Result<String, actix_web::Error> {
/// let err = io::Error::new(io::ErrorKind::Other, "error");
/// Err(error::ErrorBadRequest(err))
/// }
/// ```
pub struct InternalError<T> {
cause: T,
status: InternalErrorType,
}
enum InternalErrorType {
Status(StatusCode),
Response(RefCell<Option<HttpResponse>>),
}
impl<T> InternalError<T> {
/// Constructs an `InternalError` with given status code.
pub fn new(cause: T, status: StatusCode) -> Self {
InternalError {
cause,
status: InternalErrorType::Status(status),
}
}
/// Constructs an `InternalError` with pre-defined response.
pub fn from_response(cause: T, response: HttpResponse) -> Self {
InternalError {
cause,
status: InternalErrorType::Response(RefCell::new(Some(response))),
}
}
}
impl<T: fmt::Debug> fmt::Debug for InternalError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.cause.fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for InternalError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.cause.fmt(f)
}
}
impl<T> ResponseError for InternalError<T>
where
T: fmt::Debug + fmt::Display,
{
fn status_code(&self) -> StatusCode {
match self.status {
InternalErrorType::Status(st) => st,
InternalErrorType::Response(ref resp) => {
if let Some(resp) = resp.borrow().as_ref() {
resp.head().status
} else {
StatusCode::INTERNAL_SERVER_ERROR
}
}
}
}
fn error_response(&self) -> Response<Body> {
match self.status {
InternalErrorType::Status(status) => {
let mut res = Response::new(status);
let mut buf = BytesMut::new().writer();
let _ = write!(buf, "{}", self);
res.headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain; charset=utf-8"),
);
res.set_body(Body::from(buf.into_inner())).into()
}
InternalErrorType::Response(ref resp) => {
if let Some(resp) = resp.borrow_mut().take() {
resp.into()
} else {
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
}
}
macro_rules! error_helper {
($name:ident, $status:ident) => {
paste::paste! {
#[doc = "Helper function that wraps any error and generates a `" $status "` response."]
#[allow(non_snake_case)]
pub fn $name<T>(err: T) -> Error
where
T: fmt::Debug + fmt::Display + 'static,
{
InternalError::new(err, StatusCode::$status).into()
}
}
}
}
error_helper!(ErrorBadRequest, BAD_REQUEST);
error_helper!(ErrorUnauthorized, UNAUTHORIZED);
error_helper!(ErrorPaymentRequired, PAYMENT_REQUIRED);
error_helper!(ErrorForbidden, FORBIDDEN);
error_helper!(ErrorNotFound, NOT_FOUND);
error_helper!(ErrorMethodNotAllowed, METHOD_NOT_ALLOWED);
error_helper!(ErrorNotAcceptable, NOT_ACCEPTABLE);
error_helper!(
ErrorProxyAuthenticationRequired,
PROXY_AUTHENTICATION_REQUIRED
);
error_helper!(ErrorRequestTimeout, REQUEST_TIMEOUT);
error_helper!(ErrorConflict, CONFLICT);
error_helper!(ErrorGone, GONE);
error_helper!(ErrorLengthRequired, LENGTH_REQUIRED);
error_helper!(ErrorPayloadTooLarge, PAYLOAD_TOO_LARGE);
error_helper!(ErrorUriTooLong, URI_TOO_LONG);
error_helper!(ErrorUnsupportedMediaType, UNSUPPORTED_MEDIA_TYPE);
error_helper!(ErrorRangeNotSatisfiable, RANGE_NOT_SATISFIABLE);
error_helper!(ErrorImATeapot, IM_A_TEAPOT);
error_helper!(ErrorMisdirectedRequest, MISDIRECTED_REQUEST);
error_helper!(ErrorUnprocessableEntity, UNPROCESSABLE_ENTITY);
error_helper!(ErrorLocked, LOCKED);
error_helper!(ErrorFailedDependency, FAILED_DEPENDENCY);
error_helper!(ErrorUpgradeRequired, UPGRADE_REQUIRED);
error_helper!(ErrorPreconditionFailed, PRECONDITION_FAILED);
error_helper!(ErrorPreconditionRequired, PRECONDITION_REQUIRED);
error_helper!(ErrorTooManyRequests, TOO_MANY_REQUESTS);
error_helper!(
ErrorRequestHeaderFieldsTooLarge,
REQUEST_HEADER_FIELDS_TOO_LARGE
);
error_helper!(
ErrorUnavailableForLegalReasons,
UNAVAILABLE_FOR_LEGAL_REASONS
);
error_helper!(ErrorExpectationFailed, EXPECTATION_FAILED);
error_helper!(ErrorInternalServerError, INTERNAL_SERVER_ERROR);
error_helper!(ErrorNotImplemented, NOT_IMPLEMENTED);
error_helper!(ErrorBadGateway, BAD_GATEWAY);
error_helper!(ErrorServiceUnavailable, SERVICE_UNAVAILABLE);
error_helper!(ErrorGatewayTimeout, GATEWAY_TIMEOUT);
error_helper!(ErrorHttpVersionNotSupported, HTTP_VERSION_NOT_SUPPORTED);
error_helper!(ErrorVariantAlsoNegotiates, VARIANT_ALSO_NEGOTIATES);
error_helper!(ErrorInsufficientStorage, INSUFFICIENT_STORAGE);
error_helper!(ErrorLoopDetected, LOOP_DETECTED);
error_helper!(ErrorNotExtended, NOT_EXTENDED);
error_helper!(
ErrorNetworkAuthenticationRequired,
NETWORK_AUTHENTICATION_REQUIRED
);
#[cfg(test)]
mod tests {
use actix_http::{error::ParseError, Response};
use super::*;
#[test]
fn test_internal_error() {
let err = InternalError::from_response(ParseError::Method, HttpResponse::Ok().finish());
let resp: Response<Body> = err.error_response();
assert_eq!(resp.status(), StatusCode::OK);
}
#[test]
fn test_error_helpers() {
let res: Response<Body> = ErrorBadRequest("err").into();
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let res: Response<Body> = ErrorUnauthorized("err").into();
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
let res: Response<Body> = ErrorPaymentRequired("err").into();
assert_eq!(res.status(), StatusCode::PAYMENT_REQUIRED);
let res: Response<Body> = ErrorForbidden("err").into();
assert_eq!(res.status(), StatusCode::FORBIDDEN);
let res: Response<Body> = ErrorNotFound("err").into();
assert_eq!(res.status(), StatusCode::NOT_FOUND);
let res: Response<Body> = ErrorMethodNotAllowed("err").into();
assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
let res: Response<Body> = ErrorNotAcceptable("err").into();
assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
let res: Response<Body> = ErrorProxyAuthenticationRequired("err").into();
assert_eq!(res.status(), StatusCode::PROXY_AUTHENTICATION_REQUIRED);
let res: Response<Body> = ErrorRequestTimeout("err").into();
assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
let res: Response<Body> = ErrorConflict("err").into();
assert_eq!(res.status(), StatusCode::CONFLICT);
let res: Response<Body> = ErrorGone("err").into();
assert_eq!(res.status(), StatusCode::GONE);
let res: Response<Body> = ErrorLengthRequired("err").into();
assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
let res: Response<Body> = ErrorPreconditionFailed("err").into();
assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED);
let res: Response<Body> = ErrorPayloadTooLarge("err").into();
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
let res: Response<Body> = ErrorUriTooLong("err").into();
assert_eq!(res.status(), StatusCode::URI_TOO_LONG);
let res: Response<Body> = ErrorUnsupportedMediaType("err").into();
assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
let res: Response<Body> = ErrorRangeNotSatisfiable("err").into();
assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);
let res: Response<Body> = ErrorExpectationFailed("err").into();
assert_eq!(res.status(), StatusCode::EXPECTATION_FAILED);
let res: Response<Body> = ErrorImATeapot("err").into();
assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);
let res: Response<Body> = ErrorMisdirectedRequest("err").into();
assert_eq!(res.status(), StatusCode::MISDIRECTED_REQUEST);
let res: Response<Body> = ErrorUnprocessableEntity("err").into();
assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
let res: Response<Body> = ErrorLocked("err").into();
assert_eq!(res.status(), StatusCode::LOCKED);
let res: Response<Body> = ErrorFailedDependency("err").into();
assert_eq!(res.status(), StatusCode::FAILED_DEPENDENCY);
let res: Response<Body> = ErrorUpgradeRequired("err").into();
assert_eq!(res.status(), StatusCode::UPGRADE_REQUIRED);
let res: Response<Body> = ErrorPreconditionRequired("err").into();
assert_eq!(res.status(), StatusCode::PRECONDITION_REQUIRED);
let res: Response<Body> = ErrorTooManyRequests("err").into();
assert_eq!(res.status(), StatusCode::TOO_MANY_REQUESTS);
let res: Response<Body> = ErrorRequestHeaderFieldsTooLarge("err").into();
assert_eq!(res.status(), StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE);
let res: Response<Body> = ErrorUnavailableForLegalReasons("err").into();
assert_eq!(res.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
let res: Response<Body> = ErrorInternalServerError("err").into();
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
let res: Response<Body> = ErrorNotImplemented("err").into();
assert_eq!(res.status(), StatusCode::NOT_IMPLEMENTED);
let res: Response<Body> = ErrorBadGateway("err").into();
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
let res: Response<Body> = ErrorServiceUnavailable("err").into();
assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);
let res: Response<Body> = ErrorGatewayTimeout("err").into();
assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
let res: Response<Body> = ErrorHttpVersionNotSupported("err").into();
assert_eq!(res.status(), StatusCode::HTTP_VERSION_NOT_SUPPORTED);
let res: Response<Body> = ErrorVariantAlsoNegotiates("err").into();
assert_eq!(res.status(), StatusCode::VARIANT_ALSO_NEGOTIATES);
let res: Response<Body> = ErrorInsufficientStorage("err").into();
assert_eq!(res.status(), StatusCode::INSUFFICIENT_STORAGE);
let res: Response<Body> = ErrorLoopDetected("err").into();
assert_eq!(res.status(), StatusCode::LOOP_DETECTED);
let res: Response<Body> = ErrorNotExtended("err").into();
assert_eq!(res.status(), StatusCode::NOT_EXTENDED);
let res: Response<Body> = ErrorNetworkAuthenticationRequired("err").into();
assert_eq!(res.status(), StatusCode::NETWORK_AUTHENTICATION_REQUIRED);
}
}

View File

@ -9,6 +9,10 @@ use url::ParseError as UrlParseError;
use crate::http::StatusCode;
mod internal;
pub use self::internal::*;
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
///
/// This type alias is generally used to avoid writing out `actix_http::Error` directly.

View File

@ -1,9 +1,9 @@
use std::{any::type_name, ops::Deref};
use actix_http::error::{Error, ErrorInternalServerError};
use actix_http::error::Error;
use actix_utils::future::{err, ok, Ready};
use crate::{dev::Payload, FromRequest, HttpRequest};
use crate::{dev::Payload, error::ErrorInternalServerError, FromRequest, HttpRequest};
/// Request-local data extractor.
///

View File

@ -2,12 +2,11 @@ use std::{borrow::Cow, fmt};
use actix_http::{
body::Body,
error::InternalError,
http::{header::IntoHeaderPair, Error as HttpError, HeaderMap, StatusCode},
};
use bytes::{Bytes, BytesMut};
use crate::{Error, HttpRequest, HttpResponse, HttpResponseBuilder};
use crate::{error::InternalError, Error, HttpRequest, HttpResponse, HttpResponseBuilder};
/// Trait implemented by types that can be converted to an HTTP response.
///

View File

@ -2,12 +2,16 @@
use std::{fmt, ops, sync::Arc};
use actix_http::error::{Error, ErrorNotFound};
use actix_http::error::Error;
use actix_router::PathDeserializer;
use actix_utils::future::{ready, Ready};
use serde::de;
use crate::{dev::Payload, error::PathError, FromRequest, HttpRequest};
use crate::{
dev::Payload,
error::{ErrorNotFound, PathError},
FromRequest, HttpRequest,
};
/// Extract typed data from request path segments.
///

View File

@ -7,14 +7,17 @@ use std::{
task::{Context, Poll},
};
use actix_http::error::{ErrorBadRequest, PayloadError};
use actix_http::error::PayloadError;
use actix_utils::future::{ready, Either, Ready};
use bytes::{Bytes, BytesMut};
use encoding_rs::{Encoding, UTF_8};
use futures_core::{ready, stream::Stream};
use mime::Mime;
use crate::{dev, http::header, web, Error, FromRequest, HttpMessage, HttpRequest};
use crate::{
dev, error::ErrorBadRequest, http::header, web, Error, FromRequest, HttpMessage,
HttpRequest,
};
/// Extract a request's raw payload stream.
///