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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//!Handler context and request body reading extensions.
//!
//!#Context
//!
//!The [`Context`][context] contains all the input data for the request
//!handlers, as well as some utilities. This is where request data, like
//!headers, client address and the request body can be retrieved from and it
//!can safely be picked apart, since its ownership is transferred to the
//!handler.
//!
//!##Accessing Headers
//!
//!The headers are stored in the `headers` field. See the [`Headers`][headers]
//!struct for more information about how to access them.
//!
//!```
//!use rustful::{Context, Response};
//!use rustful::header::UserAgent;
//!
//!fn my_handler(context: Context, response: Response) {
//!    if let Some(&UserAgent(ref user_agent)) = context.headers.get() {
//!        response.send(format!("got user agent string \"{}\"", user_agent));
//!    } else {
//!        response.send("no user agent string provided");
//!    }
//!}
//!```
//!
//!##Path Variables
//!
//!A router may collect variable data from paths (for example `id` in
//!`/products/:id`). The values from these variables can be accessed through
//!the `variables` field.
//!
//!```
//!use rustful::{Context, Response};
//!
//!fn my_handler(context: Context, response: Response) {
//!    if let Some(id) = context.variables.get("id") {
//!        response.send(format!("asking for product with id \"{}\"", id));
//!    } else {
//!        //This will usually not happen, unless the handler is also
//!        //assigned to a path without the `id` variable
//!        response.send("no id provided");
//!    }
//!}
//!```
//!
//!##Other URL Parts
//!
//! * Query variables (`http://example.com?a=b&c=d`) can be found in the
//!`query` field and they are accessed in exactly the same fashion as path
//!variables are used.
//!
//! * The fragment (`http://example.com#foo`) is also parsed and can be
//!accessed through `fragment` as an optional `String`.
//!
//!##Global Data
//!
//!There is also infrastructure for globally accessible data, that can be
//!accessed through the `global` field. This is meant to provide a place for
//!things like database connections or cached data that should be available to
//!all handlers. The storage space itself is immutable when the server has
//!started, so the only way to change it is through some kind of inner
//!mutability.
//!
//!```
//!# #[macro_use] extern crate rustful;
//!#[macro_use] extern crate log;
//!use rustful::{Context, Response};
//!use rustful::StatusCode::InternalServerError;
//!
//!fn my_handler(context: Context, mut response: Response) {
//!    if let Some(some_wise_words) = context.global.get::<&str>() {
//!        response.send(format!("food for thought: {}", some_wise_words));
//!    } else {
//!        error!("there should be a string literal in `global`");
//!        response.set_status(InternalServerError);
//!    }
//!}
//!
//!# fn main() {}
//!```
//!
//!##Request Body
//!
//!The body will not be read in advance, unlike the other parts of the
//!request. It is instead available as a `BodyReader` in the field `body`,
//!through which it can be read and parsed as various data formats, like JSON
//!and query strings. The documentation for [`BodyReader`][body_reader] gives
//!more examples.
//!
//!```
//!use std::io::{BufReader, BufRead};
//!use rustful::{Context, Response};
//!
//!fn my_handler(context: Context, response: Response) {
//!    let mut numbered_lines = BufReader::new(context.body).lines().enumerate();
//!    let mut writer = response.into_chunked();
//!
//!    while let Some((line_no, Ok(line))) = numbered_lines.next() {
//!        writer.send(format!("{}: {}", line_no + 1, line));
//!    }
//!}
//!```
//!
//![context]: struct.Context.html
//![headers]: ../header/struct.Headers.html
//![log]: ../log/index.html
//![body_reader]: body/struct.BodyReader.html

use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::fmt;
use std::borrow::Cow;

use HttpVersion;
use Method;
use header::Headers;
use server::Global;

use self::body::BodyReader;
use self::hypermedia::Link;

pub mod body;
pub mod hypermedia;

mod maybe_utf8;
pub use self::maybe_utf8::{MaybeUtf8, MaybeUtf8Owned, MaybeUtf8Slice, Buffer};

