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
use std::io;
use std::rc::Rc;
use std::marker::PhantomData;

use actix::Actor;
use bytes::Bytes;
use http::{header, Version};
use futures::Stream;

use task::Task;
use context::HttpContext;
use resource::Reply;
use payload::Payload;
use httprequest::HttpRequest;
use httpresponse::{Body, HttpResponse};
use httpcodes::HTTPExpectationFailed;

#[doc(hidden)]
#[derive(Debug)]
#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))]
pub enum Frame {
    Message(HttpResponse),
    Payload(Option<Bytes>),
}

/// Trait defines object that could be regestered as resource route
#[allow(unused_variables)]
pub trait RouteHandler<S>: 'static {
    /// Handle request
    fn handle(&self, req: &mut HttpRequest, payload: Payload, state: Rc<S>) -> Task;

    /// Set route prefix
    fn set_prefix(&mut self, prefix: String) {}
}

/// Request handling result.
pub type RouteResult<T> = Result<Reply<T>, HttpResponse>;

/// Actors with ability to handle http requests.
#[allow(unused_variables)]
pub trait Route: Actor {
    /// Shared state. State is shared with all routes within same application
    /// and could be accessed with `HttpContext::state()` method.
    type State;

    /// Handle `EXPECT` header. By default respones with `HTTP/1.1 100 Continue`
    fn expect(req: &HttpRequest, ctx: &mut Self::Context) -> Result<(), HttpResponse>
        where Self: Actor<Context=HttpContext<Self>>
    {
        // handle expect header only for HTTP/1.1
        if req.version() == Version::HTTP_11 {
            if let Some(expect) = req.headers().get(header::EXPECT) {
                if let Ok(expect) = expect.to_str() {
                    if expect.to_lowercase() == "100-continue" {
                        ctx.write("HTTP/1.1 100 Continue\r\n\r\n");
                        Ok(())
                    } else {
                        Err(HTTPExpectationFailed.with_body(
                            Body::Binary("Unknown Expect".into())))
                    }
                } else {
                    Err(HTTPExpectationFailed.with_body(
                        Body::Binary("Unknown Expect".into())))
                }
            } else {
                Ok(())
            }
        } else {
            Ok(())
        }
    }

    /// Handle incoming request. Route actor can return
    /// result immediately with `Reply::reply`.
    /// Actor itself can be returned with `Reply::stream` for handling streaming
    /// request/response or websocket connection.
    /// In that case `HttpContext::start` and `HttpContext::write` has to be used
    /// for writing response.
    fn request(req: &mut HttpRequest,
               payload: Payload, ctx: &mut Self::Context) -> RouteResult<Self>;

    /// This method creates `RouteFactory` for this actor.
    fn factory() -> RouteFactory<Self, Self::State> {
        RouteFactory(PhantomData)
    }
}

/// This is used for routes registration within `Resource`
pub struct RouteFactory<A: Route<State=S>, S>(PhantomData<A>);

impl<A, S> RouteHandler<S> for RouteFactory<A, S>
    where A: Actor<Context=HttpContext<A>> + Route<State=S>,
          S: 'static
{
    fn handle(&self, req: &mut HttpRequest, payload: Payload, state: Rc<A::State>) -> Task
    {
        let mut ctx = HttpContext::new(state);

        // handle EXPECT header
        if req.headers().contains_key(header::EXPECT) {
            if let Err(resp) = A::expect(req, &mut ctx) {
                return Task::reply(resp)
            }
        }
        match A::request(req, payload, &mut ctx) {
            Ok(reply) => reply.into(ctx),
            Err(err) => Task::reply(err),
        }
    }
}

/// Fn() route handler
pub(crate)
struct FnHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Into<HttpResponse>,
          S: 'static,
{
    f: Box<F>,
    s: PhantomData<S>,
}

impl<S, R, F> FnHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Into<HttpResponse> + 'static,
          S: 'static,
{
    pub fn new(f: F) -> Self {
        FnHandler{f: Box::new(f), s: PhantomData}
    }
}

impl<S, R, F> RouteHandler<S> for FnHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Into<HttpResponse> + 'static,
          S: 'static,
{
    fn handle(&self, req: &mut HttpRequest, payload: Payload, state: Rc<S>) -> Task
    {
        Task::reply((self.f)(req, payload, &state).into())
    }
}

/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Stream<Item=Frame, Error=()> + 'static,
          S: 'static,
{
    f: Box<F>,
    s: PhantomData<S>,
}

impl<S, R, F> StreamHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Stream<Item=Frame, Error=()> + 'static,
          S: 'static,
{
    pub fn new(f: F) -> Self {
        StreamHandler{f: Box::new(f), s: PhantomData}
    }
}

impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
    where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static,
          R: Stream<Item=Frame, Error=()> + 'static,
          S: 'static,
{
    fn handle(&self, req: &mut HttpRequest, payload: Payload, state: Rc<S>) -> Task
    {
        Task::with_stream(
            (self.f)(req, payload, &state).map_err(
                |_| io::Error::new(io::ErrorKind::Other, ""))
        )
    }
}