2016-09-30 22:20:11 +00:00
|
|
|
use std::io::Read;
|
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};
|
|
|
|
use http::hyper::{header, HyperCookie, HyperHeaders, HyperRequest, HyperRequestUri};
|
2016-10-04 00:09:13 +00:00
|
|
|
use http::{Method, ContentType, Cookies};
|
2016-08-26 08:55:11 +00:00
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// The type for all incoming web requests.
|
|
|
|
///
|
|
|
|
/// 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
|
|
|
|
/// the information for a given web request. This includes the HTTP method, URI,
|
|
|
|
/// cookies, headers, and more.
|
2016-03-22 05:04:39 +00:00
|
|
|
pub struct Request<'a> {
|
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-01 03:22:06 +00:00
|
|
|
/// The URI associated with the request.
|
2016-08-26 08:55:11 +00:00
|
|
|
pub uri: URIBuf, // FIXME: Should be URI (without Hyper).
|
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>
|
|
|
|
///
|
|
|
|
/// The data in the request.
|
2016-08-26 08:55:11 +00:00
|
|
|
pub data: Vec<u8>, // FIXME: Don't read this! (bad Hyper.)
|
2016-09-12 01:57:04 +00:00
|
|
|
cookies: Cookies,
|
2016-08-26 08:55:11 +00:00
|
|
|
headers: HyperHeaders, // This sucks.
|
2016-09-12 01:57:04 +00:00
|
|
|
params: RefCell<Option<Vec<&'a str>>>, // This also sucks.
|
2016-03-22 05:04:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Request<'a> {
|
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-09-07 06:24:20 +00:00
|
|
|
pub fn get_param<T: FromParam<'a>>(&self, n: usize) -> Result<T, Error> {
|
2016-08-26 08:55:11 +00:00
|
|
|
let params = self.params.borrow();
|
|
|
|
if params.is_none() || n >= params.as_ref().unwrap().len() {
|
2016-09-08 07:02:17 +00:00
|
|
|
debug!("{} is >= param count {}", n, params.as_ref().unwrap().len());
|
2016-08-26 08:55:11 +00:00
|
|
|
Err(Error::NoKey)
|
|
|
|
} else {
|
2016-09-30 08:25:07 +00:00
|
|
|
T::from_param(params.as_ref().unwrap()[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-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-09-30 22:20:11 +00:00
|
|
|
pub fn get_segments<'r: 'a, T: FromSegments<'a>>(&'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.
|
|
|
|
let mut segments = self.uri.segments();
|
|
|
|
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-08-26 08:55:11 +00:00
|
|
|
params: RefCell::new(None),
|
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),
|
|
|
|
data: vec![],
|
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-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-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-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-08-26 08:55:11 +00:00
|
|
|
pub fn uri(&'a self) -> URI<'a> {
|
|
|
|
self.uri.as_uri()
|
|
|
|
}
|
|
|
|
|
2016-09-08 07:02:17 +00:00
|
|
|
// FIXME: Don't need a refcell for this.
|
2016-10-01 03:22:06 +00:00
|
|
|
#[doc(hidden)]
|
2016-08-26 08:55:11 +00:00
|
|
|
pub fn set_params(&'a self, route: &Route) {
|
|
|
|
*self.params.borrow_mut() = Some(route.get_params(self.uri.as_uri()))
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
#[doc(hidden)]
|
2016-08-26 08:55:11 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
pub fn set_content_type(&mut self, ct: ContentType) {
|
|
|
|
let hyper_ct = header::ContentType(ct.into());
|
|
|
|
self.headers.set::<header::ContentType>(hyper_ct)
|
|
|
|
}
|
|
|
|
|
2016-10-01 03:22:06 +00:00
|
|
|
/// Create a Rocket Request from a Hyper Request.
|
|
|
|
#[doc(hidden)]
|
2016-08-27 01:37:28 +00:00
|
|
|
pub fn from_hyp<'h, 'k>(hyper_req: HyperRequest<'h, 'k>)
|
2016-09-30 22:20:11 +00:00
|
|
|
-> Result<Request<'a>, String> {
|
2016-08-26 08:55:11 +00:00
|
|
|
let (_, h_method, h_headers, h_uri, _, mut h_body) = hyper_req.deconstruct();
|
|
|
|
|
|
|
|
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) {
|
|
|
|
Some(m) => m,
|
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-09-30 22:20:11 +00:00
|
|
|
// TODO: What to do about key?
|
|
|
|
Some(cookie) => cookie.to_cookie_jar(&[]),
|
|
|
|
None => Cookies::new(&[]),
|
2016-09-12 01:57:04 +00:00
|
|
|
};
|
|
|
|
|
2016-08-26 08:55:11 +00:00
|
|
|
// FIXME: GRRR.
|
|
|
|
let mut data = vec![];
|
|
|
|
h_body.read_to_end(&mut data).unwrap();
|
|
|
|
|
2016-08-27 01:37:28 +00:00
|
|
|
let request = Request {
|
2016-08-26 08:55:11 +00:00
|
|
|
params: RefCell::new(None),
|
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,
|
|
|
|
data: data,
|
|
|
|
headers: h_headers,
|
2016-08-27 01:37:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(request)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'r> fmt::Display for Request<'r> {
|
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 {
|
|
|
|
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.uri))
|
2016-03-15 03:43:52 +00:00
|
|
|
}
|
|
|
|
}
|