From 3a192400a68135c0c1e5a069145e91f91cba3aa2 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 23 Dec 2020 23:47:07 +0800 Subject: [PATCH] Simplify handler (#1843) --- src/handler.rs | 184 ++++++++++++++++--------------------------------- src/route.rs | 9 ++- 2 files changed, 63 insertions(+), 130 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index 0dc06b3c..3d0a2382 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,4 +1,3 @@ -use std::convert::Infallible; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; @@ -6,7 +5,7 @@ use std::task::{Context, Poll}; use actix_http::{Error, Response}; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ok, Ready}; +use futures_util::future::{ready, Ready}; use futures_util::ready; use pin_project::pin_project; @@ -36,9 +35,11 @@ where } #[doc(hidden)] +/// Extract arguments from request, run factory function and make response. pub struct Handler where F: Factory, + T: FromRequest, R: Future, O: Responder, { @@ -49,6 +50,7 @@ where impl Handler where F: Factory, + T: FromRequest, R: Future, O: Responder, { @@ -63,6 +65,7 @@ where impl Clone for Handler where F: Factory, + T: FromRequest, R: Future, O: Responder, { @@ -74,172 +77,103 @@ where } } -impl Service for Handler +impl ServiceFactory for Handler where F: Factory, + T: FromRequest, R: Future, O: Responder, -{ - type Request = (T, HttpRequest); - type Response = ServiceResponse; - type Error = Infallible; - type Future = HandlerServiceResponse; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - let fut = self.hnd.call(param); - HandlerServiceResponse::Future(fut, Some(req)) - } -} - -#[doc(hidden)] -#[pin_project(project = HandlerProj)] -pub enum HandlerServiceResponse -where - T: Future, - R: Responder, -{ - Future(#[pin] T, Option), - Responder(#[pin] R::Future, Option), -} - -impl Future for HandlerServiceResponse -where - T: Future, - R: Responder, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - loop { - match self.as_mut().project() { - HandlerProj::Future(fut, req) => { - let res = ready!(fut.poll(cx)); - let fut = res.respond_to(req.as_ref().unwrap()); - let state = HandlerServiceResponse::Responder(fut, req.take()); - self.as_mut().set(state); - } - HandlerProj::Responder(fut, req) => { - let res = ready!(fut.poll(cx)); - let req = req.take().unwrap(); - return match res { - Ok(res) => Poll::Ready(Ok(ServiceResponse::new(req, res))), - Err(e) => { - let res: Response = e.into().into(); - Poll::Ready(Ok(ServiceResponse::new(req, res))) - } - }; - } - } - } - } -} - -/// Extract arguments from request -pub struct Extract { - service: S, - _t: PhantomData, -} - -impl Extract { - pub fn new(service: S) -> Self { - Extract { - service, - _t: PhantomData, - } - } -} - -impl ServiceFactory for Extract -where - S: Service< - Request = (T, HttpRequest), - Response = ServiceResponse, - Error = Infallible, - > + Clone, { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; type Config = (); - type Service = ExtractService; + type Service = Self; type InitError = (); type Future = Ready>; fn new_service(&self, _: ()) -> Self::Future { - ok(ExtractService { - _t: PhantomData, - service: self.service.clone(), - }) + ready(Ok(self.clone())) } } -pub struct ExtractService { - service: S, - _t: PhantomData, -} - -impl Service for ExtractService +// Handler is both it's ServiceFactory and Service Type. +impl Service for Handler where - S: Service< - Request = (T, HttpRequest), - Response = ServiceResponse, - Error = Infallible, - > + Clone, + F: Factory, + T: FromRequest, + R: Future, + O: Responder, { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = ExtractResponse; + type Future = HandlerServiceFuture; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, req: ServiceRequest) -> Self::Future { + fn call(&mut self, req: Self::Request) -> Self::Future { let (req, mut payload) = req.into_parts(); let fut = T::from_request(&req, &mut payload); - ExtractResponse::Future(fut, Some(req), self.service.clone()) + HandlerServiceFuture::Extract(fut, Some(req), self.hnd.clone()) } } -#[pin_project(project = ExtractProj)] -pub enum ExtractResponse { - Future(#[pin] T::Future, Option, S), - Response(#[pin] S::Future), +#[doc(hidden)] +#[pin_project(project = HandlerProj)] +pub enum HandlerServiceFuture +where + F: Factory, + T: FromRequest, + R: Future, + O: Responder, +{ + Extract(#[pin] T::Future, Option, F), + Handle(#[pin] R, Option), + Respond(#[pin] O::Future, Option), } -impl Future for ExtractResponse +impl Future for HandlerServiceFuture where - S: Service< - Request = (T, HttpRequest), - Response = ServiceResponse, - Error = Infallible, - >, + F: Factory, + T: FromRequest, + R: Future, + O: Responder, { + // Error type in this future is a placeholder type. + // all instances of error must be converted to ServiceResponse and return in Ok. type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { match self.as_mut().project() { - ExtractProj::Future(fut, req, srv) => { - let res = ready!(fut.poll(cx)); - let req = req.take().unwrap(); - match res { - Err(e) => { - let req = ServiceRequest::new(req); - return Poll::Ready(Ok(req.error_response(e.into()))); - } + HandlerProj::Extract(fut, req, handle) => { + match ready!(fut.poll(cx)) { Ok(item) => { - let fut = srv.call((item, req)); - self.as_mut().set(ExtractResponse::Response(fut)); + let fut = handle.call(item); + let state = HandlerServiceFuture::Handle(fut, req.take()); + self.as_mut().set(state); } - } + Err(e) => { + let res: Response = e.into().into(); + let req = req.take().unwrap(); + return Poll::Ready(Ok(ServiceResponse::new(req, res))); + } + }; + } + HandlerProj::Handle(fut, req) => { + let res = ready!(fut.poll(cx)); + let fut = res.respond_to(req.as_ref().unwrap()); + let state = HandlerServiceFuture::Respond(fut, req.take()); + self.as_mut().set(state); + } + HandlerProj::Respond(fut, req) => { + let res = ready!(fut.poll(cx)).unwrap_or_else(|e| e.into().into()); + let req = req.take().unwrap(); + return Poll::Ready(Ok(ServiceResponse::new(req, res))); } - ExtractProj::Response(fut) => return fut.poll(cx).map_err(|_| panic!()), } } } diff --git a/src/route.rs b/src/route.rs index f8ef458f..439ae6c4 100644 --- a/src/route.rs +++ b/src/route.rs @@ -11,7 +11,7 @@ use futures_util::future::{ready, FutureExt, LocalBoxFuture}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; -use crate::handler::{Extract, Factory, Handler}; +use crate::handler::{Factory, Handler}; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; @@ -51,9 +51,9 @@ impl Route { #[allow(clippy::new_without_default)] pub fn new() -> Route { Route { - service: Box::new(RouteNewService::new(Extract::new(Handler::new(|| { + service: Box::new(RouteNewService::new(Handler::new(|| { ready(HttpResponse::NotFound()) - })))), + }))), guards: Rc::new(Vec::new()), } } @@ -226,8 +226,7 @@ impl Route { R: Future + 'static, U: Responder + 'static, { - self.service = - Box::new(RouteNewService::new(Extract::new(Handler::new(handler)))); + self.service = Box::new(RouteNewService::new(Handler::new(handler))); self } }