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 = ();
fn from_request(request: &'r Request) -> request::Outcome<Self, ()> {
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, ()> {
Success(HeaderCount(request.headers().len()))
}
}

View File

@ -10,15 +10,15 @@ use rocket::response::{self, Responder};
use rocket::handler::Outcome;
use rocket::http::Method::*;
fn forward(_req: &Request, data: Data) -> Outcome {
fn forward(_req: &Request, data: Data) -> Outcome<'static> {
Outcome::forward(data)
}
fn hi(_req: &Request, _: Data) -> Outcome {
fn hi(_req: &Request, _: Data) -> Outcome<'static> {
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"))
}
@ -27,7 +27,7 @@ fn echo_url(req: &Request, _: Data) -> Outcome<'static> {
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() {
println!(" => Content-Type of upload must be text/plain. Ignoring.");
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())
}
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()
}

View File

@ -79,11 +79,11 @@ impl Catcher {
/// use rocket::{Catcher, Request, Error};
/// 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()
/// }
///
/// fn handle_500(_: Error, _: &Request) -> Result {
/// fn handle_500<'r>(_: Error, _: &'r Request) -> Result<'r> {
/// "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> {
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.
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> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 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 request::Request;
use outcome::Outcome::*;
use http::{Status, ContentType, Method, Cookies};
use http::uri::URI;
/// Type alias for the `Outcome` of a `FromRequest` conversion.
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"
/// }
///
/// impl<'r> FromRequest<'r> for APIKey {
/// impl<'a, 'r> FromRequest<'a, 'r> for APIKey {
/// 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();
/// if keys.len() != 1 {
/// return Outcome::Failure((Status::BadRequest, ()));
@ -114,8 +116,8 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
///
/// # fn main() { }
/// ```
pub trait FromRequest<'r>: Sized {
/// The associated error to be returned when derivation fails.
pub trait FromRequest<'a, 'r>: Sized {
/// The associated error to be returned if derivation fails.
type Error: Debug;
/// 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.
/// `Forward` is returned to indicate that the request should be forwarded
/// 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 = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
Success(request)
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.uri())
}
}
impl<'r> FromRequest<'r> for Method {
impl<'a, 'r> FromRequest<'a, 'r> for Method {
type Error = ();
fn from_request(request: &'r Request) -> Outcome<Self, Self::Error> {
Success(request.method)
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
Success(request.method())
}
}
impl<'r> FromRequest<'r> for &'r Cookies {
impl<'a, 'r> FromRequest<'a, 'r> for &'a Cookies {
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())
}
}
impl<'r> FromRequest<'r> for ContentType {
impl<'a, 'r> FromRequest<'a, 'r> for ContentType {
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())
}
}
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 = ();
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) {
Success(val) => Success(Ok(val)),
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 = ();
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) {
Success(val) => Success(Some(val)),
Failure(_) => Success(None),

View File

@ -13,6 +13,57 @@ use http::{Method, ContentType, Header, HeaderMap, Cookies};
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.
///
/// 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
/// the information for a given web request except for the body data. This
/// includes the HTTP method, URI, cookies, headers, and more.
pub struct Request {
/// The HTTP method associated with the request.
pub method: Method,
uri: URIBuf, // FIXME: Should be URI (without hyper).
params: RefCell<Vec<&'static str>>,
pub struct Request<'r> {
method: Method,
uri: CowURI<'r>,
headers: HeaderMap<'r>,
params: RefCell<Vec<(usize, usize)>>,
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
/// request. Returns `Error::NoKey` if `n` is greater than the number of
/// 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);
/// }
/// ```
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)?;
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.
#[doc(hidden)]
pub fn get_param_str(&self, n: usize) -> Option<&str> {
let params = self.params.borrow();
if n >= params.len() {
debug!("{} is >= param count {}", n, params.len());
None
} else {
Some(&params[n])
return None;
}
}
/// 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<'r>(&'r self) -> &'r Cookies {
&self.cookies
let (i, j) = params[n];
let uri_str = self.uri.as_str();
if j > uri_str.len() {
error!("Couldn't retrieve parameter: internal count incorrect.");
return None;
}
Some(&uri_str[i..j])
}
/// 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
/// `request.get_segments::<T>(1)` will attempt to parse the segments
/// `"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> {
let segments = self.get_raw_segments(i).ok_or(Error::NoKey)?;
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.
#[doc(hidden)]
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());
None
} 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.as_uri().segments();
// but the std lib doesn't implement `into_inner` for Skip.
let mut segments = self.uri.segments();
for _ in segments.by_ref().take(i) { /* do nothing */ }
Some(segments)
}
}
// FIXME: Make this `new`. Make current `new` a `from_hyp` method.
#[doc(hidden)]
pub fn mock(method: Method, uri: &str) -> Request {
Request {
params: RefCell::new(vec![]),
method: method,
cookies: Cookies::new(&[]),
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> {
pub fn from_hyp(h_method: hyper::Method,
h_headers: hyper::header::Headers,
h_uri: hyper::RequestUri)
-> Result<Request<'static>, String> {
// Get a copy of the URI for later use.
let uri = match h_uri {
hyper::RequestUri::AbsolutePath(s) => URIBuf::from(s),
_ => 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) {
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>() {
// TODO: Retrieve key from config.
Some(cookie) => cookie.to_cookie_jar(&[]),
None => Cookies::new(&[]),
};
// Construct the request object.
let mut request = Request::new(method, uri);
let mut headers = HeaderMap::new();
for h_header in h_headers.iter() {
headers.add_raw(h_header.name().to_string(), h_header.value_string())
// Set the request cookies, if they exist. TODO: Use session key.
if let Some(cookies) = h_headers.get::<hyper::header::Cookie>() {
request.set_cookies(cookies.to_cookie_jar(&[]));
}
let request = Request {
params: RefCell::new(vec![]),
method: method,
cookies: cookies,
uri: uri,
headers: headers,
};
// Set the rest of the headers.
for hyp in h_headers.iter() {
let header = Header::new(hyp.name().to_string(), hyp.value_string());
request.add_header(header);
}
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
/// infrastructure.
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
/// in `request`: `Option<FlashMessage>`.
impl<'r> FromRequest<'r> for Flash<()> {
impl<'a, 'r> FromRequest<'a, 'r> for Flash<()> {
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.");
let r = request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| {
// Clear the flash message.

View File

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

View File

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

View File

@ -64,9 +64,7 @@ impl Route {
/// Sets the path of the route. Does not update the rank or any other
/// parameters.
pub fn set_path<S>(&mut self, path: S)
where S: AsRef<str>
{
pub fn set_path<S>(&mut self, path: S) where S: AsRef<str> {
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
/// dynamic segments in this route.
#[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 uri_segs = uri.segments();
let start_addr = uri.as_str().as_ptr() as usize;
let mut result = Vec::with_capacity(self.path.segment_count());
for (route_seg, uri_seg) in route_segs.zip(uri_segs) {
if route_seg.ends_with("..>") {
// FIXME: Here.
break;
} else if route_seg.ends_with('>') {
// FIXME: Here.
result.push(uri_seg);
let i = (uri_seg.as_ptr() as usize) - start_addr;
let j = i + uri_seg.len();
result.push((i, j));
}
}
@ -151,9 +150,9 @@ impl Collider for Route {
}
}
impl Collider<Request> for Route {
fn collides_with(&self, req: &Request) -> bool {
self.method == req.method
impl<'r> Collider<Request<'r>> for Route {
fn collides_with(&self, req: &Request<'r>) -> bool {
self.method == req.method()
&& req.uri().collides_with(&self.path.as_uri())
&& 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 http::{Method, Header, Cookie};
use http::uri::URIBuf;
/// A type for mocking requests for testing Rocket applications.
pub struct MockRequest {
request: Request,
request: Request<'static>,
data: Data
}
@ -119,7 +120,7 @@ impl MockRequest {
#[inline]
pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
MockRequest {
request: Request::mock(method, uri.as_ref()),
request: Request::new(method, URIBuf::new(uri.as_ref().to_string())),
data: Data::new(vec![])
}
}