mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-01 16:55:08 +02:00
refined error model (#2253)
This commit is contained in:
@ -1,12 +1,13 @@
|
||||
use std::{any::type_name, ops::Deref, sync::Arc};
|
||||
|
||||
use actix_http::{error::Error, Extensions};
|
||||
use actix_http::Extensions;
|
||||
use actix_utils::future::{err, ok, Ready};
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
|
||||
Error,
|
||||
};
|
||||
|
||||
/// Data factory.
|
||||
|
76
src/error/error.rs
Normal file
76
src/error/error.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use std::{error::Error as StdError, fmt};
|
||||
|
||||
use actix_http::{body::AnyBody, Response};
|
||||
|
||||
use crate::{HttpResponse, ResponseError};
|
||||
|
||||
/// General purpose actix web error.
|
||||
///
|
||||
/// An actix web error is used to carry errors from `std::error`
|
||||
/// through actix in a convenient way. It can be created through
|
||||
/// converting errors with `into()`.
|
||||
///
|
||||
/// Whenever it is created from an external object a response error is created
|
||||
/// for it that can be used to create an HTTP response from it this means that
|
||||
/// if you have access to an actix `Error` you can always get a
|
||||
/// `ResponseError` reference from it.
|
||||
pub struct Error {
|
||||
cause: Box<dyn ResponseError>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Returns the reference to the underlying `ResponseError`.
|
||||
pub fn as_response_error(&self) -> &dyn ResponseError {
|
||||
self.cause.as_ref()
|
||||
}
|
||||
|
||||
/// Similar to `as_response_error` but downcasts.
|
||||
pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
|
||||
<dyn ResponseError>::downcast_ref(self.cause.as_ref())
|
||||
}
|
||||
|
||||
/// Shortcut for creating an `HttpResponse`.
|
||||
pub fn error_response(&self) -> HttpResponse {
|
||||
self.cause.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.cause, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", &self.cause)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
// TODO: populate if replacement for Box<dyn Error> is found
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for Error {
|
||||
fn from(val: std::convert::Infallible) -> Self {
|
||||
match val {}
|
||||
}
|
||||
}
|
||||
|
||||
/// `Error` for any error that implements `ResponseError`
|
||||
impl<T: ResponseError + 'static> From<T> for Error {
|
||||
fn from(err: T) -> Error {
|
||||
Error {
|
||||
cause: Box::new(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Response<AnyBody> {
|
||||
fn from(err: Error) -> Response<AnyBody> {
|
||||
err.error_response().into()
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use std::{cell::RefCell, fmt, io::Write as _};
|
||||
|
||||
use actix_http::{body::Body, header, Response, StatusCode};
|
||||
use actix_http::{body::Body, header, StatusCode};
|
||||
use bytes::{BufMut as _, BytesMut};
|
||||
|
||||
use crate::{Error, HttpResponse, ResponseError};
|
||||
use crate::{Error, HttpRequest, HttpResponse, Responder, ResponseError};
|
||||
|
||||
/// Wraps errors to alter the generated response status code.
|
||||
///
|
||||
@ -77,10 +77,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> Response<Body> {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
match self.status {
|
||||
InternalErrorType::Status(status) => {
|
||||
let mut res = Response::new(status);
|
||||
let mut res = HttpResponse::new(status);
|
||||
let mut buf = BytesMut::new().writer();
|
||||
let _ = write!(buf, "{}", self);
|
||||
|
||||
@ -88,20 +88,29 @@ where
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
res.set_body(Body::from(buf.into_inner())).into()
|
||||
res.set_body(Body::from(buf.into_inner()))
|
||||
}
|
||||
|
||||
InternalErrorType::Response(ref resp) => {
|
||||
if let Some(resp) = resp.borrow_mut().take() {
|
||||
resp.into()
|
||||
resp
|
||||
} else {
|
||||
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Responder for InternalError<T>
|
||||
where
|
||||
T: fmt::Debug + fmt::Display + 'static,
|
||||
{
|
||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||
HttpResponse::from_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! error_helper {
|
||||
($name:ident, $status:ident) => {
|
||||
paste::paste! {
|
||||
@ -171,134 +180,134 @@ error_helper!(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_http::{error::ParseError, Response};
|
||||
use actix_http::error::ParseError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_internal_error() {
|
||||
let err = InternalError::from_response(ParseError::Method, HttpResponse::Ok().finish());
|
||||
let resp: Response<Body> = err.error_response();
|
||||
let resp: HttpResponse = err.error_response();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_helpers() {
|
||||
let res: Response<Body> = ErrorBadRequest("err").into();
|
||||
let res: HttpResponse = ErrorBadRequest("err").into();
|
||||
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let res: Response<Body> = ErrorUnauthorized("err").into();
|
||||
let res: HttpResponse = ErrorUnauthorized("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let res: Response<Body> = ErrorPaymentRequired("err").into();
|
||||
let res: HttpResponse = ErrorPaymentRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PAYMENT_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorForbidden("err").into();
|
||||
let res: HttpResponse = ErrorForbidden("err").into();
|
||||
assert_eq!(res.status(), StatusCode::FORBIDDEN);
|
||||
|
||||
let res: Response<Body> = ErrorNotFound("err").into();
|
||||
let res: HttpResponse = ErrorNotFound("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let res: Response<Body> = ErrorMethodNotAllowed("err").into();
|
||||
let res: HttpResponse = ErrorMethodNotAllowed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
|
||||
let res: Response<Body> = ErrorNotAcceptable("err").into();
|
||||
let res: HttpResponse = ErrorNotAcceptable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
|
||||
|
||||
let res: Response<Body> = ErrorProxyAuthenticationRequired("err").into();
|
||||
let res: HttpResponse = ErrorProxyAuthenticationRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PROXY_AUTHENTICATION_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorRequestTimeout("err").into();
|
||||
let res: HttpResponse = ErrorRequestTimeout("err").into();
|
||||
assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
|
||||
|
||||
let res: Response<Body> = ErrorConflict("err").into();
|
||||
let res: HttpResponse = ErrorConflict("err").into();
|
||||
assert_eq!(res.status(), StatusCode::CONFLICT);
|
||||
|
||||
let res: Response<Body> = ErrorGone("err").into();
|
||||
let res: HttpResponse = ErrorGone("err").into();
|
||||
assert_eq!(res.status(), StatusCode::GONE);
|
||||
|
||||
let res: Response<Body> = ErrorLengthRequired("err").into();
|
||||
let res: HttpResponse = ErrorLengthRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorPreconditionFailed("err").into();
|
||||
let res: HttpResponse = ErrorPreconditionFailed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED);
|
||||
|
||||
let res: Response<Body> = ErrorPayloadTooLarge("err").into();
|
||||
let res: HttpResponse = ErrorPayloadTooLarge("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||
|
||||
let res: Response<Body> = ErrorUriTooLong("err").into();
|
||||
let res: HttpResponse = ErrorUriTooLong("err").into();
|
||||
assert_eq!(res.status(), StatusCode::URI_TOO_LONG);
|
||||
|
||||
let res: Response<Body> = ErrorUnsupportedMediaType("err").into();
|
||||
let res: HttpResponse = ErrorUnsupportedMediaType("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
|
||||
|
||||
let res: Response<Body> = ErrorRangeNotSatisfiable("err").into();
|
||||
let res: HttpResponse = ErrorRangeNotSatisfiable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||
|
||||
let res: Response<Body> = ErrorExpectationFailed("err").into();
|
||||
let res: HttpResponse = ErrorExpectationFailed("err").into();
|
||||
assert_eq!(res.status(), StatusCode::EXPECTATION_FAILED);
|
||||
|
||||
let res: Response<Body> = ErrorImATeapot("err").into();
|
||||
let res: HttpResponse = ErrorImATeapot("err").into();
|
||||
assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);
|
||||
|
||||
let res: Response<Body> = ErrorMisdirectedRequest("err").into();
|
||||
let res: HttpResponse = ErrorMisdirectedRequest("err").into();
|
||||
assert_eq!(res.status(), StatusCode::MISDIRECTED_REQUEST);
|
||||
|
||||
let res: Response<Body> = ErrorUnprocessableEntity("err").into();
|
||||
let res: HttpResponse = ErrorUnprocessableEntity("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||
|
||||
let res: Response<Body> = ErrorLocked("err").into();
|
||||
let res: HttpResponse = ErrorLocked("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LOCKED);
|
||||
|
||||
let res: Response<Body> = ErrorFailedDependency("err").into();
|
||||
let res: HttpResponse = ErrorFailedDependency("err").into();
|
||||
assert_eq!(res.status(), StatusCode::FAILED_DEPENDENCY);
|
||||
|
||||
let res: Response<Body> = ErrorUpgradeRequired("err").into();
|
||||
let res: HttpResponse = ErrorUpgradeRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UPGRADE_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorPreconditionRequired("err").into();
|
||||
let res: HttpResponse = ErrorPreconditionRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::PRECONDITION_REQUIRED);
|
||||
|
||||
let res: Response<Body> = ErrorTooManyRequests("err").into();
|
||||
let res: HttpResponse = ErrorTooManyRequests("err").into();
|
||||
assert_eq!(res.status(), StatusCode::TOO_MANY_REQUESTS);
|
||||
|
||||
let res: Response<Body> = ErrorRequestHeaderFieldsTooLarge("err").into();
|
||||
let res: HttpResponse = ErrorRequestHeaderFieldsTooLarge("err").into();
|
||||
assert_eq!(res.status(), StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
|
||||
let res: Response<Body> = ErrorUnavailableForLegalReasons("err").into();
|
||||
let res: HttpResponse = ErrorUnavailableForLegalReasons("err").into();
|
||||
assert_eq!(res.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
|
||||
|
||||
let res: Response<Body> = ErrorInternalServerError("err").into();
|
||||
let res: HttpResponse = ErrorInternalServerError("err").into();
|
||||
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||
|
||||
let res: Response<Body> = ErrorNotImplemented("err").into();
|
||||
let res: HttpResponse = ErrorNotImplemented("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_IMPLEMENTED);
|
||||
|
||||
let res: Response<Body> = ErrorBadGateway("err").into();
|
||||
let res: HttpResponse = ErrorBadGateway("err").into();
|
||||
assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
|
||||
|
||||
let res: Response<Body> = ErrorServiceUnavailable("err").into();
|
||||
let res: HttpResponse = ErrorServiceUnavailable("err").into();
|
||||
assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
|
||||
let res: Response<Body> = ErrorGatewayTimeout("err").into();
|
||||
let res: HttpResponse = ErrorGatewayTimeout("err").into();
|
||||
assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
|
||||
|
||||
let res: Response<Body> = ErrorHttpVersionNotSupported("err").into();
|
||||
let res: HttpResponse = ErrorHttpVersionNotSupported("err").into();
|
||||
assert_eq!(res.status(), StatusCode::HTTP_VERSION_NOT_SUPPORTED);
|
||||
|
||||
let res: Response<Body> = ErrorVariantAlsoNegotiates("err").into();
|
||||
let res: HttpResponse = ErrorVariantAlsoNegotiates("err").into();
|
||||
assert_eq!(res.status(), StatusCode::VARIANT_ALSO_NEGOTIATES);
|
||||
|
||||
let res: Response<Body> = ErrorInsufficientStorage("err").into();
|
||||
let res: HttpResponse = ErrorInsufficientStorage("err").into();
|
||||
assert_eq!(res.status(), StatusCode::INSUFFICIENT_STORAGE);
|
||||
|
||||
let res: Response<Body> = ErrorLoopDetected("err").into();
|
||||
let res: HttpResponse = ErrorLoopDetected("err").into();
|
||||
assert_eq!(res.status(), StatusCode::LOOP_DETECTED);
|
||||
|
||||
let res: Response<Body> = ErrorNotExtended("err").into();
|
||||
let res: HttpResponse = ErrorNotExtended("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NOT_EXTENDED);
|
||||
|
||||
let res: Response<Body> = ErrorNetworkAuthenticationRequired("err").into();
|
||||
let res: HttpResponse = ErrorNetworkAuthenticationRequired("err").into();
|
||||
assert_eq!(res.status(), StatusCode::NETWORK_AUTHENTICATION_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
109
src/error/macros.rs
Normal file
109
src/error/macros.rs
Normal file
@ -0,0 +1,109 @@
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __downcast_get_type_id {
|
||||
() => {
|
||||
/// A helper method to get the type ID of the type
|
||||
/// this trait is implemented on.
|
||||
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
||||
/// on the returned `TypeId` to perform a cast.
|
||||
///
|
||||
/// Unfortunately, Rust has no notion of a trait method that is
|
||||
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
||||
/// to *call*). As a workaround, we require this method
|
||||
/// to return a private type along with the `TypeId`. This
|
||||
/// private type (`PrivateHelper`) has a private constructor,
|
||||
/// making it impossible for safe code to construct outside of
|
||||
/// this module. This ensures that safe code cannot violate
|
||||
/// type-safety by implementing this method.
|
||||
///
|
||||
/// We also take `PrivateHelper` as a parameter, to ensure that
|
||||
/// safe code cannot obtain a `PrivateHelper` instance by
|
||||
/// delegating to an existing implementation of `__private_get_type_id__`
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
fn __private_get_type_id__(&self, _: PrivateHelper) -> (std::any::TypeId, PrivateHelper)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
(std::any::TypeId::of::<Self>(), PrivateHelper(()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//Generate implementation for dyn $name
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __downcast_dyn {
|
||||
($name:ident) => {
|
||||
/// A struct with a private constructor, for use with
|
||||
/// `__private_get_type_id__`. Its single field is private,
|
||||
/// ensuring that it can only be constructed from this module
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PrivateHelper(());
|
||||
|
||||
impl dyn $name + 'static {
|
||||
/// Downcasts generic body to a specific type.
|
||||
#[allow(dead_code)]
|
||||
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__(PrivateHelper(())).0
|
||||
== std::any::TypeId::of::<T>()
|
||||
{
|
||||
// SAFETY: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&*(self as *const dyn $name as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Downcasts a generic body to a mutable specific type.
|
||||
#[allow(dead_code)]
|
||||
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||
if self.__private_get_type_id__(PrivateHelper(())).0
|
||||
== std::any::TypeId::of::<T>()
|
||||
{
|
||||
// SAFETY: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&mut *(self as *const dyn $name as *const T as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
trait MB {
|
||||
__downcast_get_type_id!();
|
||||
}
|
||||
|
||||
__downcast_dyn!(MB);
|
||||
|
||||
impl MB for String {}
|
||||
impl MB for () {}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_any_casting() {
|
||||
let mut body = String::from("hello cast");
|
||||
let resp_body: &mut dyn MB = &mut body;
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast");
|
||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||
body.push('!');
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast!");
|
||||
let not_body = resp_body.downcast_ref::<()>();
|
||||
assert!(not_body.is_none());
|
||||
}
|
||||
}
|
@ -9,14 +9,20 @@ use url::ParseError as UrlParseError;
|
||||
|
||||
use crate::http::StatusCode;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
mod error;
|
||||
mod internal;
|
||||
mod macros;
|
||||
mod response_error;
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::internal::*;
|
||||
pub use self::response_error::ResponseError;
|
||||
|
||||
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
|
||||
///
|
||||
/// This type alias is generally used to avoid writing out `actix_http::Error` directly.
|
||||
pub type Result<T, E = actix_http::Error> = std::result::Result<T, E>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// Errors which can occur when attempting to generate resource uri.
|
||||
#[derive(Debug, PartialEq, Display, Error, From)]
|
||||
|
144
src/error/response_error.rs
Normal file
144
src/error/response_error.rs
Normal file
@ -0,0 +1,144 @@
|
||||
//! `ResponseError` trait and foreign impls.
|
||||
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt,
|
||||
io::{self, Write as _},
|
||||
};
|
||||
|
||||
use actix_http::{body::AnyBody, header, Response, StatusCode};
|
||||
use bytes::BytesMut;
|
||||
|
||||
use crate::{__downcast_dyn, __downcast_get_type_id};
|
||||
use crate::{helpers, HttpResponse};
|
||||
|
||||
/// Errors that can generate responses.
|
||||
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found
|
||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||
/// Returns appropriate status code for error.
|
||||
///
|
||||
/// A 500 Internal Server Error is used by default. If [error_response](Self::error_response) is
|
||||
/// also implemented and does not call `self.status_code()`, then this will not be used.
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
/// Creates full response for error.
|
||||
///
|
||||
/// By default, the generated response uses a 500 Internal Server Error status code, a
|
||||
/// `Content-Type` of `text/plain`, and the body is set to `Self`'s `Display` impl.
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
let mut res = HttpResponse::new(self.status_code());
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
let _ = write!(helpers::MutWriter(&mut buf), "{}", self);
|
||||
|
||||
res.headers_mut().insert(
|
||||
header::CONTENT_TYPE,
|
||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
||||
);
|
||||
|
||||
res.set_body(AnyBody::from(buf))
|
||||
}
|
||||
|
||||
__downcast_get_type_id!();
|
||||
}
|
||||
|
||||
__downcast_dyn!(ResponseError);
|
||||
|
||||
impl ResponseError for Box<dyn StdError + 'static> {}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
||||
|
||||
impl ResponseError for serde::de::value::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for std::str::Utf8Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for std::io::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: decide if these errors should consider not found or permission errors
|
||||
match self.kind() {
|
||||
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
|
||||
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::HttpError {}
|
||||
|
||||
impl ResponseError for actix_http::Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
// TODO: map error kinds to status code better
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::new(self.status_code()).set_body(self.to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::header::InvalidHeaderValue {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::ParseError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::error::BlockingError {}
|
||||
|
||||
impl ResponseError for actix_http::error::PayloadError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
actix_http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::ws::ProtocolError {}
|
||||
|
||||
impl ResponseError for actix_http::error::ContentTypeError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseError for actix_http::ws::HandshakeError {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
Response::from(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_error_casting() {
|
||||
use actix_http::error::{ContentTypeError, PayloadError};
|
||||
|
||||
let err = PayloadError::Overflow;
|
||||
let resp_err: &dyn ResponseError = &err;
|
||||
|
||||
let err = resp_err.downcast_ref::<PayloadError>().unwrap();
|
||||
assert_eq!(err.to_string(), "Payload reached size limit.");
|
||||
|
||||
let not_err = resp_err.downcast_ref::<ContentTypeError>();
|
||||
assert!(not_err.is_none());
|
||||
}
|
||||
}
|
@ -47,8 +47,7 @@ pub trait FromRequest: Sized {
|
||||
///
|
||||
/// If the FromRequest for T fails, return None rather than returning an error response
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
@ -139,8 +138,7 @@ where
|
||||
///
|
||||
/// If the `FromRequest` for T fails, inject Err into handler rather than returning an error response
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest};
|
||||
/// use actix_web::error::ErrorBadRequest;
|
||||
|
@ -3,18 +3,14 @@ use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_http::Error;
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use actix_utils::future::{ready, Ready};
|
||||
use futures_core::ready;
|
||||
use pin_project::pin_project;
|
||||
|
||||
use crate::{
|
||||
extract::FromRequest,
|
||||
request::HttpRequest,
|
||||
responder::Responder,
|
||||
response::HttpResponse,
|
||||
service::{ServiceRequest, ServiceResponse},
|
||||
Error, FromRequest, HttpRequest, HttpResponse, Responder,
|
||||
};
|
||||
|
||||
/// A request handler is an async function that accepts zero or more parameters that can be
|
||||
|
25
src/helpers.rs
Normal file
25
src/helpers.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use std::io;
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
/// An `io::Write`r that only requires mutable reference and assumes that there is space available
|
||||
/// in the buffer for every write operation or that it can be extended implicitly (like
|
||||
/// `bytes::BytesMut`, for example).
|
||||
///
|
||||
/// This is slightly faster (~10%) than `bytes::buf::Writer` in such cases because it does not
|
||||
/// perform a remaining length check before writing.
|
||||
pub(crate) struct MutWriter<'a, B>(pub(crate) &'a mut B);
|
||||
|
||||
impl<'a, B> io::Write for MutWriter<'a, B>
|
||||
where
|
||||
B: BufMut,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.put_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
21
src/lib.rs
21
src/lib.rs
@ -1,7 +1,6 @@
|
||||
//! Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! # Examples
|
||||
//! ```no_run
|
||||
//! use actix_web::{get, web, App, HttpServer, Responder};
|
||||
//!
|
||||
@ -20,8 +19,7 @@
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Documentation & Community Resources
|
||||
//!
|
||||
//! # Documentation & Community Resources
|
||||
//! In addition to this API documentation, several other resources are available:
|
||||
//!
|
||||
//! * [Website & User Guide](https://actix.rs/)
|
||||
@ -44,8 +42,7 @@
|
||||
//! structs represent HTTP requests and responses and expose methods for creating, inspecting,
|
||||
//! and otherwise utilizing them.
|
||||
//!
|
||||
//! ## Features
|
||||
//!
|
||||
//! # Features
|
||||
//! * Supports *HTTP/1.x* and *HTTP/2*
|
||||
//! * Streaming and pipelining
|
||||
//! * Keep-alive and slow requests handling
|
||||
@ -59,8 +56,7 @@
|
||||
//! * Includes an async [HTTP client](https://docs.rs/awc/)
|
||||
//! * Runs on stable Rust 1.46+
|
||||
//!
|
||||
//! ## Crate Features
|
||||
//!
|
||||
//! # Crate Features
|
||||
//! * `compress` - content encoding compression support (enabled by default)
|
||||
//! * `cookies` - cookies support (enabled by default)
|
||||
//! * `openssl` - HTTPS support via `openssl` crate, supports `HTTP/2`
|
||||
@ -80,6 +76,7 @@ pub mod error;
|
||||
mod extract;
|
||||
pub mod guard;
|
||||
mod handler;
|
||||
mod helpers;
|
||||
pub mod http;
|
||||
mod info;
|
||||
pub mod middleware;
|
||||
@ -98,7 +95,7 @@ pub(crate) mod types;
|
||||
pub mod web;
|
||||
|
||||
pub use actix_http::Response as BaseHttpResponse;
|
||||
pub use actix_http::{body, Error, HttpMessage, ResponseError};
|
||||
pub use actix_http::{body, HttpMessage};
|
||||
#[doc(inline)]
|
||||
pub use actix_rt as rt;
|
||||
pub use actix_web_codegen::*;
|
||||
@ -106,7 +103,7 @@ pub use actix_web_codegen::*;
|
||||
pub use cookie;
|
||||
|
||||
pub use crate::app::App;
|
||||
pub use crate::error::Result;
|
||||
pub use crate::error::{Error, ResponseError, Result};
|
||||
pub use crate::extract::FromRequest;
|
||||
pub use crate::request::HttpRequest;
|
||||
pub use crate::resource::Resource;
|
||||
@ -140,7 +137,9 @@ pub mod dev {
|
||||
pub use crate::types::json::JsonBody;
|
||||
pub use crate::types::readlines::Readlines;
|
||||
|
||||
pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream};
|
||||
pub use actix_http::body::{
|
||||
AnyBody, Body, BodySize, MessageBody, ResponseBody, SizedStream,
|
||||
};
|
||||
#[cfg(feature = "compress")]
|
||||
pub use actix_http::encoding::Decoder as Decompress;
|
||||
pub use actix_http::ResponseBuilder as BaseHttpResponseBuilder;
|
||||
|
@ -50,7 +50,7 @@ where
|
||||
T: Transform<S, Req>,
|
||||
T::Future: 'static,
|
||||
T::Response: MapServiceResponseBody,
|
||||
Error: From<T::Error>,
|
||||
T::Error: Into<Error>,
|
||||
{
|
||||
type Response = ServiceResponse;
|
||||
type Error = Error;
|
||||
@ -75,7 +75,7 @@ impl<S, Req> Service<Req> for CompatMiddleware<S>
|
||||
where
|
||||
S: Service<Req>,
|
||||
S::Response: MapServiceResponseBody,
|
||||
Error: From<S::Error>,
|
||||
S::Error: Into<Error>,
|
||||
{
|
||||
type Response = ServiceResponse;
|
||||
type Error = Error;
|
||||
@ -99,12 +99,16 @@ impl<Fut, T, E> Future for CompatMiddlewareFuture<Fut>
|
||||
where
|
||||
Fut: Future<Output = Result<T, E>>,
|
||||
T: MapServiceResponseBody,
|
||||
Error: From<E>,
|
||||
E: Into<Error>,
|
||||
{
|
||||
type Output = Result<ServiceResponse, Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let res = ready!(self.project().fut.poll(cx))?;
|
||||
let res = match ready!(self.project().fut.poll(cx)) {
|
||||
Ok(res) => res,
|
||||
Err(err) => return Poll::Ready(Err(err.into())),
|
||||
};
|
||||
|
||||
Poll::Ready(Ok(res.map_body()))
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use actix_http::{
|
||||
body::{MessageBody, ResponseBody},
|
||||
encoding::Encoder,
|
||||
http::header::{ContentEncoding, ACCEPT_ENCODING},
|
||||
Error,
|
||||
};
|
||||
use actix_service::{Service, Transform};
|
||||
use actix_utils::future::{ok, Ready};
|
||||
@ -23,6 +22,7 @@ use pin_project::pin_project;
|
||||
use crate::{
|
||||
dev::BodyEncoding,
|
||||
service::{ServiceRequest, ServiceResponse},
|
||||
Error,
|
||||
};
|
||||
|
||||
/// Middleware for compressing response payloads.
|
||||
|
@ -13,8 +13,8 @@ use futures_core::{future::LocalBoxFuture, ready};
|
||||
|
||||
use crate::{
|
||||
dev::{ServiceRequest, ServiceResponse},
|
||||
error::{Error, Result},
|
||||
http::StatusCode,
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
/// Return type for [`ErrorHandlers`] custom handlers.
|
||||
|
@ -7,7 +7,7 @@ use std::{
|
||||
|
||||
use actix_http::{
|
||||
http::{HeaderMap, Method, Uri, Version},
|
||||
Error, Extensions, HttpMessage, Message, Payload, RequestHead,
|
||||
Extensions, HttpMessage, Message, Payload, RequestHead,
|
||||
};
|
||||
use actix_router::{Path, Url};
|
||||
use actix_utils::future::{ok, Ready};
|
||||
@ -17,7 +17,7 @@ use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
app_service::AppInitServiceState, config::AppConfig, error::UrlGenerationError,
|
||||
extract::FromRequest, info::ConnectionInfo, rmap::ResourceMap,
|
||||
info::ConnectionInfo, rmap::ResourceMap, Error, FromRequest,
|
||||
};
|
||||
|
||||
#[cfg(feature = "cookies")]
|
||||
@ -356,8 +356,7 @@ impl Drop for HttpRequest {
|
||||
|
||||
/// It is possible to get `HttpRequest` as an extractor handler parameter
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use actix_web::{web, App, HttpRequest};
|
||||
/// use serde_derive::Deserialize;
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::{any::type_name, ops::Deref};
|
||||
|
||||
use actix_http::error::Error;
|
||||
use actix_utils::future::{err, ok, Ready};
|
||||
|
||||
use crate::{dev::Payload, error::ErrorInternalServerError, FromRequest, HttpRequest};
|
||||
use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpRequest};
|
||||
|
||||
/// Request-local data extractor.
|
||||
///
|
||||
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
|
||||
use actix_http::{Error, Extensions};
|
||||
use actix_http::Extensions;
|
||||
use actix_router::IntoPattern;
|
||||
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
||||
use actix_service::{
|
||||
@ -13,14 +13,16 @@ use actix_service::{
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
use futures_util::future::join_all;
|
||||
|
||||
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
|
||||
use crate::extract::FromRequest;
|
||||
use crate::guard::Guard;
|
||||
use crate::handler::Handler;
|
||||
use crate::responder::Responder;
|
||||
use crate::route::{Route, RouteService};
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::{data::Data, HttpResponse};
|
||||
use crate::{
|
||||
data::Data,
|
||||
dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef},
|
||||
guard::Guard,
|
||||
handler::Handler,
|
||||
responder::Responder,
|
||||
route::{Route, RouteService},
|
||||
service::{ServiceRequest, ServiceResponse},
|
||||
Error, FromRequest, HttpResponse,
|
||||
};
|
||||
|
||||
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
|
||||
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{borrow::Cow, fmt};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use actix_http::{
|
||||
body::Body,
|
||||
@ -6,7 +6,7 @@ use actix_http::{
|
||||
};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::{error::InternalError, Error, HttpRequest, HttpResponse, HttpResponseBuilder};
|
||||
use crate::{Error, HttpRequest, HttpResponse, HttpResponseBuilder};
|
||||
|
||||
/// Trait implemented by types that can be converted to an HTTP response.
|
||||
///
|
||||
@ -226,15 +226,6 @@ impl<T: Responder> Responder for CustomResponder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Responder for InternalError<T>
|
||||
where
|
||||
T: fmt::Debug + fmt::Display + 'static,
|
||||
{
|
||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||
HttpResponse::from_error(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use actix_service::Service;
|
||||
|
@ -1,13 +1,14 @@
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
convert::TryInto,
|
||||
error::Error as StdError,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::{Body, BodyStream},
|
||||
body::{AnyBody, BodyStream},
|
||||
http::{
|
||||
header::{self, HeaderName, IntoHeaderPair, IntoHeaderValue},
|
||||
ConnectionType, Error as HttpError, StatusCode,
|
||||
@ -32,7 +33,7 @@ use crate::{
|
||||
///
|
||||
/// This type can be used to construct an instance of `Response` through a builder-like pattern.
|
||||
pub struct HttpResponseBuilder {
|
||||
res: Option<Response<Body>>,
|
||||
res: Option<Response<AnyBody>>,
|
||||
err: Option<HttpError>,
|
||||
#[cfg(feature = "cookies")]
|
||||
cookies: Option<CookieJar>,
|
||||
@ -310,7 +311,7 @@ impl HttpResponseBuilder {
|
||||
///
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn body<B: Into<Body>>(&mut self, body: B) -> HttpResponse<Body> {
|
||||
pub fn body<B: Into<AnyBody>>(&mut self, body: B) -> HttpResponse<AnyBody> {
|
||||
match self.message_body(body.into()) {
|
||||
Ok(res) => res,
|
||||
Err(err) => HttpResponse::from_error(err),
|
||||
@ -354,9 +355,9 @@ impl HttpResponseBuilder {
|
||||
pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
|
||||
where
|
||||
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
|
||||
E: Into<Error> + 'static,
|
||||
E: Into<Box<dyn StdError>> + 'static,
|
||||
{
|
||||
self.body(Body::from_message(BodyStream::new(stream)))
|
||||
self.body(AnyBody::from_message(BodyStream::new(stream)))
|
||||
}
|
||||
|
||||
/// Set a json body and generate `Response`
|
||||
@ -375,9 +376,9 @@ impl HttpResponseBuilder {
|
||||
self.insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
|
||||
}
|
||||
|
||||
self.body(Body::from(body))
|
||||
self.body(AnyBody::from(body))
|
||||
}
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,7 +387,7 @@ impl HttpResponseBuilder {
|
||||
/// `HttpResponseBuilder` can not be used after this call.
|
||||
#[inline]
|
||||
pub fn finish(&mut self) -> HttpResponse {
|
||||
self.body(Body::Empty)
|
||||
self.body(AnyBody::Empty)
|
||||
}
|
||||
|
||||
/// This method construct new `HttpResponseBuilder`
|
||||
@ -415,7 +416,7 @@ impl From<HttpResponseBuilder> for HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpResponseBuilder> for Response<Body> {
|
||||
impl From<HttpResponseBuilder> for Response<AnyBody> {
|
||||
fn from(mut builder: HttpResponseBuilder) -> Self {
|
||||
builder.finish().into()
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::{Body, MessageBody},
|
||||
body::{AnyBody, Body, MessageBody},
|
||||
http::{header::HeaderMap, StatusCode},
|
||||
Extensions, Response, ResponseHead,
|
||||
};
|
||||
@ -25,12 +25,12 @@ use {
|
||||
use crate::{error::Error, HttpResponseBuilder};
|
||||
|
||||
/// An HTTP Response
|
||||
pub struct HttpResponse<B = Body> {
|
||||
pub struct HttpResponse<B = AnyBody> {
|
||||
res: Response<B>,
|
||||
pub(crate) error: Option<Error>,
|
||||
}
|
||||
|
||||
impl HttpResponse<Body> {
|
||||
impl HttpResponse<AnyBody> {
|
||||
/// Create HTTP response builder with specific status.
|
||||
#[inline]
|
||||
pub fn build(status: StatusCode) -> HttpResponseBuilder {
|
||||
@ -48,13 +48,8 @@ impl HttpResponse<Body> {
|
||||
|
||||
/// Create an error response.
|
||||
#[inline]
|
||||
pub fn from_error(error: Error) -> Self {
|
||||
let res = error.as_response_error().error_response();
|
||||
|
||||
Self {
|
||||
res,
|
||||
error: Some(error),
|
||||
}
|
||||
pub fn from_error(error: impl Into<Error>) -> Self {
|
||||
error.into().as_response_error().error_response()
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +233,6 @@ impl<B> HttpResponse<B> {
|
||||
impl<B> fmt::Debug for HttpResponse<B>
|
||||
where
|
||||
B: MessageBody,
|
||||
B::Error: Into<Error>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("HttpResponse")
|
||||
|
18
src/route.rs
18
src/route.rs
@ -2,19 +2,19 @@
|
||||
|
||||
use std::{future::Future, rc::Rc};
|
||||
|
||||
use actix_http::{http::Method, Error};
|
||||
use actix_http::http::Method;
|
||||
use actix_service::{
|
||||
boxed::{self, BoxService, BoxServiceFactory},
|
||||
Service, ServiceFactory,
|
||||
};
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
use crate::extract::FromRequest;
|
||||
use crate::guard::{self, Guard};
|
||||
use crate::handler::{Handler, HandlerService};
|
||||
use crate::responder::Responder;
|
||||
use crate::service::{ServiceRequest, ServiceResponse};
|
||||
use crate::HttpResponse;
|
||||
use crate::{
|
||||
guard::{self, Guard},
|
||||
handler::{Handler, HandlerService},
|
||||
service::{ServiceRequest, ServiceResponse},
|
||||
Error, FromRequest, HttpResponse, Responder,
|
||||
};
|
||||
|
||||
/// Resource route definition
|
||||
///
|
||||
@ -188,7 +188,7 @@ impl Route {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
use std::{convert::Infallible, time::Duration};
|
||||
|
||||
use actix_rt::time::sleep;
|
||||
use bytes::Bytes;
|
||||
@ -215,7 +215,7 @@ mod tests {
|
||||
}))
|
||||
.route(web::post().to(|| async {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
Ok::<_, ()>(HttpResponse::Created())
|
||||
Ok::<_, Infallible>(HttpResponse::Created())
|
||||
}))
|
||||
.route(web::delete().to(|| async {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
|
@ -1,23 +1,25 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
cmp, fmt, io,
|
||||
cmp,
|
||||
error::Error as StdError,
|
||||
fmt, io,
|
||||
marker::PhantomData,
|
||||
net,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use actix_http::{
|
||||
body::MessageBody, Error, Extensions, HttpService, KeepAlive, Request, Response,
|
||||
};
|
||||
use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response};
|
||||
use actix_server::{Server, ServerBuilder};
|
||||
use actix_service::{map_config, IntoServiceFactory, Service, ServiceFactory};
|
||||
use actix_service::{
|
||||
map_config, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,
|
||||
};
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
use actix_tls::accept::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
|
||||
#[cfg(feature = "rustls")]
|
||||
use actix_tls::accept::rustls::ServerConfig as RustlsServerConfig;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::{config::AppConfig, Error};
|
||||
|
||||
struct Socket {
|
||||
scheme: &'static str,
|
||||
@ -81,7 +83,7 @@ where
|
||||
S::Service: 'static,
|
||||
// S::Service: 'static,
|
||||
B: MessageBody + 'static,
|
||||
B::Error: Into<Error>,
|
||||
B::Error: Into<Box<dyn StdError>>,
|
||||
{
|
||||
/// Create new HTTP server with application factory
|
||||
pub fn new(factory: F) -> Self {
|
||||
@ -301,7 +303,11 @@ where
|
||||
svc
|
||||
};
|
||||
|
||||
svc.finish(map_config(factory(), move |_| {
|
||||
let fac = factory()
|
||||
.into_factory()
|
||||
.map_err(|err| err.into().error_response());
|
||||
|
||||
svc.finish(map_config(fac, move |_| {
|
||||
AppConfig::new(false, host.clone(), addr)
|
||||
}))
|
||||
.tcp()
|
||||
@ -356,7 +362,11 @@ where
|
||||
svc
|
||||
};
|
||||
|
||||
svc.finish(map_config(factory(), move |_| {
|
||||
let fac = factory()
|
||||
.into_factory()
|
||||
.map_err(|err| err.into().error_response());
|
||||
|
||||
svc.finish(map_config(fac, move |_| {
|
||||
AppConfig::new(true, host.clone(), addr)
|
||||
}))
|
||||
.openssl(acceptor.clone())
|
||||
@ -410,7 +420,11 @@ where
|
||||
svc
|
||||
};
|
||||
|
||||
svc.finish(map_config(factory(), move |_| {
|
||||
let fac = factory()
|
||||
.into_factory()
|
||||
.map_err(|err| err.into().error_response());
|
||||
|
||||
svc.finish(map_config(fac, move |_| {
|
||||
AppConfig::new(true, host.clone(), addr)
|
||||
}))
|
||||
.rustls(config.clone())
|
||||
@ -533,7 +547,11 @@ where
|
||||
svc
|
||||
};
|
||||
|
||||
svc.finish(map_config(factory(), move |_| config.clone()))
|
||||
let fac = factory()
|
||||
.into_factory()
|
||||
.map_err(|err| err.into().error_response());
|
||||
|
||||
svc.finish(map_config(fac, move |_| config.clone()))
|
||||
})
|
||||
})?;
|
||||
Ok(self)
|
||||
@ -568,14 +586,20 @@ where
|
||||
c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)),
|
||||
socket_addr,
|
||||
);
|
||||
|
||||
let fac = factory()
|
||||
.into_factory()
|
||||
.map_err(|err| err.into().error_response());
|
||||
|
||||
fn_service(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }).and_then(
|
||||
HttpService::build()
|
||||
.keep_alive(c.keep_alive)
|
||||
.client_timeout(c.client_timeout)
|
||||
.finish(map_config(factory(), move |_| config.clone())),
|
||||
.finish(map_config(fac, move |_| config.clone())),
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
@ -2,24 +2,24 @@ use std::cell::{Ref, RefMut};
|
||||
use std::rc::Rc;
|
||||
use std::{fmt, net};
|
||||
|
||||
use actix_http::body::{Body, MessageBody};
|
||||
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||
use actix_http::{
|
||||
Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead,
|
||||
body::{AnyBody, MessageBody},
|
||||
http::{HeaderMap, Method, StatusCode, Uri, Version},
|
||||
Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead,
|
||||
};
|
||||
use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
|
||||
use actix_service::{IntoServiceFactory, ServiceFactory};
|
||||
#[cfg(feature = "cookies")]
|
||||
use cookie::{Cookie, ParseError as CookieParseError};
|
||||
|
||||
use crate::dev::insert_slash;
|
||||
use crate::guard::Guard;
|
||||
use crate::info::ConnectionInfo;
|
||||
use crate::request::HttpRequest;
|
||||
use crate::rmap::ResourceMap;
|
||||
use crate::{
|
||||
config::{AppConfig, AppService},
|
||||
HttpResponse,
|
||||
dev::insert_slash,
|
||||
guard::Guard,
|
||||
info::ConnectionInfo,
|
||||
request::HttpRequest,
|
||||
rmap::ResourceMap,
|
||||
Error, HttpResponse,
|
||||
};
|
||||
|
||||
pub trait HttpServiceFactory {
|
||||
@ -330,15 +330,15 @@ impl fmt::Debug for ServiceRequest {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServiceResponse<B = Body> {
|
||||
pub struct ServiceResponse<B = AnyBody> {
|
||||
request: HttpRequest,
|
||||
response: HttpResponse<B>,
|
||||
}
|
||||
|
||||
impl ServiceResponse<Body> {
|
||||
impl ServiceResponse<AnyBody> {
|
||||
/// Create service response from the error
|
||||
pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self {
|
||||
let response = HttpResponse::from_error(err.into());
|
||||
let response = HttpResponse::from_error(err);
|
||||
ServiceResponse { request, response }
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ impl<T: Serialize> Responder for Form<T> {
|
||||
Ok(body) => HttpResponse::Ok()
|
||||
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
|
||||
.body(body),
|
||||
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err).into()),
|
||||
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ impl<T: Serialize> Responder for Json<T> {
|
||||
Ok(body) => HttpResponse::Ok()
|
||||
.content_type(mime::APPLICATION_JSON)
|
||||
.body(body),
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -500,7 +500,7 @@ mod tests {
|
||||
};
|
||||
let resp =
|
||||
HttpResponse::BadRequest().body(serde_json::to_string(&msg).unwrap());
|
||||
InternalError::from_response(err, resp.into()).into()
|
||||
InternalError::from_response(err, resp).into()
|
||||
}))
|
||||
.to_http_parts();
|
||||
|
||||
|
@ -2,14 +2,13 @@
|
||||
|
||||
use std::{fmt, ops, sync::Arc};
|
||||
|
||||
use actix_http::error::Error;
|
||||
use actix_router::PathDeserializer;
|
||||
use actix_utils::future::{ready, Ready};
|
||||
use serde::de;
|
||||
|
||||
use crate::{
|
||||
dev::Payload,
|
||||
error::{ErrorNotFound, PathError},
|
||||
error::{Error, ErrorNotFound, PathError},
|
||||
FromRequest, HttpRequest,
|
||||
};
|
||||
|
||||
@ -296,11 +295,8 @@ mod tests {
|
||||
async fn test_custom_err_handler() {
|
||||
let (req, mut pl) = TestRequest::with_uri("/name/user1/")
|
||||
.app_data(PathConfig::default().error_handler(|err, _| {
|
||||
error::InternalError::from_response(
|
||||
err,
|
||||
HttpResponse::Conflict().finish().into(),
|
||||
)
|
||||
.into()
|
||||
error::InternalError::from_response(err, HttpResponse::Conflict().finish())
|
||||
.into()
|
||||
}))
|
||||
.to_http_parts();
|
||||
|
||||
|
@ -267,7 +267,7 @@ mod tests {
|
||||
let req = TestRequest::with_uri("/name/user1/")
|
||||
.app_data(QueryConfig::default().error_handler(|e, _| {
|
||||
let resp = HttpResponse::UnprocessableEntity().finish();
|
||||
InternalError::from_response(e, resp.into()).into()
|
||||
InternalError::from_response(e, resp).into()
|
||||
}))
|
||||
.to_srv_request();
|
||||
|
||||
|
Reference in New Issue
Block a user