migrate error type (except tests)

This commit is contained in:
Rob Ede 2021-06-05 01:29:24 +01:00
parent 93840507e1
commit d514680631
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
42 changed files with 757 additions and 483 deletions

View File

@ -83,7 +83,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true }
actix-server = "2.0.0-beta.3" actix-server = "2.0.0-beta.3"
actix-http-test = { version = "3.0.0-beta.4", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.4", features = ["openssl"] }
actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] } actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] }
criterion = "0.3" criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.8" env_logger = "0.8"
rcgen = "0.8" rcgen = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -1,4 +1,4 @@
use std::io; use std::{convert::Infallible, io};
use actix_http::{http::StatusCode, HttpService, Response}; use actix_http::{http::StatusCode, HttpService, Response};
use actix_server::Server; use actix_server::Server;
@ -22,7 +22,7 @@ async fn main() -> io::Result<()> {
HeaderValue::from_static("dummy value!"), HeaderValue::from_static("dummy value!"),
)); ));
Ok::<_, ()>(res.body("Hello world!")) Ok::<_, Infallible>(res.body("Hello world!"))
}) })
.tcp() .tcp()
})? })?

View File

@ -6,9 +6,9 @@
//! $ echo 'GET / HTTP/1.1\n\n' | nc 127.0.0.1 8080 //! $ echo 'GET / HTTP/1.1\n\n' | nc 127.0.0.1 8080
//! ``` //! ```
use std::{io, time::Duration}; use std::{convert::Infallible, io, time::Duration};
use actix_http::{body::BodyStream, Error, HttpService, Response}; use actix_http::{body::BodyStream, HttpService, Response};
use actix_server::Server; use actix_server::Server;
use async_stream::stream; use async_stream::stream;
use bytes::Bytes; use bytes::Bytes;
@ -24,13 +24,13 @@ async fn main() -> io::Result<()> {
log::info!("{:?}", req); log::info!("{:?}", req);
let res = Response::ok(); let res = Response::ok();
Ok::<_, ()>(res.set_body(BodyStream::new(stream! { Ok::<_, Infallible>(res.set_body(BodyStream::new(stream! {
yield Ok(Bytes::from("123")); yield Ok(Bytes::from("123"));
yield Ok(Bytes::from("456")); yield Ok(Bytes::from("456"));
actix_rt::time::sleep(Duration::from_millis(1000)).await; actix_rt::time::sleep(Duration::from_millis(1000)).await;
yield Err(Error::from(())); yield Err(io::Error::new(io::ErrorKind::Other, ""));
}))) })))
}) })
.tcp() .tcp()

View File

@ -76,7 +76,9 @@ impl MessageBody for AnyBody {
// TODO: MSRV 1.51: poll_map_err // TODO: MSRV 1.51: poll_map_err
AnyBody::Message(body) => match ready!(body.as_pin_mut().poll_next(cx)) { AnyBody::Message(body) => match ready!(body.as_pin_mut().poll_next(cx)) {
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), Some(Err(err)) => {
Poll::Ready(Some(Err(Error::new_body().with_cause(err))))
}
Some(Ok(val)) => Poll::Ready(Some(Ok(val))), Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
None => Poll::Ready(None), None => Poll::Ready(None),
}, },
@ -222,7 +224,7 @@ impl MessageBody for BoxAnyBody {
) -> Poll<Option<Result<Bytes, Self::Error>>> { ) -> Poll<Option<Result<Bytes, Self::Error>>> {
// TODO: MSRV 1.51: poll_map_err // TODO: MSRV 1.51: poll_map_err
match ready!(self.0.as_mut().poll_next(cx)) { match ready!(self.0.as_mut().poll_next(cx)) {
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), Some(Err(err)) => Poll::Ready(Some(Err(Error::new_body().with_cause(err)))),
Some(Ok(val)) => Poll::Ready(Some(Ok(val))), Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
None => Poll::Ready(None), None => Poll::Ready(None),
} }

View File

@ -191,11 +191,15 @@ mod tests {
} }
#[actix_rt::test] #[actix_rt::test]
async fn test_box() { async fn test_box_and_pin() {
let val = Box::new(()); let val = Box::new(());
pin!(val); pin!(val);
assert_eq!(val.size(), BodySize::Empty); assert_eq!(val.size(), BodySize::Empty);
assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none()); assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none());
let mut val = Box::pin(());
assert_eq!(val.size(), BodySize::Empty);
assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none());
} }
#[actix_rt::test] #[actix_rt::test]

View File

