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-http-test = { version = "3.0.0-beta.4", 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"
rcgen = "0.8"
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_server::Server;
@ -22,7 +22,7 @@ async fn main() -> io::Result<()> {
HeaderValue::from_static("dummy value!"),
));
Ok::<_, ()>(res.body("Hello world!"))
Ok::<_, Infallible>(res.body("Hello world!"))
})
.tcp()
})?

View File

@ -6,9 +6,9 @@
//! $ 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 async_stream::stream;
use bytes::Bytes;
@ -24,13 +24,13 @@ async fn main() -> io::Result<()> {
log::info!("{:?}", req);
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("456"));
actix_rt::time::sleep(Duration::from_millis(1000)).await;
yield Err(Error::from(()));
yield Err(io::Error::new(io::ErrorKind::Other, ""));
})))
})
.tcp()

View File

@ -76,7 +76,9 @@ impl MessageBody for AnyBody {
// TODO: MSRV 1.51: poll_map_err
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))),
None => Poll::Ready(None),
},
@ -222,7 +224,7 @@ impl MessageBody for BoxAnyBody {
) -> Poll<Option<Result<Bytes, Self::Error>>> {
// TODO: MSRV 1.51: poll_map_err
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))),
None => Poll::Ready(None),
}

View File

@ -191,11 +191,15 @@ mod tests {
}
#[actix_rt::test]
async fn test_box() {
async fn test_box_and_pin() {
let val = Box::new(());
pin!(val);
assert_eq!(val.size(), BodySize::Empty);
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]

View File

@ -5,8 +5,8 @@ use derive_more::{Display, From};
#[cfg(feature = "openssl")]
use actix_tls::accept::openssl::SslError;
use crate::error::{Error, ParseError, ResponseError};
use crate::http::{Error as HttpError, StatusCode};
use crate::error::{Error, ParseError};
use crate::http::Error as HttpError;
/// A set of errors that can occur while connecting to an HTTP host
#[derive(Debug, Display, From)]
@ -119,19 +119,6 @@ pub enum 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
#[derive(Debug, Display, From)]
pub enum FreezeRequestError {

View File

@ -317,7 +317,7 @@ pub enum EncoderError<E> {
Body(E),
#[display(fmt = "boxed")]
Boxed(Error),
Boxed(Box<dyn StdError>),
#[display(fmt = "blocking")]
Blocking(BlockingError),
@ -331,14 +331,3 @@ impl<E: StdError> StdError for EncoderError<E> {
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
use std::{
error::Error as StdError,
fmt,
io::{self, Write as _},
str::Utf8Error,
string::FromUtf8Error,
};
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
use bytes::BytesMut;
use derive_more::{Display, Error, From};
use http::{header, uri::InvalidUri, StatusCode};
use serde::de::value::Error as DeError;
use http::{uri::InvalidUri, StatusCode};
use crate::{
body::{AnyBody, Body},
helpers::Writer,
Response,
};
use crate::{Response, body::{AnyBody, Body}, ws};
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 {
cause: Box<dyn ResponseError>,
inner: Box<ErrorInner>,
}
pub(crate) struct ErrorInner {
#[allow(dead_code)]
kind: Kind,
cause: Option<Box<dyn StdError>>,
}
impl Error {
/// Returns the reference to the underlying `ResponseError`.
pub fn as_response_error(&self) -> &dyn ResponseError {
self.cause.as_ref()
fn new(kind: Kind) -> Self {
Self {
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())
pub(crate) fn new_http() -> Self {
Self::new(Kind::Http)
}
pub(crate) fn new_parse() -> Self {
Self::new(Kind::Parse)
}
pub(crate) fn new_payload() -> Self {
Self::new(Kind::Payload)
}
pub(crate) fn new_body() -> Self {
Self::new(Kind::Body)
}
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
}
}
/// 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
}
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,
};
/// 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) -> 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))
Response::new(status_code).set_body(Body::from(err.to_string()))
}
downcast_get_type_id!();
}
downcast!(ResponseError);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
pub enum Kind {
#[display(fmt = "error processing HTTP")]
Http,
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.cause, f)
}
#[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 {
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 {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Error::from(UnitError)
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.inner.cause.as_ref().map(|err| err.as_ref())
}
}
impl From<std::convert::Infallible> for Error {
fn from(val: std::convert::Infallible) -> Self {
match val {}
fn from(err: std::convert::Infallible) -> Self {
match err {}
}
}
/// Convert `Error` to a `Response` instance
impl From<Error> for Response<Body> {
fn from(err: Error) -> Self {
Response::from_error(err)
impl From<ws::ProtocolError> for Error {
fn from(err: ws::ProtocolError) -> Self {
Self::new_ws().with_cause(err)
}
}
/// `Error` for any error that implements `ResponseError`
impl<T: ResponseError + 'static> From<T> for Error {
fn from(err: T) -> Error {
Error {
cause: Box::new(err),
}
impl From<HttpError> for Error {
fn from(err: HttpError) -> Self {
Self::new_http().with_cause(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 ResponseError for Utf8Error {
fn status_code(&self) -> StatusCode {
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
impl From<ws::HandshakeError> for Error {
fn from(err: ws::HandshakeError) -> Self {
Self::new_ws().with_cause(err)
}
}
@ -222,13 +187,6 @@ pub enum ParseError {
Utf8(Utf8Error),
}
/// Return `BadRequest` for `ParseError`
impl ResponseError for ParseError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
impl From<io::Error> for ParseError {
fn from(err: io::Error) -> ParseError {
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.
#[derive(Debug, Display, Error)]
#[display(fmt = "Blocking thread pool is gone")]
pub struct BlockingError;
/// `InternalServerError` for `BlockingError`
impl ResponseError for BlockingError {}
/// A set of errors that can occur during payload parsing.
#[derive(Debug, Display)]
#[non_exhaustive]
@ -348,16 +315,9 @@ impl From<BlockingError> for PayloadError {
}
}
/// `PayloadError` returns two possible results:
///
/// - `Overflow` returns `PayloadTooLarge`
/// - Other errors returns `BadRequest`
impl ResponseError for PayloadError {
fn status_code(&self) -> StatusCode {
match *self {
PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
_ => StatusCode::BAD_REQUEST,
}
impl From<PayloadError> for Error {
fn from(err: PayloadError) -> Self {
Self::new_payload().with_cause(err)
}
}
@ -444,12 +404,6 @@ mod content_type_test_impls {
}
}
impl ResponseError for ContentTypeError {
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -458,42 +412,33 @@ mod tests {
#[test]
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);
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);
}
#[test]
fn test_as_response() {
let orig = io::Error::new(io::ErrorKind::Other, "other");
let e: Error = ParseError::Io(orig).into();
assert_eq!(format!("{}", e.as_response_error()), "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);
let err: Error = ParseError::Io(orig).into();
assert_eq!(format!("{}", err), "error parsing HTTP message: IO error: other");
}
#[test]
fn test_error_display() {
let orig = io::Error::new(io::ErrorKind::Other, "other");
let desc = orig.to_string();
let e = Error::from(orig);
assert_eq!(format!("{}", e), desc);
let err = Error::new_io().with_cause(orig);
assert_eq!("connection error: other", err.to_string());
}
#[test]
fn test_error_http_response() {
let orig = io::Error::new(io::ErrorKind::Other, "other");
let e = Error::from(orig);
let resp: Response<Body> = e.into();
let err = Error::new_io().with_cause(orig);
let resp: Response<AnyBody> = err.into();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
@ -545,14 +490,4 @@ mod tests {
from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
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 crate::body::BodySize;
use crate::config::ServiceConfig;
use crate::header::{map::Value, HeaderName};
use crate::helpers;
use crate::http::header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
use crate::http::{HeaderMap, StatusCode, Version};
use crate::message::{ConnectionType, RequestHeadType};
use crate::response::Response;
use crate::{
body::BodySize,
config::ServiceConfig,
header::{map::Value, HeaderName},
helpers,
http::{
header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
HeaderMap, StatusCode, Version,
},
message::{ConnectionType, RequestHeadType},
response::Response,
};
const AVERAGE_HEADER_SIZE: usize = 30;
@ -287,7 +291,7 @@ impl MessageType for RequestHeadType {
let head = self.as_ref();
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
write!(
helpers::Writer(dst),
helpers::MutWriter(dst),
"{} {} {}",
head.method,
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
@ -420,7 +424,7 @@ impl TransferEncoding {
*eof = true;
buf.extend_from_slice(b"0\r\n\r\n");
} 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))?;
buf.reserve(msg.len() + 2);

View File

@ -81,7 +81,9 @@ where
let _ = this.body.take();
}
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,
}
@ -92,7 +94,10 @@ where
// flush write buffer
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(_) => {
if body_ready {
continue;
@ -106,7 +111,9 @@ where
// send response
if let Some(res) = this.res.take() {
framed.write(res)?;
framed
.write(res)
.map_err(|err| Error::new_send_response().with_cause(err))?;
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' ');
}
/// 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) {
if n == 0 {
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");
}
// TODO: bench why this is needed vs Buf::writer
/// An `io` writer for a `BufMut` that should only be used once and on an empty buffer.
pub(crate) struct Writer<'a, B>(pub &'a mut B);
/// 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 Writer<'a, B>
impl<'a, B> io::Write for MutWriter<'a, B>
where
B: BufMut,
{

View File

@ -54,7 +54,7 @@ pub mod ws;
pub use self::builder::HttpServiceBuilder;
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::header::ContentEncoding;
pub use self::http_message::HttpMessage;

View File

@ -66,16 +66,6 @@ impl Response<AnyBody> {
}
// 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> {
@ -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> {
fn from(val: std::convert::Infallible) -> Self {
match val {}

View File

@ -14,7 +14,7 @@ use bytes::Bytes;
use futures_core::Stream;
use crate::{
body::{Body, BodyStream},
body::{AnyBody, BodyStream},
error::{Error, HttpError},
header::{self, IntoHeaderPair, IntoHeaderValue},
message::{BoxedResponseHead, ConnectionType, ResponseHead},
@ -236,9 +236,9 @@ impl ResponseBuilder {
///
/// This `ResponseBuilder` will be left in a useless state.
#[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())
.unwrap_or_else(Response::from_error)
.unwrap_or_else(Response::from)
}
/// Generate response with a body.
@ -246,7 +246,7 @@ impl ResponseBuilder {
/// This `ResponseBuilder` will be left in a useless state.
pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {
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");
@ -257,20 +257,20 @@ impl ResponseBuilder {
///
/// This `ResponseBuilder` will be left in a useless state.
#[inline]
pub fn streaming<S, E>(&mut self, stream: S) -> Response<Body>
pub fn streaming<S, E>(&mut self, stream: S) -> Response<AnyBody>
where
S: Stream<Item = Result<Bytes, E>> + '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.
///
/// This `ResponseBuilder` will be left in a useless state.
#[inline]
pub fn finish(&mut self) -> Response<Body> {
self.body(Body::Empty)
pub fn finish(&mut self) -> Response<AnyBody> {
self.body(AnyBody::Empty)
}
/// 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 {
type Output = Result<Response<Body>, Error>;
type Output = Result<Response<AnyBody>, Error>;
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(Ok(self.finish()))

View File

@ -72,7 +72,7 @@ mod inner {
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use crate::{body::AnyBody, Response, ResponseError};
use crate::{body::AnyBody, Response};
/// Framed transport errors
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>
where
E: fmt::Debug + fmt::Display,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
use std::{
cell::Cell,
convert::Infallible,
task::{Context, Poll},
};
use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_http::{
body::BodySize,
body::{AnyBody, BodySize},
h1,
ws::{self, CloseCode, Frame, Item, Message},
Error, HttpService, Request, Response,
@ -13,6 +14,7 @@ use actix_http::{
use actix_http_test::test_server;
use actix_service::{fn_factory, Service};
use bytes::Bytes;
use derive_more::{Display, Error, From};
use futures_core::future::LocalBoxFuture;
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
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
{
type Response = ();
type Error = Error;
type Error = WsServiceError;
type Future = LocalBoxFuture<'static, Result<Self::Response, 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());
ws::Dispatcher::with(framed, service).await?;
ws::Dispatcher::with(framed, service)
.await
.map_err(|_| WsServiceError::Dispatcher)?;
Ok(())
})
@ -72,7 +103,7 @@ async fn service(msg: Frame) -> Result<Message, Error> {
Frame::Binary(bin) => Message::Binary(bin),
Frame::Continuation(item) => Message::Continuation(item),
Frame::Close(reason) => Message::Close(reason),
_ => return Err(Error::from(ws::ProtocolError::BadOpCode)),
_ => return Err(ws::ProtocolError::BadOpCode.into()),
};
Ok(msg)
@ -82,8 +113,10 @@ async fn service(msg: Frame) -> Result<Message, Error> {
async fn test_simple() {
let mut srv = test_server(|| {
HttpService::build()
.upgrade(fn_factory(|| async { Ok::<_, ()>(WsService::new()) }))
.finish(|_| async { Ok::<_, ()>(Response::not_found()) })
.upgrade(fn_factory(|| async {
Ok::<_, Infallible>(WsService::new())
}))
.finish(|_| async { Ok::<_, Infallible>(Response::not_found()) })
.tcp()
})
.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::ProtocolError as WsProtocolError;
use actix_http::ResponseError;
use serde_json::error::Error as JsonError;
use actix_http::http::{header::HeaderValue, StatusCode};
@ -77,6 +76,3 @@ pub enum JsonPayloadError {
}
impl std::error::Error for JsonPayloadError {}
/// Return `InternalServerError` for `JsonPayloadError`
impl ResponseError for JsonPayloadError {}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
use std::{cell::RefCell, fmt, io::Write as _};
use actix_http::{body::Body, header, Response, StatusCode};
use actix_http::{body::Body, header, StatusCode};
use bytes::{BufMut as _, BytesMut};
use crate::{Error, HttpResponse, ResponseError};
@ -77,10 +77,10 @@ where
}
}
fn error_response(&self) -> Response<Body> {
fn error_response(&self) -> HttpResponse {
match self.status {
InternalErrorType::Status(status) => {
let mut res = Response::new(status);
let mut res = HttpResponse::new(status);
let mut buf = BytesMut::new().writer();
let _ = write!(buf, "{}", self);
@ -95,7 +95,7 @@ where
if let Some(resp) = resp.borrow_mut().take() {
resp.into()
} 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;
mod internal;
mod macros;
mod response_error;
pub use self::internal::*;
pub use self::response_error::{Error, ResponseError};
/// 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::task::{Context, Poll};
use actix_http::Error;
use actix_service::{Service, ServiceFactory};
use actix_utils::future::{ready, Ready};
use futures_core::ready;
use pin_project::pin_project;
use crate::{
extract::FromRequest,
request::HttpRequest,
responder::Responder,
response::HttpResponse,
service::{ServiceRequest, ServiceResponse},
Error, FromRequest, HttpRequest, HttpResponse, Responder,
};
/// A request handler is an async function that accepts zero or more parameters that can be

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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