From c3c59e40d10db8f9a97797e99c4080d760a299d0 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 30 Nov 2021 18:13:28 +0000 Subject: [PATCH] force message body error types to be errors --- actix-http/src/body/body_stream.rs | 21 ++-- actix-http/src/body/message_body.rs | 123 +++++------------------ actix-http/src/encoding/encoder.rs | 35 +++---- actix-http/src/h1/utils.rs | 38 +++++--- actix-http/src/h2/dispatcher.rs | 6 +- actix-http/src/response.rs | 43 ++++---- actix-http/src/response_builder.rs | 1 - actix-http/src/service.rs | 146 +++++++++++++++------------- actix-http/src/ws/dispatcher.rs | 20 ++-- actix-web-actors/Cargo.toml | 2 +- actix-web-actors/src/ws.rs | 16 +-- src/app.rs | 54 +++++----- src/handler.rs | 29 ++---- src/lib.rs | 2 + src/resource.rs | 9 +- src/responder.rs | 47 +++++---- src/response/response.rs | 12 ++- src/route.rs | 28 +++--- src/service.rs | 4 +- src/types/either.rs | 7 +- 20 files changed, 293 insertions(+), 350 deletions(-) diff --git a/actix-http/src/body/body_stream.rs b/actix-http/src/body/body_stream.rs index 31de9b48f..1213733cc 100644 --- a/actix-http/src/body/body_stream.rs +++ b/actix-http/src/body/body_stream.rs @@ -75,6 +75,7 @@ mod tests { use derive_more::{Display, Error}; use futures_core::ready; use futures_util::{stream, FutureExt as _}; + use pin_project_lite::pin_project; use static_assertions::{assert_impl_all, assert_not_impl_all}; use super::*; @@ -166,12 +167,14 @@ mod tests { BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)])); assert!(matches!(to_bytes(body).await, Err(StreamErr))); - #[pin_project::pin_project(project = TimeDelayStreamProj)] - #[derive(Debug)] - enum TimeDelayStream { - Start, - Sleep(Pin>), - Done, + pin_project! { + #[derive(Debug)] + #[project = TimeDelayStreamProj] + enum TimeDelayStream { + Start, + Sleep { delay: Pin> }, + Done, + } } impl Stream for TimeDelayStream { @@ -184,12 +187,14 @@ mod tests { match self.as_mut().get_mut() { TimeDelayStream::Start => { let sleep = sleep(Duration::from_millis(1)); - self.as_mut().set(TimeDelayStream::Sleep(Box::pin(sleep))); + self.as_mut().set(TimeDelayStream::Sleep { + delay: Box::pin(sleep), + }); cx.waker().wake_by_ref(); Poll::Pending } - TimeDelayStream::Sleep(ref mut delay) => { + TimeDelayStream::Sleep { ref mut delay } => { ready!(delay.poll_unpin(cx)); self.set(TimeDelayStream::Done); cx.waker().wake_by_ref(); diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 5021ff832..a0c672cf2 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -2,6 +2,7 @@ use std::{ convert::Infallible, + error::Error as StdError, mem, pin::Pin, task::{Context, Poll}, @@ -16,7 +17,7 @@ use super::BodySize; /// An interface types that can converted to bytes and used as response bodies. // TODO: examples pub trait MessageBody { - type Error; + type Error: Into>; /// Body size hint. fn size(&self) -> BodySize; @@ -135,25 +136,6 @@ impl MessageBody for Bytes { } } -// impl<'a> MessageBody for &'a Bytes { -// type Error = Infallible; - -// fn size(&self) -> BodySize { -// BodySize::Sized(self.len() as u64) -// } - -// fn poll_next( -// self: Pin<&mut Self>, -// _cx: &mut Context<'_>, -// ) -> Poll>> { -// if self.is_empty() { -// Poll::Ready(None) -// } else { -// Poll::Ready(Some(Ok(self.clone()))) -// } -// } -// } - impl MessageBody for BytesMut { type Error = Infallible; @@ -168,29 +150,31 @@ impl MessageBody for BytesMut { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze()))) + let bytes = mem::take(self.get_mut()).freeze(); + Poll::Ready(Some(Ok(bytes))) } } } -// impl<'a> MessageBody for &'a BytesMut { -// type Error = Infallible; +impl MessageBody for Vec { + type Error = Infallible; -// fn size(&self) -> BodySize { -// BodySize::Sized(self.len() as u64) -// } + fn size(&self) -> BodySize { + BodySize::Sized(self.len() as u64) + } -// fn poll_next( -// self: Pin<&mut Self>, -// _cx: &mut Context<'_>, -// ) -> Poll>> { -// if self.is_empty() { -// Poll::Ready(None) -// } else { -// Poll::Ready(Some(Ok(self.clone().freeze()))) -// } -// } -// } + fn poll_next( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { + if self.is_empty() { + Poll::Ready(None) + } else { + let bytes = mem::take(self.get_mut()); + Poll::Ready(Some(Ok(Bytes::from(bytes)))) + } + } +} impl MessageBody for &'static str { type Error = Infallible; @@ -213,25 +197,6 @@ impl MessageBody for &'static str { } } -impl MessageBody for Vec { - type Error = Infallible; - - fn size(&self) -> BodySize { - BodySize::Sized(self.len() as u64) - } - - fn poll_next( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - ) -> Poll>> { - if self.is_empty() { - Poll::Ready(None) - } else { - Poll::Ready(Some(Ok(Bytes::from(mem::take(self.get_mut()))))) - } - } -} - impl MessageBody for String { type Error = Infallible; @@ -246,51 +211,12 @@ impl MessageBody for String { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(Bytes::from(mem::take(self.get_mut()))))) + let string = mem::take(self.get_mut()); + Poll::Ready(Some(Ok(Bytes::from(string)))) } } } -// impl<'a> MessageBody for &'a String { -// type Error = Infallible; - -// fn size(&self) -> BodySize { -// BodySize::Sized(self.len() as u64) -// } - -// fn poll_next( -// self: Pin<&mut Self>, -// _cx: &mut Context<'_>, -// ) -> Poll>> { -// if self.is_empty() { -// Poll::Ready(None) -// } else { -// Poll::Ready(Some(Ok(Bytes::from(self.clone())))) -// } -// } -// } - -// impl MessageBody for Cow<'_, str> { -// type Error = Infallible; - -// fn size(&self) -> BodySize { -// BodySize::Sized(self.len() as u64) -// } - -// fn poll_next( -// self: Pin<&mut Self>, -// cx: &mut Context<'_>, -// ) -> Poll>> { -// if self.is_empty() { -// Poll::Ready(None) -// } else { -// let cow = Pin::into_inner(self); -// let mut string = cow.clone().into_owned(); -// Pin::new(&mut string).poll_next(cx) -// } -// } -// } - impl MessageBody for bytestring::ByteString { type Error = Infallible; @@ -307,8 +233,6 @@ impl MessageBody for bytestring::ByteString { } } -// TODO: ensure consistent impls of MessageBody that always terminate - pin_project! { pub(crate) struct MessageBodyMapErr { #[pin] @@ -334,6 +258,7 @@ impl MessageBody for MessageBodyMapErr where B: MessageBody, F: FnOnce(B::Error) -> E, + E: Into>, { type Error = E; diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 5b892716b..542c958c0 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -103,8 +103,10 @@ pin_project! { #[project = EncoderBodyProj] enum EncoderBody { None, + // TODO: this variant is not used but RA can't see it because of macro wrapper - Bytes { bytes: Bytes }, + Bytes { body: Bytes }, + Stream { #[pin] body: B }, } } @@ -113,12 +115,12 @@ impl MessageBody for EncoderBody where B: MessageBody, { - type Error = EncoderError; + type Error = EncoderError; fn size(&self) -> BodySize { match self { EncoderBody::None => BodySize::None, - EncoderBody::Bytes { bytes } => bytes.size(), + EncoderBody::Bytes { body } => body.size(), EncoderBody::Stream { body } => body.size(), } } @@ -130,17 +132,16 @@ where match self.project() { EncoderBodyProj::None => Poll::Ready(None), - EncoderBodyProj::Bytes { bytes } => { - if bytes.is_empty() { + EncoderBodyProj::Bytes { body } => { + if body.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(std::mem::take(bytes)))) + Poll::Ready(Some(Ok(std::mem::take(body)))) } } - - EncoderBodyProj::Stream { body } => { - body.poll_next(cx).map_err(EncoderError::Body) - } + EncoderBodyProj::Stream { body } => body + .poll_next(cx) + .map_err(|err| EncoderError::Body(err.into())), } } } @@ -149,7 +150,7 @@ impl MessageBody for Encoder where B: MessageBody, { - type Error = EncoderError; + type Error = EncoderError; fn size(&self) -> BodySize { if self.encoder.is_none() { @@ -369,9 +370,9 @@ impl ContentEncoder { #[derive(Debug, Display)] #[non_exhaustive] -pub enum EncoderError { +pub enum EncoderError { #[display(fmt = "body")] - Body(E), + Body(Box), #[display(fmt = "blocking")] Blocking(BlockingError), @@ -380,18 +381,18 @@ pub enum EncoderError { Io(io::Error), } -impl StdError for EncoderError { +impl StdError for EncoderError { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - EncoderError::Body(err) => Some(err), + EncoderError::Body(err) => Some(&**err), EncoderError::Blocking(err) => Some(err), EncoderError::Io(err) => Some(err), } } } -impl From> for crate::Error { - fn from(err: EncoderError) -> Self { +impl From for crate::Error { + fn from(err: EncoderError) -> Self { crate::Error::new_encoder().with_cause(err) } } diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 2547f4494..905585a32 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -1,22 +1,30 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; +use pin_project_lite::pin_project; -use crate::body::{BodySize, MessageBody}; -use crate::error::Error; -use crate::h1::{Codec, Message}; -use crate::response::Response; +use crate::{ + body::{BodySize, MessageBody}, + error::Error, + h1::{Codec, Message}, + response::Response, +}; -/// Send HTTP/1 response -#[pin_project::pin_project] -pub struct SendResponse { - res: Option, BodySize)>>, - #[pin] - body: Option, - #[pin] - framed: Option>, +pin_project! { + /// Send HTTP/1 response + pub struct SendResponse { + res: Option, BodySize)>>, + + #[pin] + body: Option, + + #[pin] + framed: Option>, + } } impl SendResponse diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 5dfd01e04..322919873 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -51,7 +51,7 @@ where { pub(crate) fn new( flow: Rc>, - mut connection: Connection, + mut conn: Connection, on_connect_data: OnConnectData, config: ServiceConfig, peer_addr: Option, @@ -66,14 +66,14 @@ where }) .unwrap_or_else(|| Box::pin(sleep(dur))), on_flight: false, - ping_pong: connection.ping_pong().unwrap(), + ping_pong: conn.ping_pong().unwrap(), }); Self { flow, config, peer_addr, - connection, + connection: conn, on_connect_data, ping_pong, _phantom: PhantomData, diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 9b255dc02..79d354a1a 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -15,7 +15,7 @@ use crate::{ header::{self, IntoHeaderValue}, http::{HeaderMap, StatusCode}, message::{BoxedResponseHead, ResponseHead}, - ResponseBuilder, + Error, ResponseBuilder, }; /// An HTTP response. @@ -233,21 +233,16 @@ impl Default for Response { } } -// TODO: fix this impl -// impl From> for Response -// where -// B: MessageBody + 'static, -// B::Error: Into>, -// I: Into>, -// E: Into, -// { -// fn from(res: Result) -> Self { -// match res { -// Ok(val) => val.into(), -// Err(err) => err.into().into(), -// } -// } -// } +impl>, E: Into> From> + for Response +{ + fn from(res: Result) -> Self { + match res { + Ok(val) => val.into(), + Err(err) => Response::from(err.into()), + } + } +} impl From for Response { fn from(mut builder: ResponseBuilder) -> Self { @@ -288,12 +283,14 @@ impl From for Response { } } -// TODO: was this is useful impl -// impl<'a> From<&'a String> for Response<&'a String> { -// fn from(val: &'a String) -> Self { -// todo!() -// } -// } +impl From<&String> for Response { + fn from(val: &String) -> Self { + let mut res = Response::with_body(StatusCode::OK, val.clone()); + let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap(); + res.headers_mut().insert(header::CONTENT_TYPE, mime); + res + } +} impl From for Response { fn from(val: Bytes) -> Self { @@ -322,8 +319,6 @@ impl From for Response { } } -// TODO: impl into Response for ByteString - #[cfg(test)] mod tests { use super::*; diff --git a/actix-http/src/response_builder.rs b/actix-http/src/response_builder.rs index 295dfc3e7..dae204000 100644 --- a/actix-http/src/response_builder.rs +++ b/actix-http/src/response_builder.rs @@ -239,7 +239,6 @@ impl ResponseBuilder { { match self.message_body(body) { Ok(res) => res.map_body(|_, body| EitherBody::left(body)), - // TODO: add error path Err(err) => Response::from(err).map_body(|_, body| EitherBody::right(body)), } } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 85e1cf834..2e2d44ba9 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -15,7 +15,7 @@ use actix_service::{ fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _, }; use futures_core::{future::LocalBoxFuture, ready}; -use pin_project::pin_project; +use pin_project_lite::pin_project; use crate::{ body::{BoxBody, MessageBody}, @@ -519,23 +519,27 @@ where match proto { Protocol::Http2 => HttpServiceHandlerResponse { - state: State::H2Handshake(Some(( - h2::handshake_with_timeout(io, &self.cfg), - self.cfg.clone(), - self.flow.clone(), - on_connect_data, - peer_addr, - ))), + state: State::H2Handshake { + handshake: Some(( + h2::handshake_with_timeout(io, &self.cfg), + self.cfg.clone(), + self.flow.clone(), + on_connect_data, + peer_addr, + )), + }, }, Protocol::Http1 => HttpServiceHandlerResponse { - state: State::H1(h1::Dispatcher::new( - io, - self.cfg.clone(), - self.flow.clone(), - on_connect_data, - peer_addr, - )), + state: State::H1 { + dispatcher: h1::Dispatcher::new( + io, + self.cfg.clone(), + self.flow.clone(), + on_connect_data, + peer_addr, + ), + }, }, proto => unimplemented!("Unsupported HTTP version: {:?}.", proto), @@ -543,58 +547,67 @@ where } } -#[pin_project(project = StateProj)] -enum State -where - T: AsyncRead + AsyncWrite + Unpin, +pin_project! { + #[project = StateProj] + enum State + where + T: AsyncRead, + T: AsyncWrite, + T: Unpin, - S: Service, - S::Future: 'static, - S::Error: Into>, + S: Service, + S::Future: 'static, + S::Error: Into>, - B: MessageBody, - B::Error: Into>, + B: MessageBody, + B::Error: Into>, - X: Service, - X::Error: Into>, + X: Service, + X::Error: Into>, - U: Service<(Request, Framed), Response = ()>, - U::Error: fmt::Display, -{ - H1(#[pin] h1::Dispatcher), - H2(#[pin] h2::Dispatcher), - H2Handshake( - Option<( - h2::HandshakeWithTimeout, - ServiceConfig, - Rc>, - OnConnectData, - Option, - )>, - ), + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + H1 { #[pin] dispatcher: h1::Dispatcher }, + H2 { #[pin] dispatcher: h2::Dispatcher }, + H2Handshake { + handshake: Option<( + h2::HandshakeWithTimeout, + ServiceConfig, + Rc>, + OnConnectData, + Option, + )>, + }, + } } -#[pin_project] -pub struct HttpServiceHandlerResponse -where - T: AsyncRead + AsyncWrite + Unpin, +pin_project! { + pub struct HttpServiceHandlerResponse + where + T: AsyncRead, + T: AsyncWrite, + T: Unpin, - S: Service, - S::Error: Into> + 'static, - S::Future: 'static, - S::Response: Into> + 'static, + S: Service, + S::Error: Into>, + S::Error: 'static, + S::Future: 'static, + S::Response: Into>, + S::Response: 'static, - B: MessageBody, - B::Error: Into>, + B: MessageBody, + B::Error: Into>, - X: Service, - X::Error: Into>, + X: Service, + X::Error: Into>, - U: Service<(Request, Framed), Response = ()>, - U::Error: fmt::Display, -{ - #[pin] - state: State, + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + #[pin] + state: State, + } } impl Future for HttpServiceHandlerResponse @@ -619,23 +632,24 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.as_mut().project().state.project() { - StateProj::H1(disp) => disp.poll(cx), - StateProj::H2(disp) => disp.poll(cx), - StateProj::H2Handshake(data) => { + StateProj::H1 { dispatcher } => dispatcher.poll(cx), + StateProj::H2 { dispatcher } => dispatcher.poll(cx), + StateProj::H2Handshake { handshake: data } => { match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) { Ok((conn, timer)) => { - let (_, cfg, srv, on_connect_data, peer_addr) = + let (_, config, flow, on_connect_data, peer_addr) = data.take().unwrap(); - self.as_mut().project().state.set(State::H2( - h2::Dispatcher::new( - srv, + + self.as_mut().project().state.set(State::H2 { + dispatcher: h2::Dispatcher::new( + flow, conn, on_connect_data, - cfg, + config, peer_addr, timer, ), - )); + }); self.poll(cx) } Err(err) => { diff --git a/actix-http/src/ws/dispatcher.rs b/actix-http/src/ws/dispatcher.rs index f73eb3163..a3f766e9c 100644 --- a/actix-http/src/ws/dispatcher.rs +++ b/actix-http/src/ws/dispatcher.rs @@ -4,17 +4,21 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_service::{IntoService, Service}; +use pin_project_lite::pin_project; use super::{Codec, Frame, Message}; -#[pin_project::pin_project] -pub struct Dispatcher -where - S: Service + 'static, - T: AsyncRead + AsyncWrite, -{ - #[pin] - inner: inner::Dispatcher, +pin_project! { + pub struct Dispatcher + where + S: Service, + S: 'static, + T: AsyncRead, + T: AsyncWrite, + { + #[pin] + inner: inner::Dispatcher, + } } impl Dispatcher diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index e7b8cd0f0..28b5b29ea 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -22,7 +22,7 @@ actix-web = { version = "4.0.0-beta.11", default-features = false } bytes = "1" bytestring = "1" futures-core = { version = "0.3.7", default-features = false } -pin-project = "1.0.0" +pin-project-lite = "0.2" tokio = { version = "1", features = ["sync"] } [dev-dependencies] diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index f0a53d4e0..b6323c2ed 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -30,6 +30,7 @@ use actix_web::{ use bytes::{Bytes, BytesMut}; use bytestring::ByteString; use futures_core::Stream; +use pin_project_lite::pin_project; use tokio::sync::oneshot::Sender; /// Perform WebSocket handshake and start actor. @@ -462,13 +463,14 @@ where } } -#[pin_project::pin_project] -struct WsStream { - #[pin] - stream: S, - decoder: Codec, - buf: BytesMut, - closed: bool, +pin_project! { + struct WsStream { + #[pin] + stream: S, + decoder: Codec, + buf: BytesMut, + closed: bool, + } } impl WsStream diff --git a/src/app.rs b/src/app.rs index 8c3dc6ac7..36063ec79 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,37 +1,35 @@ -use std::cell::RefCell; -use std::fmt; -use std::future::Future; -use std::marker::PhantomData; -use std::rc::Rc; +use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc}; -use actix_http::body::{BoxBody, MessageBody}; -use actix_http::{Extensions, Request}; -use actix_service::boxed::{self, BoxServiceFactory}; +use actix_http::{ + body::{BoxBody, MessageBody}, + Extensions, Request, +}; use actix_service::{ - apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, Transform, + apply, apply_fn_factory, boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, + Transform, }; use futures_util::future::FutureExt as _; -use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; -use crate::config::ServiceConfig; -use crate::data::{Data, DataFactory, FnDataFactory}; -use crate::dev::ResourceDef; -use crate::error::Error; -use crate::resource::Resource; -use crate::route::Route; -use crate::service::{ - AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest, - ServiceResponse, +use crate::{ + app_service::{AppEntry, AppInit, AppRoutingFactory}, + config::ServiceConfig, + data::{Data, DataFactory, FnDataFactory}, + dev::ResourceDef, + error::Error, + resource::Resource, + route::Route, + service::{ + AppServiceFactory, BoxedHttpServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, + ServiceRequest, ServiceResponse, + }, }; -type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; - /// Application builder - structure that follows the builder pattern /// for building application instances. pub struct App { endpoint: T, services: Vec>, - default: Option>, + default: Option>, factory_ref: Rc>>, data_factories: Vec, external: Vec, @@ -287,7 +285,7 @@ where /// ); /// } /// ``` - pub fn default_service(mut self, f: F) -> Self + pub fn default_service(mut self, svc: F) -> Self where F: IntoServiceFactory, U: ServiceFactory< @@ -298,10 +296,12 @@ where > + 'static, U::InitError: fmt::Debug, { - // create and configure default resource - self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err( - |e| log::error!("Can not construct default service: {:?}", e), - )))); + let svc = svc + .into_factory() + .map(|res| res.map_into_boxed_body()) + .map_init_err(|e| log::error!("Can not construct default service: {:?}", e)); + + self.default = Some(Rc::new(boxed::factory(svc))); self } diff --git a/src/handler.rs b/src/handler.rs index 375f8abee..8ed40e1d7 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,14 +1,11 @@ use std::future::Future; -use actix_service::{ - boxed::{self, BoxServiceFactory}, - fn_service, -}; +use actix_service::{boxed, fn_service}; use crate::{ - body::EitherBody, - service::{ServiceRequest, ServiceResponse}, - Error, FromRequest, HttpResponse, Responder, + body::MessageBody, + service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, + BoxError, FromRequest, HttpResponse, Responder, }; /// A request handler is an async function that accepts zero or more parameters that can be @@ -25,20 +22,14 @@ where fn call(&self, param: T) -> R; } -pub fn handler_service( - handler: F, -) -> BoxServiceFactory< - (), - ServiceRequest, - ServiceResponse::Body>>, - Error, - (), -> +pub fn handler_service(handler: F) -> BoxedHttpServiceFactory where F: Handler, T: FromRequest, R: Future, R::Output: Responder, + ::Body: MessageBody, + <::Body as MessageBody>::Error: Into, { boxed::factory(fn_service(move |req: ServiceRequest| { let handler = handler.clone(); @@ -46,15 +37,13 @@ where async move { let (req, mut payload) = req.into_parts(); let res = match T::from_request(&req, &mut payload).await { - Err(err) => { - HttpResponse::from_error(err).map_body(|_, body| EitherBody::right(body)) - } + Err(err) => HttpResponse::from_error(err).map_into_boxed_body(), Ok(data) => handler .call(data) .await .respond_to(&req) - .map_body(|_, body| EitherBody::left(body)), + .map_into_boxed_body(), }; Ok(ServiceResponse::new(req, res)) diff --git a/src/lib.rs b/src/lib.rs index 3ad77ff5f..f6ec4082a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,3 +115,5 @@ pub use crate::scope::Scope; pub use crate::server::HttpServer; // TODO: is exposing the error directly really needed pub use crate::types::{Either, EitherExtractError}; + +pub(crate) type BoxError = Box; diff --git a/src/resource.rs b/src/resource.rs index bd9518bd0..fc417bac2 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, error::Error as StdError, fmt, future::Future, rc::Rc}; +use std::{cell::RefCell, fmt, future::Future, rc::Rc}; use actix_http::Extensions; use actix_router::{IntoPatterns, Patterns}; @@ -21,7 +21,7 @@ use crate::{ BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest, ServiceResponse, }, - Error, FromRequest, HttpResponse, + BoxError, Error, FromRequest, HttpResponse, }; /// *Resource* is an entry in resources table which corresponds to requested URL. @@ -239,9 +239,8 @@ where I: FromRequest + 'static, R: Future + 'static, R::Output: Responder + 'static, - ::Body: MessageBody + 'static, - <::Body as MessageBody>::Error: - Into>, + ::Body: MessageBody, + <::Body as MessageBody>::Error: Into, { self.routes.push(Route::new().to(handler)); self diff --git a/src/responder.rs b/src/responder.rs index 2878c1992..e3dbb8731 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,4 +1,4 @@ -use std::error::Error as StdError; +use std::borrow::Cow; use actix_http::{ body::{BoxBody, EitherBody, MessageBody}, @@ -6,7 +6,7 @@ use actix_http::{ }; use bytes::{Bytes, BytesMut}; -use crate::{Error, HttpRequest, HttpResponse, HttpResponseBuilder}; +use crate::{BoxError, Error, HttpRequest, HttpResponse, HttpResponseBuilder}; /// Trait implemented by types that can be converted to an HTTP response. /// @@ -99,7 +99,7 @@ impl Responder for actix_http::ResponseBuilder { impl Responder for Option where T: Responder, - ::Error: Into>, + ::Error: Into, { type Body = EitherBody; @@ -118,7 +118,7 @@ where impl Responder for Result where T: Responder, - ::Error: Into>, + ::Error: Into, E: Into, { type Body = EitherBody; @@ -129,8 +129,8 @@ where .respond_to(req) .map_body(|_, body| EitherBody::left(body)), - Err(e) => { - HttpResponse::from_error(e.into()).map_body(|_, body| EitherBody::right(body)) + Err(err) => { + HttpResponse::from_error(err.into()).map_body(|_, body| EitherBody::right(body)) } } } @@ -170,25 +170,22 @@ impl_responder_by_forward_into_base_response!(BytesMut); impl_responder_by_forward_into_base_response!(&'static str); impl_responder_by_forward_into_base_response!(String); -// macro_rules! impl_responder { -// ($res:ty, $body:ty, $ct:path) => { -// impl Responder for $res { -// type Body = $body; +macro_rules! impl_into_string_responder { + ($res:ty) => { + impl Responder for $res { + type Body = String; -// fn respond_to(self, _: &HttpRequest) -> HttpResponse { -// HttpResponse::Ok().content_type($ct).body(self) -// } -// } -// }; + fn respond_to(self, _: &HttpRequest) -> HttpResponse { + let string: String = self.into(); + let res: actix_http::Response<_> = string.into(); + res.into() + } + } + }; +} -// ($res:ty, $ct:path) => { -// impl_responder!($res, $res, $ct); -// }; -// } - -// impl_responder!(&'_ String, mime::TEXT_PLAIN_UTF_8); - -// impl_responder!(Cow<'_, str>, mime::TEXT_PLAIN_UTF_8); +impl_into_string_responder!(&'_ String); +impl_into_string_responder!(Cow<'_, str>); /// Allows overriding status code and headers for a responder. pub struct CustomResponder { @@ -257,7 +254,7 @@ impl CustomResponder { impl Responder for CustomResponder where T: Responder, - ::Error: Into>, + ::Error: Into, { type Body = EitherBody; @@ -265,7 +262,7 @@ where let headers = match self.headers { Ok(headers) => headers, Err(err) => { - return HttpResponse::from_error(Error::from(err)) + return HttpResponse::from_error(err) .map_body(|_, body| EitherBody::right(body)) } }; diff --git a/src/response/response.rs b/src/response/response.rs index fb2a76624..73964d732 100644 --- a/src/response/response.rs +++ b/src/response/response.rs @@ -9,7 +9,7 @@ use std::{ }; use actix_http::{ - body::{BoxBody, MessageBody}, + body::{BoxBody, EitherBody, MessageBody}, http::{header::HeaderMap, StatusCode}, Extensions, Response, ResponseHead, }; @@ -228,7 +228,15 @@ impl HttpResponse { } } - // TODO: old into_body equivalent, maybe + // TODO: docs for the body map methods below + + pub fn map_into_left_body(self) -> HttpResponse> { + self.map_body(|_, body| EitherBody::left(body)) + } + + pub fn map_into_right_body(self) -> HttpResponse> { + self.map_body(|_, body| EitherBody::right(body)) + } pub fn map_into_boxed_body(self) -> HttpResponse where diff --git a/src/route.rs b/src/route.rs index 66fda5068..e6ab45745 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,11 +1,11 @@ #![allow(clippy::rc_buffer)] // inner value is mutated before being shared (`Rc::get_mut`) -use std::{error::Error as StdError, future::Future, mem, rc::Rc}; +use std::{future::Future, mem, rc::Rc}; use actix_http::http::Method; use actix_service::{ - boxed::{self, BoxService, BoxServiceFactory}, - Service, ServiceFactory, ServiceFactoryExt, + boxed::{self, BoxService}, + fn_service, Service, ServiceFactory, ServiceFactoryExt, }; use futures_core::future::LocalBoxFuture; @@ -13,8 +13,8 @@ use crate::{ body::MessageBody, guard::{self, Guard}, handler::{handler_service, Handler}, - service::{ServiceRequest, ServiceResponse}, - Error, FromRequest, HttpResponse, Responder, + service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, + BoxError, Error, FromRequest, HttpResponse, Responder, }; /// Resource route definition @@ -22,7 +22,7 @@ use crate::{ /// Route uses builder-like pattern for configuration. /// If handler is not explicitly set, default *404 Not Found* handler is used. pub struct Route { - service: BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>, + service: BoxedHttpServiceFactory, guards: Rc>>, } @@ -31,10 +31,9 @@ impl Route { #[allow(clippy::new_without_default)] pub fn new() -> Route { Route { - // TODO: remove double boxing - service: boxed::factory( - handler_service(HttpResponse::NotFound).map(|res| res.map_into_boxed_body()), - ), + service: boxed::factory(fn_service(|req: ServiceRequest| async { + Ok(req.into_response(HttpResponse::NotFound())) + })), guards: Rc::new(Vec::new()), } } @@ -185,13 +184,10 @@ impl Route { T: FromRequest + 'static, R: Future + 'static, R::Output: Responder + 'static, - ::Body: MessageBody + 'static, - <::Body as MessageBody>::Error: - Into>, + ::Body: MessageBody, + <::Body as MessageBody>::Error: Into, { - // TODO: remove double boxing - self.service = - boxed::factory(handler_service(handler).map(|res| res.map_into_boxed_body())); + self.service = handler_service(handler); self } diff --git a/src/service.rs b/src/service.rs index 614e51394..4d8d2b4b9 100644 --- a/src/service.rs +++ b/src/service.rs @@ -27,9 +27,9 @@ use crate::{ Error, HttpRequest, HttpResponse, }; -pub(crate) type BoxedHttpService = BoxService; +pub(crate) type BoxedHttpService = BoxService, Error>; pub(crate) type BoxedHttpServiceFactory = - BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; + BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; pub trait HttpServiceFactory { fn register(self, config: &mut AppService); diff --git a/src/types/either.rs b/src/types/either.rs index df6d2f9c7..e4a322caa 100644 --- a/src/types/either.rs +++ b/src/types/either.rs @@ -1,7 +1,6 @@ //! For either helper, see [`Either`]. use std::{ - error::Error as StdError, future::Future, mem, pin::Pin, @@ -15,7 +14,7 @@ use pin_project_lite::pin_project; use crate::{ body, dev, web::{Form, Json}, - Error, FromRequest, HttpRequest, HttpResponse, Responder, + BoxError, Error, FromRequest, HttpRequest, HttpResponse, Responder, }; /// Combines two extractor or responder types into a single type. @@ -145,9 +144,9 @@ impl Either { impl Responder for Either where L: Responder, - ::Error: Into>, + ::Error: Into, R: Responder, - ::Error: Into>, + ::Error: Into, { type Body = body::EitherBody;