@ -5,8 +5,8 @@ use derive_more::{Display, From};
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use actix_tls::accept::openssl::SslError; use actix_tls::accept::openssl::SslError;
use crate::error::{Error, ParseError, ResponseError}; use crate::error::{Error, ParseError};
use crate::http::{Error as HttpError, StatusCode}; use crate::http::Error as HttpError;
/// A set of errors that can occur while connecting to an HTTP host /// A set of errors that can occur while connecting to an HTTP host
#[derive(Debug, Display, From)] #[derive(Debug, Display, From)]
@ -119,19 +119,6 @@ pub enum SendRequestError {
impl std::error::Error for SendRequestError {} impl std::error::Error for SendRequestError {}
/// Convert `SendRequestError` to a server `Response`
impl ResponseError for SendRequestError {
fn status_code(&self) -> StatusCode {
match *self {
SendRequestError::Connect(ConnectError::Timeout) => {
StatusCode::GATEWAY_TIMEOUT
}
SendRequestError::Connect(_) => StatusCode::BAD_REQUEST,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
/// A set of errors that can occur during freezing a request /// A set of errors that can occur during freezing a request
#[derive(Debug, Display, From)] #[derive(Debug, Display, From)]
pub enum FreezeRequestError { pub enum FreezeRequestError {

View File

@ -317,7 +317,7 @@ pub enum EncoderError<E> {
Body(E), Body(E),
#[display(fmt = "boxed")] #[display(fmt = "boxed")]
Boxed(Error), Boxed(Box<dyn StdError>),
#[display(fmt = "blocking")] #[display(fmt = "blocking")]
Blocking(BlockingError), Blocking(BlockingError),
@ -331,14 +331,3 @@ impl<E: StdError> StdError for EncoderError<E> {
None None
} }
} }
impl<E: Into<Error>> From<EncoderError<E>> for Error {
fn from(err: EncoderError<E>) -> Self {
match err {
EncoderError::Body(err) => err.into(),
EncoderError::Boxed(err) => err,
EncoderError::Blocking(err) => err.into(),
EncoderError::Io(err) => err.into(),
}
}
}

View File

@ -1,178 +1,143 @@
//! Error and Result module //! Error and Result module
use std::{ use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
error::Error as StdError,
fmt,
io::{self, Write as _},
str::Utf8Error,
string::FromUtf8Error,
};
use bytes::BytesMut;
use derive_more::{Display, Error, From}; use derive_more::{Display, Error, From};
use http::{header, uri::InvalidUri, StatusCode}; use http::{uri::InvalidUri, StatusCode};
use serde::de::value::Error as DeError;
use crate::{ use crate::{Response, body::{AnyBody, Body}, ws};
body::{AnyBody, Body},
helpers::Writer,
Response,
};
pub use http::Error as HttpError; pub use http::Error as HttpError;
/// 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 { pub struct Error {
cause: Box<dyn ResponseError>, inner: Box<ErrorInner>,
}
pub(crate) struct ErrorInner {
#[allow(dead_code)]
kind: Kind,
cause: Option<Box<dyn StdError>>,
} }
impl Error { impl Error {
/// Returns the reference to the underlying `ResponseError`. fn new(kind: Kind) -> Self {
pub fn as_response_error(&self) -> &dyn ResponseError { Self {
self.cause.as_ref() inner: Box::new(ErrorInner { kind, cause: None }),
}
/// 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())
} }
} }
/// Errors that can generate responses. pub(crate) fn new_http() -> Self {
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found Self::new(Kind::Http)
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. pub(crate) fn new_parse() -> Self {
/// Self::new(Kind::Parse)
/// 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) -> Response<Body> {
let mut resp = Response::new(self.status_code());
let mut buf = BytesMut::new();
let _ = write!(Writer(&mut buf), "{}", self);
resp.headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain; charset=utf-8"),
);
resp.set_body(Body::from(buf))
} }
downcast_get_type_id!(); pub(crate) fn new_payload() -> Self {
Self::new(Kind::Payload)
} }
downcast!(ResponseError); pub(crate) fn new_body() -> Self {
Self::new(Kind::Body)
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.cause, f)
} }
pub(crate) fn new_send_response() -> Self {
Self::new(Kind::SendResponse)
}
pub(crate) fn new_io() -> Self {
Self::new(Kind::Io)
}
pub(crate) fn new_ws() -> Self {
Self::new(Kind::Ws)
}
pub(crate) fn with_cause(mut self, cause: impl Into<Box<dyn StdError>>) -> Self {
self.inner.cause = Some(cause.into());
self
}
}
impl From<Error> for Response<AnyBody> {
fn from(err: Error) -> Self {
let status_code = match err.inner.kind {
Kind::Parse => StatusCode::BAD_REQUEST,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
Response::new(status_code).set_body(Body::from(err.to_string()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
pub enum Kind {
#[display(fmt = "error processing HTTP")]
Http,
#[display(fmt = "error parsing HTTP message")]
Parse,
#[display(fmt = "request payload read error")]
Payload,
#[display(fmt = "response body write error")]
Body,
#[display(fmt = "send response error")]
SendResponse,
#[display(fmt = "error in WebSocket process")]
Ws,
#[display(fmt = "connection error")]
Io,
} }
impl fmt::Debug for Error { impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", &self.cause) // TODO: more detail
f.write_str("actix_http::Error")
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner.cause.as_ref() {
Some(err) => write!(f, "{}: {}", &self.inner.kind, err),
None => write!(f, "{}", &self.inner.kind),
}
} }
} }
impl StdError for Error { impl StdError for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
None self.inner.cause.as_ref().map(|err| err.as_ref())
}
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from(UnitError)
} }
} }
impl From<std::convert::Infallible> for Error { impl From<std::convert::Infallible> for Error {
fn from(val: std::convert::Infallible) -> Self { fn from(err: std::convert::Infallible) -> Self {
match val {} match err {}
} }
} }
/// Convert `Error` to a `Response` instance impl From<ws::ProtocolError> for Error {
impl From<Error> for Response<Body> { fn from(err: ws::ProtocolError) -> Self {
fn from(err: Error) -> Self { Self::new_ws().with_cause(err)
Response::from_error(err)
} }
} }
/// `Error` for any error that implements `ResponseError` impl From<HttpError> for Error {
impl<T: ResponseError + 'static> From<T> for Error { fn from(err: HttpError) -> Self {
fn from(err: T) -> Error { Self::new_http().with_cause(err)
Error {
cause: Box::new(err),
}
}
}
#[derive(Debug, Display, Error)]
#[display(fmt = "Unknown Error")]
pub(crate) struct UnitError;
impl ResponseError for Box<dyn StdError + 'static> {}
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`UnitError`].
impl ResponseError for UnitError {}
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix_tls::accept::openssl::SslError`].
#[cfg(feature = "openssl")]
impl ResponseError for actix_tls::accept::openssl::SslError {}
/// Returns [`StatusCode::BAD_REQUEST`] for [`DeError`].
impl ResponseError for DeError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
} }
} }
/// Returns [`StatusCode::BAD_REQUEST`] for [`Utf8Error`]. impl From<ws::HandshakeError> for Error {
impl ResponseError for Utf8Error { fn from(err: ws::HandshakeError) -> Self {
fn status_code(&self) -> StatusCode { Self::new_ws().with_cause(err)
StatusCode::BAD_REQUEST
}
}
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`HttpError`].
impl ResponseError for HttpError {}
/// Inspects the underlying [`io::ErrorKind`] and returns an appropriate status code.
///
/// If the error is [`io::ErrorKind::NotFound`], [`StatusCode::NOT_FOUND`] is returned. If the
/// error is [`io::ErrorKind::PermissionDenied`], [`StatusCode::FORBIDDEN`] is returned. Otherwise,
/// [`StatusCode::INTERNAL_SERVER_ERROR`] is returned.
impl ResponseError for io::Error {
fn status_code(&self) -> StatusCode {
match self.kind() {
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
/// Returns [`StatusCode::BAD_REQUEST`] for [`header::InvalidHeaderValue`].
impl ResponseError for header::InvalidHeaderValue {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
} }
} }
@ -222,13 +187,6 @@ pub enum ParseError {
Utf8(Utf8Error), Utf8(Utf8Error),
} }
/// Return `BadRequest` for `ParseError`
impl ResponseError for ParseError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
impl From<io::Error> for ParseError { impl From<io::Error> for ParseError {
fn from(err: io::Error) -> ParseError { fn from(err: io::Error) -> ParseError {
ParseError::Io(err) ParseError::Io(err)
@ -267,14 +225,23 @@ impl From<httparse::Error> for ParseError {
} }
} }
impl From<ParseError> for Error {
fn from(err: ParseError) -> Self {
Self::new_parse().with_cause(err)
}
}
impl From<ParseError> for Response<AnyBody> {
fn from(err: ParseError) -> Self {
Error::from(err).into()
}
}
/// A set of errors that can occur running blocking tasks in thread pool. /// A set of errors that can occur running blocking tasks in thread pool.
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "Blocking thread pool is gone")] #[display(fmt = "Blocking thread pool is gone")]
pub struct BlockingError; pub struct BlockingError;
/// `InternalServerError` for `BlockingError`
impl ResponseError for BlockingError {}
/// A set of errors that can occur during payload parsing. /// A set of errors that can occur during payload parsing.
#[derive(Debug, Display)] #[derive(Debug, Display)]
#[non_exhaustive] #[non_exhaustive]
@ -348,16 +315,9 @@ impl From<BlockingError> for PayloadError {
} }
} }
/// `PayloadError` returns two possible results: impl From<PayloadError> for Error {
/// fn from(err: PayloadError) -> Self {
/// - `Overflow` returns `PayloadTooLarge` Self::new_payload().with_cause(err)
/// - Other errors returns `BadRequest`
impl ResponseError for PayloadError {
fn status_code(&self) -> StatusCode {
match *self {
PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
_ => StatusCode::BAD_REQUEST,
}
} }
} }
@ -444,12 +404,6 @@ mod content_type_test_impls {
} }
} }
impl ResponseError for ContentTypeError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -458,42 +412,33 @@ mod tests {
#[test] #[test]
fn test_into_response() { fn test_into_response() {
let resp: Response<Body> = ParseError::Incomplete.error_response(); let resp: Response<AnyBody> = ParseError::Incomplete.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into(); let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
let resp: Response<Body> = err.error_response(); let resp: Response<AnyBody> = Error::new_http().with_cause(err).into();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
} }
#[test] #[test]
fn test_as_response() { fn test_as_response() {
let orig = io::Error::new(io::ErrorKind::Other, "other"); let orig = io::Error::new(io::ErrorKind::Other, "other");
let e: Error = ParseError::Io(orig).into(); let err: Error = ParseError::Io(orig).into();
assert_eq!(format!("{}", e.as_response_error()), "IO error: other"); assert_eq!(format!("{}", err), "error parsing HTTP message: IO error: other");
}
#[test]
fn test_error_cause() {
let orig = io::Error::new(io::ErrorKind::Other, "other");
let desc = orig.to_string();
let e = Error::from(orig);
assert_eq!(format!("{}", e.as_response_error()), desc);
} }
#[test] #[test]
fn test_error_display() { fn test_error_display() {
let orig = io::Error::new(io::ErrorKind::Other, "other"); let orig = io::Error::new(io::ErrorKind::Other, "other");
let desc = orig.to_string(); let err = Error::new_io().with_cause(orig);
let e = Error::from(orig); assert_eq!("connection error: other", err.to_string());
assert_eq!(format!("{}", e), desc);
} }
#[test] #[test]
fn test_error_http_response() { fn test_error_http_response() {
let orig = io::Error::new(io::ErrorKind::Other, "other"); let orig = io::Error::new(io::ErrorKind::Other, "other");
let e = Error::from(orig); let err = Error::new_io().with_cause(orig);
let resp: Response<Body> = e.into(); let resp: Response<AnyBody> = err.into();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
} }
@ -545,14 +490,4 @@ mod tests {
from!(httparse::Error::TooManyHeaders => ParseError::TooLarge); from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
from!(httparse::Error::Version => ParseError::Version); from!(httparse::Error::Version => ParseError::Version);
} }
#[test]
fn test_error_casting() {
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());
}
} }

View File

@ -6,14 +6,18 @@ use std::{cmp, io};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use crate::body::BodySize; use crate::{
use crate::config::ServiceConfig; body::BodySize,
use crate::header::{map::Value, HeaderName}; config::ServiceConfig,
use crate::helpers; header::{map::Value, HeaderName},
use crate::http::header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; helpers,
use crate::http::{HeaderMap, StatusCode, Version}; http::{
use crate::message::{ConnectionType, RequestHeadType}; header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
use crate::response::Response; HeaderMap, StatusCode, Version,
},
message::{ConnectionType, RequestHeadType},
response::Response,
};
const AVERAGE_HEADER_SIZE: usize = 30; const AVERAGE_HEADER_SIZE: usize = 30;
@ -287,7 +291,7 @@ impl MessageType for RequestHeadType {
let head = self.as_ref(); let head = self.as_ref();
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE); dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
write!( write!(
helpers::Writer(dst), helpers::MutWriter(dst),
"{} {} {}", "{} {} {}",
head.method, head.method,
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"), head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
@ -420,7 +424,7 @@ impl TransferEncoding {
*eof = true; *eof = true;
buf.extend_from_slice(b"0\r\n\r\n"); buf.extend_from_slice(b"0\r\n\r\n");
} else { } else {
writeln!(helpers::Writer(buf), "{:X}\r", msg.len()) writeln!(helpers::MutWriter(buf), "{:X}\r", msg.len())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
buf.reserve(msg.len() + 2); buf.reserve(msg.len() + 2);

View File

@ -81,7 +81,9 @@ where
let _ = this.body.take(); let _ = this.body.take();
} }
let framed = this.framed.as_mut().as_pin_mut().unwrap(); let framed = this.framed.as_mut().as_pin_mut().unwrap();
framed.write(Message::Chunk(item))?; framed.write(Message::Chunk(item)).map_err(|err| {
Error::new_send_response().with_cause(err)
})?;
} }
Poll::Pending => body_ready = false, Poll::Pending => body_ready = false,
} }
@ -92,7 +94,10 @@ where
// flush write buffer // flush write buffer
if !framed.is_write_buf_empty() { if !framed.is_write_buf_empty() {
match framed.flush(cx)? { match framed
.flush(cx)
.map_err(|err| Error::new_send_response().with_cause(err))?
{
Poll::Ready(_) => { Poll::Ready(_) => {
if body_ready { if body_ready {
continue; continue;
@ -106,7 +111,9 @@ where
// send response // send response
if let Some(res) = this.res.take() { if let Some(res) = this.res.take() {
framed.write(res)?; framed
.write(res)
.map_err(|err| Error::new_send_response().with_cause(err))?;
continue; continue;
} }

View File

@ -27,7 +27,9 @@ pub(crate) fn write_status_line<B: BufMut>(version: Version, n: u16, buf: &mut B
buf.put_u8(b' '); buf.put_u8(b' ');
} }
/// NOTE: bytes object has to contain enough space /// Write out content length header.
///
/// Buffer must to contain enough space or be implicitly extendable.
pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) { pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) {
if n == 0 { if n == 0 {
buf.put_slice(b"\r\ncontent-length: 0\r\n"); buf.put_slice(b"\r\ncontent-length: 0\r\n");
@ -41,11 +43,15 @@ pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) {
buf.put_slice(b"\r\n"); buf.put_slice(b"\r\n");
} }
// TODO: bench why this is needed vs Buf::writer /// An `io::Write`r that only requires mutable reference and assumes that there is space available
/// An `io` writer for a `BufMut` that should only be used once and on an empty buffer. /// in the buffer for every write operation or that it can be extended implicitly (like
pub(crate) struct Writer<'a, B>(pub &'a mut B); /// `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 Writer<'a, B> impl<'a, B> io::Write for MutWriter<'a, B>
where where
B: BufMut, B: BufMut,
{ {

View File

@ -54,7 +54,7 @@ pub mod ws;
pub use self::builder::HttpServiceBuilder; pub use self::builder::HttpServiceBuilder;
pub use self::config::{KeepAlive, ServiceConfig}; pub use self::config::{KeepAlive, ServiceConfig};
pub use self::error::{Error, ResponseError}; pub use self::error::Error;
pub use self::extensions::Extensions; pub use self::extensions::Extensions;
pub use self::header::ContentEncoding; pub use self::header::ContentEncoding;
pub use self::http_message::HttpMessage; pub use self::http_message::HttpMessage;

View File

@ -66,16 +66,6 @@ impl Response<AnyBody> {
} }
// end shortcuts // end shortcuts
/// Constructs a new response from an error.
#[inline]
pub fn from_error(error: Error) -> Response<AnyBody> {
let resp = error.as_response_error().error_response();
if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR {
debug!("Internal Server Error: {:?}", error);
}
resp
}
} }
impl<B> Response<B> { impl<B> Response<B> {
@ -251,14 +241,6 @@ impl From<ResponseBuilder> for Response<AnyBody> {
} }
} }
impl From<()> for Response<AnyBody> {
fn from(_: ()) -> Self {
Error::from(crate::error::UnitError)
.as_response_error()
.error_response()
}
}
impl From<std::convert::Infallible> for Response<AnyBody> { impl From<std::convert::Infallible> for Response<AnyBody> {
fn from(val: std::convert::Infallible) -> Self { fn from(val: std::convert::Infallible) -> Self {
match val {} match val {}

View File

@ -14,7 +14,7 @@ use bytes::Bytes;
use futures_core::Stream; use futures_core::Stream;
use crate::{ use crate::{
body::{Body, BodyStream}, body::{AnyBody, BodyStream},
error::{Error, HttpError}, error::{Error, HttpError},
header::{self, IntoHeaderPair, IntoHeaderValue}, header::{self, IntoHeaderPair, IntoHeaderValue},
message::{BoxedResponseHead, ConnectionType, ResponseHead}, message::{BoxedResponseHead, ConnectionType, ResponseHead},
@ -236,9 +236,9 @@ impl ResponseBuilder {
/// ///
/// This `ResponseBuilder` will be left in a useless state. /// This `ResponseBuilder` will be left in a useless state.
#[inline] #[inline]
pub fn body<B: Into<Body>>(&mut self, body: B) -> Response<Body> { pub fn body<B: Into<AnyBody>>(&mut self, body: B) -> Response<AnyBody> {
self.message_body(body.into()) self.message_body(body.into())
.unwrap_or_else(Response::from_error) .unwrap_or_else(Response::from)
} }
/// Generate response with a body. /// Generate response with a body.
@ -246,7 +246,7 @@ impl ResponseBuilder {
/// This `ResponseBuilder` will be left in a useless state. /// This `ResponseBuilder` will be left in a useless state.
pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> { pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {
if let Some(err) = self.err.take() { if let Some(err) = self.err.take() {
return Err(err.into()); return Err(Error::new_http().with_cause(err));
} }
let head = self.head.take().expect("cannot reuse response builder"); let head = self.head.take().expect("cannot reuse response builder");
@ -257,20 +257,20 @@ impl ResponseBuilder {
/// ///
/// This `ResponseBuilder` will be left in a useless state. /// This `ResponseBuilder` will be left in a useless state.
#[inline] #[inline]
pub fn streaming<S, E>(&mut self, stream: S) -> Response<Body> pub fn streaming<S, E>(&mut self, stream: S) -> Response<AnyBody>
where where
S: Stream<Item = Result<Bytes, E>> + 'static, S: Stream<Item = Result<Bytes, E>> + 'static,
E: Into<Box<dyn StdError>> + 'static, E: Into<Box<dyn StdError>> + 'static,
{ {
self.body(Body::from_message(BodyStream::new(stream))) self.body(AnyBody::from_message(BodyStream::new(stream)))
} }
/// Generate response with an empty body. /// Generate response with an empty body.
/// ///
/// This `ResponseBuilder` will be left in a useless state. /// This `ResponseBuilder` will be left in a useless state.
#[inline] #[inline]
pub fn finish(&mut self) -> Response<Body> { pub fn finish(&mut self) -> Response<AnyBody> {
self.body(Body::Empty) self.body(AnyBody::Empty)
} }
/// Create an owned `ResponseBuilder`, leaving the original in a useless state. /// Create an owned `ResponseBuilder`, leaving the original in a useless state.
@ -328,7 +328,7 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
} }
impl Future for ResponseBuilder { impl Future for ResponseBuilder {
type Output = Result<Response<Body>, Error>; type Output = Result<Response<AnyBody>, Error>;
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(Ok(self.finish())) Poll::Ready(Ok(self.finish()))

View File

@ -72,7 +72,7 @@ mod inner {
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use crate::{body::AnyBody, Response, ResponseError}; use crate::{body::AnyBody, Response};
/// Framed transport errors /// Framed transport errors
pub enum DispatcherError<E, U, I> pub enum DispatcherError<E, U, I>
@ -136,15 +136,6 @@ mod inner {
} }
} }
impl<E, U, I> ResponseError for DispatcherError<E, U, I>
where
E: fmt::Debug + fmt::Display,
U: Encoder<I> + Decoder,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
}
impl<E, U, I> From<DispatcherError<E, U, I>> for Response<AnyBody> impl<E, U, I> From<DispatcherError<E, U, I>> for Response<AnyBody>
where where
E: fmt::Debug + fmt::Display, E: fmt::Debug + fmt::Display,

View File

@ -9,8 +9,8 @@ use derive_more::{Display, Error, From};
use http::{header, Method, StatusCode}; use http::{header, Method, StatusCode};
use crate::{ use crate::{
body::Body, error::ResponseError, header::HeaderValue, message::RequestHead, body::AnyBody, header::HeaderValue, message::RequestHead, response::Response,
response::Response, ResponseBuilder, ResponseBuilder,
}; };
mod codec; mod codec;
@ -68,8 +68,6 @@ pub enum ProtocolError {
Io(io::Error), Io(io::Error),
} }
impl ResponseError for ProtocolError {}
/// WebSocket handshake errors /// WebSocket handshake errors
#[derive(Debug, PartialEq, Display, Error)] #[derive(Debug, PartialEq, Display, Error)]
pub enum HandshakeError { pub enum HandshakeError {
@ -98,43 +96,54 @@ pub enum HandshakeError {
BadWebsocketKey, BadWebsocketKey,
} }
impl ResponseError for HandshakeError { impl From<&HandshakeError> for Response<AnyBody> {
fn error_response(&self) -> Response<Body> { fn from(err: &HandshakeError) -> Self {
match self { match err {
HandshakeError::GetMethodRequired => { HandshakeError::GetMethodRequired => {
Response::build(StatusCode::METHOD_NOT_ALLOWED) let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
.insert_header((header::ALLOW, "GET")) res.headers_mut()
.finish() .insert(header::ALLOW, HeaderValue::from_static("GET"));
res
} }
HandshakeError::NoWebsocketUpgrade => { HandshakeError::NoWebsocketUpgrade => {
Response::build(StatusCode::BAD_REQUEST) let mut res = Response::bad_request();
.reason("No WebSocket Upgrade header found") res.head_mut().reason = Some("No WebSocket Upgrade header found");
.finish() res
} }
HandshakeError::NoConnectionUpgrade => { HandshakeError::NoConnectionUpgrade => {
Response::build(StatusCode::BAD_REQUEST) let mut res = Response::bad_request();
.reason("No Connection upgrade") res.head_mut().reason = Some("No Connection upgrade");
.finish() res
} }
HandshakeError::NoVersionHeader => Response::build(StatusCode::BAD_REQUEST) HandshakeError::NoVersionHeader => {
.reason("WebSocket version header is required") let mut res = Response::bad_request();
.finish(), res.head_mut().reason = Some("WebSocket version header is required");
res
}
HandshakeError::UnsupportedVersion => { HandshakeError::UnsupportedVersion => {
Response::build(StatusCode::BAD_REQUEST) let mut res = Response::bad_request();
.reason("Unsupported WebSocket version") res.head_mut().reason = Some("Unsupported WebSocket version");
.finish() res
} }
HandshakeError::BadWebsocketKey => Response::build(StatusCode::BAD_REQUEST) HandshakeError::BadWebsocketKey => {
.reason("Handshake error") let mut res = Response::bad_request();
.finish(), res.head_mut().reason = Some("Handshake error");
res
} }
} }
} }
}
impl From<HandshakeError> for Response<AnyBody> {
fn from(err: HandshakeError) -> Self {
err.into()
}
}
/// Verify WebSocket handshake request and create handshake response. /// Verify WebSocket handshake request and create handshake response.
pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> { pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
@ -213,7 +222,7 @@ pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::TestRequest; use crate::{body::AnyBody, test::TestRequest};
use http::{header, Method}; use http::{header, Method};
#[test] #[test]
@ -327,18 +336,18 @@ mod tests {
} }
#[test] #[test]
fn test_wserror_http_response() { fn test_ws_error_http_response() {
let resp = HandshakeError::GetMethodRequired.error_response(); let resp: Response<AnyBody> = HandshakeError::GetMethodRequired.into();
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let resp = HandshakeError::NoWebsocketUpgrade.error_response(); let resp: Response<AnyBody> = HandshakeError::NoWebsocketUpgrade.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp = HandshakeError::NoConnectionUpgrade.error_response(); let resp: Response<AnyBody> = HandshakeError::NoConnectionUpgrade.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp = HandshakeError::NoVersionHeader.error_response(); let resp: Response<AnyBody> = HandshakeError::NoVersionHeader.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp = HandshakeError::UnsupportedVersion.error_response(); let resp: Response<AnyBody> = HandshakeError::UnsupportedVersion.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
let resp = HandshakeError::BadWebsocketKey.error_response(); let resp: Response<AnyBody> = HandshakeError::BadWebsocketKey.into();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST); assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
} }
} }

View File

@ -1,6 +1,7 @@
use std::convert::Infallible;
use actix_http::{ use actix_http::{
body::AnyBody, http, http::StatusCode, HttpMessage, HttpService, Request, Response, body::AnyBody, http, http::StatusCode, HttpMessage, HttpService, Request, Response,
ResponseError,
}; };
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::ServiceFactoryExt; use actix_service::ServiceFactoryExt;
@ -35,7 +36,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
async fn test_h1_v2() { async fn test_h1_v2() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|_| future::ok::<_, ()>(Response::ok().set_body(STR))) .finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
}) })
.await; .await;
@ -63,7 +64,7 @@ async fn test_h1_v2() {
async fn test_connection_close() { async fn test_connection_close() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|_| future::ok::<_, ()>(Response::ok().set_body(STR))) .finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
.map(|_| ()) .map(|_| ())
}) })
@ -77,11 +78,11 @@ async fn test_connection_close() {
async fn test_with_query_parameter() { async fn test_with_query_parameter() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|req: Request| { .finish(|req: Request| async move {
if req.uri().query().unwrap().contains("qp=") { if req.uri().query().unwrap().contains("qp=") {
future::ok::<_, ()>(Response::ok()) Ok::<_, Infallible>(Response::ok())
} else { } else {
future::ok::<_, ()>(Response::bad_request()) Ok(Response::bad_request())
} }
}) })
.tcp() .tcp()
@ -98,15 +99,9 @@ async fn test_with_query_parameter() {
#[display(fmt = "expect failed")] #[display(fmt = "expect failed")]
struct ExpectFailed; struct ExpectFailed;
impl ResponseError for ExpectFailed {
fn status_code(&self) -> StatusCode {
StatusCode::EXPECTATION_FAILED
}
}
impl From<ExpectFailed> for Response<AnyBody> { impl From<ExpectFailed> for Response<AnyBody> {
fn from(res: ExpectFailed) -> Self { fn from(_: ExpectFailed) -> Self {
res.error_response() Response::new(StatusCode::EXPECTATION_FAILED)
} }
} }
@ -130,7 +125,7 @@ async fn test_h1_expect() {
let str = std::str::from_utf8(&buf).unwrap(); let str = std::str::from_utf8(&buf).unwrap();
assert_eq!(str, "expect body"); assert_eq!(str, "expect body");
Ok::<_, ()>(Response::ok()) Ok::<_, Infallible>(Response::ok())
}) })
.tcp() .tcp()
}) })