mod parameters;
pub use self::parameters::Parameters;

///A container for handler input, like request data and utilities.
pub struct Context<'a, 'b: 'a, 'l, 'g> {
    ///Headers from the HTTP request.
    pub headers: Headers,

    ///The HTTP version used in the request.
    pub http_version: HttpVersion,

    ///The client address
    pub address: SocketAddr,

    ///The HTTP method.
    pub method: Method,

    ///The requested path.
    pub uri_path: UriPath,

    ///Hyperlinks from the current endpoint.
    pub hyperlinks: Vec<Link<'l>>,

    ///Route variables.
    pub variables: Parameters,

    ///Query variables from the path.
    pub query: Parameters,

    ///The fragment part of the URL (after #), if provided.
    pub fragment: Option<MaybeUtf8Owned>,

    ///Globally accessible data.
    pub global: &'g Global,

    ///A reader for the request body.
    pub body: BodyReader<'a, 'b>,
}

impl<'a, 'b, 'l, 'g> Context<'a, 'b, 'l, 'g> {
    ///Create a context with minimal setup, for testing purposes.
    pub fn mock<P: Into<String>>(method: Method, path: P, headers: Headers, global: &'g Global) -> Context<'static, 'static, 'l, 'g> {
        let body = BodyReader::mock(&headers);

        Context {
            headers: headers,
            http_version: HttpVersion::Http11,
            address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 80)),
            method: method,
            uri_path: UriPath::Path(path.into().into()),
            hyperlinks: vec![],
            variables: Parameters::new(),
            query: Parameters::new(),
            fragment: None,
            global: global,
            body: body
        }
    }

    ///Replace the hyperlinks. This consumes the context and returns a new one
    ///with a different lifetime, together with the old hyperlinks.
    pub fn replace_hyperlinks<'n>(self, hyperlinks: Vec<Link<'n>>) -> (Context<'a, 'b, 'n, 'g>, Vec<Link<'l>>) {
        let old_links = self.hyperlinks;

        (
            Context {
                headers: self.headers,
                http_version: self.http_version,
                address: self.address,
                method: self.method,
                uri_path: self.uri_path,
                hyperlinks: hyperlinks,
                variables: self.variables,
                query: self.query,
                fragment: self.fragment,
                global: self.global,
                body: self.body,
            },
            old_links
        )
    }
}

///A URI Path that can be a path or an asterisk (`*`).
///
///The URI Path may be an invalid UTF-8 path and it is therefore represented as a
///percent decoded byte vector, but can easily be parsed as a string.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UriPath {
    ///A path URI.
    Path(MaybeUtf8Owned),
    ///An asterisk (`*`) URI.
    Asterisk
}

impl UriPath {
    ///Borrow the URI as a raw path.
    pub fn as_path(&self) -> Option<MaybeUtf8Slice> {
        match *self {
            UriPath::Path(ref path) => Some(path.as_slice()),
            UriPath::Asterisk => None
        }
    }

    ///Borrow the URI as a UTF-8 path, if valid.
    pub fn as_utf8_path(&self) -> Option<&str> {
        match *self {
            UriPath::Path(ref path) => path.as_utf8(),
            UriPath::Asterisk => None
        }
    }

    ///Borrow the URI as a UTF-8 path, if valid, or convert it to a valid
    ///UTF-8 string.
    pub fn as_utf8_path_lossy(&self) -> Option<Cow<str>> {
        match *self {
            UriPath::Path(ref path) => Some(path.as_utf8_lossy()),
            UriPath::Asterisk => None
        }
    }

    ///Check if the URI is a path.
    pub fn is_path(&self) -> bool {
        match *self {
            UriPath::Path(_) => true,
            UriPath::Asterisk => false
        }
    }

    ///Check if the URI is an asterisk (`*`).
    pub fn is_asterisk(&self) -> bool {
        match *self {
            UriPath::Path(_) => false,
            UriPath::Asterisk => true
        }
    }
}

impl fmt::Display for UriPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_utf8_path_lossy().unwrap_or_else(|| "*".into()).fmt(f)
    }
}