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) } }