mirror of https://github.com/rwf2/Rocket.git
Cache parsed ContentType and Accept headers.
This is a breaking change. `Request::content_type` now returns a borrow to `ContentType`. `FromRequest` for `ContentType` is no longer implemented. Instead, `FromRequest` for `&ContentType` is implemented.
This commit is contained in:
parent
e50164115b
commit
a25a3c69c6
|
@ -25,7 +25,7 @@ url = "1"
|
|||
hyper = { version = "0.10.4", default-features = false }
|
||||
toml = { version = "0.2", default-features = false }
|
||||
num_cpus = "1"
|
||||
state = "0.2"
|
||||
state = "0.2.1"
|
||||
time = "0.1"
|
||||
memchr = "1"
|
||||
base64 = "0.4"
|
||||
|
|
|
@ -104,7 +104,7 @@ impl<'a, S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
|
|||
/// fn from_data(req: &Request, data: Data) -> data::Outcome<Self, String> {
|
||||
/// // Ensure the content type is correct before opening the data.
|
||||
/// let person_ct = ContentType::new("application", "x-person");
|
||||
/// if req.content_type() != Some(person_ct) {
|
||||
/// if req.content_type() != Some(&person_ct) {
|
||||
/// return Outcome::Forward(data);
|
||||
/// }
|
||||
///
|
||||
|
|
|
@ -220,7 +220,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for Accept {
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a Accept {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
|
||||
|
@ -231,7 +231,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Accept {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for ContentType {
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for &'a ContentType {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::str;
|
|||
use term_painter::Color::*;
|
||||
use term_painter::ToStyle;
|
||||
|
||||
use state::Container;
|
||||
use state::{Container, Storage};
|
||||
|
||||
use error::Error;
|
||||
use super::{FromParam, FromSegments};
|
||||
|
@ -15,7 +15,6 @@ use router::Route;
|
|||
use http::uri::{URI, Segments};
|
||||
use http::{Method, Header, HeaderMap, Cookies, Session, CookieJar, Key};
|
||||
use http::{RawStr, ContentType, Accept, MediaType};
|
||||
use http::parse::media_type;
|
||||
use http::hyper;
|
||||
|
||||
struct PresetState<'r> {
|
||||
|
@ -28,6 +27,8 @@ struct RequestState<'r> {
|
|||
params: RefCell<Vec<(usize, usize)>>,
|
||||
cookies: RefCell<CookieJar>,
|
||||
session: RefCell<CookieJar>,
|
||||
accept: Storage<Option<Accept>>,
|
||||
content_type: Storage<Option<ContentType>>,
|
||||
}
|
||||
|
||||
/// The type of an incoming web request.
|
||||
|
@ -71,6 +72,8 @@ impl<'r> Request<'r> {
|
|||
params: RefCell::new(Vec::new()),
|
||||
cookies: RefCell::new(CookieJar::new()),
|
||||
session: RefCell::new(CookieJar::new()),
|
||||
accept: Storage::new(),
|
||||
content_type: Storage::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,11 +241,11 @@ impl<'r> Request<'r> {
|
|||
/// let mut request = Request::new(Method::Get, "/uri");
|
||||
/// assert!(request.headers().is_empty());
|
||||
///
|
||||
/// request.add_header(ContentType::HTML);
|
||||
/// assert_eq!(request.content_type(), Some(ContentType::HTML));
|
||||
/// request.add_header(ContentType::Any);
|
||||
/// assert_eq!(request.headers().get_one("Content-Type"), Some("*/*"));
|
||||
///
|
||||
/// request.replace_header(ContentType::JSON);
|
||||
/// assert_eq!(request.content_type(), Some(ContentType::JSON));
|
||||
/// request.replace_header(ContentType::PNG);
|
||||
/// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn replace_header<H: Into<Header<'r>>>(&mut self, header: H) {
|
||||
|
@ -307,7 +310,9 @@ impl<'r> Request<'r> {
|
|||
}
|
||||
|
||||
/// Returns `Some` of the Content-Type header of `self`. If the header is
|
||||
/// not present, returns `None`.
|
||||
/// not present, returns `None`. The Content-Type header is cached after the
|
||||
/// first call to this function. As a result, subsequent calls will always
|
||||
/// return the same value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -317,38 +322,47 @@ impl<'r> Request<'r> {
|
|||
///
|
||||
/// let mut request = Request::new(Method::Get, "/uri");
|
||||
/// assert_eq!(request.content_type(), None);
|
||||
/// ```
|
||||
///
|
||||
/// request.replace_header(ContentType::JSON);
|
||||
/// assert_eq!(request.content_type(), Some(ContentType::JSON));
|
||||
/// ```rust
|
||||
/// use rocket::Request;
|
||||
/// use rocket::http::{Method, ContentType};
|
||||
///
|
||||
/// let mut request = Request::new(Method::Get, "/uri");
|
||||
/// request.add_header(ContentType::JSON);
|
||||
/// assert_eq!(request.content_type(), Some(&ContentType::JSON));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn content_type(&self) -> Option<ContentType> {
|
||||
// FIXME: Don't reparse each time! Use RC? Smarter than that?
|
||||
// Use state::Storage!
|
||||
self.headers().get_one("Content-Type").and_then(|value| value.parse().ok())
|
||||
pub fn content_type(&self) -> Option<&ContentType> {
|
||||
self.extra.content_type.get_or_set(|| {
|
||||
self.headers().get_one("Content-Type").and_then(|v| v.parse().ok())
|
||||
}).as_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn accept(&self) -> Option<Accept> {
|
||||
pub fn accept(&self) -> Option<&Accept> {
|
||||
self.extra.accept.get_or_set(|| {
|
||||
self.headers().get_one("Accept").and_then(|v| v.parse().ok())
|
||||
}).as_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn accept_first(&self) -> Option<MediaType> {
|
||||
self.headers().get_one("Accept").and_then(|mut v| media_type(&mut v).ok())
|
||||
pub fn accept_first(&self) -> Option<&MediaType> {
|
||||
self.accept().and_then(|accept| accept.first()).map(|wmt| wmt.media_type())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn format(&self) -> Option<MediaType> {
|
||||
pub fn format(&self) -> Option<&MediaType> {
|
||||
static ANY: MediaType = MediaType::Any;
|
||||
if self.method.supports_payload() {
|
||||
self.content_type().map(|ct| ct.into_media_type())
|
||||
self.content_type().map(|ct| ct.media_type())
|
||||
} else {
|
||||
// FIXME: Should we be using `accept_first` or `preferred`? Or
|
||||
// should we be checking neither and instead pass things through
|
||||
// where the client accepts the thing at all?
|
||||
self.accept()
|
||||
.map(|accept| accept.preferred().media_type().clone())
|
||||
.or(Some(MediaType::Any))
|
||||
.map(|accept| accept.preferred().media_type())
|
||||
.or(Some(&ANY))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue