relax bound on MessageBody::Error to Debug

This commit is contained in:
Rob Ede 2021-12-16 23:34:07 +00:00
parent 44b7302845
commit 35d279e594
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
24 changed files with 104 additions and 119 deletions

View File

@ -1,5 +1,5 @@
use std::{
error::Error as StdError,
fmt,
pin::Pin,
task::{Context, Poll},
};
@ -25,7 +25,7 @@ pin_project! {
impl<S, E> BodyStream<S>
where
S: Stream<Item = Result<Bytes, E>>,
E: Into<Box<dyn StdError>> + 'static,
E: fmt::Debug + 'static,
{
#[inline]
pub fn new(stream: S) -> Self {
@ -36,7 +36,7 @@ where
impl<S, E> MessageBody for BodyStream<S>
where
S: Stream<Item = Result<Bytes, E>>,
E: Into<Box<dyn StdError>> + '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<dyn Error>` does not impl `Error`
// but it does impl `Into<Box<dyn Error>>`
let body = BodyStream::new(stream::once(async {
Err(Box::<dyn StdError>::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)]));

View File

@ -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<Box<dyn MessageBody<Error = Box<dyn StdError>>>>);
pub struct BoxBody(Pin<Box<dyn MessageBody<Error = Box<dyn fmt::Debug>>>>);
impl BoxBody {
/// Boxes a `MessageBody` and any errors it generates.
#[inline]
pub fn new<B>(body: B) -> Self
where
B: MessageBody + 'static,
{
let body = MessageBodyMapErr::new(body, Into::into);
fn box_it<T: fmt::Debug + 'static>(it: T) -> Box<dyn fmt::Debug> {
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<Error = Box<dyn StdError>>)> {
#[inline]
pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody<Error = Box<dyn fmt::Debug>>)> {
self.0.as_mut()
}
}
@ -36,22 +40,22 @@ impl fmt::Debug for BoxBody {
}
impl MessageBody for BoxBody {
type Error = Error;
type Error = Box<dyn fmt::Debug>;
#[inline]
fn size(&self) -> BodySize {
self.0.size()
}
#[inline]
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Self::Error>>> {
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()
}

View File

@ -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))),
}
}

View File

@ -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<Box<StdError>> on String
type Error: Into<Box<dyn StdError>>;
/// 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<B, F, E> MessageBody for MessageBodyMapErr<B, F>
where
B: MessageBody,
F: FnOnce(B::Error) -> E,
E: Into<Box<dyn StdError>>,
E: fmt::Debug + 'static,
{
type Error = E;

View File

@ -1,5 +1,5 @@
use std::{
error::Error as StdError,
fmt,
pin::Pin,
task::{Context, Poll},
};
@ -25,7 +25,7 @@ pin_project! {
impl<S, E> SizedStream<S>
where
S: Stream<Item = Result<Bytes, E>>,
E: Into<Box<dyn StdError>> + 'static,
E: fmt::Debug + 'static,
{
#[inline]
pub fn new(size: u64, stream: S) -> Self {
@ -38,7 +38,7 @@ where
impl<S, E> MessageBody for SizedStream<S>
where
S: Stream<Item = Result<Bytes, E>>,
E: Into<Box<dyn StdError>> + '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<dyn Error>` does not impl `Error`
// but it does impl `Into<Box<dyn Error>>`
let body = SizedStream::new(
0,
stream::once(async { Err(Box::<dyn StdError>::from("stringy error")) }),
);
assert_eq!(to_bytes(body).await.unwrap(), Bytes::new());
let body = SizedStream::new(
1,
stream::once(async { Err(Box::<dyn StdError>::from("stringy error")) }),
);
assert_eq!(
to_bytes(body).await.unwrap_err().to_string(),
"stringy error"
);
}
}

View File

@ -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())),
}
}

View File

@ -335,28 +335,27 @@ impl From<PayloadError> 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<BoxBody>),
/// Body error
// FIXME: display and error type
/// Body error.
#[display(fmt = "Body Error")]
Body(#[error(not(source))] Box<dyn StdError>),
ResponseBody(#[error(not(source))] Box<dyn fmt::Debug>),
/// 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,
}

View File

@ -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),

View File