View File

@ -11,7 +11,7 @@ use actix_http::{
header::{self, HeaderName, HeaderValue}, header::{self, HeaderName, HeaderValue},
Method, StatusCode, Version, Method, StatusCode, Version,
}, },
Error, HttpMessage, HttpService, Request, Response, ResponseError, Error, HttpMessage, HttpService, Request, Response,
}; };
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_service, ServiceFactoryExt}; use actix_service::{fn_service, ServiceFactoryExt};
@ -136,7 +136,7 @@ async fn test_h2_content_length() {
StatusCode::OK, StatusCode::OK,
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
]; ];
ok::<_, ()>(Response::new(statuses[idx])) ok::<_, Infallible>(Response::new(statuses[idx]))
}) })
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
@ -206,7 +206,7 @@ async fn test_h2_headers() {
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
)); ));
} }
ok::<_, ()>(builder.body(data.clone())) ok::<_, Infallible>(builder.body(data.clone()))
}) })
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
@ -246,7 +246,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
async fn test_h2_body2() { async fn test_h2_body2() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
}) })
@ -264,7 +264,7 @@ async fn test_h2_body2() {
async fn test_h2_head_empty() { async fn test_h2_head_empty() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|_| ok::<_, ()>(Response::ok().set_body(STR))) .finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
}) })
@ -288,7 +288,7 @@ async fn test_h2_head_empty() {
async fn test_h2_head_binary() { async fn test_h2_head_binary() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
}) })
@ -311,7 +311,7 @@ async fn test_h2_head_binary() {
async fn test_h2_head_binary2() { async fn test_h2_head_binary2() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())
}) })
@ -358,7 +358,7 @@ async fn test_h2_body_chunked_explicit() {
HttpService::build() HttpService::build()
.h2(|_| { .h2(|_| {
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((header::TRANSFER_ENCODING, "chunked")) .insert_header((header::TRANSFER_ENCODING, "chunked"))
.streaming(body), .streaming(body),
@ -386,7 +386,7 @@ async fn test_h2_response_http_error_handling() {
HttpService::build() HttpService::build()
.h2(fn_service(|_| { .h2(fn_service(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((header::CONTENT_TYPE, broken_header)) .insert_header((header::CONTENT_TYPE, broken_header))
.body(STR), .body(STR),
@ -402,22 +402,16 @@ async fn test_h2_response_http_error_handling() {
// read response // read response
let bytes = srv.load_body(response).await.unwrap(); let bytes = srv.load_body(response).await.unwrap();
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); assert_eq!(bytes, Bytes::from_static(b"error processing HTTP: failed to parse header value"));
} }
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "error")] #[display(fmt = "error")]
struct BadRequest; struct BadRequest;
impl ResponseError for BadRequest {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
impl From<BadRequest> for Response<AnyBody> { impl From<BadRequest> for Response<AnyBody> {
fn from(res: BadRequest) -> Self { fn from(err: BadRequest) -> Self {
res.error_response() Response::build(StatusCode::BAD_REQUEST).body(err.to_string())
} }
} }
@ -448,7 +442,7 @@ async fn test_h2_on_connect() {
}) })
.h2(|req: Request| { .h2(|req: Request| {
assert!(req.extensions().contains::<isize>()); assert!(req.extensions().contains::<isize>());
ok::<_, ()>(Response::ok()) ok::<_, Infallible>(Response::ok())
}) })
.openssl(tls_config()) .openssl(tls_config())
.map_err(|_| ()) .map_err(|_| ())

View File

@ -14,7 +14,7 @@ use actix_http::{
header::{self, HeaderName, HeaderValue}, header::{self, HeaderName, HeaderValue},
Method, StatusCode, Version, Method, StatusCode, Version,
}, },
Error, HttpService, Request, Response, ResponseError, Error, HttpService, Request, Response,
}; };
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_factory_with_config, fn_service}; use actix_service::{fn_factory_with_config, fn_service};
@ -152,7 +152,7 @@ async fn test_h2_content_length() {
StatusCode::OK, StatusCode::OK,
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
]; ];
ok::<_, ()>(Response::new(statuses[indx])) ok::<_, Infallible>(Response::new(statuses[indx]))
}) })
.rustls(tls_config()) .rustls(tls_config())
}) })
@ -221,7 +221,7 @@ async fn test_h2_headers() {
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
)); ));
} }
ok::<_, ()>(config.body(data.clone())) ok::<_, Infallible>(config.body(data.clone()))
}) })
.rustls(tls_config()) .rustls(tls_config())
}).await; }).await;
@ -260,7 +260,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
async fn test_h2_body2() { async fn test_h2_body2() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -277,7 +277,7 @@ async fn test_h2_body2() {
async fn test_h2_head_empty() { async fn test_h2_head_empty() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.finish(|_| ok::<_, ()>(Response::ok().set_body(STR))) .finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -303,7 +303,7 @@ async fn test_h2_head_empty() {
async fn test_h2_head_binary() { async fn test_h2_head_binary() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -328,7 +328,7 @@ async fn test_h2_head_binary() {
async fn test_h2_head_binary2() { async fn test_h2_head_binary2() {
let srv = test_server(move || { let srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.rustls(tls_config()) .rustls(tls_config())
}) })
.await; .await;
@ -351,7 +351,7 @@ async fn test_h2_body_length() {
HttpService::build() HttpService::build()
.h2(|_| { .h2(|_| {
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>( ok::<_, Infallible>(
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)), Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
) )
}) })
@ -373,7 +373,7 @@ async fn test_h2_body_chunked_explicit() {
HttpService::build() HttpService::build()
.h2(|_| { .h2(|_| {
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((header::TRANSFER_ENCODING, "chunked")) .insert_header((header::TRANSFER_ENCODING, "chunked"))
.streaming(body), .streaming(body),
@ -399,9 +399,9 @@ async fn test_h2_response_http_error_handling() {
let mut srv = test_server(move || { let mut srv = test_server(move || {
HttpService::build() HttpService::build()
.h2(fn_factory_with_config(|_: ()| { .h2(fn_factory_with_config(|_: ()| {
ok::<_, ()>(fn_service(|_| { ok::<_, Infallible>(fn_service(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((http::header::CONTENT_TYPE, broken_header)) .insert_header((http::header::CONTENT_TYPE, broken_header))
.body(STR), .body(STR),
@ -424,15 +424,9 @@ async fn test_h2_response_http_error_handling() {
#[display(fmt = "error")] #[display(fmt = "error")]
struct BadRequest; struct BadRequest;
impl ResponseError for BadRequest {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
impl From<BadRequest> for Response<AnyBody> { impl From<BadRequest> for Response<AnyBody> {
fn from(res: BadRequest) -> Self { fn from(_: BadRequest) -> Self {
res.error_response() Response::bad_request()
} }
} }

View File

@ -7,18 +7,19 @@ use std::{
use actix_http::{ use actix_http::{
body::{AnyBody, Body, SizedStream}, body::{AnyBody, Body, SizedStream},
http::{self, header, StatusCode}, header, http, Error, HttpMessage, HttpService, KeepAlive, Request, Response,
Error, HttpService, KeepAlive, Request, Response, StatusCode,
}; };
use actix_http::{HttpMessage, ResponseError};
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_rt::time::sleep; use actix_rt::time::sleep;
use actix_service::fn_service; use actix_service::fn_service;
use actix_utils::future::{err, ok, ready}; use actix_utils::future::{err, ok, ready};
use bytes::Bytes; use bytes::Bytes;
use derive_more::{Display, Error}; use derive_more::{Display, Error};
use futures_util::stream::{once, StreamExt as _}; use futures_util::{
use futures_util::FutureExt as _; stream::{once, StreamExt as _},
FutureExt as _,
};
use regex::Regex; use regex::Regex;
#[actix_rt::test] #[actix_rt::test]
@ -30,7 +31,7 @@ async fn test_h1() {
.client_disconnect(1000) .client_disconnect(1000)
.h1(|req: Request| { .h1(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
ok::<_, ()>(Response::ok()) ok::<_, Infallible>(Response::ok())
}) })
.tcp() .tcp()
}) })
@ -50,7 +51,7 @@ async fn test_h1_2() {
.finish(|req: Request| { .finish(|req: Request| {
assert!(req.peer_addr().is_some()); assert!(req.peer_addr().is_some());
assert_eq!(req.version(), http::Version::HTTP_11); assert_eq!(req.version(), http::Version::HTTP_11);
ok::<_, ()>(Response::ok()) ok::<_, Infallible>(Response::ok())
}) })
.tcp() .tcp()
}) })
@ -64,15 +65,9 @@ async fn test_h1_2() {
#[display(fmt = "expect failed")] #[display(fmt = "expect failed")]
struct ExpectFailed; struct ExpectFailed;
impl ResponseError for ExpectFailed {
fn status_code(&self) -> StatusCode {
StatusCode::PRECONDITION_FAILED
}
}
impl From<ExpectFailed> for Response<AnyBody> { impl From<ExpectFailed> for Response<AnyBody> {
fn from(res: ExpectFailed) -> Self { fn from(_: ExpectFailed) -> Self {
res.error_response() Response::new(StatusCode::EXPECTATION_FAILED)
} }
} }
@ -87,7 +82,7 @@ async fn test_expect_continue() {
err(ExpectFailed) err(ExpectFailed)
} }
})) }))
.finish(|_| ok::<_, ()>(Response::ok())) .finish(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -96,7 +91,7 @@ async fn test_expect_continue() {
let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
let mut data = String::new(); let mut data = String::new();
let _ = stream.read_to_string(&mut data); let _ = stream.read_to_string(&mut data);
assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); assert!(data.starts_with("HTTP/1.1 412 Expectation Failed\r\ncontent-length"));
let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
@ -118,7 +113,7 @@ async fn test_expect_continue_h1() {
} }
}) })
})) }))
.h1(fn_service(|_| ok::<_, ()>(Response::ok()))) .h1(fn_service(|_| ok::<_, Infallible>(Response::ok())))
.tcp() .tcp()
}) })
.await; .await;
@ -199,7 +194,7 @@ async fn test_slow_request() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.client_timeout(100) .client_timeout(100)
.finish(|_| ok::<_, ()>(Response::ok())) .finish(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -215,7 +210,7 @@ async fn test_slow_request() {
async fn test_http1_malformed_request() { async fn test_http1_malformed_request() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -231,7 +226,7 @@ async fn test_http1_malformed_request() {
async fn test_http1_keepalive() { async fn test_http1_keepalive() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -253,7 +248,7 @@ async fn test_http1_keepalive_timeout() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.keep_alive(1) .keep_alive(1)
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -274,7 +269,7 @@ async fn test_http1_keepalive_timeout() {
async fn test_http1_keepalive_close() { async fn test_http1_keepalive_close() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -295,7 +290,7 @@ async fn test_http1_keepalive_close() {
async fn test_http10_keepalive_default_close() { async fn test_http10_keepalive_default_close() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -315,7 +310,7 @@ async fn test_http10_keepalive_default_close() {
async fn test_http10_keepalive() { async fn test_http10_keepalive() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -343,7 +338,7 @@ async fn test_http1_keepalive_disabled() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.keep_alive(KeepAlive::Disabled) .keep_alive(KeepAlive::Disabled)
.h1(|_| ok::<_, ()>(Response::ok())) .h1(|_| ok::<_, Infallible>(Response::ok()))
.tcp() .tcp()
}) })
.await; .await;
@ -378,7 +373,7 @@ async fn test_content_length() {
StatusCode::OK, StatusCode::OK,
StatusCode::NOT_FOUND, StatusCode::NOT_FOUND,
]; ];
ok::<_, ()>(Response::new(statuses[indx])) ok::<_, Infallible>(Response::new(statuses[indx]))
}) })
.tcp() .tcp()
}) })
@ -433,7 +428,7 @@ async fn test_h1_headers() {
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
)); ));
} }
ok::<_, ()>(builder.body(data.clone())) ok::<_, Infallible>(builder.body(data.clone()))
}).tcp() }).tcp()
}).await; }).await;
@ -471,7 +466,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
async fn test_h1_body() { async fn test_h1_body() {
let mut srv = test_server(|| { let mut srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
}) })
.await; .await;
@ -488,7 +483,7 @@ async fn test_h1_body() {
async fn test_h1_head_empty() { async fn test_h1_head_empty() {
let mut srv = test_server(|| { let mut srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
}) })
.await; .await;
@ -513,7 +508,7 @@ async fn test_h1_head_empty() {
async fn test_h1_head_binary() { async fn test_h1_head_binary() {
let mut srv = test_server(|| { let mut srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
}) })
.await; .await;
@ -538,7 +533,7 @@ async fn test_h1_head_binary() {
async fn test_h1_head_binary2() { async fn test_h1_head_binary2() {
let srv = test_server(|| { let srv = test_server(|| {
HttpService::build() HttpService::build()
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR))) .h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
.tcp() .tcp()
}) })
.await; .await;
@ -561,7 +556,7 @@ async fn test_h1_body_length() {
HttpService::build() HttpService::build()
.h1(|_| { .h1(|_| {
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>( ok::<_, Infallible>(
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)), Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
) )
}) })
@ -583,7 +578,7 @@ async fn test_h1_body_chunked_explicit() {
HttpService::build() HttpService::build()
.h1(|_| { .h1(|_| {
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((header::TRANSFER_ENCODING, "chunked")) .insert_header((header::TRANSFER_ENCODING, "chunked"))
.streaming(body), .streaming(body),
@ -618,7 +613,7 @@ async fn test_h1_body_chunked_implicit() {
HttpService::build() HttpService::build()
.h1(|_| { .h1(|_| {
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
ok::<_, ()>(Response::build(StatusCode::OK).streaming(body)) ok::<_, Infallible>(Response::build(StatusCode::OK).streaming(body))
}) })
.tcp() .tcp()
}) })
@ -647,7 +642,7 @@ async fn test_h1_response_http_error_handling() {
HttpService::build() HttpService::build()
.h1(fn_service(|_| { .h1(fn_service(|_| {
let broken_header = Bytes::from_static(b"\0\0\0"); let broken_header = Bytes::from_static(b"\0\0\0");
ok::<_, ()>( ok::<_, Infallible>(
Response::build(StatusCode::OK) Response::build(StatusCode::OK)
.insert_header((http::header::CONTENT_TYPE, broken_header)) .insert_header((http::header::CONTENT_TYPE, broken_header))
.body(STR), .body(STR),
@ -669,15 +664,9 @@ async fn test_h1_response_http_error_handling() {
#[display(fmt = "error")] #[display(fmt = "error")]
struct BadRequest; struct BadRequest;
impl ResponseError for BadRequest {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
impl From<BadRequest> for Response<AnyBody> { impl From<BadRequest> for Response<AnyBody> {
fn from(res: BadRequest) -> Self { fn from(_: BadRequest) -> Self {
res.error_response() Response::bad_request()
} }
} }
@ -707,7 +696,7 @@ async fn test_h1_on_connect() {
}) })
.h1(|req: Request| { .h1(|req: Request| {
assert!(req.extensions().contains::<isize>()); assert!(req.extensions().contains::<isize>());
ok::<_, ()>(Response::ok()) ok::<_, Infallible>(Response::ok())
}) })
.tcp() .tcp()
}) })

View File

@ -1,11 +1,12 @@
use std::{ use std::{
cell::Cell, cell::Cell,
convert::Infallible,
task::{Context, Poll}, task::{Context, Poll},
}; };
use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::{ use actix_http::{
body::BodySize, body::{AnyBody, BodySize},
h1, h1,
ws::{self, CloseCode, Frame, Item, Message}, ws::{self, CloseCode, Frame, Item, Message},
Error, HttpService, Request, Response, Error, HttpService, Request, Response,
@ -13,6 +14,7 @@ use actix_http::{
use actix_http_test::test_server; use actix_http_test::test_server;
use actix_service::{fn_factory, Service}; use actix_service::{fn_factory, Service};
use bytes::Bytes; use bytes::Bytes;
use derive_more::{Display, Error, From};
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use futures_util::{SinkExt as _, StreamExt as _}; use futures_util::{SinkExt as _, StreamExt as _};
@ -33,12 +35,39 @@ impl WsService {
} }
} }
#[derive(Debug, Display, Error, From)]
enum WsServiceError {
#[display(fmt = "http error")]
Http(actix_http::Error),
#[display(fmt = "ws handshake error")]
Ws(actix_http::ws::HandshakeError),
#[display(fmt = "io error")]
Io(std::io::Error),
#[display(fmt = "dispatcher error")]
Dispatcher,
}
impl From<WsServiceError> for Response<AnyBody> {
fn from(err: WsServiceError) -> Self {
match err {
WsServiceError::Http(err) => err.into(),
WsServiceError::Ws(err) => err.into(),
WsServiceError::Io(_err) => unreachable!(),
WsServiceError::Dispatcher => Response::internal_server_error()
.set_body(AnyBody::from(format!("{}", err))),
}
}
}
impl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService impl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
type Response = (); type Response = ();
type Error = Error; type Error = WsServiceError;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>; type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -56,7 +85,9 @@ where
let framed = framed.replace_codec(ws::Codec::new()); let framed = framed.replace_codec(ws::Codec::new());
ws::Dispatcher::with(framed, service).await?; ws::Dispatcher::with(framed, service)
.await
.map_err(|_| WsServiceError::Dispatcher)?;
Ok(()) Ok(())
}) })
@ -72,7 +103,7 @@ async fn service(msg: Frame) -> Result<Message, Error> {
Frame::Binary(bin) => Message::Binary(bin), Frame::Binary(bin) => Message::Binary(bin),
Frame::Continuation(item) => Message::Continuation(item), Frame::Continuation(item) => Message::Continuation(item),
Frame::Close(reason) => Message::Close(reason), Frame::Close(reason) => Message::Close(reason),
_ => return Err(Error::from(ws::ProtocolError::BadOpCode)), _ => return Err(ws::ProtocolError::BadOpCode.into()),
}; };
Ok(msg) Ok(msg)
@ -82,8 +113,10 @@ async fn service(msg: Frame) -> Result<Message, Error> {
async fn test_simple() { async fn test_simple() {
let mut srv = test_server(|| { let mut srv = test_server(|| {
HttpService::build() HttpService::build()
.upgrade(fn_factory(|| async { Ok::<_, ()>(WsService::new()) })) .upgrade(fn_factory(|| async {
.finish(|_| async { Ok::<_, ()>(Response::not_found()) }) Ok::<_, Infallible>(WsService::new())
}))
.finish(|_| async { Ok::<_, Infallible>(Response::not_found()) })
.tcp() .tcp()
}) })
.await; .await;

View File

@ -6,7 +6,6 @@ pub use actix_http::http::Error as HttpError;
pub use actix_http::ws::HandshakeError as WsHandshakeError; pub use actix_http::ws::HandshakeError as WsHandshakeError;
pub use actix_http::ws::ProtocolError as WsProtocolError; pub use actix_http::ws::ProtocolError as WsProtocolError;
use actix_http::ResponseError;
use serde_json::error::Error as JsonError; use serde_json::error::Error as JsonError;
use actix_http::http::{header::HeaderValue, StatusCode}; use actix_http::http::{header::HeaderValue, StatusCode};
@ -77,6 +76,3 @@ pub enum JsonPayloadError {
} }
impl std::error::Error for JsonPayloadError {} impl std::error::Error for JsonPayloadError {}
/// Return `InternalServerError` for `JsonPayloadError`
impl ResponseError for JsonPayloadError {}

View File

@ -1,7 +1,7 @@
use std::{ use std::{
error::Error as StdError, error::Error as StdError,
future::Future, future::Future,
io, net, net,
pin::Pin, pin::Pin,
rc::Rc, rc::Rc,
task::{Context, Poll}, task::{Context, Poll},
@ -25,10 +25,10 @@ use serde::Serialize;
#[cfg(feature = "compress")] #[cfg(feature = "compress")]
use actix_http::{encoding::Decoder, http::header::ContentEncoding, Payload, PayloadStream}; use actix_http::{encoding::Decoder, http::header::ContentEncoding, Payload, PayloadStream};
use crate::connect::{ConnectRequest, ConnectResponse}; use crate::{
use crate::error::{FreezeRequestError, InvalidUrl, SendRequestError}; error::{FreezeRequestError, InvalidUrl, SendRequestError},
use crate::response::ClientResponse; ClientConfig, ClientResponse, ConnectRequest, ConnectResponse,
use crate::ClientConfig; };
#[derive(Debug, From)] #[derive(Debug, From)]
pub(crate) enum PrepForSendingError { pub(crate) enum PrepForSendingError {
@ -211,7 +211,7 @@ impl RequestSender {
let body = match serde_json::to_string(value) { let body = match serde_json::to_string(value) {
Ok(body) => body, Ok(body) => body,
// TODO: own error type // TODO: own error type
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(), Err(_e) => todo!(),
}; };
if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") { if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") {
@ -238,7 +238,7 @@ impl RequestSender {
let body = match serde_urlencoded::to_string(value) { let body = match serde_urlencoded::to_string(value) {
Ok(body) => body, Ok(body) => body,
// TODO: own error type // TODO: own error type
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(), Err(_e) => todo!(),
}; };
// set content-type // set content-type

View File

@ -1,12 +1,12 @@
use std::{any::type_name, ops::Deref, sync::Arc}; 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 actix_utils::future::{err, ok, Ready};
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use serde::Serialize; use serde::Serialize;
use crate::{ use crate::{
dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest, dev::Payload,Error, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
}; };
/// Data factory. /// Data factory.

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, fmt, io::Write as _}; 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 bytes::{BufMut as _, BytesMut};
use crate::{Error, HttpResponse, ResponseError}; use crate::{Error, HttpResponse, ResponseError};
@ -77,10 +77,10 @@ where
} }
} }
fn error_response(&self) -> Response<Body> { fn error_response(&self) -> HttpResponse {
match self.status { match self.status {
InternalErrorType::Status(status) => { InternalErrorType::Status(status) => {
let mut res = Response::new(status); let mut res = HttpResponse::new(status);
let mut buf = BytesMut::new().writer(); let mut buf = BytesMut::new().writer();
let _ = write!(buf, "{}", self); let _ = write!(buf, "{}", self);
@ -95,7 +95,7 @@ where
if let Some(resp) = resp.borrow_mut().take() { if let Some(resp) = resp.borrow_mut().take() {
resp.into() resp.into()
} else { } else {
Response::new(StatusCode::INTERNAL_SERVER_ERROR) HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
} }
} }
} }

109
src/error/macros.rs Normal file
View 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());
}
}

View File

@ -10,8 +10,11 @@ use url::ParseError as UrlParseError;
use crate::http::StatusCode; use crate::http::StatusCode;
mod internal; mod internal;
mod macros;
mod response_error;
pub use self::internal::*; pub use self::internal::*;
pub use self::response_error::{Error, ResponseError};
/// A convenience [`Result`](std::result::Result) for Actix Web operations. /// A convenience [`Result`](std::result::Result) for Actix Web operations.
/// ///

219
src/error/response_error.rs Normal file
View File

@ -0,0 +1,219 @@
//! `ResponseError` trait and foreign impls.
use std::{
error::Error as StdError,
fmt,
io::{self, Write as _},
};
use actix_http::{
body::{AnyBody, Body},
header, Response, StatusCode,
};
use bytes::BytesMut;
use crate::{__downcast_dyn, __downcast_get_type_id};
use crate::{helpers, HttpResponse};
/// 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())
}
/// TODO
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)> {
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),
}
}
}
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/////////////////////
/// 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(Body::from(self.to_string()))
}
}
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::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() {
let err = actix_http::error::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());
}
}

