Reorganize extra request state. Add 'accept' and 'accept_first' methods to Request.

This commit is contained in:
Sergio Benitez 2017-03-28 03:10:18 -07:00
parent 1fb1cdfc58
commit fb29b37f30
7 changed files with 81 additions and 59 deletions

View File

@ -29,8 +29,8 @@ time = "0.1"
memchr = "1"
base64 = "0.4"
smallvec = "0.3"
pear = "0.0.7"
pear_codegen = "0.0.7"
pear = "0.0.8"
pear_codegen = "0.0.8"
[dependencies.cookie]
version = "0.7.2"

View File

@ -18,7 +18,8 @@ mod content_type;
mod status;
mod header;
mod accept;
mod parse;
pub(crate) mod parse;
// We need to export these for codegen, but otherwise it's unnecessary.
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)

View File

@ -21,7 +21,7 @@ fn accept<'a>(input: &mut &'a str) -> ParseResult<&'a str, Accept> {
let mut media_types = vec![];
repeat_while!(eat(','), {
skip_while(is_whitespace);
let media_type = media_type(input);
let media_type = media_type();
let weight = q_value(&media_type);
media_types.push(WeightedMediaType(media_type, weight));
});

View File

@ -25,8 +25,9 @@ fn quoted_string<'a>(input: &mut &'a str) -> ParseResult<&'a str, &'a str> {
}
#[parser]
pub fn media_type<'a>(input: &mut &'a str,
source: &'a str) -> ParseResult<&'a str, MediaType> {
pub fn media_type<'a>(input: &mut &'a str) -> ParseResult<&'a str, MediaType> {
let source: &str = *input;
let top = take_some_while(|c| is_valid_token(c) && c != '/');
eat('/');
let sub = take_some_while(is_valid_token);
@ -58,7 +59,7 @@ pub fn media_type<'a>(input: &mut &'a str,
}
pub fn parse_media_type(mut input: &str) -> Result<MediaType, ParseError<&str>> {
parse!(&mut input, (media_type(input), eof()).0).into()
parse!(&mut input, (media_type(), eof()).0).into()
}
#[cfg(test)]

View File

