From 6c00d362c099cf0d047a4ef59b46b01e709aeb1a Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sat, 20 May 2017 11:41:44 -0700 Subject: [PATCH] Add ResponseBuilder::raw_body. Use OrderMap for HeaderMap. --- lib/Cargo.toml | 1 + lib/src/http/content_type.rs | 7 +++++++ lib/src/http/header.rs | 9 +++++---- lib/src/http/uncased.rs | 7 ++++++- lib/src/lib.rs | 1 + lib/src/response/response.rs | 23 +++++++++++++++++++++++ 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 38b15483..ab547e53 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -33,6 +33,7 @@ pear_codegen = "0.0.8" rustls = { version = "0.7.0", optional = true } cookie = { version = "0.8.1", features = ["percent-encode", "secure"] } hyper = { version = "0.10.9", default-features = false } +ordermap = "0.2" [dependencies.hyper-rustls] git = "https://github.com/SergioBenitez/hyper-rustls" diff --git a/lib/src/http/content_type.rs b/lib/src/http/content_type.rs index ccc06791..8313d4f4 100644 --- a/lib/src/http/content_type.rs +++ b/lib/src/http/content_type.rs @@ -278,6 +278,13 @@ impl fmt::Display for ContentType { impl Into> for ContentType { #[inline(always)] fn into(self) -> Header<'static> { + // FIXME: For known media types, don't do `to_string`. Store the whole + // string as a `source` and have a way to know that the source is + // everything. That removes the allocation here. Then, in + // `MediaType::fmt`, write the source string out directly as well. + // + // We could also use an `enum` for MediaType. But that kinda sucks. But + // maybe it's what we want. Header::new("Content-Type", self.to_string()) } } diff --git a/lib/src/http/header.rs b/lib/src/http/header.rs index 24685b7d..f034556b 100644 --- a/lib/src/http/header.rs +++ b/lib/src/http/header.rs @@ -1,7 +1,8 @@ -use std::collections::HashMap; use std::borrow::{Borrow, Cow}; use std::fmt; +use ordermap::OrderMap; + use http::uncased::{Uncased, UncasedStr}; /// Simple representation of an HTTP header. @@ -109,14 +110,14 @@ impl<'h> fmt::Display for Header<'h> { /// A collection of headers, mapping a header name to its many ordered values. #[derive(Clone, Debug, PartialEq, Default)] pub struct HeaderMap<'h> { - headers: HashMap, Vec>> + headers: OrderMap, Vec>> } impl<'h> HeaderMap<'h> { /// Returns an empty collection. #[inline(always)] pub fn new() -> HeaderMap<'h> { - HeaderMap { headers: HashMap::new() } + HeaderMap { headers: OrderMap::new() } } /// Returns true if `self` contains a header with the name `name`. @@ -134,7 +135,7 @@ impl<'h> HeaderMap<'h> { /// ``` #[inline] pub fn contains(&self, name: &str) -> bool { - self.headers.get(name.into() : &UncasedStr).is_some() + self.headers.get(UncasedStr::new(name)).is_some() } /// Returns the number of _values_ stored in the map. diff --git a/lib/src/http/uncased.rs b/lib/src/http/uncased.rs index b156f01a..7d784f93 100644 --- a/lib/src/http/uncased.rs +++ b/lib/src/http/uncased.rs @@ -23,6 +23,11 @@ use std::fmt; pub struct UncasedStr(str); impl UncasedStr { + #[inline(always)] + pub fn new(string: &str) -> &UncasedStr { + unsafe { &*(string as *const str as *const UncasedStr) } + } + #[inline(always)] pub fn as_str(&self) -> &str { &self.0 @@ -67,7 +72,7 @@ impl<'a> PartialEq for &'a str { impl<'a> From<&'a str> for &'a UncasedStr { #[inline(always)] fn from(string: &'a str) -> &'a UncasedStr { - unsafe { ::std::mem::transmute(string) } + UncasedStr::new(string) } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2f2c0b63..38311b08 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -113,6 +113,7 @@ extern crate time; extern crate memchr; extern crate base64; extern crate smallvec; +extern crate ordermap; #[cfg(test)] #[macro_use] extern crate lazy_static; diff --git a/lib/src/response/response.rs b/lib/src/response/response.rs index 48d03b1c..e1aea303 100644 --- a/lib/src/response/response.rs +++ b/lib/src/response/response.rs @@ -415,6 +415,29 @@ impl<'r> ResponseBuilder<'r> { self } + /// Sets the body of `self` to be `body`. This method should typically not + /// be used, opting instead for one of `sized_body`, `streamed_body`, or + /// `chunked_body`. + /// + /// # Example + /// + /// ```rust + /// use std::io::Cursor; + /// use rocket::response::{Response, Body}; + /// + /// # #[allow(unused_variables)] + /// let response = Response::build() + /// .raw_body(Body::Sized(Cursor::new("Hello!"), 6)) + /// .finalize(); + /// ``` + #[inline(always)] + pub fn raw_body(&mut self, body: Body) + -> &mut ResponseBuilder<'r> + { + self.response.set_raw_body(body); + self + } + /// Merges the `other` `Response` into `self` by setting any fields in /// `self` to the corresponding value in `other` if they are set in `other`. /// Fields in `self` are unchanged if they are not set in `other`. If a