View File

@ -3,18 +3,14 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_http::Error;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::future::{ready, Ready}; use actix_utils::future::{ready, Ready};
use futures_core::ready; use futures_core::ready;
use pin_project::pin_project; use pin_project::pin_project;
use crate::{ use crate::{
extract::FromRequest,
request::HttpRequest,
responder::Responder,
response::HttpResponse,
service::{ServiceRequest, ServiceResponse}, service::{ServiceRequest, ServiceResponse},
Error, FromRequest, HttpRequest, HttpResponse, Responder,
}; };
/// A request handler is an async function that accepts zero or more parameters that can be /// A request handler is an async function that accepts zero or more parameters that can be

25
src/helpers.rs Normal file
View 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(())
}
}

View File

@ -79,6 +79,7 @@ pub mod error;
mod extract; mod extract;
pub mod guard; pub mod guard;
mod handler; mod handler;
mod helpers;
pub mod http; pub mod http;
mod info; mod info;
pub mod middleware; pub mod middleware;
@ -97,7 +98,7 @@ pub(crate) mod types;
pub mod web; pub mod web;
pub use actix_http::Response as BaseHttpResponse; pub use actix_http::Response as BaseHttpResponse;
pub use actix_http::{body, Error, HttpMessage, ResponseError}; pub use actix_http::{body, HttpMessage};
#[doc(inline)] #[doc(inline)]
pub use actix_rt as rt; pub use actix_rt as rt;
pub use actix_web_codegen::*; pub use actix_web_codegen::*;
@ -105,7 +106,7 @@ pub use actix_web_codegen::*;
pub use cookie; pub use cookie;
pub use crate::app::App; 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::extract::FromRequest;
pub use crate::request::HttpRequest; pub use crate::request::HttpRequest;
pub use crate::resource::Resource; pub use crate::resource::Resource;

