mirror of https://github.com/rwf2/Rocket.git
Rework Request: add lifetime to future proof, remove unsafe.
This commit is contained in:
parent
5f311c3654
commit
6815a56cb5
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
/// }
|
||||
///
|
||||
|
|
|
@ -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>>>
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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(¶ms[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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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!")
|
||||
/// }
|
||||
///
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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![])
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue