mirror of https://github.com/rwf2/Rocket.git
Initial cleanup of 'http' docs. Add 'handler::Outcome' docs.
This commit also changes the signature of the 'ContentType' 'from_extension" method so that it returns an 'Option<ContentType>' as opposed to 'ContentType'. This commit also disallows negative quality values in 'Accept' media types.
This commit is contained in:
parent
cdf9ff9bde
commit
6a7fde6d70
|
@ -197,10 +197,10 @@ impl RouteGenerateExt for RouteParams {
|
|||
#[allow(non_snake_case)]
|
||||
let $ident: $ty = match
|
||||
::rocket::request::FromRequest::from_request(__req) {
|
||||
::rocket::outcome::Outcome::Success(v) => v,
|
||||
::rocket::outcome::Outcome::Forward(_) =>
|
||||
return ::rocket::Outcome::forward(__data),
|
||||
::rocket::outcome::Outcome::Failure((code, _)) => {
|
||||
::rocket::Outcome::Success(v) => v,
|
||||
::rocket::Outcome::Forward(_) =>
|
||||
return ::rocket::Outcome::Forward(__data),
|
||||
::rocket::Outcome::Failure((code, _)) => {
|
||||
return ::rocket::Outcome::Failure(code)
|
||||
},
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ impl Context {
|
|||
}
|
||||
|
||||
let data_type = data_type_str.as_ref()
|
||||
.map(|ext| ContentType::from_extension(ext))
|
||||
.and_then(|ext| ContentType::from_extension(ext))
|
||||
.unwrap_or(ContentType::HTML);
|
||||
|
||||
templates.insert(name, TemplateInfo {
|
||||
|
|
|
@ -5,12 +5,7 @@ use super::data::BodyReader;
|
|||
use http::hyper::net::NetworkStream;
|
||||
use http::hyper::h1::HttpReader;
|
||||
|
||||
// It's very unfortunate that we have to wrap `BodyReader` in a `BufReader`
|
||||
// since it already contains another `BufReader`. The issue is that Hyper's
|
||||
// `HttpReader` doesn't implement `BufRead`. Unfortunately, this will likely
|
||||
// stay "double buffered" until we switch HTTP libraries.
|
||||
// |-- peek buf --|
|
||||
// pub type InnerStream = Chain<Cursor<Vec<u8>>, BufReader<BodyReader>>;
|
||||
pub type InnerStream = Chain<Cursor<Vec<u8>>, BodyReader>;
|
||||
|
||||
/// Raw data stream of a request body.
|
||||
|
@ -18,9 +13,11 @@ pub type InnerStream = Chain<Cursor<Vec<u8>>, BodyReader>;
|
|||
/// This stream can only be obtained by calling
|
||||
/// [Data::open](/rocket/data/struct.Data.html#method.open). The stream contains
|
||||
/// all of the data in the body of the request. It exposes no methods directly.
|
||||
/// Instead, it must be used as an opaque `Read` or `BufRead` structure.
|
||||
/// Instead, it must be used as an opaque `Read` structure.
|
||||
pub struct DataStream(pub(crate) InnerStream);
|
||||
|
||||
// TODO: Have a `BufRead` impl for `DataStream`. At the moment, this isn't
|
||||
// possible since Hyper's `HttpReader` doesn't implement `BufRead`.
|
||||
impl Read for DataStream {
|
||||
#[inline(always)]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
|
@ -29,18 +26,6 @@ impl Read for DataStream {
|
|||
}
|
||||
}
|
||||
|
||||
// impl BufRead for DataStream {
|
||||
// #[inline(always)]
|
||||
// fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
// self.0.fill_buf()
|
||||
// }
|
||||
|
||||
// #[inline(always)]
|
||||
// fn consume(&mut self, amt: usize) {
|
||||
// self.0.consume(amt)
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn kill_stream(stream: &mut BodyReader) {
|
||||
// Only do the expensive reading if we're not sure we're done.
|
||||
use self::HttpReader::*;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
//! fairings to rewrite or record information about requests and responses, or
|
||||
//! to perform an action once a Rocket application has launched.
|
||||
//!
|
||||
//! To learn more about writing a fairing, see the [`Fairing` trait
|
||||
//! documentation](/rocket/fairing/trait.Fairing.html).
|
||||
//!
|
||||
//! ## Attaching
|
||||
//!
|
||||
//! You must inform Rocket about fairings that you wish to be active by calling
|
||||
|
|
|
@ -11,6 +11,22 @@ use outcome;
|
|||
pub type Outcome<'r> = outcome::Outcome<Response<'r>, Status, Data>;
|
||||
|
||||
impl<'r> Outcome<'r> {
|
||||
/// Return the `Outcome` of response to `req` from `responder`.
|
||||
///
|
||||
/// If the responder responds with `Ok`, an outcome of `Success` is returns
|
||||
/// with the response. If the outcomes reeturns `Err`, an outcome of
|
||||
/// `Failure` is returned with the status code.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data};
|
||||
/// use rocket::handler::Outcome;
|
||||
///
|
||||
/// fn str_responder(req: &Request, _: Data) -> Outcome<'static> {
|
||||
/// Outcome::from(req, "Hello, world!")
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from<T: Responder<'r>>(req: &Request, responder: T) -> Outcome<'r> {
|
||||
match responder.respond_to(req) {
|
||||
|
@ -19,11 +35,44 @@ impl<'r> Outcome<'r> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Failure` with the status code `code`. This is
|
||||
/// equivalent to `Outcome::Failure(code)`.
|
||||
///
|
||||
/// This method exists to be used during manual routing where
|
||||
/// `rocket::handler::Outcome` is imported instead of `rocket::Outcome`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data};
|
||||
/// use rocket::handler::Outcome;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// fn bad_req_route(_: &Request, _: Data) -> Outcome<'static> {
|
||||
/// Outcome::failure(Status::BadRequest)
|
||||
/// }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn failure(code: Status) -> Outcome<'static> {
|
||||
outcome::Outcome::Failure(code)
|
||||
}
|
||||
|
||||
/// Return an `Outcome` of `Forward` with the data `data`. This is
|
||||
/// equivalent to `Outcome::Forward(data)`.
|
||||
///
|
||||
/// This method exists to be used during manual routing where
|
||||
/// `rocket::handler::Outcome` is imported instead of `rocket::Outcome`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::{Request, Data};
|
||||
/// use rocket::handler::Outcome;
|
||||
///
|
||||
/// fn always_forward(_: &Request, data: Data) -> Outcome<'static> {
|
||||
/// Outcome::forward(data)
|
||||
/// }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn forward(data: Data) -> Outcome<'static> {
|
||||
outcome::Outcome::Forward(data)
|
||||
|
|
|
@ -26,11 +26,6 @@ impl WeightedMediaType {
|
|||
pub fn media_type(&self) -> &MediaType {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_media_type(self) -> MediaType {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MediaType> for WeightedMediaType {
|
||||
|
|
|
@ -46,29 +46,13 @@ macro_rules! content_types {
|
|||
($($name:ident ($check:ident): $str:expr, $t:expr,
|
||||
$s:expr $(; $k:expr => $v:expr)*),+) => {
|
||||
$(
|
||||
#[doc="Media type for <b>"] #[doc=$str] #[doc="</b>: <i>"]
|
||||
#[doc="Content-Type for <b>"] #[doc=$str] #[doc="</b>: <i>"]
|
||||
#[doc=$t] #[doc="/"] #[doc=$s]
|
||||
$(#[doc="; "] #[doc=$k] #[doc=" = "] #[doc=$v])*
|
||||
#[doc="</i>"]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const $name: ContentType = ContentType(MediaType::$name);
|
||||
|
||||
#[doc="Returns `true` if `self` is the media type for <b>"]
|
||||
#[doc=$str]
|
||||
#[doc="</b>, "]
|
||||
/// without considering parameters.
|
||||
#[inline(always)]
|
||||
pub fn $check(&self) -> bool {
|
||||
*self == ContentType::$name
|
||||
}
|
||||
)+
|
||||
|
||||
/// Returns `true` if this `ContentType` is known to Rocket, that is,
|
||||
/// there is an associated constant for `self`.
|
||||
pub fn is_known(&self) -> bool {
|
||||
$(if self.$check() { return true })+
|
||||
false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,11 +79,12 @@ impl ContentType {
|
|||
ContentType(MediaType::new(top, sub))
|
||||
}
|
||||
|
||||
/// Returns the Content-Type associated with the extension `ext`. Not all
|
||||
/// extensions are recognized. If an extensions is not recognized, then this
|
||||
/// method returns a ContentType of `Any`. The currently recognized
|
||||
/// extensions are: txt, html, htm, xml, js, css, json, png, gif, bmp, jpeg,
|
||||
/// jpg, and pdf.
|
||||
/// Returns the Content-Type associated with the extension `ext` if the
|
||||
/// extension is recognized. Not all extensions are recognized. If an
|
||||
/// extensions is not recognized, then this method returns `None`. The
|
||||
/// currently recognized extensions are txt, html, htm, xml, csv, js, css,
|
||||
/// json, png, gif, bmp, jpeg, jpg, webp, svg, pdf, ttf, otf, woff, and
|
||||
/// woff2.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -109,7 +94,7 @@ impl ContentType {
|
|||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// let xml = ContentType::from_extension("xml");
|
||||
/// assert!(xml.is_xml());
|
||||
/// assert_eq!(xml, Some(ContentType::XML));
|
||||
/// ```
|
||||
///
|
||||
/// An unrecognized content type:
|
||||
|
@ -118,12 +103,11 @@ impl ContentType {
|
|||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// let foo = ContentType::from_extension("foo");
|
||||
/// assert!(foo.is_any());
|
||||
/// assert!(foo.is_none());
|
||||
/// ```
|
||||
pub fn from_extension(ext: &str) -> ContentType {
|
||||
MediaType::from_extension(ext)
|
||||
.map(|mt| ContentType(mt))
|
||||
.unwrap_or(ContentType::Any)
|
||||
#[inline]
|
||||
pub fn from_extension(ext: &str) -> Option<ContentType> {
|
||||
MediaType::from_extension(ext).map(ContentType)
|
||||
}
|
||||
|
||||
/// Creates a new `ContentType` with top-level type `top`, subtype `sub`,
|
||||
|
@ -159,16 +143,21 @@ impl ContentType {
|
|||
ContentType(MediaType::with_params(top, sub, ps))
|
||||
}
|
||||
|
||||
/// Borrows the inner `MediaType` of `self`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::{ContentType, MediaType};
|
||||
///
|
||||
/// let http = ContentType::HTML;
|
||||
/// let media_type = http.media_type();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn media_type(&self) -> &MediaType {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_media_type(self) -> MediaType {
|
||||
self.0
|
||||
}
|
||||
|
||||
known_media_types!(content_types);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,30 +100,29 @@ macro_rules! media_types {
|
|||
macro_rules! from_extension {
|
||||
($($ext:expr => $name:ident),*) => (
|
||||
/// Returns the Media-Type associated with the extension `ext`. Not all
|
||||
/// extensions are recognized. If an extensions is not recognized, then this
|
||||
/// method returns a ContentType of `Any`. The currently recognized
|
||||
/// extensions include
|
||||
/// extensions are recognized. If an extensions is not recognized,
|
||||
/// `None` is returned. The currently recognized extensions are
|
||||
$(#[doc=$ext]#[doc=","])*
|
||||
/// and is likely to grow.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// A recognized content type:
|
||||
/// A recognized media type:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::ContentType;
|
||||
/// use rocket::http::MediaType;
|
||||
///
|
||||
/// let xml = ContentType::from_extension("xml");
|
||||
/// assert!(xml.is_xml());
|
||||
/// let xml = MediaType::from_extension("xml");
|
||||
/// assert_eq!(xml, Some(MediaType::XML));
|
||||
/// ```
|
||||
///
|
||||
/// An unrecognized content type:
|
||||
/// An unrecognized media type:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::ContentType;
|
||||
/// use rocket::http::MediaType;
|
||||
///
|
||||
/// let foo = ContentType::from_extension("foo");
|
||||
/// assert!(foo.is_any());
|
||||
/// let foo = MediaType::from_extension("foo");
|
||||
/// assert!(foo.is_none());
|
||||
/// ```
|
||||
pub fn from_extension(ext: &str) -> Option<MediaType> {
|
||||
match ext {
|
||||
|
|
|
@ -9,6 +9,7 @@ fn q<'a>(_: &'a str, media_type: &MediaType) -> ParseResult<&'a str, Option<f32>
|
|||
match media_type.params().next() {
|
||||
Some(("q", value)) if value.len() <= 5 => match value.parse::<f32>().ok() {
|
||||
Some(q) if q > 1. => ParseError::custom("accept", "q value must be <= 1"),
|
||||
Some(q) if q < 0. => ParseError::custom("accept", "q value must be > 0"),
|
||||
Some(q) => ParseResult::Done(Some(q)),
|
||||
None => ParseError::custom("accept", "q value must be float")
|
||||
},
|
||||
|
|
|
@ -14,8 +14,8 @@ use std::fmt;
|
|||
/// A reference to an uncased (case-preserving) ASCII string. This is typically
|
||||
/// created from an `&str` as follows:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use rocket::http::ascii::UncasedStr;
|
||||
/// ```rust
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let ascii_ref: &UncasedStr = "Hello, world!".into();
|
||||
/// ```
|
||||
|
@ -23,11 +23,34 @@ use std::fmt;
|
|||
pub struct UncasedStr(str);
|
||||
|
||||
impl UncasedStr {
|
||||
/// Returns a reference to an `UncasedStr` from an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let uncased_str = UncasedStr::new("Hello!");
|
||||
/// assert_eq!(uncased_str, "hello!");
|
||||
/// assert_eq!(uncased_str, "Hello!");
|
||||
/// assert_eq!(uncased_str, "HeLLo!");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new(string: &str) -> &UncasedStr {
|
||||
unsafe { &*(string as *const str as *const UncasedStr) }
|
||||
}
|
||||
|
||||
/// Returns `self` as an `&str`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::uncased::UncasedStr;
|
||||
///
|
||||
/// let uncased_str = UncasedStr::new("Hello!");
|
||||
/// assert_eq!(uncased_str.as_str(), "Hello!");
|
||||
/// assert_ne!(uncased_str.as_str(), "hELLo!");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
|
@ -109,39 +132,46 @@ impl fmt::Display for UncasedStr {
|
|||
}
|
||||
}
|
||||
|
||||
/// An uncased (case-preserving) ASCII string.
|
||||
/// An uncased (case-preserving), owned _or_ borrowed ASCII string.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Uncased<'s> {
|
||||
#[doc(hidden)]
|
||||
pub string: Cow<'s, str>
|
||||
}
|
||||
|
||||
impl<'s> Uncased<'s> {
|
||||
/// Creates a new UncaseAscii string.
|
||||
/// Creates a new `Uncased` string from `string` without allocating.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use rocket::http::ascii::Uncased;
|
||||
/// ```rust
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let uncased_ascii = UncasedAScii::new("Content-Type");
|
||||
/// let uncased = Uncased::new("Content-Type");
|
||||
/// assert_eq!(uncased, "content-type");
|
||||
/// assert_eq!(uncased, "CONTENT-Type");
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn new<S: Into<Cow<'s, str>>>(string: S) -> Uncased<'s> {
|
||||
Uncased { string: string.into() }
|
||||
}
|
||||
|
||||
/// Converts `self` into an owned `String`, allocating if necessary,
|
||||
/// Converts `self` into an owned `String`, allocating if necessary.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::http::uncased::Uncased;
|
||||
///
|
||||
/// let uncased = Uncased::new("Content-Type");
|
||||
/// let string = uncased.into_string();
|
||||
/// assert_eq!(string, "Content-Type".to_string());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn into_string(self) -> String {
|
||||
self.string.into_owned()
|
||||
}
|
||||
|
||||
/// Borrows the inner string.
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.string.borrow()
|
||||
}
|
||||
|
||||
/// Returns the inner `Cow`.
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
|
@ -155,14 +185,14 @@ impl<'a> Deref for Uncased<'a> {
|
|||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &UncasedStr {
|
||||
self.as_str().into()
|
||||
UncasedStr::new(self.string.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<UncasedStr> for Uncased<'a>{
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &UncasedStr {
|
||||
self.as_str().into()
|
||||
UncasedStr::new(self.string.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,9 +295,10 @@ impl<'s> Hash for Uncased<'s> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if `s1` and `s2` are equal without considering case. That is,
|
||||
/// for ASCII strings, this function returns s1.to_lower() == s2.to_lower(), but
|
||||
/// does it in a much faster way.
|
||||
/// Returns true if `s1` and `s2` are equal without considering case.
|
||||
///
|
||||
/// That is, for ASCII strings, this function returns `s1.to_lower() ==
|
||||
/// s2.to_lower()`, but does it in a much faster way.
|
||||
#[inline(always)]
|
||||
pub fn uncased_eq<S1: AsRef<str>, S2: AsRef<str>>(s1: S1, s2: S2) -> bool {
|
||||
let ascii_ref_1: &UncasedStr = s1.as_ref().into();
|
||||
|
|
|
@ -85,8 +85,7 @@ impl Responder<'static> for NamedFile {
|
|||
if let Some(ext) = self.path().extension() {
|
||||
// TODO: Use Cow for lowercase.
|
||||
let ext_string = ext.to_string_lossy().to_lowercase();
|
||||
let content_type = ContentType::from_extension(&ext_string);
|
||||
if !content_type.is_any() {
|
||||
if let Some(content_type) = ContentType::from_extension(&ext_string) {
|
||||
response.set_header(content_type);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue