Rework Request: add lifetime to future proof, remove unsafe.

This commit is contained in:
Sergio Benitez 2016-12-16 03:07:23 -08:00
parent 5f311c3654
commit 6815a56cb5
13 changed files with 239 additions and 183 deletions

View File

@ -16,9 +16,9 @@ impl fmt::Display for HeaderCount {
} }
} }
impl<'r> FromRequest<'r> for HeaderCount { impl<'a, 'r> FromRequest<'a, 'r> for HeaderCount {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> request::Outcome<Self, ()> { fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
Success(HeaderCount(request.headers().len())) Success(HeaderCount(request.headers().len()))
} }
} }

View File

@ -10,15 +10,15 @@ use rocket::response::{self, Responder};
use rocket::handler::Outcome; use rocket::handler::Outcome;
use rocket::http::Method::*; use rocket::http::Method::*;
fn forward(_req: &Request, data: Data) -> Outcome { fn forward(_req: &Request, data: Data) -> Outcome<'static> {
Outcome::forward(data) Outcome::forward(data)
} }
fn hi(_req: &Request, _: Data) -> Outcome { fn hi(_req: &Request, _: Data) -> Outcome<'static> {
Outcome::of("Hello!") Outcome::of("Hello!")
} }
fn name<'a>(req: &'a Request, _: Data) -> Outcome { fn name<'a>(req: &'a Request, _: Data) -> Outcome<'a> {
Outcome::of(req.get_param(0).unwrap_or("unnamed")) Outcome::of(req.get_param(0).unwrap_or("unnamed"))
} }
@ -27,7 +27,7 @@ fn echo_url(req: &Request, _: Data) -> Outcome<'static> {
Outcome::of(String::from_param(param).unwrap()) Outcome::of(String::from_param(param).unwrap())
} }
fn upload(req: &Request, data: Data) -> Outcome { fn upload<'r>(req: &'r Request, data: Data) -> Outcome<'r> {
if !req.content_type().is_plain() { if !req.content_type().is_plain() {
println!(" => Content-Type of upload must be text/plain. Ignoring."); println!(" => Content-Type of upload must be text/plain. Ignoring.");
return Outcome::failure(Status::BadRequest); return Outcome::failure(Status::BadRequest);
@ -47,11 +47,11 @@ fn upload(req: &Request, data: Data) -> Outcome {
} }
} }
fn get_upload(_: &Request, _: Data) -> Outcome { fn get_upload(_: &Request, _: Data) -> Outcome<'static> {
Outcome::of(File::open("/tmp/upload.txt").ok()) Outcome::of(File::open("/tmp/upload.txt").ok())
} }
fn not_found_handler(_: Error, req: &Request) -> response::Result { fn not_found_handler<'r>(_: Error, req: &'r Request) -> response::Result<'r> {
format!("Couldn't find: {}", req.uri()).respond() format!("Couldn't find: {}", req.uri()).respond()
} }

View File

@ -79,11 +79,11 @@ impl Catcher {
/// use rocket::{Catcher, Request, Error}; /// use rocket::{Catcher, Request, Error};
/// use rocket::response::{Result, Responder}; /// use rocket::response::{Result, Responder};
/// ///
/// fn handle_404(_: Error, req: &Request) -> Result { /// fn handle_404<'r>(_: Error, req: &'r Request) -> Result<'r> {
/// format!("Couldn't find: {}", req.uri()).respond() /// format!("Couldn't find: {}", req.uri()).respond()
/// } /// }
/// ///
/// fn handle_500(_: Error, _: &Request) -> Result { /// fn handle_500<'r>(_: Error, _: &'r Request) -> Result<'r> {
/// "Whoops, we messed up!".respond() /// "Whoops, we messed up!".respond()
/// } /// }
/// ///

View File

@ -36,6 +36,7 @@ impl<T> From<T> for Header<'static> where T: hyper::Header + hyper::HeaderFormat
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub struct HeaderMap<'h> { pub struct HeaderMap<'h> {
headers: HashMap<Cow<'h, str>, Vec<Cow<'h, str>>> headers: HashMap<Cow<'h, str>, Vec<Cow<'h, str>>>
} }

View File

@ -24,4 +24,3 @@ pub use hyper::buffer;
// TODO: Remove from Rocket in favor of a more flexible HTTP library. // TODO: Remove from Rocket in favor of a more flexible HTTP library.
pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>;