@ -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<dyn StdError>),
/// Receiving body chunk failed.
#[display(fmt = "Receiving body chunk failed")]
ResponseBody(#[error(not(source))] Box<dyn fmt::Debug>),
}
impl DispatchError {
fn response_body<E: fmt::Debug + 'static>(err: E) -> Self {
Self::ResponseBody(Box::new(err))
}
}
async fn handle_response<B>(
@ -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);

View File

@ -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<B> From<BytesMut> for AnyBody<B> {
impl<S, E> From<SizedStream<S>> for AnyBody
where
S: Stream<Item = Result<Bytes, E>> + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
fn from(stream: SizedStream<S>) -> Self {
AnyBody::new_boxed(stream)
@ -223,7 +223,7 @@ where
impl<S, E> From<BodyStream<S>> for AnyBody
where
S: Stream<Item = Result<Bytes, E>> + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
fn from(stream: BodyStream<S>) -> Self {
AnyBody::new_boxed(stream)

View File

@ -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<S, E>(&self, stream: S) -> SendClientRequest
where
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
E: Into<BoxError> + '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<S, E>(self, stream: S) -> SendClientRequest
where
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
if let Some(e) = self.err {
return e.into();

View File

@ -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<S, E>(self, stream: S) -> SendClientRequest
where
S: Stream<Item = Result<Bytes, E>> + Unpin + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
let slf = match self.prep_for_sending() {
Ok(slf) => slf,

View File

@ -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<Item = Result<Bytes, E>> + Unpin + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
self.send_body(
addr,

View File

@ -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<dyn fmt::Debug>;
/// Body size hint.
fn size(&self) -> crate::body::BodySize {

View File

@ -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,
<R::Output as Responder>::Body: MessageBody,
<<R::Output as Responder>::Body as MessageBody>::Error: Into<BoxError>,
<<R::Output as Responder>::Body as MessageBody>::Error: fmt::Debug,
{
boxed::factory(fn_service(move |req: ServiceRequest| {
let handler = handler.clone();

View File

@ -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<dyn std::error::Error>;

View File

@ -325,9 +325,8 @@ pin_project! {
impl<B> MessageBody for StreamLog<B>
where
B: MessageBody,
B::Error: Into<Error>,
{
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),
}
}

View File

@ -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,
<R::Output as Responder>::Body: MessageBody,
<<R::Output as Responder>::Body as MessageBody>::Error: Into<BoxError>,
<<R::Output as Responder>::Body as MessageBody>::Error: fmt::Debug,
{
self.routes.push(Route::new().to(handler));
self

View File

@ -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<S, E>(&mut self, stream: S) -> HttpResponse
where
S: Stream<Item = Result<Bytes, E>> + 'static,
E: Into<BoxError> + 'static,
E: fmt::Debug + 'static,
{
self.body(BodyStream::new(stream))
}

View File

@ -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<R: Responder> CustomizeResponder<R> {
impl<T> Responder for CustomizeResponder<T>
where
T: Responder,
<T::Body as MessageBody>::Error: Into<BoxError>,
<T::Body as MessageBody>::Error: fmt::Debug,
{
type Body = EitherBody<T::Body>;

View File

@ -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<T> Responder for Option<T>
where
T: Responder,
<T::Body as MessageBody>::Error: Into<BoxError>,
<T::Body as MessageBody>::Error: fmt::Debug,
{
type Body = EitherBody<T::Body>;
@ -111,7 +111,7 @@ where
impl<T, E> Responder for Result<T, E>
where
T: Responder,
<T::Body as MessageBody>::Error: Into<BoxError>,
<T::Body as MessageBody>::Error: fmt::Debug,
E: Into<Error>,
{
type Body = EitherBody<T::Body>;

View File

@ -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,
<R::Output as Responder>::Body: MessageBody,
<<R::Output as Responder>::Body as MessageBody>::Error: Into<BoxError>,
<<R::Output as Responder>::Body as MessageBody>::Error: fmt::Debug,
{
self.service = handler_service(handler);
self

View File

@ -470,7 +470,6 @@ impl<B> From<ServiceResponse<B>> for Response<B> {
impl<B> fmt::Debug for ServiceResponse<B>
where
B: MessageBody,
B::Error: Into<Error>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let res = writeln!(

View File

@ -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,
<R::Output as Responder>::Body: MessageBody + 'static,
<<R::Output as Responder>::Body as MessageBody>::Error: Into<Box<dyn StdError + 'static>>,
<<R::Output as Responder>::Body as MessageBody>::Error: fmt::Debug,
{
Route::new().to(handler)
}