View File

@ -13,8 +13,8 @@ use futures_core::{future::LocalBoxFuture, ready};
use crate::{ use crate::{
dev::{ServiceRequest, ServiceResponse}, dev::{ServiceRequest, ServiceResponse},
error::{Error, Result},
http::StatusCode, http::StatusCode,
Error, Result,
}; };
/// Return type for [`ErrorHandlers`] custom handlers. /// Return type for [`ErrorHandlers`] custom handlers.

View File

@ -1,9 +1,8 @@
use std::{any::type_name, ops::Deref}; use std::{any::type_name, ops::Deref};
use actix_http::error::Error;
use actix_utils::future::{err, ok, Ready}; 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. /// Request-local data extractor.
/// ///

View File

@ -3,7 +3,7 @@ use std::fmt;
use std::future::Future; use std::future::Future;
use std::rc::Rc; use std::rc::Rc;
use actix_http::{Error, Extensions}; use actix_http::Extensions;
use actix_router::IntoPattern; use actix_router::IntoPattern;
use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::boxed::{self, BoxService, BoxServiceFactory};
use actix_service::{ use actix_service::{
@ -13,14 +13,16 @@ use actix_service::{
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use futures_util::future::join_all; use futures_util::future::join_all;
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::{
use crate::extract::FromRequest; data::Data,
use crate::guard::Guard; dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef},
use crate::handler::Handler; guard::Guard,
use crate::responder::Responder; handler::Handler,
use crate::route::{Route, RouteService}; responder::Responder,
use crate::service::{ServiceRequest, ServiceResponse}; route::{Route, RouteService},
use crate::{data::Data, HttpResponse}; service::{ServiceRequest, ServiceResponse},
Error, FromRequest, HttpResponse,
};
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>; type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;

View File

@ -231,7 +231,7 @@ where
T: fmt::Debug + fmt::Display + 'static, T: fmt::Debug + fmt::Display + 'static,
{ {
fn respond_to(self, _: &HttpRequest) -> HttpResponse { fn respond_to(self, _: &HttpRequest) -> HttpResponse {
HttpResponse::from_error(self.into()) HttpResponse::from_error(self)
} }
} }

View File

@ -378,7 +378,7 @@ impl HttpResponseBuilder {
self.body(Body::from(body)) self.body(Body::from(body))
} }
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()), Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
} }
} }

