diff --git a/actix-http/src/body/body_stream.rs b/actix-http/src/body/body_stream.rs index cf4f488b2..3988da9e7 100644 --- a/actix-http/src/body/body_stream.rs +++ b/actix-http/src/body/body_stream.rs @@ -1,5 +1,5 @@ use std::{ - error::Error as StdError, + fmt, pin::Pin, task::{Context, Poll}, }; @@ -25,7 +25,7 @@ pin_project! { impl BodyStream where S: Stream>, - E: Into> + 'static, + E: fmt::Debug + 'static, { #[inline] pub fn new(stream: S) -> Self { @@ -36,7 +36,7 @@ where impl MessageBody for BodyStream where S: Stream>, - E: Into> + 'static, + E: fmt::Debug + 'static, { type Error = E; @@ -150,21 +150,6 @@ mod tests { assert!(matches!(to_bytes(body).await, Err("stringy error"))); } - #[actix_rt::test] - async fn stream_boxed_error() { - // `Box` does not impl `Error` - // but it does impl `Into>` - - let body = BodyStream::new(stream::once(async { - Err(Box::::from("stringy error")) - })); - - assert_eq!( - to_bytes(body).await.unwrap_err().to_string(), - "stringy error" - ); - } - #[actix_rt::test] async fn stream_delayed_error() { let body = BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)])); diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index d4737aab8..51618b590 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -1,5 +1,4 @@ use std::{ - error::Error as StdError, fmt, pin::Pin, task::{Context, Poll}, @@ -8,23 +7,28 @@ use std::{ use bytes::Bytes; use super::{BodySize, MessageBody, MessageBodyMapErr}; -use crate::Error; /// A boxed message body with boxed errors. -pub struct BoxBody(Pin>>>); +pub struct BoxBody(Pin>>>); impl BoxBody { /// Boxes a `MessageBody` and any errors it generates. + #[inline] pub fn new(body: B) -> Self where B: MessageBody + 'static, { - let body = MessageBodyMapErr::new(body, Into::into); + fn box_it(it: T) -> Box { + Box::new(it) + } + + let body = MessageBodyMapErr::new(body, box_it); Self(Box::pin(body)) } /// Returns a mutable pinned reference to the inner message body type. - pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { + #[inline] + pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { self.0.as_mut() } } @@ -36,22 +40,22 @@ impl fmt::Debug for BoxBody { } impl MessageBody for BoxBody { - type Error = Error; + type Error = Box; + #[inline] fn size(&self) -> BodySize { self.0.size() } + #[inline] fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - self.0 - .as_mut() - .poll_next(cx) - .map_err(|err| Error::new_body().with_cause(err)) + self.0.as_mut().poll_next(cx) } + #[inline] fn is_complete_body(&self) -> bool { self.0.is_complete_body() } diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 103b39c5d..1d6c35ce5 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -66,10 +66,10 @@ where match self.project() { EitherBodyProj::Left { body } => body .poll_next(cx) - .map_err(|err| Error::new_body().with_cause(err)), + .map_err(|err| Error::new_body().with_cause(format!("{:?}", err))), EitherBodyProj::Right { body } => body .poll_next(cx) - .map_err(|err| Error::new_body().with_cause(err)), + .map_err(|err| Error::new_body().with_cause(format!("{:?}", err))), } } diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 10a7260f4..f1d367da4 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -2,8 +2,7 @@ use std::{ convert::Infallible, - error::Error as StdError, - mem, + fmt, mem, pin::Pin, task::{Context, Poll}, }; @@ -17,9 +16,11 @@ use super::BodySize; /// An interface types that can converted to bytes and used as response bodies. // TODO: examples pub trait MessageBody { - // TODO: consider this bound to only fmt::Display since the error type is not really used - // and there is an impl for Into> on String - type Error: Into>; + /// The type of error that will be returned if streaming response fails. + /// + /// Since it is not appropriate to generate a response mid-stream, it only requires `Debug` for + /// internal logging. + type Error: fmt::Debug + 'static; /// Body size hint. fn size(&self) -> BodySize; @@ -450,7 +451,7 @@ impl MessageBody for MessageBodyMapErr where B: MessageBody, F: FnOnce(B::Error) -> E, - E: Into>, + E: fmt::Debug + 'static, { type Error = E; diff --git a/actix-http/src/body/sized_stream.rs b/actix-http/src/body/sized_stream.rs index 9c1727246..fc56c7286 100644 --- a/actix-http/src/body/sized_stream.rs +++ b/actix-http/src/body/sized_stream.rs @@ -1,5 +1,5 @@ use std::{ - error::Error as StdError, + fmt, pin::Pin, task::{Context, Poll}, }; @@ -25,7 +25,7 @@ pin_project! { impl SizedStream where S: Stream>, - E: Into> + 'static, + E: fmt::Debug + 'static, { #[inline] pub fn new(size: u64, stream: S) -> Self { @@ -38,7 +38,7 @@ where impl MessageBody for SizedStream where S: Stream>, - E: Into> + 'static, + E: fmt::Debug + 'static, { type Error = E; @@ -147,25 +147,4 @@ mod tests { let body = SizedStream::new(1, stream::once(async { Err("stringy error") })); assert!(matches!(to_bytes(body).await, Err("stringy error"))); } - - #[actix_rt::test] - async fn stream_boxed_error() { - // `Box` does not impl `Error` - // but it does impl `Into>` - - let body = SizedStream::new( - 0, - stream::once(async { Err(Box::::from("stringy error")) }), - ); - assert_eq!(to_bytes(body).await.unwrap(), Bytes::new()); - - let body = SizedStream::new( - 1, - stream::once(async { Err(Box::::from("stringy error")) }), - ); - assert_eq!( - to_bytes(body).await.unwrap_err().to_string(), - "stringy error" - ); - } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index fa294ab0d..ed11499f3 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -129,7 +129,7 @@ where } EncoderBodyProj::Stream { body } => body .poll_next(cx) - .map_err(|err| EncoderError::Body(err.into())), + .map_err(|err| EncoderError::Body(format!("{:?}", err).into())), } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index a04867ae1..f58e39950 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -335,28 +335,27 @@ impl From for Error { #[derive(Debug, Display, Error, From)] #[non_exhaustive] pub enum DispatchError { - /// Service error + /// Service error. // FIXME: display and error type #[display(fmt = "Service Error")] Service(#[error(not(source))] Response), - /// Body error - // FIXME: display and error type + /// Body error. #[display(fmt = "Body Error")] - Body(#[error(not(source))] Box), + ResponseBody(#[error(not(source))] Box), - /// Upgrade service error + /// Upgrade service error. Upgrade, /// An `io::Error` that occurred while trying to read or write to a network stream. - #[display(fmt = "IO error: {}", _0)] + #[display(fmt = "IO error")] Io(io::Error), - /// Http request parse error. - #[display(fmt = "Parse error: {}", _0)] + /// Request parse error. + #[display(fmt = "Request parse error")] Parse(ParseError), - /// Http/2 error + /// HTTP/2 error. #[display(fmt = "{}", _0)] H2(h2::Error), @@ -364,23 +363,23 @@ pub enum DispatchError { #[display(fmt = "The first request did not complete within the specified timeout")] SlowRequestTimeout, - /// Disconnect timeout. Makes sense for ssl streams. + /// Disconnect timeout. Makes sense for TLS streams. #[display(fmt = "Connection shutdown timeout")] DisconnectTimeout, - /// Payload is not consumed + /// Payload is not consumed. #[display(fmt = "Task is completed but request's payload is not consumed")] PayloadIsNotConsumed, - /// Malformed request + /// Malformed request. #[display(fmt = "Malformed request")] MalformedRequest, - /// Internal error + /// Internal error. #[display(fmt = "Internal error")] InternalError, - /// Unknown error + /// Unknown error. #[display(fmt = "Unknown error")] Unknown, } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 64bf83e03..8e2f25761 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -426,7 +426,7 @@ where } Poll::Ready(Some(Err(err))) => { - return Err(DispatchError::Body(err.into())) + return Err(DispatchError::ResponseBody(Box::new(err))) } Poll::Pending => return Ok(PollResponse::DoNothing), @@ -458,7 +458,7 @@ where } Poll::Ready(Some(Err(err))) => { - return Err(DispatchError::Service(err.into())) + return Err(DispatchError::ResponseBody(err)) } Poll::Pending => return Ok(PollResponse::DoNothing), diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 8fbefe6de..2184693f9 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -1,6 +1,5 @@ use std::{ - cmp, - error::Error as StdError, + cmp, fmt, future::Future, marker::PhantomData, net, @@ -14,6 +13,7 @@ use actix_rt::time::{sleep, Sleep}; use actix_service::Service; use actix_utils::future::poll_fn; use bytes::{Bytes, BytesMut}; +use derive_more::{Display, Error}; use futures_core::ready; use h2::{ server::{Connection, SendResponse}, @@ -187,10 +187,25 @@ where } } +#[derive(Debug, Display, Error)] enum DispatchError { + /// Send response head failed. + #[display(fmt = "Send response head failed")] SendResponse(h2::Error), + + /// Send response data failed. + #[display(fmt = "Send response data failed")] SendData(h2::Error), - ResponseBody(Box), + + /// Receiving body chunk failed. + #[display(fmt = "Receiving body chunk failed")] + ResponseBody(#[error(not(source))] Box), +} + +impl DispatchError { + fn response_body(err: E) -> Self { + Self::ResponseBody(Box::new(err)) + } } async fn handle_response( @@ -221,7 +236,7 @@ where actix_rt::pin!(body); while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await { - let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?; + let mut chunk = res.map_err(DispatchError::response_body)?; 'send: loop { let chunk_size = cmp::min(chunk.len(), CHUNK_SIZE); diff --git a/awc/src/any_body.rs b/awc/src/any_body.rs index cb9038ff3..8bbbc48d0 100644 --- a/awc/src/any_body.rs +++ b/awc/src/any_body.rs @@ -11,8 +11,6 @@ use pin_project_lite::pin_project; use actix_http::body::{BodySize, BodyStream, BoxBody, MessageBody, SizedStream}; -use crate::BoxError; - pin_project! { /// Represents various types of HTTP message body. #[derive(Clone)] @@ -117,7 +115,9 @@ where } } - AnyBodyProj::Body { body } => body.poll_next(cx).map_err(|err| err.into()), + AnyBodyProj::Body { body } => body + .poll_next(cx) + .map_err(|err| format!("{:?}", err).into()), } } } @@ -213,7 +213,7 @@ impl From for AnyBody { impl From> for AnyBody where S: Stream> + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { fn from(stream: SizedStream) -> Self { AnyBody::new_boxed(stream) @@ -223,7 +223,7 @@ where impl From> for AnyBody where S: Stream> + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { fn from(stream: BodyStream) -> Self { AnyBody::new_boxed(stream) diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index cd93a1d60..930348a3a 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, net, rc::Rc, time::Duration}; +use std::{convert::TryFrom, fmt, net, rc::Rc, time::Duration}; use bytes::Bytes; use futures_core::Stream; @@ -13,7 +13,7 @@ use actix_http::{ use crate::{ any_body::AnyBody, sender::{RequestSender, SendClientRequest}, - BoxError, ClientConfig, + ClientConfig, }; /// `FrozenClientRequest` struct represents cloneable client request. @@ -83,7 +83,7 @@ impl FrozenClientRequest { pub fn send_stream(&self, stream: S) -> SendClientRequest where S: Stream> + Unpin + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { RequestSender::Rc(self.head.clone(), None).send_stream( self.addr, @@ -208,7 +208,7 @@ impl FrozenSendBuilder { pub fn send_stream(self, stream: S) -> SendClientRequest where S: Stream> + Unpin + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { if let Some(e) = self.err { return e.into(); diff --git a/awc/src/request.rs b/awc/src/request.rs index 9e37b2755..9ca0b38c3 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -15,7 +15,7 @@ use crate::{ error::{FreezeRequestError, InvalidUrl}, frozen::FrozenClientRequest, sender::{PrepForSendingError, RequestSender, SendClientRequest}, - BoxError, ClientConfig, + ClientConfig, }; #[cfg(feature = "cookies")] @@ -394,7 +394,7 @@ impl ClientRequest { pub fn send_stream(self, stream: S) -> SendClientRequest where S: Stream> + Unpin + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { let slf = match self.prep_for_sending() { Ok(slf) => slf, diff --git a/awc/src/sender.rs b/awc/src/sender.rs index f83a70a9b..d329ce788 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -1,4 +1,5 @@ use std::{ + fmt, future::Future, net, pin::Pin, @@ -25,7 +26,7 @@ use actix_http::{encoding::Decoder, header::ContentEncoding, Payload, PayloadStr use crate::{ any_body::AnyBody, error::{FreezeRequestError, InvalidUrl, SendRequestError}, - BoxError, ClientConfig, ClientResponse, ConnectRequest, ConnectResponse, + ClientConfig, ClientResponse, ConnectRequest, ConnectResponse, }; #[derive(Debug, From)] @@ -275,7 +276,7 @@ impl RequestSender { ) -> SendClientRequest where S: Stream> + Unpin + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { self.send_body( addr, diff --git a/src/dev.rs b/src/dev.rs index d4a64985c..0741bb90e 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -3,6 +3,8 @@ //! Most users will not have to interact with the types in this module, but it is useful for those //! writing extractors, middleware, libraries, or interacting with the service API directly. +use std::fmt; + pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] pub use crate::handler::Handler; @@ -114,7 +116,7 @@ pub(crate) enum AnyBody { } impl crate::body::MessageBody for AnyBody { - type Error = crate::BoxError; + type Error = Box; /// Body size hint. fn size(&self) -> crate::body::BodySize { diff --git a/src/handler.rs b/src/handler.rs index e543ecc7f..87beec5c4 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,11 +1,11 @@ -use std::future::Future; +use std::{fmt, future::Future}; use actix_service::{boxed, fn_service}; use crate::{ body::MessageBody, service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, - BoxError, FromRequest, HttpResponse, Responder, + FromRequest, HttpResponse, Responder, }; /// A request handler is an async function that accepts zero or more parameters that can be @@ -31,7 +31,7 @@ where R: Future, R::Output: Responder, ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + <::Body as MessageBody>::Error: fmt::Debug, { boxed::factory(fn_service(move |req: ServiceRequest| { let handler = handler.clone(); diff --git a/src/lib.rs b/src/lib.rs index 171a2d101..f4f40765c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,5 +113,3 @@ pub use crate::route::Route; pub use crate::scope::Scope; pub use crate::server::HttpServer; pub use crate::types::Either; - -pub(crate) type BoxError = Box; diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 74daa26d5..08c2668cc 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -325,9 +325,8 @@ pin_project! { impl MessageBody for StreamLog where B: MessageBody, - B::Error: Into, { - type Error = Error; + type Error = B::Error; fn size(&self) -> BodySize { self.body.size() @@ -344,7 +343,7 @@ where *this.size += chunk.len(); Poll::Ready(Some(Ok(chunk))) } - Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), + Some(Err(err)) => Poll::Ready(Some(Err(err))), None => Poll::Ready(None), } } diff --git a/src/resource.rs b/src/resource.rs index 53104930a..d56fd0488 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -20,7 +20,7 @@ use crate::{ BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest, ServiceResponse, }, - BoxError, Error, FromRequest, HttpResponse, Responder, + Error, FromRequest, HttpResponse, Responder, }; /// *Resource* is an entry in resources table which corresponds to requested URL. @@ -239,7 +239,7 @@ where R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + <::Body as MessageBody>::Error: fmt::Debug, { self.routes.push(Route::new().to(handler)); self diff --git a/src/response/builder.rs b/src/response/builder.rs index b500ab331..f8604ad39 100644 --- a/src/response/builder.rs +++ b/src/response/builder.rs @@ -1,6 +1,7 @@ use std::{ cell::{Ref, RefMut}, convert::TryInto, + fmt, future::Future, pin::Pin, task::{Context, Poll}, @@ -23,7 +24,7 @@ use cookie::{Cookie, CookieJar}; use crate::{ error::{Error, JsonPayloadError}, - BoxError, HttpResponse, + HttpResponse, }; /// An HTTP response builder. @@ -349,7 +350,7 @@ impl HttpResponseBuilder { pub fn streaming(&mut self, stream: S) -> HttpResponse where S: Stream> + 'static, - E: Into + 'static, + E: fmt::Debug + 'static, { self.body(BodyStream::new(stream)) } diff --git a/src/response/customize_responder.rs b/src/response/customize_responder.rs index 11f6b2916..8c4339747 100644 --- a/src/response/customize_responder.rs +++ b/src/response/customize_responder.rs @@ -1,3 +1,5 @@ +use std::fmt; + use actix_http::{ body::{EitherBody, MessageBody}, error::HttpError, @@ -6,7 +8,7 @@ use actix_http::{ StatusCode, }; -use crate::{BoxError, HttpRequest, HttpResponse, Responder}; +use crate::{HttpRequest, HttpResponse, Responder}; /// Allows overriding status code and headers for a [`Responder`]. /// @@ -143,7 +145,7 @@ impl CustomizeResponder { impl Responder for CustomizeResponder where T: Responder, - ::Error: Into, + ::Error: fmt::Debug, { type Body = EitherBody; diff --git a/src/response/responder.rs b/src/response/responder.rs index 319b824f1..439b3dadb 100644 --- a/src/response/responder.rs +++ b/src/response/responder.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, fmt}; use actix_http::{ body::{BoxBody, EitherBody, MessageBody}, @@ -7,7 +7,7 @@ use actix_http::{ }; use bytes::{Bytes, BytesMut}; -use crate::{BoxError, Error, HttpRequest, HttpResponse, HttpResponseBuilder}; +use crate::{Error, HttpRequest, HttpResponse, HttpResponseBuilder}; use super::CustomizeResponder; @@ -96,7 +96,7 @@ impl Responder for actix_http::ResponseBuilder { impl Responder for Option where T: Responder, - ::Error: Into, + ::Error: fmt::Debug, { type Body = EitherBody; @@ -111,7 +111,7 @@ where impl Responder for Result where T: Responder, - ::Error: Into, + ::Error: fmt::Debug, E: Into, { type Body = EitherBody; diff --git a/src/route.rs b/src/route.rs index 4447bff50..4c5842c13 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,4 +1,4 @@ -use std::{future::Future, mem, rc::Rc}; +use std::{fmt, future::Future, mem, rc::Rc}; use actix_http::Method; use actix_service::{ @@ -12,7 +12,7 @@ use crate::{ guard::{self, Guard}, handler::{handler_service, Handler}, service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, - BoxError, Error, FromRequest, HttpResponse, Responder, + Error, FromRequest, HttpResponse, Responder, }; /// Resource route definition @@ -183,7 +183,7 @@ impl Route { R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + <::Body as MessageBody>::Error: fmt::Debug, { self.service = handler_service(handler); self diff --git a/src/service.rs b/src/service.rs index 36b3858e6..8bd14b8dd 100644 --- a/src/service.rs +++ b/src/service.rs @@ -470,7 +470,6 @@ impl From> for Response { impl fmt::Debug for ServiceResponse where B: MessageBody, - B::Error: Into, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let res = writeln!( diff --git a/src/web.rs b/src/web.rs index 042b8a008..dd01da286 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,6 +1,6 @@ //! Essentials helper functions and types for application registration. -use std::{error::Error as StdError, future::Future}; +use std::{fmt, future::Future}; use actix_http::Method; use actix_router::IntoPatterns; @@ -146,7 +146,7 @@ where R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody + 'static, - <::Body as MessageBody>::Error: Into>, + <::Body as MessageBody>::Error: fmt::Debug, { Route::new().to(handler) }