2016-08-26 08:55:11 +00:00
|
|
|
use std::cell::RefCell;
|
2016-08-27 01:37:28 +00:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
use term_painter::Color::*;
|
|
|
|
use term_painter::ToStyle;
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-03-15 03:43:52 +00:00
|
|
|
use error::Error;
|
2016-09-30 08:25:07 +00:00
|
|
|
use super::{FromParam, FromSegments};
|
2016-03-15 03:43:52 +00:00
|
|
|
|
2016-08-26 08:55:11 +00:00
|
|
|
use router::Route;
|
2016-10-04 00:25:27 +00:00
|
|
|
use http::uri::{URI, URIBuf};
|
2016-10-09 04:37:28 +00:00
|
|
|
use http::hyper::{header, HyperCookie, HyperHeaders, HyperMethod, HyperRequestUri};
|
2016-10-04 00:09:13 +00:00
|
|
|
use http::{Method, ContentType, Cookies};
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-10-21 09:56:57 +00:00
|
|
|
/// The type of an incoming web request.
|
2016-10-01 03:22:06 +00:00
|
|
|
///
|
|
|
|
/// This should be used sparingly in Rocket applications. In particular, it
|
|
|
|
/// should likely only be used when writing
|
|
|
|
/// [FromRequest](trait.FromRequest.html) implementations. It contains all of
|
2016-10-21 09:56:57 +00:00
|
|
|
/// the information for a given web request except for the body data. This
|
|
|
|
/// includes the HTTP method, URI, cookies, headers, and more.
|
2016-10-08 06:20:49 +00:00
|
|
|
pub struct Request {
|
2016-10-01 03:22:06 +00:00
|
|
|
/// The HTTP method associated with the request.
|
2016-07-16 04:09:08 +00:00
|
|
|
pub method: Method,
|
2016-10-21 09:56:57 +00:00
|
|
|
uri: URIBuf, // FIXME: Should be URI (without hyper).
|
2016-10-07 03:57:17 +00:00
|
|
|
params: RefCell<Vec<&'static str>>,
|
2016-09-12 01:57:04 +00:00
|
|
|
cookies: Cookies,
|
2016-10-21 09:56:57 +00:00
|
|
|
headers: HyperHeaders, // Don't use hyper's headers.
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-08 06:20:49 +00:00
|
|
|
impl Request {
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Retrieves and parses into `T` the `n`th dynamic parameter from the
|
|
|
|
/// request. Returns `Error::NoKey` if `n` is greater than the number of
|
|
|
|
/// params. Returns `Error::BadParse` if the parameter type `T` can't be
|
|
|
|
/// parsed from the parameter.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// To retrieve parameter `n` as some type `T` that implements
|
|
|
|
/// [FromParam](trait.FromParam.html) inside a
|
|
|
|
/// [FromRequest](trait.FromRequest.html) implementation:
|
|
|
|
///
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// fn from_request(request: &'r Request<'c>) -> .. {
|
|
|
|
/// let my_param: T = request.get_param(n);
|
|
|
|
/// }
|
|
|
|
/// ```
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-10-07 03:57:17 +00:00
|
|
|
pub fn get_param<'r, T: FromParam<'r>>(&'r self, n: usize) -> Result<T, Error> {
|
2016-08-26 08:55:11 +00:00
|
|
|
let params = self.params.borrow();
|
2016-10-07 03:57:17 +00:00
|
|
|
if n >= params.len() {
|
|
|
|
debug!("{} is >= param count {}", n, params.len());
|
2016-08-26 08:55:11 +00:00
|
|
|
Err(Error::NoKey)
|
|
|
|
} else {
|
2016-10-07 03:57:17 +00:00
|
|
|
T::from_param(params[n]).map_err(|_| Error::BadParse)
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Returns a borrow to the cookies sent with this request. Note that
|
|
|
|
/// `Cookie` implements internal mutability, so this method allows you to
|
|
|
|
/// get _and_ set cookies in the given Request.
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-09-12 01:57:04 +00:00
|
|
|
pub fn cookies<'r>(&'r self) -> &'r Cookies {
|
|
|
|
&self.cookies
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Retrieves and parses into `T` all of the path segments in the request
|
|
|
|
/// URI beginning and including the 0-indexed `i`. `T` must implement
|
|
|
|
/// [FromSegments](trait.FromSegments.html), which is used to parse the
|
|
|
|
/// segments.
|
|
|
|
///
|
|
|
|
/// For example, if the request URI is `"/hello/there/i/am/here"`, then
|
|
|
|
/// `request.get_segments::<T>(1)` will attempt to parse the segments
|
|
|
|
/// `"there/i/am/here"` as type `T`.
|
2016-10-07 03:38:13 +00:00
|
|
|
pub fn get_segments<'r, T: FromSegments<'r>>(&'r self, i: usize) -> Result<T, Error> {
|
2016-09-08 07:02:17 +00:00
|
|
|
if i >= self.uri().segment_count() {
|
|
|
|
debug!("{} is >= segment count {}", i, self.uri().segment_count());
|
|
|
|
Err(Error::NoKey)
|
|
|
|
} else {
|
|
|
|
// TODO: Really want to do self.uri.segments().skip(i).into_inner(),
|
|
|
|
// but the std lib doesn't implement it for Skip.
|
2016-10-18 02:29:58 +00:00
|
|
|
let mut segments = self.uri.as_uri().segments();
|
2016-09-08 07:02:17 +00:00
|
|
|
for _ in segments.by_ref().take(i) { /* do nothing */ }
|
2016-09-30 22:20:11 +00:00
|
|
|
|
2016-09-30 08:25:07 +00:00
|
|
|
T::from_segments(segments).map_err(|_| Error::BadParse)
|
2016-09-08 07:02:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
// FIXME: Implement a testing framework for Rocket.
|
|
|
|
#[doc(hidden)]
|
2016-08-26 08:55:11 +00:00
|
|
|
pub fn mock(method: Method, uri: &str) -> Request {
|
2016-03-22 05:04:39 +00:00
|
|
|
Request {
|
2016-10-07 03:57:17 +00:00
|
|
|
params: RefCell::new(vec![]),
|
2016-07-16 04:09:08 +00:00
|
|
|
method: method,
|
2016-09-12 01:57:04 +00:00
|
|
|
cookies: Cookies::new(&[]),
|
2016-08-26 08:55:11 +00:00
|
|
|
uri: URIBuf::from(uri),
|
2016-09-30 22:20:11 +00:00
|
|
|
headers: HyperHeaders::new(),
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
Something works! A simple hacked-up handler, that is.
At the moment, I simply install the first route I see into the Rocket struct
directly. This is quite terrible. What's worse is that I assume that the Route's
path and handler are static! The handler, actually, does have to be static, but
its response may have whatever (valid) lifetime, though I'm not sure anything
but `static makes sense. I'll think about it.
In any case, the weird `static` restrictions need to be removed, and I need to
think about which lifetimes are safe here. IE: Must all routes be static? Can I
use a closure as a route? (that'd be neat). If so, how do we make that work?
In any case, it's nice to see SOMETHING work. Yay!
2016-03-15 07:41:22 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// <div class="stability" style="margin-left: 0;">
|
|
|
|
/// <em class="stab unstable">
|
|
|
|
/// Unstable
|
|
|
|
/// (<a href="https://github.com/SergioBenitez/Rocket/issues/17">#17</a>):
|
|
|
|
/// The underlying HTTP library/types are likely to change before v1.0.
|
|
|
|
/// </em>
|
|
|
|
/// </div>
|
|
|
|
///
|
|
|
|
/// Returns the headers in this request.
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-08-26 08:55:11 +00:00
|
|
|
pub fn headers(&self) -> &HyperHeaders {
|
2016-10-01 03:22:06 +00:00
|
|
|
// FIXME: Get rid of Hyper.
|
2016-08-26 08:55:11 +00:00
|
|
|
&self.headers
|
2016-03-15 03:43:52 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// <div class="stability" style="margin-left: 0;">
|
|
|
|
/// <em class="stab unstable">
|
|
|
|
/// Unstable
|
|
|
|
/// (<a href="https://github.com/SergioBenitez/Rocket/issues/17">#17</a>):
|
|
|
|
/// The underlying HTTP library/types are likely to change before v1.0.
|
|
|
|
/// </em>
|
|
|
|
/// </div>
|
|
|
|
///
|
|
|
|
/// Returns the Content-Type from the request. Althought you can retrieve
|
|
|
|
/// the content-type from the headers directly, this method is considered to
|
|
|
|
/// be more stable. If no Content-Type was specified in the request, a
|
|
|
|
/// Content-Type of [any](struct.ContentType.html#method.any) is returned.
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-08-26 08:55:11 +00:00
|
|
|
pub fn content_type(&self) -> ContentType {
|
|
|
|
let hyp_ct = self.headers().get::<header::ContentType>();
|
|
|
|
hyp_ct.map_or(ContentType::any(), |ct| ContentType::from(&ct.0))
|
|
|
|
}
|
|
|
|
|
2016-10-21 09:56:57 +00:00
|
|
|
/// <div class="stability" style="margin-left: 0;">
|
|
|
|
/// <em class="stab unstable">
|
|
|
|
/// Unstable
|
|
|
|
/// (<a href="https://github.com/SergioBenitez/Rocket/issues/17">#17</a>):
|
|
|
|
/// The underlying HTTP library/types are likely to change before v1.0.
|
|
|
|
/// </em>
|
|
|
|
/// </div>
|
|
|
|
///
|
2016-09-08 07:02:17 +00:00
|
|
|
/// Returns the first content-type accepted by this request.
|
|
|
|
pub fn accepts(&self) -> ContentType {
|
|
|
|
let accept = self.headers().get::<header::Accept>();
|
|
|
|
accept.map_or(ContentType::any(), |accept| {
|
|
|
|
let items = &accept.0;
|
|
|
|
if items.len() < 1 {
|
|
|
|
return ContentType::any();
|
|
|
|
} else {
|
2016-09-30 22:20:11 +00:00
|
|
|
return ContentType::from(items[0].item.clone());
|
2016-09-08 07:02:17 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Retrieves the URI from the request. Rocket only allows absolute URIs, so
|
|
|
|
/// the URI will be absolute.
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-10-05 02:26:33 +00:00
|
|
|
pub fn uri(&self) -> URI {
|
2016-08-26 08:55:11 +00:00
|
|
|
self.uri.as_uri()
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
#[doc(hidden)]
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-10-07 03:57:17 +00:00
|
|
|
pub fn set_params(&self, route: &Route) {
|
|
|
|
// We use transmute to cast the lifetime of self.uri.as_uri() to
|
|
|
|
// 'static. This is because that lifetime refers to the String in URIBuf
|
|
|
|
// in this structure, which is (obviously) guaranteed to live as long as
|
|
|
|
// the structure AS LONG AS it is not moved out or changed. AS A RESULT,
|
|
|
|
// the `uri` fields MUST NEVER be changed once it is set.
|
|
|
|
// TODO: Find a way to enforce these. Look at OwningRef for inspiration.
|
|
|
|
use ::std::mem::transmute;
|
|
|
|
*self.params.borrow_mut() = unsafe {
|
|
|
|
transmute(route.get_params(self.uri.as_uri()))
|
|
|
|
};
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
#[doc(hidden)]
|
2016-10-07 04:04:35 +00:00
|
|
|
#[inline(always)]
|
2016-10-16 10:16:16 +00:00
|
|
|
pub fn set_headers(&mut self, h_headers: HyperHeaders) {
|
|
|
|
let cookies = match h_headers.get::<HyperCookie>() {
|
|
|
|
// TODO: Retrieve key from config.
|
|
|
|
Some(cookie) => cookie.to_cookie_jar(&[]),
|
|
|
|
None => Cookies::new(&[]),
|
|
|
|
};
|
|
|
|
|
|
|
|
self.headers = h_headers;
|
|
|
|
self.cookies = cookies;
|
2016-08-26 08:55:11 +00:00
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
#[doc(hidden)]
|
2016-10-09 04:37:28 +00:00
|
|
|
pub fn new(h_method: HyperMethod,
|
|
|
|
h_headers: HyperHeaders,
|
|
|
|
h_uri: HyperRequestUri)
|
|
|
|
-> Result<Request, String> {
|
2016-08-26 08:55:11 +00:00
|
|
|
let uri = match h_uri {
|
|
|
|
HyperRequestUri::AbsolutePath(s) => URIBuf::from(s),
|
2016-09-30 22:20:11 +00:00
|
|
|
_ => return Err(format!("Bad URI: {}", h_uri)),
|
2016-08-27 01:37:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let method = match Method::from_hyp(&h_method) {
|
2016-10-09 04:37:28 +00:00
|
|
|
Some(method) => method,
|
2016-09-30 22:20:11 +00:00
|
|
|
_ => return Err(format!("Bad method: {}", h_method)),
|
2016-08-26 08:55:11 +00:00
|
|
|
};
|
|
|
|
|
2016-09-12 01:57:04 +00:00
|
|
|
let cookies = match h_headers.get::<HyperCookie>() {
|
2016-10-09 04:37:28 +00:00
|
|
|
// TODO: Retrieve key from config.
|
2016-09-30 22:20:11 +00:00
|
|
|
Some(cookie) => cookie.to_cookie_jar(&[]),
|
|
|
|
None => Cookies::new(&[]),
|
2016-09-12 01:57:04 +00:00
|
|
|
};
|
|
|
|
|
2016-08-27 01:37:28 +00:00
|
|
|
let request = Request {
|
2016-10-07 03:57:17 +00:00
|
|
|
params: RefCell::new(vec![]),
|
2016-08-27 01:37:28 +00:00
|
|
|
method: method,
|
2016-09-12 01:57:04 +00:00
|
|
|
cookies: cookies,
|
2016-08-26 08:55:11 +00:00
|
|
|
uri: uri,
|
|
|
|
headers: h_headers,
|
2016-08-27 01:37:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(request)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-08 06:20:49 +00:00
|
|
|
impl fmt::Display for Request {
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Pretty prints a Request. This is primarily used by Rocket's logging
|
|
|
|
/// infrastructure.
|
2016-08-27 01:37:28 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2016-10-13 02:08:19 +00:00
|
|
|
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.uri))?;
|
|
|
|
if self.method.supports_payload() && !self.content_type().is_any() {
|
|
|
|
write!(f, " {}", Yellow.paint(self.content_type()))?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2016-03-15 03:43:52 +00:00
|
|
|
}
|
|
|
|
}
|