View File

@ -8,7 +8,7 @@ use std::{
}; };
use actix_http::{ use actix_http::{
body::{Body, MessageBody}, body::{AnyBody, Body, MessageBody},
http::{header::HeaderMap, StatusCode}, http::{header::HeaderMap, StatusCode},
Extensions, Response, ResponseHead, Extensions, Response, ResponseHead,
}; };
@ -22,15 +22,18 @@ use {
cookie::Cookie, cookie::Cookie,
}; };
use crate::{error::Error, HttpResponseBuilder}; use crate::{
error::{Error, ResponseError},
HttpResponseBuilder,
};
/// An HTTP Response /// An HTTP Response
pub struct HttpResponse<B = Body> { pub struct HttpResponse<B = AnyBody> {
res: Response<B>, res: Response<B>,
pub(crate) error: Option<Error>, pub(crate) error: Option<Error>,
} }
impl HttpResponse<Body> { impl HttpResponse<AnyBody> {
/// Create HTTP response builder with specific status. /// Create HTTP response builder with specific status.
#[inline] #[inline]
pub fn build(status: StatusCode) -> HttpResponseBuilder { pub fn build(status: StatusCode) -> HttpResponseBuilder {
@ -48,13 +51,14 @@ impl HttpResponse<Body> {
/// Create an error response. /// Create an error response.
#[inline] #[inline]
pub fn from_error(error: Error) -> Self { pub fn from_error(error: impl Into<Error>) -> Self {
let res = error.as_response_error().error_response(); error.into().as_response_error().error_response()
Self {
res,
error: Some(error),
} }
/// Create an error response.
#[inline]
pub fn from_http_error(error: &dyn ResponseError) -> Self {
error.error_response()
} }
} }
@ -238,7 +242,7 @@ impl<B> HttpResponse<B> {
impl<B> fmt::Debug for HttpResponse<B> impl<B> fmt::Debug for HttpResponse<B>
where where
B: MessageBody, B: MessageBody,
B::Error: Into<Error>, B::Error: Into<actix_http::Error>,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HttpResponse") f.debug_struct("HttpResponse")

View File

@ -2,19 +2,19 @@
use std::{future::Future, rc::Rc}; use std::{future::Future, rc::Rc};
use actix_http::{http::Method, Error}; use actix_http::http::Method;
use actix_service::{ use actix_service::{
boxed::{self, BoxService, BoxServiceFactory}, boxed::{self, BoxService, BoxServiceFactory},
Service, ServiceFactory, Service, ServiceFactory,
}; };
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use crate::extract::FromRequest; use crate::{
use crate::guard::{self, Guard}; guard::{self, Guard},
use crate::handler::{Handler, HandlerService}; handler::{Handler, HandlerService},
use crate::responder::Responder; service::{ServiceRequest, ServiceResponse},
use crate::service::{ServiceRequest, ServiceResponse}; Error, FromRequest, HttpResponse, Responder,
use crate::HttpResponse; };
/// Resource route definition /// Resource route definition
/// ///

View File

@ -5,21 +5,21 @@ use std::{fmt, net};
use actix_http::body::{Body, MessageBody}; use actix_http::body::{Body, MessageBody};
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version}; use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
use actix_http::{ use actix_http::{
Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead,
}; };
use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url}; use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
use actix_service::{IntoServiceFactory, ServiceFactory}; use actix_service::{IntoServiceFactory, ServiceFactory};
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
use cookie::{Cookie, ParseError as CookieParseError}; 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::{ use crate::{
config::{AppConfig, AppService}, config::{AppConfig, AppService},
HttpResponse, dev::insert_slash,
guard::Guard,
info::ConnectionInfo,
request::HttpRequest,
rmap::ResourceMap,
Error, HttpResponse,
}; };
pub trait HttpServiceFactory { pub trait HttpServiceFactory {
@ -338,7 +338,7 @@ pub struct ServiceResponse<B = Body> {
impl ServiceResponse<Body> { impl ServiceResponse<Body> {
/// Create service response from the error /// Create service response from the error
pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self { 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 } ServiceResponse { request, response }
} }
} }

View File

@ -188,7 +188,7 @@ impl<T: Serialize> Responder for Form<T> {
Ok(body) => HttpResponse::Ok() Ok(body) => HttpResponse::Ok()
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED) .content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
.body(body), .body(body),
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err).into()), Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err)),
} }
} }
} }

View File

@ -127,7 +127,7 @@ impl<T: Serialize> Responder for Json<T> {
Ok(body) => HttpResponse::Ok() Ok(body) => HttpResponse::Ok()
.content_type(mime::APPLICATION_JSON) .content_type(mime::APPLICATION_JSON)
.body(body), .body(body),
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()), Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
} }
} }
} }

View File

@ -2,14 +2,13 @@
use std::{fmt, ops, sync::Arc}; use std::{fmt, ops, sync::Arc};
use actix_http::error::Error;
use actix_router::PathDeserializer; use actix_router::PathDeserializer;
use actix_utils::future::{ready, Ready}; use actix_utils::future::{ready, Ready};
use serde::de; use serde::de;
use crate::{ use crate::{
dev::Payload, dev::Payload,
error::{ErrorNotFound, PathError}, error::{Error, ErrorNotFound, PathError},
FromRequest, HttpRequest, FromRequest, HttpRequest,
}; };