View File

@ -256,6 +256,13 @@ impl<'a> URI<'a> {
} }
} }
impl<'a> From<&'a str> for URI<'a> {
#[inline(always)]
fn from(uri: &'a str) -> URI<'a> {
URI::new(uri)
}
}
impl<'a> fmt::Display for URI<'a> { impl<'a> fmt::Display for URI<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// If this is the root path, then there are "zero" segments. // If this is the root path, then there are "zero" segments.

View File

@ -3,7 +3,9 @@ use std::fmt::Debug;
use outcome::{self, IntoOutcome}; use outcome::{self, IntoOutcome};
use request::Request; use request::Request;
use outcome::Outcome::*; use outcome::Outcome::*;
use http::{Status, ContentType, Method, Cookies}; use http::{Status, ContentType, Method, Cookies};
use http::uri::URI;
/// Type alias for the `Outcome` of a `FromRequest` conversion. /// Type alias for the `Outcome` of a `FromRequest` conversion.
pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), ()>; pub type Outcome<S, E> = outcome::Outcome<S, (Status, E), ()>;
@ -89,10 +91,10 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// key == "valid_api_key" /// key == "valid_api_key"
/// } /// }
/// ///
/// impl<'r> FromRequest<'r> for APIKey { /// impl<'a, 'r> FromRequest<'a, 'r> for APIKey {
/// type Error = (); /// type Error = ();
/// ///
/// fn from_request(request: &'r Request) -> request::Outcome<APIKey, ()> { /// fn from_request(request: &'a Request<'r>) -> request::Outcome<APIKey, ()> {
/// let keys: Vec<_> = request.headers().get("x-api-key").collect(); /// let keys: Vec<_> = request.headers().get("x-api-key").collect();
/// if keys.len() != 1 { /// if keys.len() != 1 {
/// return Outcome::Failure((Status::BadRequest, ())); /// return Outcome::Failure((Status::BadRequest, ()));
@ -114,8 +116,8 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// ///
/// # fn main() { } /// # fn main() { }
/// ``` /// ```
pub trait FromRequest<'r>: Sized { pub trait FromRequest<'a, 'r>: Sized {
/// The associated error to be returned when derivation fails. /// The associated error to be returned if derivation fails.
type Error: Debug; type Error: Debug;
/// Derives an instance of `Self` from the incoming request metadata. /// Derives an instance of `Self` from the incoming request metadata.
@ -124,45 +126,45 @@ pub trait FromRequest<'r>: Sized {
/// the derivation fails in an unrecoverable fashion, `Failure` is returned. /// the derivation fails in an unrecoverable fashion, `Failure` is returned.
/// `Forward` is returned to indicate that the request should be forwarded /// `Forward` is returned to indicate that the request should be forwarded
/// to other matching routes, if any. /// to other matching routes, if any.
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error>; fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error>;
} }
impl<'r> FromRequest<'r> for &'r Request { impl<'a, 'r> FromRequest<'a, 'r> for URI<'a> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request) Success(request.uri())
} }
} }
impl<'r> FromRequest<'r> for Method { impl<'a, 'r> FromRequest<'a, 'r> for Method {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.method) Success(request.method())
} }
} }
impl<'r> FromRequest<'r> for &'r Cookies { impl<'a, 'r> FromRequest<'a, 'r> for &'a Cookies {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.cookies()) Success(request.cookies())
} }
} }
impl<'r> FromRequest<'r> for ContentType { impl<'a, 'r> FromRequest<'a, 'r> for ContentType {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.content_type()) Success(request.content_type())
} }
} }
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> { impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Result<T, T::Error> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
match T::from_request(request) { match T::from_request(request) {
Success(val) => Success(Ok(val)), Success(val) => Success(Ok(val)),
Failure((_, e)) => Success(Err(e)), Failure((_, e)) => Success(Err(e)),
@ -171,10 +173,10 @@ impl<'r, T: FromRequest<'r>> FromRequest<'r> for Result<T, T::Error> {
} }
} }
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> { impl<'a, 'r, T: FromRequest<'a, 'r>> FromRequest<'a, 'r> for Option<T> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
match T::from_request(request) { match T::from_request(request) {
Success(val) => Success(Some(val)), Success(val) => Success(Some(val)),
Failure(_) => Success(None), Failure(_) => Success(None),

View File

@ -13,6 +13,57 @@ use http::{Method, ContentType, Header, HeaderMap, Cookies};
use http::hyper; use http::hyper;
#[derive(Debug, Clone, PartialEq)]
pub enum CowURI<'b> {
Borrowed(URI<'b>),
Owned(URIBuf)
}
impl<'b> CowURI<'b> {
/// Returns raw URI.
fn as_str(&self) -> &str {
match *self {
CowURI::Borrowed(ref uri) => uri.as_str(),
CowURI::Owned(ref uri) => uri.as_str()
}
}
fn segment_count(&self) -> usize {
match *self {
CowURI::Borrowed(ref uri) => uri.segment_count(),
CowURI::Owned(ref uri) => uri.segment_count()
}
}
fn segments(&self) -> Segments {
match *self {
CowURI::Borrowed(ref uri) => uri.segments(),
CowURI::Owned(ref uri) => uri.as_uri().segments()
}
}
}
impl<'b> From<URI<'b>> for CowURI<'b> {
fn from(uri: URI<'b>) -> CowURI<'b> {
CowURI::Borrowed(uri)
}
}
impl<'b> From<URIBuf> for CowURI<'b> {
fn from(uri: URIBuf) -> CowURI<'b> {
CowURI::Owned(uri)
}
}
impl<'b> fmt::Display for CowURI<'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CowURI::Borrowed(ref uri) => uri.fmt(f),
CowURI::Owned(ref uri) => uri.fmt(f)
}
}
}
/// The type of an incoming web request. /// The type of an incoming web request.
/// ///
/// This should be used sparingly in Rocket applications. In particular, it /// This should be used sparingly in Rocket applications. In particular, it
@ -20,17 +71,87 @@ use http::hyper;
/// [FromRequest](trait.FromRequest.html) implementations. It contains all of /// [FromRequest](trait.FromRequest.html) implementations. It contains all of
/// the information for a given web request except for the body data. This /// the information for a given web request except for the body data. This
/// includes the HTTP method, URI, cookies, headers, and more. /// includes the HTTP method, URI, cookies, headers, and more.
pub struct Request { pub struct Request<'r> {
/// The HTTP method associated with the request. method: Method,
pub method: Method, uri: CowURI<'r>,
uri: URIBuf, // FIXME: Should be URI (without hyper). headers: HeaderMap<'r>,
params: RefCell<Vec<&'static str>>, params: RefCell<Vec<(usize, usize)>>,
cookies: Cookies, cookies: Cookies,
// TODO: Allow non-static here.
headers: HeaderMap<'static>,
} }
impl Request { impl<'r> Request<'r> {
pub fn new<U: Into<CowURI<'r>>>(method: Method, uri: U) -> Request<'r> {
Request {
method: method,
uri: uri.into(),
headers: HeaderMap::new(),
params: RefCell::new(Vec::new()),
cookies: Cookies::new(&[]),
}
}
#[inline(always)]
pub fn method(&self) -> Method {
self.method
}
#[inline(always)]
pub fn set_method(&mut self, method: Method) {
self.method = method;
}
/// Retrieves the URI from the request. Rocket only allows absolute URIs, so
/// the URI will be absolute.
#[inline(always)]
pub fn uri(&self) -> URI {
match self.uri {
CowURI::Borrowed(ref uri) => uri.clone(),
CowURI::Owned(ref uri) => uri.as_uri(),
}
}
// Sets the URI for the request. To retrieve parameters, the `set_params`
// method needs to be called first.
#[inline(always)]
pub fn set_uri<'u: 'r, U: Into<CowURI<'u>>>(&mut self, uri: U) {
self.uri = uri.into();
self.params = RefCell::new(Vec::new());
}
// Add the `header` to this request's header list.
#[inline(always)]
pub fn add_header(&mut self, header: Header<'r>) {
self.headers.add(header);
}
/// Returns the headers in this request.
#[inline(always)]
pub fn headers(&self) -> &HeaderMap<'r> {
&self.headers
}
/// 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.
#[inline(always)]
pub fn cookies(&self) -> &Cookies {
&self.cookies
}
#[inline(always)]
pub fn set_cookies(&mut self, cookies: Cookies) {
self.cookies = cookies;
}
/// Returns the Content-Type of the request. Returns `ContentType::Any` if
/// there was none or if the Content-Type was "*/*".
#[inline(always)]
pub fn content_type(&self) -> ContentType {
self.headers().get_one("Content-Type")
.and_then(|value| value.parse().ok())
.unwrap_or(ContentType::Any)
}
/// Retrieves and parses into `T` the `n`th dynamic parameter from the /// Retrieves and parses into `T` the `n`th dynamic parameter from the
/// request. Returns `Error::NoKey` if `n` is greater than the number of /// request. Returns `Error::NoKey` if `n` is greater than the number of
/// params. Returns `Error::BadParse` if the parameter type `T` can't be /// params. Returns `Error::BadParse` if the parameter type `T` can't be
@ -47,29 +168,34 @@ impl Request {
/// let my_param: T = request.get_param(n); /// let my_param: T = request.get_param(n);
/// } /// }
/// ``` /// ```
pub fn get_param<'r, T: FromParam<'r>>(&'r self, n: usize) -> Result<T, Error> { pub fn get_param<'a, T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
let param = self.get_param_str(n).ok_or(Error::NoKey)?; let param = self.get_param_str(n).ok_or(Error::NoKey)?;
T::from_param(param).map_err(|_| Error::BadParse) T::from_param(param).map_err(|_| Error::BadParse)
} }
#[doc(hidden)]
#[inline(always)]
pub fn set_params(&self, route: &Route) {
*self.params.borrow_mut() = route.get_param_indexes(self.uri());
}
/// Get the `n`th path parameter, if it exists. /// Get the `n`th path parameter, if it exists.
#[doc(hidden)] #[doc(hidden)]
pub fn get_param_str(&self, n: usize) -> Option<&str> { pub fn get_param_str(&self, n: usize) -> Option<&str> {
let params = self.params.borrow(); let params = self.params.borrow();
if n >= params.len() { if n >= params.len() {
debug!("{} is >= param count {}", n, params.len()); debug!("{} is >= param count {}", n, params.len());
None return None;
} else {
Some(&params[n])
} }
}
/// Returns a borrow to the cookies sent with this request. Note that let (i, j) = params[n];
/// `Cookie` implements internal mutability, so this method allows you to let uri_str = self.uri.as_str();
/// get _and_ set cookies in the given Request. if j > uri_str.len() {
#[inline(always)] error!("Couldn't retrieve parameter: internal count incorrect.");
pub fn cookies<'r>(&'r self) -> &'r Cookies { return None;
&self.cookies }
Some(&uri_str[i..j])
} }
/// Retrieves and parses into `T` all of the path segments in the request /// Retrieves and parses into `T` all of the path segments in the request
@ -80,7 +206,7 @@ impl Request {
/// For example, if the request URI is `"/hello/there/i/am/here"`, then /// For example, if the request URI is `"/hello/there/i/am/here"`, then
/// `request.get_segments::<T>(1)` will attempt to parse the segments /// `request.get_segments::<T>(1)` will attempt to parse the segments
/// `"there/i/am/here"` as type `T`. /// `"there/i/am/here"` as type `T`.
pub fn get_segments<'r, T: FromSegments<'r>>(&'r self, i: usize) pub fn get_segments<'a, T: FromSegments<'a>>(&'a self, i: usize)
-> Result<T, Error> { -> Result<T, Error> {
let segments = self.get_raw_segments(i).ok_or(Error::NoKey)?; let segments = self.get_raw_segments(i).ok_or(Error::NoKey)?;
T::from_segments(segments).map_err(|_| Error::BadParse) T::from_segments(segments).map_err(|_| Error::BadParse)
@ -89,132 +215,54 @@ impl Request {
/// Get the segments beginning at the `i`th, if they exists. /// Get the segments beginning at the `i`th, if they exists.
#[doc(hidden)] #[doc(hidden)]
pub fn get_raw_segments(&self, i: usize) -> Option<Segments> { pub fn get_raw_segments(&self, i: usize) -> Option<Segments> {
if i >= self.uri().segment_count() { if i >= self.uri.segment_count() {
debug!("{} is >= segment count {}", i, self.uri().segment_count()); debug!("{} is >= segment count {}", i, self.uri().segment_count());
None None
} else { } else {
// TODO: Really want to do self.uri.segments().skip(i).into_inner(), // TODO: Really want to do self.uri.segments().skip(i).into_inner(),
// but the std lib doesn't implement it for Skip. // but the std lib doesn't implement `into_inner` for Skip.
let mut segments = self.uri.as_uri().segments(); let mut segments = self.uri.segments();
for _ in segments.by_ref().take(i) { /* do nothing */ } for _ in segments.by_ref().take(i) { /* do nothing */ }
Some(segments) Some(segments)
} }
} }
// FIXME: Make this `new`. Make current `new` a `from_hyp` method.
#[doc(hidden)] #[doc(hidden)]
pub fn mock(method: Method, uri: &str) -> Request { pub fn from_hyp(h_method: hyper::Method,
Request { h_headers: hyper::header::Headers,
params: RefCell::new(vec![]), h_uri: hyper::RequestUri)
method: method, -> Result<Request<'static>, String> {
cookies: Cookies::new(&[]), // Get a copy of the URI for later use.
uri: URIBuf::from(uri),
headers: HeaderMap::new(),
}
}
/// <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.
#[inline(always)]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// <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.
#[inline(always)]
pub fn content_type(&self) -> ContentType {
self.headers().get_one("Content-Type")
.and_then(|value| value.parse().ok())
.unwrap_or(ContentType::Any)
}
/// Retrieves the URI from the request. Rocket only allows absolute URIs, so
/// the URI will be absolute.
#[inline(always)]
pub fn uri(&self) -> URI {
self.uri.as_uri()
}
#[doc(hidden)]
#[inline(always)]
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 ecapsulate this better. Look at OwningRef/Rental
// for inspiration.
use ::std::mem::transmute;
*self.params.borrow_mut() = unsafe {
transmute(route.get_params(self.uri.as_uri()))
};
}
#[doc(hidden)]
#[inline(always)]
pub fn add_header(&mut self, header: Header<'static>) {
self.headers.add(header);
}
#[doc(hidden)]
pub fn new(h_method: hyper::Method,
h_headers: hyper::header::Headers,
h_uri: hyper::RequestUri)
-> Result<Request, String> {
let uri = match h_uri { let uri = match h_uri {
hyper::RequestUri::AbsolutePath(s) => URIBuf::from(s), hyper::RequestUri::AbsolutePath(s) => URIBuf::from(s),
_ => return Err(format!("Bad URI: {}", h_uri)), _ => return Err(format!("Bad URI: {}", h_uri)),
}; };
// Ensure that the method is known. TODO: Allow made-up methods?
let method = match Method::from_hyp(&h_method) { let method = match Method::from_hyp(&h_method) {
Some(method) => method, Some(method) => method,
_ => return Err(format!("Bad method: {}", h_method)), None => return Err(format!("Invalid method: {}", h_method))
}; };
let cookies = match h_headers.get::<hyper::header::Cookie>() { // Construct the request object.
// TODO: Retrieve key from config. let mut request = Request::new(method, uri);
Some(cookie) => cookie.to_cookie_jar(&[]),
None => Cookies::new(&[]),
};
let mut headers = HeaderMap::new(); // Set the request cookies, if they exist. TODO: Use session key.
for h_header in h_headers.iter() { if let Some(cookies) = h_headers.get::<hyper::header::Cookie>() {
headers.add_raw(h_header.name().to_string(), h_header.value_string()) request.set_cookies(cookies.to_cookie_jar(&[]));
} }
let request = Request { // Set the rest of the headers.
params: RefCell::new(vec![]), for hyp in h_headers.iter() {
method: method, let header = Header::new(hyp.name().to_string(), hyp.value_string());
cookies: cookies, request.add_header(header);
uri: uri, }
headers: headers,
};
Ok(request) Ok(request)
} }
} }
impl fmt::Display for Request { impl<'r> fmt::Display for Request<'r> {
/// Pretty prints a Request. This is primarily used by Rocket's logging /// Pretty prints a Request. This is primarily used by Rocket's logging
/// infrastructure. /// infrastructure.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -210,10 +210,10 @@ impl Flash<()> {
/// ///
/// The suggested use is through an `Option` and the `FlashMessage` type alias /// The suggested use is through an `Option` and the `FlashMessage` type alias
/// in `request`: `Option<FlashMessage>`. /// in `request`: `Option<FlashMessage>`.
impl<'r> FromRequest<'r> for Flash<()> { impl<'a, 'r> FromRequest<'a, 'r> for Flash<()> {
type Error = (); type Error = ();
fn from_request(request: &'r Request) -> request::Outcome<Self, Self::Error> { fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
trace_!("Flash: attemping to retrieve message."); trace_!("Flash: attemping to retrieve message.");
let r = request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| { let r = request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| {
// Clear the flash message. // Clear the flash message.

View File

@ -20,6 +20,7 @@ use error::Error;
use http::{Method, Status}; use http::{Method, Status};
use http::hyper::{self, header}; use http::hyper::{self, header};
use http::uri::URI;
/// The main `Rocket` type: used to mount routes and catchers and launch the /// The main `Rocket` type: used to mount routes and catchers and launch the
/// application. /// application.
@ -44,16 +45,13 @@ impl hyper::Handler for Rocket {
// Get all of the information from Hyper. // Get all of the information from Hyper.
let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct(); let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();
// Get a copy of the URI for later use. // Convert the Hyper request into a Rocket request.
let uri = h_uri.to_string(); let mut request = match Request::from_hyp(h_method, h_headers, h_uri) {
Ok(request) => request,
// Try to create a Rocket request from the hyper request info. Err(e) => {
let mut request = match Request::new(h_method, h_headers, h_uri) { error!("Bad incoming request: {}", e);
Ok(req) => req, let dummy = Request::new(Method::Get, URI::new("<unknown>"));
Err(ref reason) => { let r = self.handle_error(Status::InternalServerError, &dummy);
let mock = Request::mock(Method::Get, uri.as_str());
error!("{}: bad request ({}).", mock, reason);
let r = self.handle_error(Status::InternalServerError, &mock);
return self.issue_response(r, res); return self.issue_response(r, res);
} }
}; };
@ -73,7 +71,7 @@ impl hyper::Handler for Rocket {
let mut response = match self.dispatch(&request, data) { let mut response = match self.dispatch(&request, data) {
Ok(response) => response, Ok(response) => response,
Err(status) => { Err(status) => {
if status == Status::NotFound && request.method == Method::Head { if status == Status::NotFound && request.method() == Method::Head {
// FIXME: Handle unimplemented HEAD requests automatically. // FIXME: Handle unimplemented HEAD requests automatically.
info_!("Redirecting to {}.", Green.paint(Method::Get)); info_!("Redirecting to {}.", Green.paint(Method::Get));
} }
@ -171,7 +169,7 @@ impl Rocket {
let mut form_items = FormItems(form); let mut form_items = FormItems(form);
if let Some(("_method", value)) = form_items.next() { if let Some(("_method", value)) = form_items.next() {
if let Ok(method) = value.parse() { if let Ok(method) = value.parse() {
req.method = method; req.set_method(method);
} }
} }
} }
@ -340,7 +338,7 @@ impl Rocket {
/// use rocket::handler::Outcome; /// use rocket::handler::Outcome;
/// use rocket::http::Method::*; /// use rocket::http::Method::*;
/// ///
/// fn hi(_: &Request, _: Data) -> Outcome { /// fn hi(_: &Request, _: Data) -> Outcome<'static> {
/// Outcome::of("Hello!") /// Outcome::of("Hello!")
/// } /// }
/// ///

View File

@ -37,7 +37,7 @@ impl Router {
trace_!("Trying to route: {}", req); trace_!("Trying to route: {}", req);
// let num_segments = req.uri.segment_count(); // let num_segments = req.uri.segment_count();
// self.routes.get(&(req.method, num_segments)).map_or(vec![], |routes| { // self.routes.get(&(req.method, num_segments)).map_or(vec![], |routes| {
self.routes.get(&req.method).map_or(vec![], |routes| { self.routes.get(&req.method()).map_or(vec![], |routes| {
let mut matches: Vec<_> = routes.iter() let mut matches: Vec<_> = routes.iter()
.filter(|r| r.collides_with(req)) .filter(|r| r.collides_with(req))
.collect(); .collect();
@ -155,7 +155,7 @@ mod test {
method: Method, method: Method,
uri: &str) uri: &str)
-> Option<&'a Route> { -> Option<&'a Route> {
let request = Request::mock(method, uri); let request = Request::new(method, URI::new(uri));
let matches = router.route(&request); let matches = router.route(&request);
if matches.len() > 0 { if matches.len() > 0 {
Some(matches[0]) Some(matches[0])
@ -165,7 +165,7 @@ mod test {
} }
fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> { fn matches<'a>(router: &'a Router, method: Method, uri: &str) -> Vec<&'a Route> {
let request = Request::mock(method, uri); let request = Request::new(method, URI::new(uri));
router.route(&request) router.route(&request)
} }
@ -321,14 +321,15 @@ mod test {
} }
fn match_params(router: &Router, path: &str, expected: &[&str]) -> bool { fn match_params(router: &Router, path: &str, expected: &[&str]) -> bool {
println!("Testing: {} (expect: {:?})", path, expected);
route(router, Get, path).map_or(false, |route| { route(router, Get, path).map_or(false, |route| {
let params = route.get_params(URI::new(path)); let params = route.get_param_indexes(URI::new(path));
if params.len() != expected.len() { if params.len() != expected.len() {
return false; return false;
} }
for i in 0..params.len() { for (k, (i, j)) in params.into_iter().enumerate() {
if params[i] != expected[i] { if &path[i..j] != expected[k] {
return false; return false;
} }
} }

View File

@ -64,9 +64,7 @@ impl Route {
/// Sets the path of the route. Does not update the rank or any other /// Sets the path of the route. Does not update the rank or any other
/// parameters. /// parameters.
pub fn set_path<S>(&mut self, path: S) pub fn set_path<S>(&mut self, path: S) where S: AsRef<str> {
where S: AsRef<str>
{
self.path = URIBuf::from(path.as_ref()); self.path = URIBuf::from(path.as_ref());
} }
@ -76,18 +74,19 @@ impl Route {
/// Given a URI, returns a vector of slices of that URI corresponding to the /// Given a URI, returns a vector of slices of that URI corresponding to the
/// dynamic segments in this route. /// dynamic segments in this route.
#[doc(hidden)] #[doc(hidden)]
pub fn get_params<'a>(&self, uri: URI<'a>) -> Vec<&'a str> { pub fn get_param_indexes(&self, uri: URI) -> Vec<(usize, usize)> {
let route_segs = self.path.as_uri().segments(); let route_segs = self.path.as_uri().segments();
let uri_segs = uri.segments(); let uri_segs = uri.segments();
let start_addr = uri.as_str().as_ptr() as usize;
let mut result = Vec::with_capacity(self.path.segment_count()); let mut result = Vec::with_capacity(self.path.segment_count());
for (route_seg, uri_seg) in route_segs.zip(uri_segs) { for (route_seg, uri_seg) in route_segs.zip(uri_segs) {
if route_seg.ends_with("..>") { if route_seg.ends_with("..>") {
// FIXME: Here.
break; break;
} else if route_seg.ends_with('>') { } else if route_seg.ends_with('>') {
// FIXME: Here. let i = (uri_seg.as_ptr() as usize) - start_addr;
result.push(uri_seg); let j = i + uri_seg.len();
result.push((i, j));
} }
} }
@ -151,9 +150,9 @@ impl Collider for Route {
} }
} }
impl Collider<Request> for Route { impl<'r> Collider<Request<'r>> for Route {
fn collides_with(&self, req: &Request) -> bool { fn collides_with(&self, req: &Request<'r>) -> bool {
self.method == req.method self.method == req.method()
&& req.uri().collides_with(&self.path.as_uri()) && req.uri().collides_with(&self.path.as_uri())
&& req.content_type().collides_with(&self.content_type) && req.content_type().collides_with(&self.content_type)
} }

View File

@ -105,12 +105,13 @@
//! } //! }
//! ``` //! ```
use http::{Method, Header, Cookie};
use ::{Rocket, Request, Response, Data}; use ::{Rocket, Request, Response, Data};
use http::{Method, Header, Cookie};
use http::uri::URIBuf;
/// A type for mocking requests for testing Rocket applications. /// A type for mocking requests for testing Rocket applications.
pub struct MockRequest { pub struct MockRequest {
request: Request, request: Request<'static>,
data: Data data: Data
} }
@ -119,7 +120,7 @@ impl MockRequest {
#[inline] #[inline]
pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self { pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
MockRequest { MockRequest {
request: Request::mock(method, uri.as_ref()), request: Request::new(method, URIBuf::new(uri.as_ref().to_string())),
data: Data::new(vec![]) data: Data::new(vec![])
} }
} }