1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use config::Config;
use error::{self, Error, ErrorKind, Catch};
use routing::{Resource, ResourceFuture, RouteSet, RouteMatch};
use util::http::HttpFuture;
use util::tuple::Either2;

use futures::{Future, Poll};
use http;
use tower_service::Service;

use std::fmt;
use std::sync::Arc;

/// Web service
pub struct RoutedService<T, U>
where
    T: Resource,
{
    /// Resource that handles the request
    resource: T,

    /// Error handler
    catch: U,

    /// Config
    config: Config,

    /// Route set. Processes request to determine how the resource will process
    /// it.
    routes: Arc<RouteSet<T::Destination>>,
}

/// Response future returned by `RoutedService`
#[derive(Debug)]
pub struct RoutedResponse<T, U>
where U: Catch,
{
    request: http::Request<()>,
    catch: U,
    state: State<T, U::Future>,
}

#[derive(Debug)]
enum State<T, U> {
    Pending(T),
    Catching(U),
}

impl<T, U> Clone for RoutedService<T, U>
where
    T: Resource,
    U: Clone,
{
    fn clone(&self) -> RoutedService<T, U> {
        RoutedService {
            resource: self.resource.clone(),
            catch: self.catch.clone(),
            config: self.config.clone(),
            routes: self.routes.clone(),
        }
    }
}

impl<T, U> fmt::Debug for RoutedService<T, U>
where
    T: Resource + fmt::Debug,
    T::Destination: fmt::Debug,
    U: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("RoutedService")
            .field("resource", &self.resource)
            .field("catch", &self.catch)
            .field("routes", &self.routes)
            .finish()
    }
}

// ===== impl RoutedService =====

impl<T, U> RoutedService<T, U>
where T: Resource,
{
    /// Create a new `RoutedService`
    pub(crate) fn new(resource: T, catch: U, config: Config, routes: RouteSet<T::Destination>) -> Self {
        let routes = Arc::new(routes);

        RoutedService {
            resource,
            catch,
            config,
            routes,
        }
    }
}

impl<T, U> Service for RoutedService<T, U>
where T: Resource,
      U: Catch,
{
    type Request = http::Request<T::RequestBody>;
    type Response = <Self::Future as Future>::Item;
    type Error = Error;
    type Future = RoutedResponse<T::Future, U>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        // Always ready
        Ok(().into())
    }

    fn call(&mut self, request: Self::Request) -> Self::Future {
        // TODO: Use the body
        let (head, body) = request.into_parts();
        let request = http::Request::from_parts(head, ());

        let state = match self.routes.test(&request) {
            Some((destination, captures)) => {
                // Create the `RouteMatch` for the routing result
                let route_match = RouteMatch::new(&request, captures, &self.config);

                // Dispatch the requeest
                let pending = self.resource
                    .dispatch(destination, &route_match, body);

                State::Pending(pending)
            }
            None => {
                let error = ErrorKind::not_found().into();
                let catching = self.catch.catch(&request, error);

                State::Catching(catching)
            }
        };

        let catch = self.catch.clone();

        RoutedResponse {
            request,
            catch,
            state,
        }
    }
}

// ===== impl RoutedResponse =====

impl<T, U> Future for RoutedResponse<T, U>
where T: ResourceFuture,
      U: Catch,
{
    type Item = http::Response<Either2<T::Body, error::Map<U::Body>>>;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        use self::State::*;
        use util::tuple::Either2::*;
        use futures::Async::*;

        loop {
            let catching = match self.state {
                Pending(ref mut fut) => {
                    let error = match fut.poll_response(&self.request) {
                        Ok(Ready(v)) => {
                            let v = v.map(A);
                            return Ok(Ready(v))
                        }
                        Ok(NotReady) => return Ok(NotReady),
                        Err(e) => e,
                    };

                    self.catch.catch(&self.request, error)
                }
                Catching(ref mut fut) => {
                    let resp = try_ready!(HttpFuture::poll_http(fut))
                        .map(|body| B(error::Map::new(body)));

                    return Ok(Ready(resp));
                }
            };

            self.state = Catching(catching);
        }
    }
}