mirror of https://github.com/fafhrd91/actix-web
migrate error type (except tests)
This commit is contained in:
parent
93840507e1
commit
d514680631
|
@ -83,7 +83,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true }
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-http-test = { version = "3.0.0-beta.4", features = ["openssl"] }
|
actix-http-test = { version = "3.0.0-beta.4", features = ["openssl"] }
|
||||||
actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] }
|
actix-tls = { version = "3.0.0-beta.5", features = ["openssl"] }
|
||||||
criterion = "0.3"
|
criterion = { version = "0.3", features = ["html_reports"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::io;
|
use std::{convert::Infallible, io};
|
||||||
|
|
||||||
use actix_http::{http::StatusCode, HttpService, Response};
|
use actix_http::{http::StatusCode, HttpService, Response};
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
|
@ -22,7 +22,7 @@ async fn main() -> io::Result<()> {
|
||||||
HeaderValue::from_static("dummy value!"),
|
HeaderValue::from_static("dummy value!"),
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok::<_, ()>(res.body("Hello world!"))
|
Ok::<_, Infallible>(res.body("Hello world!"))
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})?
|
})?
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
//! $ echo 'GET / HTTP/1.1\n\n' | nc 127.0.0.1 8080
|
//! $ echo 'GET / HTTP/1.1\n\n' | nc 127.0.0.1 8080
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::{io, time::Duration};
|
use std::{convert::Infallible, io, time::Duration};
|
||||||
|
|
||||||
use actix_http::{body::BodyStream, Error, HttpService, Response};
|
use actix_http::{body::BodyStream, HttpService, Response};
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
@ -24,13 +24,13 @@ async fn main() -> io::Result<()> {
|
||||||
log::info!("{:?}", req);
|
log::info!("{:?}", req);
|
||||||
let res = Response::ok();
|
let res = Response::ok();
|
||||||
|
|
||||||
Ok::<_, ()>(res.set_body(BodyStream::new(stream! {
|
Ok::<_, Infallible>(res.set_body(BodyStream::new(stream! {
|
||||||
yield Ok(Bytes::from("123"));
|
yield Ok(Bytes::from("123"));
|
||||||
yield Ok(Bytes::from("456"));
|
yield Ok(Bytes::from("456"));
|
||||||
|
|
||||||
actix_rt::time::sleep(Duration::from_millis(1000)).await;
|
actix_rt::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
|
||||||
yield Err(Error::from(()));
|
yield Err(io::Error::new(io::ErrorKind::Other, ""));
|
||||||
})))
|
})))
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
|
|
|
@ -76,7 +76,9 @@ impl MessageBody for AnyBody {
|
||||||
|
|
||||||
// TODO: MSRV 1.51: poll_map_err
|
// TODO: MSRV 1.51: poll_map_err
|
||||||
AnyBody::Message(body) => match ready!(body.as_pin_mut().poll_next(cx)) {
|
AnyBody::Message(body) => match ready!(body.as_pin_mut().poll_next(cx)) {
|
||||||
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
|
Some(Err(err)) => {
|
||||||
|
Poll::Ready(Some(Err(Error::new_body().with_cause(err))))
|
||||||
|
}
|
||||||
Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
|
Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
},
|
},
|
||||||
|
@ -222,7 +224,7 @@ impl MessageBody for BoxAnyBody {
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
||||||
// TODO: MSRV 1.51: poll_map_err
|
// TODO: MSRV 1.51: poll_map_err
|
||||||
match ready!(self.0.as_mut().poll_next(cx)) {
|
match ready!(self.0.as_mut().poll_next(cx)) {
|
||||||
Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
|
Some(Err(err)) => Poll::Ready(Some(Err(Error::new_body().with_cause(err)))),
|
||||||
Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
|
Some(Ok(val)) => Poll::Ready(Some(Ok(val))),
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,11 +191,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_box() {
|
async fn test_box_and_pin() {
|
||||||
let val = Box::new(());
|
let val = Box::new(());
|
||||||
pin!(val);
|
pin!(val);
|
||||||
assert_eq!(val.size(), BodySize::Empty);
|
assert_eq!(val.size(), BodySize::Empty);
|
||||||
assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none());
|
assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none());
|
||||||
|
|
||||||
|
let mut val = Box::pin(());
|
||||||
|
assert_eq!(val.size(), BodySize::Empty);
|
||||||
|
assert!(poll_fn(|cx| val.as_mut().poll_next(cx)).await.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
|
|
@ -5,8 +5,8 @@ use derive_more::{Display, From};
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
use actix_tls::accept::openssl::SslError;
|
use actix_tls::accept::openssl::SslError;
|
||||||
|
|
||||||
use crate::error::{Error, ParseError, ResponseError};
|
use crate::error::{Error, ParseError};
|
||||||
use crate::http::{Error as HttpError, StatusCode};
|
use crate::http::Error as HttpError;
|
||||||
|
|
||||||
/// A set of errors that can occur while connecting to an HTTP host
|
/// A set of errors that can occur while connecting to an HTTP host
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
|
@ -119,19 +119,6 @@ pub enum SendRequestError {
|
||||||
|
|
||||||
impl std::error::Error for SendRequestError {}
|
impl std::error::Error for SendRequestError {}
|
||||||
|
|
||||||
/// Convert `SendRequestError` to a server `Response`
|
|
||||||
impl ResponseError for SendRequestError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match *self {
|
|
||||||
SendRequestError::Connect(ConnectError::Timeout) => {
|
|
||||||
StatusCode::GATEWAY_TIMEOUT
|
|
||||||
}
|
|
||||||
SendRequestError::Connect(_) => StatusCode::BAD_REQUEST,
|
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A set of errors that can occur during freezing a request
|
/// A set of errors that can occur during freezing a request
|
||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
pub enum FreezeRequestError {
|
pub enum FreezeRequestError {
|
||||||
|
|
|
@ -317,7 +317,7 @@ pub enum EncoderError<E> {
|
||||||
Body(E),
|
Body(E),
|
||||||
|
|
||||||
#[display(fmt = "boxed")]
|
#[display(fmt = "boxed")]
|
||||||
Boxed(Error),
|
Boxed(Box<dyn StdError>),
|
||||||
|
|
||||||
#[display(fmt = "blocking")]
|
#[display(fmt = "blocking")]
|
||||||
Blocking(BlockingError),
|
Blocking(BlockingError),
|
||||||
|
@ -331,14 +331,3 @@ impl<E: StdError> StdError for EncoderError<E> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Into<Error>> From<EncoderError<E>> for Error {
|
|
||||||
fn from(err: EncoderError<E>) -> Self {
|
|
||||||
match err {
|
|
||||||
EncoderError::Body(err) => err.into(),
|
|
||||||
EncoderError::Boxed(err) => err,
|
|
||||||
EncoderError::Blocking(err) => err.into(),
|
|
||||||
EncoderError::Io(err) => err.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,178 +1,143 @@
|
||||||
//! Error and Result module
|
//! Error and Result module
|
||||||
|
|
||||||
use std::{
|
use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
|
||||||
error::Error as StdError,
|
|
||||||
fmt,
|
|
||||||
io::{self, Write as _},
|
|
||||||
str::Utf8Error,
|
|
||||||
string::FromUtf8Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bytes::BytesMut;
|
|
||||||
use derive_more::{Display, Error, From};
|
use derive_more::{Display, Error, From};
|
||||||
use http::{header, uri::InvalidUri, StatusCode};
|
use http::{uri::InvalidUri, StatusCode};
|
||||||
use serde::de::value::Error as DeError;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{Response, body::{AnyBody, Body}, ws};
|
||||||
body::{AnyBody, Body},
|
|
||||||
helpers::Writer,
|
|
||||||
Response,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use http::Error as HttpError;
|
pub use http::Error as HttpError;
|
||||||
|
|
||||||
/// General purpose actix web error.
|
|
||||||
///
|
|
||||||
/// An actix web error is used to carry errors from `std::error`
|
|
||||||
/// through actix in a convenient way. It can be created through
|
|
||||||
/// converting errors with `into()`.
|
|
||||||
///
|
|
||||||
/// Whenever it is created from an external object a response error is created
|
|
||||||
/// for it that can be used to create an HTTP response from it this means that
|
|
||||||
/// if you have access to an actix `Error` you can always get a
|
|
||||||
/// `ResponseError` reference from it.
|
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
cause: Box<dyn ResponseError>,
|
inner: Box<ErrorInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ErrorInner {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
kind: Kind,
|
||||||
|
cause: Option<Box<dyn StdError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
/// Returns the reference to the underlying `ResponseError`.
|
fn new(kind: Kind) -> Self {
|
||||||
pub fn as_response_error(&self) -> &dyn ResponseError {
|
Self {
|
||||||
self.cause.as_ref()
|
inner: Box::new(ErrorInner { kind, cause: None }),
|
||||||
}
|
|
||||||
|
|
||||||
/// Similar to `as_response_error` but downcasts.
|
|
||||||
pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
|
|
||||||
<dyn ResponseError>::downcast_ref(self.cause.as_ref())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can generate responses.
|
pub(crate) fn new_http() -> Self {
|
||||||
// TODO: add std::error::Error bound when replacement for Box<dyn Error> is found
|
Self::new(Kind::Http)
|
||||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
|
||||||
/// Returns appropriate status code for error.
|
|
||||||
///
|
|
||||||
/// A 500 Internal Server Error is used by default. If [error_response](Self::error_response) is
|
|
||||||
/// also implemented and does not call `self.status_code()`, then this will not be used.
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates full response for error.
|
pub(crate) fn new_parse() -> Self {
|
||||||
///
|
Self::new(Kind::Parse)
|
||||||
/// By default, the generated response uses a 500 Internal Server Error status code, a
|
|
||||||
/// `Content-Type` of `text/plain`, and the body is set to `Self`'s `Display` impl.
|
|
||||||
fn error_response(&self) -> Response<Body> {
|
|
||||||
let mut resp = Response::new(self.status_code());
|
|
||||||
let mut buf = BytesMut::new();
|
|
||||||
let _ = write!(Writer(&mut buf), "{}", self);
|
|
||||||
resp.headers_mut().insert(
|
|
||||||
header::CONTENT_TYPE,
|
|
||||||
header::HeaderValue::from_static("text/plain; charset=utf-8"),
|
|
||||||
);
|
|
||||||
resp.set_body(Body::from(buf))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downcast_get_type_id!();
|
pub(crate) fn new_payload() -> Self {
|
||||||
|
Self::new(Kind::Payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
downcast!(ResponseError);
|
pub(crate) fn new_body() -> Self {
|
||||||
|
Self::new(Kind::Body)
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(&self.cause, f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_send_response() -> Self {
|
||||||
|
Self::new(Kind::SendResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_io() -> Self {
|
||||||
|
Self::new(Kind::Io)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_ws() -> Self {
|
||||||
|
Self::new(Kind::Ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_cause(mut self, cause: impl Into<Box<dyn StdError>>) -> Self {
|
||||||
|
self.inner.cause = Some(cause.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for Response<AnyBody> {
|
||||||
|
fn from(err: Error) -> Self {
|
||||||
|
let status_code = match err.inner.kind {
|
||||||
|
Kind::Parse => StatusCode::BAD_REQUEST,
|
||||||
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
};
|
||||||
|
|
||||||
|
Response::new(status_code).set_body(Body::from(err.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
|
||||||
|
pub enum Kind {
|
||||||
|
#[display(fmt = "error processing HTTP")]
|
||||||
|
Http,
|
||||||
|
|
||||||
|
#[display(fmt = "error parsing HTTP message")]
|
||||||
|
Parse,
|
||||||
|
|
||||||
|
#[display(fmt = "request payload read error")]
|
||||||
|
Payload,
|
||||||
|
|
||||||
|
#[display(fmt = "response body write error")]
|
||||||
|
Body,
|
||||||
|
|
||||||
|
#[display(fmt = "send response error")]
|
||||||
|
SendResponse,
|
||||||
|
|
||||||
|
#[display(fmt = "error in WebSocket process")]
|
||||||
|
Ws,
|
||||||
|
|
||||||
|
#[display(fmt = "connection error")]
|
||||||
|
Io,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Error {
|
impl fmt::Debug for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}", &self.cause)
|
// TODO: more detail
|
||||||
|
f.write_str("actix_http::Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.inner.cause.as_ref() {
|
||||||
|
Some(err) => write!(f, "{}: {}", &self.inner.kind, err),
|
||||||
|
None => write!(f, "{}", &self.inner.kind),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {
|
impl StdError for Error {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
None
|
self.inner.cause.as_ref().map(|err| err.as_ref())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<()> for Error {
|
|
||||||
fn from(_: ()) -> Self {
|
|
||||||
Error::from(UnitError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::convert::Infallible> for Error {
|
impl From<std::convert::Infallible> for Error {
|
||||||
fn from(val: std::convert::Infallible) -> Self {
|
fn from(err: std::convert::Infallible) -> Self {
|
||||||
match val {}
|
match err {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert `Error` to a `Response` instance
|
impl From<ws::ProtocolError> for Error {
|
||||||
impl From<Error> for Response<Body> {
|
fn from(err: ws::ProtocolError) -> Self {
|
||||||
fn from(err: Error) -> Self {
|
Self::new_ws().with_cause(err)
|
||||||
Response::from_error(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Error` for any error that implements `ResponseError`
|
impl From<HttpError> for Error {
|
||||||
impl<T: ResponseError + 'static> From<T> for Error {
|
fn from(err: HttpError) -> Self {
|
||||||
fn from(err: T) -> Error {
|
Self::new_http().with_cause(err)
|
||||||
Error {
|
|
||||||
cause: Box::new(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Display, Error)]
|
|
||||||
#[display(fmt = "Unknown Error")]
|
|
||||||
pub(crate) struct UnitError;
|
|
||||||
|
|
||||||
impl ResponseError for Box<dyn StdError + 'static> {}
|
|
||||||
|
|
||||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`UnitError`].
|
|
||||||
impl ResponseError for UnitError {}
|
|
||||||
|
|
||||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`actix_tls::accept::openssl::SslError`].
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
impl ResponseError for actix_tls::accept::openssl::SslError {}
|
|
||||||
|
|
||||||
/// Returns [`StatusCode::BAD_REQUEST`] for [`DeError`].
|
|
||||||
impl ResponseError for DeError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [`StatusCode::BAD_REQUEST`] for [`Utf8Error`].
|
impl From<ws::HandshakeError> for Error {
|
||||||
impl ResponseError for Utf8Error {
|
fn from(err: ws::HandshakeError) -> Self {
|
||||||
fn status_code(&self) -> StatusCode {
|
Self::new_ws().with_cause(err)
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns [`StatusCode::INTERNAL_SERVER_ERROR`] for [`HttpError`].
|
|
||||||
impl ResponseError for HttpError {}
|
|
||||||
|
|
||||||
/// Inspects the underlying [`io::ErrorKind`] and returns an appropriate status code.
|
|
||||||
///
|
|
||||||
/// If the error is [`io::ErrorKind::NotFound`], [`StatusCode::NOT_FOUND`] is returned. If the
|
|
||||||
/// error is [`io::ErrorKind::PermissionDenied`], [`StatusCode::FORBIDDEN`] is returned. Otherwise,
|
|
||||||
/// [`StatusCode::INTERNAL_SERVER_ERROR`] is returned.
|
|
||||||
impl ResponseError for io::Error {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match self.kind() {
|
|
||||||
io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
|
|
||||||
io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
|
|
||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns [`StatusCode::BAD_REQUEST`] for [`header::InvalidHeaderValue`].
|
|
||||||
impl ResponseError for header::InvalidHeaderValue {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,13 +187,6 @@ pub enum ParseError {
|
||||||
Utf8(Utf8Error),
|
Utf8(Utf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `BadRequest` for `ParseError`
|
|
||||||
impl ResponseError for ParseError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for ParseError {
|
impl From<io::Error> for ParseError {
|
||||||
fn from(err: io::Error) -> ParseError {
|
fn from(err: io::Error) -> ParseError {
|
||||||
ParseError::Io(err)
|
ParseError::Io(err)
|
||||||
|
@ -267,14 +225,23 @@ impl From<httparse::Error> for ParseError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ParseError> for Error {
|
||||||
|
fn from(err: ParseError) -> Self {
|
||||||
|
Self::new_parse().with_cause(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseError> for Response<AnyBody> {
|
||||||
|
fn from(err: ParseError) -> Self {
|
||||||
|
Error::from(err).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A set of errors that can occur running blocking tasks in thread pool.
|
/// A set of errors that can occur running blocking tasks in thread pool.
|
||||||
#[derive(Debug, Display, Error)]
|
#[derive(Debug, Display, Error)]
|
||||||
#[display(fmt = "Blocking thread pool is gone")]
|
#[display(fmt = "Blocking thread pool is gone")]
|
||||||
pub struct BlockingError;
|
pub struct BlockingError;
|
||||||
|
|
||||||
/// `InternalServerError` for `BlockingError`
|
|
||||||
impl ResponseError for BlockingError {}
|
|
||||||
|
|
||||||
/// A set of errors that can occur during payload parsing.
|
/// A set of errors that can occur during payload parsing.
|
||||||
#[derive(Debug, Display)]
|
#[derive(Debug, Display)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@ -348,16 +315,9 @@ impl From<BlockingError> for PayloadError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `PayloadError` returns two possible results:
|
impl From<PayloadError> for Error {
|
||||||
///
|
fn from(err: PayloadError) -> Self {
|
||||||
/// - `Overflow` returns `PayloadTooLarge`
|
Self::new_payload().with_cause(err)
|
||||||
/// - Other errors returns `BadRequest`
|
|
||||||
impl ResponseError for PayloadError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match *self {
|
|
||||||
PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
|
|
||||||
_ => StatusCode::BAD_REQUEST,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,12 +404,6 @@ mod content_type_test_impls {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for ContentTypeError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -458,42 +412,33 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_into_response() {
|
fn test_into_response() {
|
||||||
let resp: Response<Body> = ParseError::Incomplete.error_response();
|
let resp: Response<AnyBody> = ParseError::Incomplete.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
|
let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
|
||||||
let resp: Response<Body> = err.error_response();
|
let resp: Response<AnyBody> = Error::new_http().with_cause(err).into();
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_as_response() {
|
fn test_as_response() {
|
||||||
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
||||||
let e: Error = ParseError::Io(orig).into();
|
let err: Error = ParseError::Io(orig).into();
|
||||||
assert_eq!(format!("{}", e.as_response_error()), "IO error: other");
|
assert_eq!(format!("{}", err), "error parsing HTTP message: IO error: other");
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_cause() {
|
|
||||||
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
|
||||||
let desc = orig.to_string();
|
|
||||||
let e = Error::from(orig);
|
|
||||||
assert_eq!(format!("{}", e.as_response_error()), desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_error_display() {
|
fn test_error_display() {
|
||||||
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
||||||
let desc = orig.to_string();
|
let err = Error::new_io().with_cause(orig);
|
||||||
let e = Error::from(orig);
|
assert_eq!("connection error: other", err.to_string());
|
||||||
assert_eq!(format!("{}", e), desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_error_http_response() {
|
fn test_error_http_response() {
|
||||||
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
let orig = io::Error::new(io::ErrorKind::Other, "other");
|
||||||
let e = Error::from(orig);
|
let err = Error::new_io().with_cause(orig);
|
||||||
let resp: Response<Body> = e.into();
|
let resp: Response<AnyBody> = err.into();
|
||||||
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,14 +490,4 @@ mod tests {
|
||||||
from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
|
from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
|
||||||
from!(httparse::Error::Version => ParseError::Version);
|
from!(httparse::Error::Version => ParseError::Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_error_casting() {
|
|
||||||
let err = PayloadError::Overflow;
|
|
||||||
let resp_err: &dyn ResponseError = &err;
|
|
||||||
let err = resp_err.downcast_ref::<PayloadError>().unwrap();
|
|
||||||
assert_eq!(err.to_string(), "Payload reached size limit.");
|
|
||||||
let not_err = resp_err.downcast_ref::<ContentTypeError>();
|
|
||||||
assert!(not_err.is_none());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,18 @@ use std::{cmp, io};
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
|
|
||||||
use crate::body::BodySize;
|
use crate::{
|
||||||
use crate::config::ServiceConfig;
|
body::BodySize,
|
||||||
use crate::header::{map::Value, HeaderName};
|
config::ServiceConfig,
|
||||||
use crate::helpers;
|
header::{map::Value, HeaderName},
|
||||||
use crate::http::header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
|
helpers,
|
||||||
use crate::http::{HeaderMap, StatusCode, Version};
|
http::{
|
||||||
use crate::message::{ConnectionType, RequestHeadType};
|
header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING},
|
||||||
use crate::response::Response;
|
HeaderMap, StatusCode, Version,
|
||||||
|
},
|
||||||
|
message::{ConnectionType, RequestHeadType},
|
||||||
|
response::Response,
|
||||||
|
};
|
||||||
|
|
||||||
const AVERAGE_HEADER_SIZE: usize = 30;
|
const AVERAGE_HEADER_SIZE: usize = 30;
|
||||||
|
|
||||||
|
@ -287,7 +291,7 @@ impl MessageType for RequestHeadType {
|
||||||
let head = self.as_ref();
|
let head = self.as_ref();
|
||||||
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
|
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
|
||||||
write!(
|
write!(
|
||||||
helpers::Writer(dst),
|
helpers::MutWriter(dst),
|
||||||
"{} {} {}",
|
"{} {} {}",
|
||||||
head.method,
|
head.method,
|
||||||
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
|
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
|
||||||
|
@ -420,7 +424,7 @@ impl TransferEncoding {
|
||||||
*eof = true;
|
*eof = true;
|
||||||
buf.extend_from_slice(b"0\r\n\r\n");
|
buf.extend_from_slice(b"0\r\n\r\n");
|
||||||
} else {
|
} else {
|
||||||
writeln!(helpers::Writer(buf), "{:X}\r", msg.len())
|
writeln!(helpers::MutWriter(buf), "{:X}\r", msg.len())
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||||
|
|
||||||
buf.reserve(msg.len() + 2);
|
buf.reserve(msg.len() + 2);
|
||||||
|
|
|
@ -81,7 +81,9 @@ where
|
||||||
let _ = this.body.take();
|
let _ = this.body.take();
|
||||||
}
|
}
|
||||||
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
let framed = this.framed.as_mut().as_pin_mut().unwrap();
|
||||||
framed.write(Message::Chunk(item))?;
|
framed.write(Message::Chunk(item)).map_err(|err| {
|
||||||
|
Error::new_send_response().with_cause(err)
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Poll::Pending => body_ready = false,
|
Poll::Pending => body_ready = false,
|
||||||
}
|
}
|
||||||
|
@ -92,7 +94,10 @@ where
|
||||||
|
|
||||||
// flush write buffer
|
// flush write buffer
|
||||||
if !framed.is_write_buf_empty() {
|
if !framed.is_write_buf_empty() {
|
||||||
match framed.flush(cx)? {
|
match framed
|
||||||
|
.flush(cx)
|
||||||
|
.map_err(|err| Error::new_send_response().with_cause(err))?
|
||||||
|
{
|
||||||
Poll::Ready(_) => {
|
Poll::Ready(_) => {
|
||||||
if body_ready {
|
if body_ready {
|
||||||
continue;
|
continue;
|
||||||
|
@ -106,7 +111,9 @@ where
|
||||||
|
|
||||||
// send response
|
// send response
|
||||||
if let Some(res) = this.res.take() {
|
if let Some(res) = this.res.take() {
|
||||||
framed.write(res)?;
|
framed
|
||||||
|
.write(res)
|
||||||
|
.map_err(|err| Error::new_send_response().with_cause(err))?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@ pub(crate) fn write_status_line<B: BufMut>(version: Version, n: u16, buf: &mut B
|
||||||
buf.put_u8(b' ');
|
buf.put_u8(b' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: bytes object has to contain enough space
|
/// Write out content length header.
|
||||||
|
///
|
||||||
|
/// Buffer must to contain enough space or be implicitly extendable.
|
||||||
pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) {
|
pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
buf.put_slice(b"\r\ncontent-length: 0\r\n");
|
buf.put_slice(b"\r\ncontent-length: 0\r\n");
|
||||||
|
@ -41,11 +43,15 @@ pub fn write_content_length<B: BufMut>(n: u64, buf: &mut B) {
|
||||||
buf.put_slice(b"\r\n");
|
buf.put_slice(b"\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: bench why this is needed vs Buf::writer
|
/// An `io::Write`r that only requires mutable reference and assumes that there is space available
|
||||||
/// An `io` writer for a `BufMut` that should only be used once and on an empty buffer.
|
/// in the buffer for every write operation or that it can be extended implicitly (like
|
||||||
pub(crate) struct Writer<'a, B>(pub &'a mut B);
|
/// `bytes::BytesMut`, for example).
|
||||||
|
///
|
||||||
|
/// This is slightly faster (~10%) than `bytes::buf::Writer` in such cases because it does not
|
||||||
|
/// perform a remaining length check before writing.
|
||||||
|
pub(crate) struct MutWriter<'a, B>(pub(crate) &'a mut B);
|
||||||
|
|
||||||
impl<'a, B> io::Write for Writer<'a, B>
|
impl<'a, B> io::Write for MutWriter<'a, B>
|
||||||
where
|
where
|
||||||
B: BufMut,
|
B: BufMut,
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub mod ws;
|
||||||
|
|
||||||
pub use self::builder::HttpServiceBuilder;
|
pub use self::builder::HttpServiceBuilder;
|
||||||
pub use self::config::{KeepAlive, ServiceConfig};
|
pub use self::config::{KeepAlive, ServiceConfig};
|
||||||
pub use self::error::{Error, ResponseError};
|
pub use self::error::Error;
|
||||||
pub use self::extensions::Extensions;
|
pub use self::extensions::Extensions;
|
||||||
pub use self::header::ContentEncoding;
|
pub use self::header::ContentEncoding;
|
||||||
pub use self::http_message::HttpMessage;
|
pub use self::http_message::HttpMessage;
|
||||||
|
|
|
@ -66,16 +66,6 @@ impl Response<AnyBody> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// end shortcuts
|
// end shortcuts
|
||||||
|
|
||||||
/// Constructs a new response from an error.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_error(error: Error) -> Response<AnyBody> {
|
|
||||||
let resp = error.as_response_error().error_response();
|
|
||||||
if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR {
|
|
||||||
debug!("Internal Server Error: {:?}", error);
|
|
||||||
}
|
|
||||||
resp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> Response<B> {
|
impl<B> Response<B> {
|
||||||
|
@ -251,14 +241,6 @@ impl From<ResponseBuilder> for Response<AnyBody> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<()> for Response<AnyBody> {
|
|
||||||
fn from(_: ()) -> Self {
|
|
||||||
Error::from(crate::error::UnitError)
|
|
||||||
.as_response_error()
|
|
||||||
.error_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::convert::Infallible> for Response<AnyBody> {
|
impl From<std::convert::Infallible> for Response<AnyBody> {
|
||||||
fn from(val: std::convert::Infallible) -> Self {
|
fn from(val: std::convert::Infallible) -> Self {
|
||||||
match val {}
|
match val {}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{Body, BodyStream},
|
body::{AnyBody, BodyStream},
|
||||||
error::{Error, HttpError},
|
error::{Error, HttpError},
|
||||||
header::{self, IntoHeaderPair, IntoHeaderValue},
|
header::{self, IntoHeaderPair, IntoHeaderValue},
|
||||||
message::{BoxedResponseHead, ConnectionType, ResponseHead},
|
message::{BoxedResponseHead, ConnectionType, ResponseHead},
|
||||||
|
@ -236,9 +236,9 @@ impl ResponseBuilder {
|
||||||
///
|
///
|
||||||
/// This `ResponseBuilder` will be left in a useless state.
|
/// This `ResponseBuilder` will be left in a useless state.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn body<B: Into<Body>>(&mut self, body: B) -> Response<Body> {
|
pub fn body<B: Into<AnyBody>>(&mut self, body: B) -> Response<AnyBody> {
|
||||||
self.message_body(body.into())
|
self.message_body(body.into())
|
||||||
.unwrap_or_else(Response::from_error)
|
.unwrap_or_else(Response::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate response with a body.
|
/// Generate response with a body.
|
||||||
|
@ -246,7 +246,7 @@ impl ResponseBuilder {
|
||||||
/// This `ResponseBuilder` will be left in a useless state.
|
/// This `ResponseBuilder` will be left in a useless state.
|
||||||
pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {
|
pub fn message_body<B>(&mut self, body: B) -> Result<Response<B>, Error> {
|
||||||
if let Some(err) = self.err.take() {
|
if let Some(err) = self.err.take() {
|
||||||
return Err(err.into());
|
return Err(Error::new_http().with_cause(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
let head = self.head.take().expect("cannot reuse response builder");
|
let head = self.head.take().expect("cannot reuse response builder");
|
||||||
|
@ -257,20 +257,20 @@ impl ResponseBuilder {
|
||||||
///
|
///
|
||||||
/// This `ResponseBuilder` will be left in a useless state.
|
/// This `ResponseBuilder` will be left in a useless state.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn streaming<S, E>(&mut self, stream: S) -> Response<Body>
|
pub fn streaming<S, E>(&mut self, stream: S) -> Response<AnyBody>
|
||||||
where
|
where
|
||||||
S: Stream<Item = Result<Bytes, E>> + 'static,
|
S: Stream<Item = Result<Bytes, E>> + 'static,
|
||||||
E: Into<Box<dyn StdError>> + 'static,
|
E: Into<Box<dyn StdError>> + 'static,
|
||||||
{
|
{
|
||||||
self.body(Body::from_message(BodyStream::new(stream)))
|
self.body(AnyBody::from_message(BodyStream::new(stream)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate response with an empty body.
|
/// Generate response with an empty body.
|
||||||
///
|
///
|
||||||
/// This `ResponseBuilder` will be left in a useless state.
|
/// This `ResponseBuilder` will be left in a useless state.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn finish(&mut self) -> Response<Body> {
|
pub fn finish(&mut self) -> Response<AnyBody> {
|
||||||
self.body(Body::Empty)
|
self.body(AnyBody::Empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an owned `ResponseBuilder`, leaving the original in a useless state.
|
/// Create an owned `ResponseBuilder`, leaving the original in a useless state.
|
||||||
|
@ -328,7 +328,7 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Future for ResponseBuilder {
|
impl Future for ResponseBuilder {
|
||||||
type Output = Result<Response<Body>, Error>;
|
type Output = Result<Response<AnyBody>, Error>;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
Poll::Ready(Ok(self.finish()))
|
Poll::Ready(Ok(self.finish()))
|
||||||
|
|
|
@ -72,7 +72,7 @@ mod inner {
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
|
||||||
|
|
||||||
use crate::{body::AnyBody, Response, ResponseError};
|
use crate::{body::AnyBody, Response};
|
||||||
|
|
||||||
/// Framed transport errors
|
/// Framed transport errors
|
||||||
pub enum DispatcherError<E, U, I>
|
pub enum DispatcherError<E, U, I>
|
||||||
|
@ -136,15 +136,6 @@ mod inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, U, I> ResponseError for DispatcherError<E, U, I>
|
|
||||||
where
|
|
||||||
E: fmt::Debug + fmt::Display,
|
|
||||||
U: Encoder<I> + Decoder,
|
|
||||||
<U as Encoder<I>>::Error: fmt::Debug,
|
|
||||||
<U as Decoder>::Error: fmt::Debug,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, U, I> From<DispatcherError<E, U, I>> for Response<AnyBody>
|
impl<E, U, I> From<DispatcherError<E, U, I>> for Response<AnyBody>
|
||||||
where
|
where
|
||||||
E: fmt::Debug + fmt::Display,
|
E: fmt::Debug + fmt::Display,
|
||||||
|
|
|
@ -9,8 +9,8 @@ use derive_more::{Display, Error, From};
|
||||||
use http::{header, Method, StatusCode};
|
use http::{header, Method, StatusCode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::Body, error::ResponseError, header::HeaderValue, message::RequestHead,
|
body::AnyBody, header::HeaderValue, message::RequestHead, response::Response,
|
||||||
response::Response, ResponseBuilder,
|
ResponseBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod codec;
|
mod codec;
|
||||||
|
@ -68,8 +68,6 @@ pub enum ProtocolError {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for ProtocolError {}
|
|
||||||
|
|
||||||
/// WebSocket handshake errors
|
/// WebSocket handshake errors
|
||||||
#[derive(Debug, PartialEq, Display, Error)]
|
#[derive(Debug, PartialEq, Display, Error)]
|
||||||
pub enum HandshakeError {
|
pub enum HandshakeError {
|
||||||
|
@ -98,43 +96,54 @@ pub enum HandshakeError {
|
||||||
BadWebsocketKey,
|
BadWebsocketKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for HandshakeError {
|
impl From<&HandshakeError> for Response<AnyBody> {
|
||||||
fn error_response(&self) -> Response<Body> {
|
fn from(err: &HandshakeError) -> Self {
|
||||||
match self {
|
match err {
|
||||||
HandshakeError::GetMethodRequired => {
|
HandshakeError::GetMethodRequired => {
|
||||||
Response::build(StatusCode::METHOD_NOT_ALLOWED)
|
let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
|
||||||
.insert_header((header::ALLOW, "GET"))
|
res.headers_mut()
|
||||||
.finish()
|
.insert(header::ALLOW, HeaderValue::from_static("GET"));
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeError::NoWebsocketUpgrade => {
|
HandshakeError::NoWebsocketUpgrade => {
|
||||||
Response::build(StatusCode::BAD_REQUEST)
|
let mut res = Response::bad_request();
|
||||||
.reason("No WebSocket Upgrade header found")
|
res.head_mut().reason = Some("No WebSocket Upgrade header found");
|
||||||
.finish()
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeError::NoConnectionUpgrade => {
|
HandshakeError::NoConnectionUpgrade => {
|
||||||
Response::build(StatusCode::BAD_REQUEST)
|
let mut res = Response::bad_request();
|
||||||
.reason("No Connection upgrade")
|
res.head_mut().reason = Some("No Connection upgrade");
|
||||||
.finish()
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeError::NoVersionHeader => Response::build(StatusCode::BAD_REQUEST)
|
HandshakeError::NoVersionHeader => {
|
||||||
.reason("WebSocket version header is required")
|
let mut res = Response::bad_request();
|
||||||
.finish(),
|
res.head_mut().reason = Some("WebSocket version header is required");
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
HandshakeError::UnsupportedVersion => {
|
HandshakeError::UnsupportedVersion => {
|
||||||
Response::build(StatusCode::BAD_REQUEST)
|
let mut res = Response::bad_request();
|
||||||
.reason("Unsupported WebSocket version")
|
res.head_mut().reason = Some("Unsupported WebSocket version");
|
||||||
.finish()
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
HandshakeError::BadWebsocketKey => Response::build(StatusCode::BAD_REQUEST)
|
HandshakeError::BadWebsocketKey => {
|
||||||
.reason("Handshake error")
|
let mut res = Response::bad_request();
|
||||||
.finish(),
|
res.head_mut().reason = Some("Handshake error");
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HandshakeError> for Response<AnyBody> {
|
||||||
|
fn from(err: HandshakeError) -> Self {
|
||||||
|
err.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Verify WebSocket handshake request and create handshake response.
|
/// Verify WebSocket handshake request and create handshake response.
|
||||||
pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
|
pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
|
||||||
|
@ -213,7 +222,7 @@ pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test::TestRequest;
|
use crate::{body::AnyBody, test::TestRequest};
|
||||||
use http::{header, Method};
|
use http::{header, Method};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -327,18 +336,18 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_wserror_http_response() {
|
fn test_ws_error_http_response() {
|
||||||
let resp = HandshakeError::GetMethodRequired.error_response();
|
let resp: Response<AnyBody> = HandshakeError::GetMethodRequired.into();
|
||||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
let resp = HandshakeError::NoWebsocketUpgrade.error_response();
|
let resp: Response<AnyBody> = HandshakeError::NoWebsocketUpgrade.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
let resp = HandshakeError::NoConnectionUpgrade.error_response();
|
let resp: Response<AnyBody> = HandshakeError::NoConnectionUpgrade.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
let resp = HandshakeError::NoVersionHeader.error_response();
|
let resp: Response<AnyBody> = HandshakeError::NoVersionHeader.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
let resp = HandshakeError::UnsupportedVersion.error_response();
|
let resp: Response<AnyBody> = HandshakeError::UnsupportedVersion.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
let resp = HandshakeError::BadWebsocketKey.error_response();
|
let resp: Response<AnyBody> = HandshakeError::BadWebsocketKey.into();
|
||||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::AnyBody, http, http::StatusCode, HttpMessage, HttpService, Request, Response,
|
body::AnyBody, http, http::StatusCode, HttpMessage, HttpService, Request, Response,
|
||||||
ResponseError,
|
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::ServiceFactoryExt;
|
use actix_service::ServiceFactoryExt;
|
||||||
|
@ -35,7 +36,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
async fn test_h1_v2() {
|
async fn test_h1_v2() {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.finish(|_| future::ok::<_, ()>(Response::ok().set_body(STR)))
|
.finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -63,7 +64,7 @@ async fn test_h1_v2() {
|
||||||
async fn test_connection_close() {
|
async fn test_connection_close() {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.finish(|_| future::ok::<_, ()>(Response::ok().set_body(STR)))
|
.finish(|_| future::ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -77,11 +78,11 @@ async fn test_connection_close() {
|
||||||
async fn test_with_query_parameter() {
|
async fn test_with_query_parameter() {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.finish(|req: Request| {
|
.finish(|req: Request| async move {
|
||||||
if req.uri().query().unwrap().contains("qp=") {
|
if req.uri().query().unwrap().contains("qp=") {
|
||||||
future::ok::<_, ()>(Response::ok())
|
Ok::<_, Infallible>(Response::ok())
|
||||||
} else {
|
} else {
|
||||||
future::ok::<_, ()>(Response::bad_request())
|
Ok(Response::bad_request())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
|
@ -98,15 +99,9 @@ async fn test_with_query_parameter() {
|
||||||
#[display(fmt = "expect failed")]
|
#[display(fmt = "expect failed")]
|
||||||
struct ExpectFailed;
|
struct ExpectFailed;
|
||||||
|
|
||||||
impl ResponseError for ExpectFailed {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::EXPECTATION_FAILED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ExpectFailed> for Response<AnyBody> {
|
impl From<ExpectFailed> for Response<AnyBody> {
|
||||||
fn from(res: ExpectFailed) -> Self {
|
fn from(_: ExpectFailed) -> Self {
|
||||||
res.error_response()
|
Response::new(StatusCode::EXPECTATION_FAILED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +125,7 @@ async fn test_h1_expect() {
|
||||||
let str = std::str::from_utf8(&buf).unwrap();
|
let str = std::str::from_utf8(&buf).unwrap();
|
||||||
assert_eq!(str, "expect body");
|
assert_eq!(str, "expect body");
|
||||||
|
|
||||||
Ok::<_, ()>(Response::ok())
|
Ok::<_, Infallible>(Response::ok())
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,7 @@ use actix_http::{
|
||||||
header::{self, HeaderName, HeaderValue},
|
header::{self, HeaderName, HeaderValue},
|
||||||
Method, StatusCode, Version,
|
Method, StatusCode, Version,
|
||||||
},
|
},
|
||||||
Error, HttpMessage, HttpService, Request, Response, ResponseError,
|
Error, HttpMessage, HttpService, Request, Response,
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_service, ServiceFactoryExt};
|
use actix_service::{fn_service, ServiceFactoryExt};
|
||||||
|
@ -136,7 +136,7 @@ async fn test_h2_content_length() {
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
];
|
];
|
||||||
ok::<_, ()>(Response::new(statuses[idx]))
|
ok::<_, Infallible>(Response::new(statuses[idx]))
|
||||||
})
|
})
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
|
@ -206,7 +206,7 @@ async fn test_h2_headers() {
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ok::<_, ()>(builder.body(data.clone()))
|
ok::<_, Infallible>(builder.body(data.clone()))
|
||||||
})
|
})
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
|
@ -246,7 +246,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
async fn test_h2_body2() {
|
async fn test_h2_body2() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -264,7 +264,7 @@ async fn test_h2_body2() {
|
||||||
async fn test_h2_head_empty() {
|
async fn test_h2_head_empty() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.finish(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -288,7 +288,7 @@ async fn test_h2_head_empty() {
|
||||||
async fn test_h2_head_binary() {
|
async fn test_h2_head_binary() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -311,7 +311,7 @@ async fn test_h2_head_binary() {
|
||||||
async fn test_h2_head_binary2() {
|
async fn test_h2_head_binary2() {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
})
|
})
|
||||||
|
@ -358,7 +358,7 @@ async fn test_h2_body_chunked_explicit() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| {
|
.h2(|_| {
|
||||||
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
||||||
.streaming(body),
|
.streaming(body),
|
||||||
|
@ -386,7 +386,7 @@ async fn test_h2_response_http_error_handling() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(fn_service(|_| {
|
.h2(fn_service(|_| {
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((header::CONTENT_TYPE, broken_header))
|
.insert_header((header::CONTENT_TYPE, broken_header))
|
||||||
.body(STR),
|
.body(STR),
|
||||||
|
@ -402,22 +402,16 @@ async fn test_h2_response_http_error_handling() {
|
||||||
|
|
||||||
// read response
|
// read response
|
||||||
let bytes = srv.load_body(response).await.unwrap();
|
let bytes = srv.load_body(response).await.unwrap();
|
||||||
assert_eq!(bytes, Bytes::from_static(b"failed to parse header value"));
|
assert_eq!(bytes, Bytes::from_static(b"error processing HTTP: failed to parse header value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Display, Error)]
|
#[derive(Debug, Display, Error)]
|
||||||
#[display(fmt = "error")]
|
#[display(fmt = "error")]
|
||||||
struct BadRequest;
|
struct BadRequest;
|
||||||
|
|
||||||
impl ResponseError for BadRequest {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BadRequest> for Response<AnyBody> {
|
impl From<BadRequest> for Response<AnyBody> {
|
||||||
fn from(res: BadRequest) -> Self {
|
fn from(err: BadRequest) -> Self {
|
||||||
res.error_response()
|
Response::build(StatusCode::BAD_REQUEST).body(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +442,7 @@ async fn test_h2_on_connect() {
|
||||||
})
|
})
|
||||||
.h2(|req: Request| {
|
.h2(|req: Request| {
|
||||||
assert!(req.extensions().contains::<isize>());
|
assert!(req.extensions().contains::<isize>());
|
||||||
ok::<_, ()>(Response::ok())
|
ok::<_, Infallible>(Response::ok())
|
||||||
})
|
})
|
||||||
.openssl(tls_config())
|
.openssl(tls_config())
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
|
|
|
@ -14,7 +14,7 @@ use actix_http::{
|
||||||
header::{self, HeaderName, HeaderValue},
|
header::{self, HeaderName, HeaderValue},
|
||||||
Method, StatusCode, Version,
|
Method, StatusCode, Version,
|
||||||
},
|
},
|
||||||
Error, HttpService, Request, Response, ResponseError,
|
Error, HttpService, Request, Response,
|
||||||
};
|
};
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_factory_with_config, fn_service};
|
use actix_service::{fn_factory_with_config, fn_service};
|
||||||
|
@ -152,7 +152,7 @@ async fn test_h2_content_length() {
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
];
|
];
|
||||||
ok::<_, ()>(Response::new(statuses[indx]))
|
ok::<_, Infallible>(Response::new(statuses[indx]))
|
||||||
})
|
})
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
})
|
})
|
||||||
|
@ -221,7 +221,7 @@ async fn test_h2_headers() {
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ok::<_, ()>(config.body(data.clone()))
|
ok::<_, Infallible>(config.body(data.clone()))
|
||||||
})
|
})
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
}).await;
|
}).await;
|
||||||
|
@ -260,7 +260,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
async fn test_h2_body2() {
|
async fn test_h2_body2() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -277,7 +277,7 @@ async fn test_h2_body2() {
|
||||||
async fn test_h2_head_empty() {
|
async fn test_h2_head_empty() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.finish(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.finish(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -303,7 +303,7 @@ async fn test_h2_head_empty() {
|
||||||
async fn test_h2_head_binary() {
|
async fn test_h2_head_binary() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -328,7 +328,7 @@ async fn test_h2_head_binary() {
|
||||||
async fn test_h2_head_binary2() {
|
async fn test_h2_head_binary2() {
|
||||||
let srv = test_server(move || {
|
let srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h2(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.rustls(tls_config())
|
.rustls(tls_config())
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -351,7 +351,7 @@ async fn test_h2_body_length() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| {
|
.h2(|_| {
|
||||||
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
|
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -373,7 +373,7 @@ async fn test_h2_body_chunked_explicit() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(|_| {
|
.h2(|_| {
|
||||||
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
||||||
.streaming(body),
|
.streaming(body),
|
||||||
|
@ -399,9 +399,9 @@ async fn test_h2_response_http_error_handling() {
|
||||||
let mut srv = test_server(move || {
|
let mut srv = test_server(move || {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h2(fn_factory_with_config(|_: ()| {
|
.h2(fn_factory_with_config(|_: ()| {
|
||||||
ok::<_, ()>(fn_service(|_| {
|
ok::<_, Infallible>(fn_service(|_| {
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((http::header::CONTENT_TYPE, broken_header))
|
.insert_header((http::header::CONTENT_TYPE, broken_header))
|
||||||
.body(STR),
|
.body(STR),
|
||||||
|
@ -424,15 +424,9 @@ async fn test_h2_response_http_error_handling() {
|
||||||
#[display(fmt = "error")]
|
#[display(fmt = "error")]
|
||||||
struct BadRequest;
|
struct BadRequest;
|
||||||
|
|
||||||
impl ResponseError for BadRequest {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BadRequest> for Response<AnyBody> {
|
impl From<BadRequest> for Response<AnyBody> {
|
||||||
fn from(res: BadRequest) -> Self {
|
fn from(_: BadRequest) -> Self {
|
||||||
res.error_response()
|
Response::bad_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,19 @@ use std::{
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::{AnyBody, Body, SizedStream},
|
body::{AnyBody, Body, SizedStream},
|
||||||
http::{self, header, StatusCode},
|
header, http, Error, HttpMessage, HttpService, KeepAlive, Request, Response,
|
||||||
Error, HttpService, KeepAlive, Request, Response,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use actix_http::{HttpMessage, ResponseError};
|
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_rt::time::sleep;
|
use actix_rt::time::sleep;
|
||||||
use actix_service::fn_service;
|
use actix_service::fn_service;
|
||||||
use actix_utils::future::{err, ok, ready};
|
use actix_utils::future::{err, ok, ready};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use derive_more::{Display, Error};
|
use derive_more::{Display, Error};
|
||||||
use futures_util::stream::{once, StreamExt as _};
|
use futures_util::{
|
||||||
use futures_util::FutureExt as _;
|
stream::{once, StreamExt as _},
|
||||||
|
FutureExt as _,
|
||||||
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -30,7 +31,7 @@ async fn test_h1() {
|
||||||
.client_disconnect(1000)
|
.client_disconnect(1000)
|
||||||
.h1(|req: Request| {
|
.h1(|req: Request| {
|
||||||
assert!(req.peer_addr().is_some());
|
assert!(req.peer_addr().is_some());
|
||||||
ok::<_, ()>(Response::ok())
|
ok::<_, Infallible>(Response::ok())
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
@ -50,7 +51,7 @@ async fn test_h1_2() {
|
||||||
.finish(|req: Request| {
|
.finish(|req: Request| {
|
||||||
assert!(req.peer_addr().is_some());
|
assert!(req.peer_addr().is_some());
|
||||||
assert_eq!(req.version(), http::Version::HTTP_11);
|
assert_eq!(req.version(), http::Version::HTTP_11);
|
||||||
ok::<_, ()>(Response::ok())
|
ok::<_, Infallible>(Response::ok())
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
@ -64,15 +65,9 @@ async fn test_h1_2() {
|
||||||
#[display(fmt = "expect failed")]
|
#[display(fmt = "expect failed")]
|
||||||
struct ExpectFailed;
|
struct ExpectFailed;
|
||||||
|
|
||||||
impl ResponseError for ExpectFailed {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::PRECONDITION_FAILED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ExpectFailed> for Response<AnyBody> {
|
impl From<ExpectFailed> for Response<AnyBody> {
|
||||||
fn from(res: ExpectFailed) -> Self {
|
fn from(_: ExpectFailed) -> Self {
|
||||||
res.error_response()
|
Response::new(StatusCode::EXPECTATION_FAILED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +82,7 @@ async fn test_expect_continue() {
|
||||||
err(ExpectFailed)
|
err(ExpectFailed)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.finish(|_| ok::<_, ()>(Response::ok()))
|
.finish(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -96,7 +91,7 @@ async fn test_expect_continue() {
|
||||||
let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
|
let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
|
||||||
let mut data = String::new();
|
let mut data = String::new();
|
||||||
let _ = stream.read_to_string(&mut data);
|
let _ = stream.read_to_string(&mut data);
|
||||||
assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length"));
|
assert!(data.starts_with("HTTP/1.1 412 Expectation Failed\r\ncontent-length"));
|
||||||
|
|
||||||
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
let mut stream = net::TcpStream::connect(srv.addr()).unwrap();
|
||||||
let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
|
let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n");
|
||||||
|
@ -118,7 +113,7 @@ async fn test_expect_continue_h1() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
.h1(fn_service(|_| ok::<_, ()>(Response::ok())))
|
.h1(fn_service(|_| ok::<_, Infallible>(Response::ok())))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -199,7 +194,7 @@ async fn test_slow_request() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.client_timeout(100)
|
.client_timeout(100)
|
||||||
.finish(|_| ok::<_, ()>(Response::ok()))
|
.finish(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -215,7 +210,7 @@ async fn test_slow_request() {
|
||||||
async fn test_http1_malformed_request() {
|
async fn test_http1_malformed_request() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -231,7 +226,7 @@ async fn test_http1_malformed_request() {
|
||||||
async fn test_http1_keepalive() {
|
async fn test_http1_keepalive() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -253,7 +248,7 @@ async fn test_http1_keepalive_timeout() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.keep_alive(1)
|
.keep_alive(1)
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -274,7 +269,7 @@ async fn test_http1_keepalive_timeout() {
|
||||||
async fn test_http1_keepalive_close() {
|
async fn test_http1_keepalive_close() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -295,7 +290,7 @@ async fn test_http1_keepalive_close() {
|
||||||
async fn test_http10_keepalive_default_close() {
|
async fn test_http10_keepalive_default_close() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -315,7 +310,7 @@ async fn test_http10_keepalive_default_close() {
|
||||||
async fn test_http10_keepalive() {
|
async fn test_http10_keepalive() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -343,7 +338,7 @@ async fn test_http1_keepalive_disabled() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.keep_alive(KeepAlive::Disabled)
|
.keep_alive(KeepAlive::Disabled)
|
||||||
.h1(|_| ok::<_, ()>(Response::ok()))
|
.h1(|_| ok::<_, Infallible>(Response::ok()))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -378,7 +373,7 @@ async fn test_content_length() {
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
StatusCode::NOT_FOUND,
|
StatusCode::NOT_FOUND,
|
||||||
];
|
];
|
||||||
ok::<_, ()>(Response::new(statuses[indx]))
|
ok::<_, Infallible>(Response::new(statuses[indx]))
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
@ -433,7 +428,7 @@ async fn test_h1_headers() {
|
||||||
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ok::<_, ()>(builder.body(data.clone()))
|
ok::<_, Infallible>(builder.body(data.clone()))
|
||||||
}).tcp()
|
}).tcp()
|
||||||
}).await;
|
}).await;
|
||||||
|
|
||||||
|
@ -471,7 +466,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
async fn test_h1_body() {
|
async fn test_h1_body() {
|
||||||
let mut srv = test_server(|| {
|
let mut srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -488,7 +483,7 @@ async fn test_h1_body() {
|
||||||
async fn test_h1_head_empty() {
|
async fn test_h1_head_empty() {
|
||||||
let mut srv = test_server(|| {
|
let mut srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -513,7 +508,7 @@ async fn test_h1_head_empty() {
|
||||||
async fn test_h1_head_binary() {
|
async fn test_h1_head_binary() {
|
||||||
let mut srv = test_server(|| {
|
let mut srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -538,7 +533,7 @@ async fn test_h1_head_binary() {
|
||||||
async fn test_h1_head_binary2() {
|
async fn test_h1_head_binary2() {
|
||||||
let srv = test_server(|| {
|
let srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| ok::<_, ()>(Response::ok().set_body(STR)))
|
.h1(|_| ok::<_, Infallible>(Response::ok().set_body(STR)))
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -561,7 +556,7 @@ async fn test_h1_body_length() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| {
|
.h1(|_| {
|
||||||
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Infallible>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
|
Response::ok().set_body(SizedStream::new(STR.len() as u64, body)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -583,7 +578,7 @@ async fn test_h1_body_chunked_explicit() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| {
|
.h1(|_| {
|
||||||
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
.insert_header((header::TRANSFER_ENCODING, "chunked"))
|
||||||
.streaming(body),
|
.streaming(body),
|
||||||
|
@ -618,7 +613,7 @@ async fn test_h1_body_chunked_implicit() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(|_| {
|
.h1(|_| {
|
||||||
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref())));
|
||||||
ok::<_, ()>(Response::build(StatusCode::OK).streaming(body))
|
ok::<_, Infallible>(Response::build(StatusCode::OK).streaming(body))
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
@ -647,7 +642,7 @@ async fn test_h1_response_http_error_handling() {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.h1(fn_service(|_| {
|
.h1(fn_service(|_| {
|
||||||
let broken_header = Bytes::from_static(b"\0\0\0");
|
let broken_header = Bytes::from_static(b"\0\0\0");
|
||||||
ok::<_, ()>(
|
ok::<_, Infallible>(
|
||||||
Response::build(StatusCode::OK)
|
Response::build(StatusCode::OK)
|
||||||
.insert_header((http::header::CONTENT_TYPE, broken_header))
|
.insert_header((http::header::CONTENT_TYPE, broken_header))
|
||||||
.body(STR),
|
.body(STR),
|
||||||
|
@ -669,15 +664,9 @@ async fn test_h1_response_http_error_handling() {
|
||||||
#[display(fmt = "error")]
|
#[display(fmt = "error")]
|
||||||
struct BadRequest;
|
struct BadRequest;
|
||||||
|
|
||||||
impl ResponseError for BadRequest {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
StatusCode::BAD_REQUEST
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BadRequest> for Response<AnyBody> {
|
impl From<BadRequest> for Response<AnyBody> {
|
||||||
fn from(res: BadRequest) -> Self {
|
fn from(_: BadRequest) -> Self {
|
||||||
res.error_response()
|
Response::bad_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,7 +696,7 @@ async fn test_h1_on_connect() {
|
||||||
})
|
})
|
||||||
.h1(|req: Request| {
|
.h1(|req: Request| {
|
||||||
assert!(req.extensions().contains::<isize>());
|
assert!(req.extensions().contains::<isize>());
|
||||||
ok::<_, ()>(Response::ok())
|
ok::<_, Infallible>(Response::ok())
|
||||||
})
|
})
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
|
convert::Infallible,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::BodySize,
|
body::{AnyBody, BodySize},
|
||||||
h1,
|
h1,
|
||||||
ws::{self, CloseCode, Frame, Item, Message},
|
ws::{self, CloseCode, Frame, Item, Message},
|
||||||
Error, HttpService, Request, Response,
|
Error, HttpService, Request, Response,
|
||||||
|
@ -13,6 +14,7 @@ use actix_http::{
|
||||||
use actix_http_test::test_server;
|
use actix_http_test::test_server;
|
||||||
use actix_service::{fn_factory, Service};
|
use actix_service::{fn_factory, Service};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use derive_more::{Display, Error, From};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use futures_util::{SinkExt as _, StreamExt as _};
|
use futures_util::{SinkExt as _, StreamExt as _};
|
||||||
|
|
||||||
|
@ -33,12 +35,39 @@ impl WsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Display, Error, From)]
|
||||||
|
enum WsServiceError {
|
||||||
|
#[display(fmt = "http error")]
|
||||||
|
Http(actix_http::Error),
|
||||||
|
|
||||||
|
#[display(fmt = "ws handshake error")]
|
||||||
|
Ws(actix_http::ws::HandshakeError),
|
||||||
|
|
||||||
|
#[display(fmt = "io error")]
|
||||||
|
Io(std::io::Error),
|
||||||
|
|
||||||
|
#[display(fmt = "dispatcher error")]
|
||||||
|
Dispatcher,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WsServiceError> for Response<AnyBody> {
|
||||||
|
fn from(err: WsServiceError) -> Self {
|
||||||
|
match err {
|
||||||
|
WsServiceError::Http(err) => err.into(),
|
||||||
|
WsServiceError::Ws(err) => err.into(),
|
||||||
|
WsServiceError::Io(_err) => unreachable!(),
|
||||||
|
WsServiceError::Dispatcher => Response::internal_server_error()
|
||||||
|
.set_body(AnyBody::from(format!("{}", err))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService
|
impl<T> Service<(Request, Framed<T, h1::Codec>)> for WsService
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
{
|
{
|
||||||
type Response = ();
|
type Response = ();
|
||||||
type Error = Error;
|
type Error = WsServiceError;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
@ -56,7 +85,9 @@ where
|
||||||
|
|
||||||
let framed = framed.replace_codec(ws::Codec::new());
|
let framed = framed.replace_codec(ws::Codec::new());
|
||||||
|
|
||||||
ws::Dispatcher::with(framed, service).await?;
|
ws::Dispatcher::with(framed, service)
|
||||||
|
.await
|
||||||
|
.map_err(|_| WsServiceError::Dispatcher)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -72,7 +103,7 @@ async fn service(msg: Frame) -> Result<Message, Error> {
|
||||||
Frame::Binary(bin) => Message::Binary(bin),
|
Frame::Binary(bin) => Message::Binary(bin),
|
||||||
Frame::Continuation(item) => Message::Continuation(item),
|
Frame::Continuation(item) => Message::Continuation(item),
|
||||||
Frame::Close(reason) => Message::Close(reason),
|
Frame::Close(reason) => Message::Close(reason),
|
||||||
_ => return Err(Error::from(ws::ProtocolError::BadOpCode)),
|
_ => return Err(ws::ProtocolError::BadOpCode.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(msg)
|
Ok(msg)
|
||||||
|
@ -82,8 +113,10 @@ async fn service(msg: Frame) -> Result<Message, Error> {
|
||||||
async fn test_simple() {
|
async fn test_simple() {
|
||||||
let mut srv = test_server(|| {
|
let mut srv = test_server(|| {
|
||||||
HttpService::build()
|
HttpService::build()
|
||||||
.upgrade(fn_factory(|| async { Ok::<_, ()>(WsService::new()) }))
|
.upgrade(fn_factory(|| async {
|
||||||
.finish(|_| async { Ok::<_, ()>(Response::not_found()) })
|
Ok::<_, Infallible>(WsService::new())
|
||||||
|
}))
|
||||||
|
.finish(|_| async { Ok::<_, Infallible>(Response::not_found()) })
|
||||||
.tcp()
|
.tcp()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
|
@ -6,7 +6,6 @@ pub use actix_http::http::Error as HttpError;
|
||||||
pub use actix_http::ws::HandshakeError as WsHandshakeError;
|
pub use actix_http::ws::HandshakeError as WsHandshakeError;
|
||||||
pub use actix_http::ws::ProtocolError as WsProtocolError;
|
pub use actix_http::ws::ProtocolError as WsProtocolError;
|
||||||
|
|
||||||
use actix_http::ResponseError;
|
|
||||||
use serde_json::error::Error as JsonError;
|
use serde_json::error::Error as JsonError;
|
||||||
|
|
||||||
use actix_http::http::{header::HeaderValue, StatusCode};
|
use actix_http::http::{header::HeaderValue, StatusCode};
|
||||||
|
@ -77,6 +76,3 @@ pub enum JsonPayloadError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for JsonPayloadError {}
|
impl std::error::Error for JsonPayloadError {}
|
||||||
|
|
||||||
/// Return `InternalServerError` for `JsonPayloadError`
|
|
||||||
impl ResponseError for JsonPayloadError {}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
error::Error as StdError,
|
error::Error as StdError,
|
||||||
future::Future,
|
future::Future,
|
||||||
io, net,
|
net,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
|
@ -25,10 +25,10 @@ use serde::Serialize;
|
||||||
#[cfg(feature = "compress")]
|
#[cfg(feature = "compress")]
|
||||||
use actix_http::{encoding::Decoder, http::header::ContentEncoding, Payload, PayloadStream};
|
use actix_http::{encoding::Decoder, http::header::ContentEncoding, Payload, PayloadStream};
|
||||||
|
|
||||||
use crate::connect::{ConnectRequest, ConnectResponse};
|
use crate::{
|
||||||
use crate::error::{FreezeRequestError, InvalidUrl, SendRequestError};
|
error::{FreezeRequestError, InvalidUrl, SendRequestError},
|
||||||
use crate::response::ClientResponse;
|
ClientConfig, ClientResponse, ConnectRequest, ConnectResponse,
|
||||||
use crate::ClientConfig;
|
};
|
||||||
|
|
||||||
#[derive(Debug, From)]
|
#[derive(Debug, From)]
|
||||||
pub(crate) enum PrepForSendingError {
|
pub(crate) enum PrepForSendingError {
|
||||||
|
@ -211,7 +211,7 @@ impl RequestSender {
|
||||||
let body = match serde_json::to_string(value) {
|
let body = match serde_json::to_string(value) {
|
||||||
Ok(body) => body,
|
Ok(body) => body,
|
||||||
// TODO: own error type
|
// TODO: own error type
|
||||||
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(),
|
Err(_e) => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") {
|
if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") {
|
||||||
|
@ -238,7 +238,7 @@ impl RequestSender {
|
||||||
let body = match serde_urlencoded::to_string(value) {
|
let body = match serde_urlencoded::to_string(value) {
|
||||||
Ok(body) => body,
|
Ok(body) => body,
|
||||||
// TODO: own error type
|
// TODO: own error type
|
||||||
Err(e) => return Error::from(io::Error::new(io::ErrorKind::Other, e)).into(),
|
Err(_e) => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// set content-type
|
// set content-type
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use std::{any::type_name, ops::Deref, sync::Arc};
|
use std::{any::type_name, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use actix_http::{error::Error, Extensions};
|
use actix_http::{ Extensions};
|
||||||
use actix_utils::future::{err, ok, Ready};
|
use actix_utils::future::{err, ok, Ready};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dev::Payload, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
|
dev::Payload,Error, error::ErrorInternalServerError, extract::FromRequest, request::HttpRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Data factory.
|
/// Data factory.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::RefCell, fmt, io::Write as _};
|
use std::{cell::RefCell, fmt, io::Write as _};
|
||||||
|
|
||||||
use actix_http::{body::Body, header, Response, StatusCode};
|
use actix_http::{body::Body, header, StatusCode};
|
||||||
use bytes::{BufMut as _, BytesMut};
|
use bytes::{BufMut as _, BytesMut};
|
||||||
|
|
||||||
use crate::{Error, HttpResponse, ResponseError};
|
use crate::{Error, HttpResponse, ResponseError};
|
||||||
|
@ -77,10 +77,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_response(&self) -> Response<Body> {
|
fn error_response(&self) -> HttpResponse {
|
||||||
match self.status {
|
match self.status {
|
||||||
InternalErrorType::Status(status) => {
|
InternalErrorType::Status(status) => {
|
||||||
let mut res = Response::new(status);
|
let mut res = HttpResponse::new(status);
|
||||||
let mut buf = BytesMut::new().writer();
|
let mut buf = BytesMut::new().writer();
|
||||||
let _ = write!(buf, "{}", self);
|
let _ = write!(buf, "{}", self);
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ where
|
||||||
if let Some(resp) = resp.borrow_mut().take() {
|
if let Some(resp) = resp.borrow_mut().take() {
|
||||||
resp.into()
|
resp.into()
|
||||||
} else {
|
} else {
|
||||||
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
|
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,11 @@ use url::ParseError as UrlParseError;
|
||||||
use crate::http::StatusCode;
|
use crate::http::StatusCode;
|
||||||
|
|
||||||
mod internal;
|
mod internal;
|
||||||
|
mod macros;
|
||||||
|
mod response_error;
|
||||||
|
|
||||||
pub use self::internal::*;
|
pub use self::internal::*;
|
||||||
|
pub use self::response_error::{Error, ResponseError};
|
||||||
|
|
||||||
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
|
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
|
||||||
///
|
///
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,18 +3,14 @@ use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use actix_http::Error;
|
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extract::FromRequest,
|
|
||||||
request::HttpRequest,
|
|
||||||
responder::Responder,
|
|
||||||
response::HttpResponse,
|
|
||||||
service::{ServiceRequest, ServiceResponse},
|
service::{ServiceRequest, ServiceResponse},
|
||||||
|
Error, FromRequest, HttpRequest, HttpResponse, Responder,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A request handler is an async function that accepts zero or more parameters that can be
|
/// A request handler is an async function that accepts zero or more parameters that can be
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,6 +79,7 @@ pub mod error;
|
||||||
mod extract;
|
mod extract;
|
||||||
pub mod guard;
|
pub mod guard;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
mod helpers;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
mod info;
|
mod info;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
@ -97,7 +98,7 @@ pub(crate) mod types;
|
||||||
pub mod web;
|
pub mod web;
|
||||||
|
|
||||||
pub use actix_http::Response as BaseHttpResponse;
|
pub use actix_http::Response as BaseHttpResponse;
|
||||||
pub use actix_http::{body, Error, HttpMessage, ResponseError};
|
pub use actix_http::{body, HttpMessage};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use actix_rt as rt;
|
pub use actix_rt as rt;
|
||||||
pub use actix_web_codegen::*;
|
pub use actix_web_codegen::*;
|
||||||
|
@ -105,7 +106,7 @@ pub use actix_web_codegen::*;
|
||||||
pub use cookie;
|
pub use cookie;
|
||||||
|
|
||||||
pub use crate::app::App;
|
pub use crate::app::App;
|
||||||
pub use crate::error::Result;
|
pub use crate::error::{Error, ResponseError, Result};
|
||||||
pub use crate::extract::FromRequest;
|
pub use crate::extract::FromRequest;
|
||||||
pub use crate::request::HttpRequest;
|
pub use crate::request::HttpRequest;
|
||||||
pub use crate::resource::Resource;
|
pub use crate::resource::Resource;
|
||||||
|
|
|
@ -13,8 +13,8 @@ use futures_core::{future::LocalBoxFuture, ready};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dev::{ServiceRequest, ServiceResponse},
|
dev::{ServiceRequest, ServiceResponse},
|
||||||
error::{Error, Result},
|
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
Error, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Return type for [`ErrorHandlers`] custom handlers.
|
/// Return type for [`ErrorHandlers`] custom handlers.
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::{any::type_name, ops::Deref};
|
use std::{any::type_name, ops::Deref};
|
||||||
|
|
||||||
use actix_http::error::Error;
|
|
||||||
use actix_utils::future::{err, ok, Ready};
|
use actix_utils::future::{err, ok, Ready};
|
||||||
|
|
||||||
use crate::{dev::Payload, error::ErrorInternalServerError, FromRequest, HttpRequest};
|
use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpRequest};
|
||||||
|
|
||||||
/// Request-local data extractor.
|
/// Request-local data extractor.
|
||||||
///
|
///
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use actix_http::{Error, Extensions};
|
use actix_http::Extensions;
|
||||||
use actix_router::IntoPattern;
|
use actix_router::IntoPattern;
|
||||||
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
use actix_service::boxed::{self, BoxService, BoxServiceFactory};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
|
@ -13,14 +13,16 @@ use actix_service::{
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
|
|
||||||
use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef};
|
use crate::{
|
||||||
use crate::extract::FromRequest;
|
data::Data,
|
||||||
use crate::guard::Guard;
|
dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef},
|
||||||
use crate::handler::Handler;
|
guard::Guard,
|
||||||
use crate::responder::Responder;
|
handler::Handler,
|
||||||
use crate::route::{Route, RouteService};
|
responder::Responder,
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
route::{Route, RouteService},
|
||||||
use crate::{data::Data, HttpResponse};
|
service::{ServiceRequest, ServiceResponse},
|
||||||
|
Error, FromRequest, HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
|
type HttpService = BoxService<ServiceRequest, ServiceResponse, Error>;
|
||||||
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
|
type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>;
|
||||||
|
|
|
@ -231,7 +231,7 @@ where
|
||||||
T: fmt::Debug + fmt::Display + 'static,
|
T: fmt::Debug + fmt::Display + 'static,
|
||||||
{
|
{
|
||||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||||
HttpResponse::from_error(self.into())
|
HttpResponse::from_error(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -378,7 +378,7 @@ impl HttpResponseBuilder {
|
||||||
|
|
||||||
self.body(Body::from(body))
|
self.body(Body::from(body))
|
||||||
}
|
}
|
||||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
body::{Body, MessageBody},
|
body::{AnyBody, Body, MessageBody},
|
||||||
http::{header::HeaderMap, StatusCode},
|
http::{header::HeaderMap, StatusCode},
|
||||||
Extensions, Response, ResponseHead,
|
Extensions, Response, ResponseHead,
|
||||||
};
|
};
|
||||||
|
@ -22,15 +22,18 @@ use {
|
||||||
cookie::Cookie,
|
cookie::Cookie,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{error::Error, HttpResponseBuilder};
|
use crate::{
|
||||||
|
error::{Error, ResponseError},
|
||||||
|
HttpResponseBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
/// An HTTP Response
|
/// An HTTP Response
|
||||||
pub struct HttpResponse<B = Body> {
|
pub struct HttpResponse<B = AnyBody> {
|
||||||
res: Response<B>,
|
res: Response<B>,
|
||||||
pub(crate) error: Option<Error>,
|
pub(crate) error: Option<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpResponse<Body> {
|
impl HttpResponse<AnyBody> {
|
||||||
/// Create HTTP response builder with specific status.
|
/// Create HTTP response builder with specific status.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(status: StatusCode) -> HttpResponseBuilder {
|
pub fn build(status: StatusCode) -> HttpResponseBuilder {
|
||||||
|
@ -48,13 +51,14 @@ impl HttpResponse<Body> {
|
||||||
|
|
||||||
/// Create an error response.
|
/// Create an error response.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_error(error: Error) -> Self {
|
pub fn from_error(error: impl Into<Error>) -> Self {
|
||||||
let res = error.as_response_error().error_response();
|
error.into().as_response_error().error_response()
|
||||||
|
|
||||||
Self {
|
|
||||||
res,
|
|
||||||
error: Some(error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an error response.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_http_error(error: &dyn ResponseError) -> Self {
|
||||||
|
error.error_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +242,7 @@ impl<B> HttpResponse<B> {
|
||||||
impl<B> fmt::Debug for HttpResponse<B>
|
impl<B> fmt::Debug for HttpResponse<B>
|
||||||
where
|
where
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
B::Error: Into<Error>,
|
B::Error: Into<actix_http::Error>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("HttpResponse")
|
f.debug_struct("HttpResponse")
|
||||||
|
|
14
src/route.rs
14
src/route.rs
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
use std::{future::Future, rc::Rc};
|
use std::{future::Future, rc::Rc};
|
||||||
|
|
||||||
use actix_http::{http::Method, Error};
|
use actix_http::http::Method;
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
boxed::{self, BoxService, BoxServiceFactory},
|
boxed::{self, BoxService, BoxServiceFactory},
|
||||||
Service, ServiceFactory,
|
Service, ServiceFactory,
|
||||||
};
|
};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
use crate::extract::FromRequest;
|
use crate::{
|
||||||
use crate::guard::{self, Guard};
|
guard::{self, Guard},
|
||||||
use crate::handler::{Handler, HandlerService};
|
handler::{Handler, HandlerService},
|
||||||
use crate::responder::Responder;
|
service::{ServiceRequest, ServiceResponse},
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
Error, FromRequest, HttpResponse, Responder,
|
||||||
use crate::HttpResponse;
|
};
|
||||||
|
|
||||||
/// Resource route definition
|
/// Resource route definition
|
||||||
///
|
///
|
||||||
|
|
|
@ -5,21 +5,21 @@ use std::{fmt, net};
|
||||||
use actix_http::body::{Body, MessageBody};
|
use actix_http::body::{Body, MessageBody};
|
||||||
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
|
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||||
use actix_http::{
|
use actix_http::{
|
||||||
Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead,
|
Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead,
|
||||||
};
|
};
|
||||||
use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
|
use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url};
|
||||||
use actix_service::{IntoServiceFactory, ServiceFactory};
|
use actix_service::{IntoServiceFactory, ServiceFactory};
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
use cookie::{Cookie, ParseError as CookieParseError};
|
use cookie::{Cookie, ParseError as CookieParseError};
|
||||||
|
|
||||||
use crate::dev::insert_slash;
|
|
||||||
use crate::guard::Guard;
|
|
||||||
use crate::info::ConnectionInfo;
|
|
||||||
use crate::request::HttpRequest;
|
|
||||||
use crate::rmap::ResourceMap;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{AppConfig, AppService},
|
config::{AppConfig, AppService},
|
||||||
HttpResponse,
|
dev::insert_slash,
|
||||||
|
guard::Guard,
|
||||||
|
info::ConnectionInfo,
|
||||||
|
request::HttpRequest,
|
||||||
|
rmap::ResourceMap,
|
||||||
|
Error, HttpResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait HttpServiceFactory {
|
pub trait HttpServiceFactory {
|
||||||
|
@ -338,7 +338,7 @@ pub struct ServiceResponse<B = Body> {
|
||||||
impl ServiceResponse<Body> {
|
impl ServiceResponse<Body> {
|
||||||
/// Create service response from the error
|
/// Create service response from the error
|
||||||
pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self {
|
pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self {
|
||||||
let response = HttpResponse::from_error(err.into());
|
let response = HttpResponse::from_error(err);
|
||||||
ServiceResponse { request, response }
|
ServiceResponse { request, response }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,7 +188,7 @@ impl<T: Serialize> Responder for Form<T> {
|
||||||
Ok(body) => HttpResponse::Ok()
|
Ok(body) => HttpResponse::Ok()
|
||||||
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
|
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
|
||||||
.body(body),
|
.body(body),
|
||||||
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err).into()),
|
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ impl<T: Serialize> Responder for Json<T> {
|
||||||
Ok(body) => HttpResponse::Ok()
|
Ok(body) => HttpResponse::Ok()
|
||||||
.content_type(mime::APPLICATION_JSON)
|
.content_type(mime::APPLICATION_JSON)
|
||||||
.body(body),
|
.body(body),
|
||||||
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
|
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
use std::{fmt, ops, sync::Arc};
|
use std::{fmt, ops, sync::Arc};
|
||||||
|
|
||||||
use actix_http::error::Error;
|
|
||||||
use actix_router::PathDeserializer;
|
use actix_router::PathDeserializer;
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use serde::de;
|
use serde::de;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dev::Payload,
|
dev::Payload,
|
||||||
error::{ErrorNotFound, PathError},
|
error::{Error, ErrorNotFound, PathError},
|
||||||
FromRequest, HttpRequest,
|
FromRequest, HttpRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue