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:
Sergio Benitez 2017-06-19 17:20:10 -07:00
parent cdf9ff9bde
commit 6a7fde6d70
11 changed files with 144 additions and 93 deletions

View File

@ -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)
},
};

View File

@ -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 {

View File

@ -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::*;

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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")
},

View File

@ -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();

View File

@ -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);
}
}