@ -13,9 +13,23 @@ use super::{FromParam, FromSegments};
use router::Route;
use http::uri::{URI, Segments};
use http::{Method, ContentType, Header, HeaderMap, Cookies, Session, CookieJar, Key};
use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar, Key};
use http::{ContentType, Accept, MediaType};
use http::parse::media_type;
use http::hyper;
struct PresetState<'r> {
key: &'r Key,
managed_state: &'r Container,
}
struct RequestState<'r> {
preset: Option<PresetState<'r>>,
params: RefCell<Vec<(usize, usize)>>,
cookies: RefCell<CookieJar>,
session: RefCell<CookieJar>,
}
/// The type of an incoming web request.
///
/// This should be used sparingly in Rocket applications. In particular, it
@ -26,13 +40,9 @@ use http::hyper;
pub struct Request<'r> {
method: Method,
uri: URI<'r>,
key: Option<&'r Key>,
headers: HeaderMap<'r>,
remote: Option<SocketAddr>,
params: RefCell<Vec<(usize, usize)>>,
cookies: RefCell<CookieJar>,
session: RefCell<CookieJar>,
state: Option<&'r Container>,
extra: RequestState<'r>
}
impl<'r> Request<'r> {
@ -49,17 +59,19 @@ impl<'r> Request<'r> {
/// # #[allow(unused_variables)]
/// let request = Request::new(Method::Get, "/uri");
/// ```
#[inline(always)]
pub fn new<U: Into<URI<'r>>>(method: Method, uri: U) -> Request<'r> {
Request {
method: method,
uri: uri.into(),
headers: HeaderMap::new(),
key: None,
remote: None,
params: RefCell::new(Vec::new()),
cookies: RefCell::new(CookieJar::new()),
session: RefCell::new(CookieJar::new()),
state: None
extra: RequestState {
preset: None,
params: RefCell::new(Vec::new()),
cookies: RefCell::new(CookieJar::new()),
session: RefCell::new(CookieJar::new()),
}
}
}
@ -132,7 +144,7 @@ impl<'r> Request<'r> {
#[inline(always)]
pub fn set_uri<'u: 'r, U: Into<URI<'u>>>(&mut self, uri: U) {
self.uri = uri.into();
self.params = RefCell::new(Vec::new());
*self.extra.params.borrow_mut() = Vec::new();
}
/// Returns the address of the remote connection that initiated this
@ -172,7 +184,6 @@ impl<'r> Request<'r> {
///
/// assert_eq!(request.remote(), Some(localhost));
/// ```
#[doc(hidden)]
#[inline(always)]
pub fn set_remote(&mut self, address: SocketAddr) {
self.remote = Some(address);
@ -257,7 +268,7 @@ impl<'r> Request<'r> {
/// ```
#[inline]
pub fn cookies(&self) -> Cookies {
match self.cookies.try_borrow_mut() {
match self.extra.cookies.try_borrow_mut() {
Ok(jar) => Cookies::new(jar),
Err(_) => {
error_!("Multiple `Cookies` instances are active at once.");
@ -271,14 +282,14 @@ impl<'r> Request<'r> {
#[inline]
pub fn session(&self) -> Session {
match self.session.try_borrow_mut() {
Ok(jar) => Session::new(jar, self.key.unwrap()),
match self.extra.session.try_borrow_mut() {
Ok(jar) => Session::new(jar, self.preset().key),
Err(_) => {
error_!("Multiple `Session` instances are active at once.");
info_!("An instance of `Session` must be dropped before another \
can be retrieved.");
warn_!("The retrieved `Session` instance will be empty.");
Session::empty(self.key.unwrap())
Session::empty(self.preset().key)
}
}
}
@ -286,13 +297,13 @@ impl<'r> Request<'r> {
/// Replace all of the cookies in `self` with those in `jar`.
#[inline]
pub(crate) fn set_cookies(&mut self, jar: CookieJar) {
self.cookies = RefCell::new(jar);
self.extra.cookies = RefCell::new(jar);
}
/// Replace all of the session cookie in `self` with those in `jar`.
#[inline]
pub(crate) fn set_session(&mut self, jar: CookieJar) {
self.session = RefCell::new(jar);
self.extra.session = RefCell::new(jar);
}
/// Returns `Some` of the Content-Type header of `self`. If the header is
@ -312,8 +323,17 @@ impl<'r> Request<'r> {
/// ```
#[inline(always)]
pub fn content_type(&self) -> Option<ContentType> {
self.headers().get_one("Content-Type")
.and_then(|value| value.parse().ok())
self.headers().get_one("Content-Type").and_then(|value| value.parse().ok())
}
#[inline(always)]
pub fn accept(&self) -> Option<Accept> {
self.headers().get_one("Accept").and_then(|v| v.parse().ok())
}
#[inline(always)]
pub fn accept_first(&self) -> Option<MediaType> {
self.headers().get_one("Accept").and_then(|mut v| media_type(&mut v).ok())
}
/// Retrieves and parses into `T` the 0-indexed `n`th dynamic parameter from
@ -349,14 +369,14 @@ impl<'r> Request<'r> {
/// TODO: Figure out the mount path from here.
#[inline]
pub(crate) fn set_params(&self, route: &Route) {
*self.params.borrow_mut() = route.get_param_indexes(self.uri());
*self.extra.params.borrow_mut() = route.get_param_indexes(self.uri());
}
/// Get the `n`th path parameter as a string, if it exists. This is used by
/// codegen.
#[doc(hidden)]
pub fn get_param_str(&self, n: usize) -> Option<&str> {
let params = self.params.borrow();
let params = self.extra.params.borrow();
if n >= params.len() {
debug!("{} is >= param count {}", n, params.len());
return None;
@ -401,7 +421,7 @@ impl<'r> Request<'r> {
/// exist. Used by codegen.
#[doc(hidden)]
pub fn get_raw_segments(&self, n: usize) -> Option<Segments> {
let params = self.params.borrow();
let params = self.extra.params.borrow();
if n >= params.len() {
debug!("{} is >= param (segments) count {}", n, params.len());
return None;
@ -418,21 +438,27 @@ impl<'r> Request<'r> {
}
/// Get the managed state container, if it exists. For internal use only!
#[inline]
pub(crate) fn get_state(&self) -> Option<&'r Container> {
self.state
/// FIXME: Expose?
#[inline(always)]
pub(crate) fn get_state<T: Send + Sync + 'static>(&self) -> Option<&'r T> {
self.preset().managed_state.try_get()
}
/// Set the state. For internal use only!
#[inline]
pub(crate) fn set_state(&mut self, state: &'r Container) {
self.state = Some(state);
#[inline(always)]
fn preset(&self) -> &PresetState<'r> {
match self.extra.preset {
Some(ref state) => state,
None => {
error_!("Internal Rocket error: preset state is unset!");
panic!("Please report this error to the GitHub issue tracker.");
}
}
}
/// Set the session key. For internal use only!
#[inline]
pub(crate) fn set_key(&mut self, key: &'r Key) {
self.key = Some(key);
/// Set the precomputed state. For internal use only!
#[inline(always)]
pub(crate) fn set_preset_state(&mut self, key: &'r Key, state: &'r Container) {
self.extra.preset = Some(PresetState { key, managed_state: state });
}
/// Convert from Hyper types into a Rocket Request.
@ -455,6 +481,7 @@ impl<'r> Request<'r> {
// Construct the request object.
let mut request = Request::new(method, uri);
request.set_remote(h_addr);
// Set the request cookies, if they exist.
if let Some(cookie_headers) = h_headers.get_raw("Cookie") {
@ -491,9 +518,6 @@ impl<'r> Request<'r> {
}
}
// Set the remote address.
request.set_remote(h_addr);
Ok(request)
}
}

View File

@ -59,6 +59,7 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
/// Using this method is typically unnecessary as `State` implements `Deref`
/// with a `Target` of `T`. This means Rocket will automatically coerce a
/// `State<T>` to an `&T` when the types call for it.
#[inline(always)]
pub fn inner(&self) -> &'r T {
self.0
}
@ -68,19 +69,14 @@ impl<'r, T: Send + Sync + 'static> State<'r, T> {
impl<'a, 'r, T: Send + Sync + 'static> FromRequest<'a, 'r> for State<'r, T> {
type Error = ();
#[inline(always)]
fn from_request(req: &'a Request<'r>) -> request::Outcome<State<'r, T>, ()> {
if let Some(state) = req.get_state() {
match state.try_get::<T>() {
Some(state) => Outcome::Success(State(state)),
None => {
error_!("Attempted to retrieve unmanaged state!");
Outcome::Failure((Status::InternalServerError, ()))
}
match req.get_state::<T>() {
Some(state) => Outcome::Success(State(state)),
None => {
error_!("Attempted to retrieve unmanaged state!");
Outcome::Failure((Status::InternalServerError, ()))
}
} else {
error_!("Internal Rocket error: managed state is unset!");
error_!("Please report this error in the Rocket GitHub issue tracker.");
Outcome::Failure((Status::InternalServerError, ()))
}
}
}
@ -88,6 +84,7 @@ impl<'a, 'r, T: Send + Sync + 'static> FromRequest<'a, 'r> for State<'r, T> {
impl<'r, T: Send + Sync + 'static> Deref for State<'r, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
self.0
}

View File

@ -182,9 +182,8 @@ impl Rocket {
-> Response<'r> {
info!("{}:", request);
// Inform the request about the state and session key.
request.set_state(&self.state);
request.set_key(&self.config.session_key());
// Inform the request about all of the precomputed state.
request.set_preset_state(&self.config.session_key(), &self.state);
// Do a bit of preprocessing before routing.
self.preprocess_request(request, &data);