From 813b56ebe59224012210f560dd1a8b4cf592d04a Mon Sep 17 00:00:00 2001
From: Nikolay Kim <fafhrd91@gmail.com>
Date: Wed, 20 Dec 2017 12:51:39 -0800
Subject: [PATCH] make async handler future more generic

---
 guide/src/qs_4.md | 10 ++++++++--
 src/handler.rs    | 47 ++++++++++++++++++++++++++++++++---------------
 src/route.rs      | 10 +++++-----
 3 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/guide/src/qs_4.md b/guide/src/qs_4.md
index 25528c45d..dda86e278 100644
--- a/guide/src/qs_4.md
+++ b/guide/src/qs_4.md
@@ -95,8 +95,9 @@ fn main() {
 
 There are two different types of async handlers. 
 
-Response object could be generated asynchronously. In this case handle must
-return `Future` object that resolves to `HttpResponse`, i.e:
+Response object could be generated asynchronously or more precisely, any type
+that implements [*Responder*](../actix_web/trait.Responder.html) trait. In this case handle must
+return `Future` object that resolves to *Responder* type, i.e:
 
 ```rust
 # extern crate actix_web;
@@ -114,9 +115,14 @@ fn index(req: HttpRequest) -> FutureResult<HttpResponse, Error> {
            .map_err(|e| e.into()))
 }
 
+fn index2(req: HttpRequest) -> FutureResult<&'static str, Error> {
+    result(Ok("Welcome!"))
+}
+
 fn main() {
     Application::new()
         .resource("/async", |r| r.route().a(index))
+        .resource("/", |r| r.route().a(index2))
         .finish();
 }
 ```
diff --git a/src/handler.rs b/src/handler.rs
index f0fbb1ea3..2293d9090 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 
 use actix::Actor;
-use futures::Future;
+use futures::future::{Future, ok, err};
 use serde_json;
 use serde::Serialize;
 use regex::Regex;
@@ -221,36 +221,53 @@ impl<S, H, R> RouteHandler<S> for WrapHandler<S, H, R>
 
 /// Async route handler
 pub(crate)
-struct AsyncHandler<S, R, F>
-    where F: Fn(HttpRequest<S>) -> R + 'static,
-          R: Future<Item=HttpResponse, Error=Error> + 'static,
+struct AsyncHandler<S, H, F, R, E>
+    where H: Fn(HttpRequest<S>) -> F + 'static,
+          F: Future<Item=R, Error=E> + 'static,
+          R: Responder + 'static,
+          E: Into<Error> + 'static,
           S: 'static,
 {
-    f: Box<F>,
+    h: Box<H>,
     s: PhantomData<S>,
 }
 
-impl<S, R, F> AsyncHandler<S, R, F>
-    where F: Fn(HttpRequest<S>) -> R + 'static,
-          R: Future<Item=HttpResponse, Error=Error> + 'static,
+impl<S, H, F, R, E> AsyncHandler<S, H, F, R, E>
+    where H: Fn(HttpRequest<S>) -> F + 'static,
+          F: Future<Item=R, Error=E> + 'static,
+          R: Responder + 'static,
+          E: Into<Error> + 'static,
           S: 'static,
 {
-    pub fn new(f: F) -> Self {
-        AsyncHandler{f: Box::new(f), s: PhantomData}
+    pub fn new(h: H) -> Self {
+        AsyncHandler{h: Box::new(h), s: PhantomData}
     }
 }
 
-impl<S, R, F> RouteHandler<S> for AsyncHandler<S, R, F>
-    where F: Fn(HttpRequest<S>) -> R + 'static,
-          R: Future<Item=HttpResponse, Error=Error> + 'static,
+impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E>
+    where H: Fn(HttpRequest<S>) -> F + 'static,
+          F: Future<Item=R, Error=E> + 'static,
+          R: Responder + 'static,
+          E: Into<Error> + 'static,
           S: 'static,
 {
     fn handle(&self, req: HttpRequest<S>) -> Reply {
-        Reply::async((self.f)(req))
+        let req2 = req.clone_without_state();
+        let fut = (self.h)(req)
+            .map_err(|e| e.into())
+            .then(move |r| {
+                match r.respond_to(req2) {
+                    Ok(reply) => match reply.into().0 {
+                        ReplyItem::Message(resp) => ok(resp),
+                        _ => panic!("Nested async replies are not supported"),
+                    }
+                    Err(e) => err(e),
+                }
+            });
+        Reply::async(fut)
     }
 }
 
-
 /// Json response helper
 ///
 /// The `Json` type allows you to respond with well-formed JSON data: simply return a value of
diff --git a/src/route.rs b/src/route.rs
index 284daaf64..194a1c06c 100644
--- a/src/route.rs
+++ b/src/route.rs
@@ -5,8 +5,6 @@ use pred::Predicate;
 use handler::{Reply, Handler, Responder, RouteHandler, AsyncHandler, WrapHandler};
 use httpcodes::HTTPNotFound;
 use httprequest::HttpRequest;
-use httpresponse::HttpResponse;
-
 
 /// Resource route definition
 ///
@@ -80,9 +78,11 @@ impl<S: 'static> Route<S> {
     }
 
     /// Set async handler function.
-    pub fn a<F, R>(&mut self, handler: F)
-        where F: Fn(HttpRequest<S>) -> R + 'static,
-              R: Future<Item=HttpResponse, Error=Error> + 'static,
+    pub fn a<H, R, F, E>(&mut self, handler: H)
+        where H: Fn(HttpRequest<S>) -> F + 'static,
+              F: Future<Item=R, Error=E> + 'static,
+              R: Responder + 'static,
+              E: Into<Error> + 'static
     {
         self.handler = Box::new(AsyncHandler::new(handler));
     }