mirror of https://github.com/rwf2/Rocket.git
parent
82f6f78189
commit
24805bbf16
|
@ -3,12 +3,13 @@ use std::borrow::{Borrow, Cow};
|
|||
use std::fmt;
|
||||
|
||||
use http::hyper::header as hyper;
|
||||
use http::ascii::{UncasedAscii, UncasedAsciiRef};
|
||||
|
||||
/// Simple representation of an HTTP header.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct Header<'h> {
|
||||
/// The name of the header.
|
||||
pub name: Cow<'h, str>,
|
||||
pub name: UncasedAscii<'h>,
|
||||
/// The value of the header.
|
||||
pub value: Cow<'h, str>,
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ impl<'h> Header<'h> {
|
|||
where N: Into<Cow<'a, str>>, V: Into<Cow<'b, str>>
|
||||
{
|
||||
Header {
|
||||
name: name.into(),
|
||||
name: UncasedAscii::new(name),
|
||||
value: value.into()
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +70,7 @@ impl<T> From<T> for Header<'static> where T: hyper::Header + hyper::HeaderFormat
|
|||
/// A collection of headers, mapping a header name to its many ordered values.
|
||||
#[derive(Clone, Debug, PartialEq, Default)]
|
||||
pub struct HeaderMap<'h> {
|
||||
headers: HashMap<Cow<'h, str>, Vec<Cow<'h, str>>>
|
||||
headers: HashMap<UncasedAscii<'h>, Vec<Cow<'h, str>>>
|
||||
}
|
||||
|
||||
impl<'h> HeaderMap<'h> {
|
||||
|
@ -94,7 +95,7 @@ impl<'h> HeaderMap<'h> {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
self.headers.get(name).is_some()
|
||||
self.headers.get(name.into() : &UncasedAsciiRef).is_some()
|
||||
}
|
||||
|
||||
/// Returns the number of _values_ stored in the map.
|
||||
|
@ -158,9 +159,10 @@ impl<'h> HeaderMap<'h> {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn get<'a>(&'a self, name: &str) -> impl Iterator<Item=&'a str> {
|
||||
self.headers.get(name).into_iter().flat_map(|values| {
|
||||
values.iter().map(|val| val.borrow())
|
||||
})
|
||||
self.headers
|
||||
.get(name.into() : &UncasedAsciiRef)
|
||||
.into_iter()
|
||||
.flat_map(|values| values.iter().map(|val| val.borrow()))
|
||||
}
|
||||
|
||||
/// Returns the _first_ value stored for the header with name `name` if
|
||||
|
@ -196,10 +198,11 @@ impl<'h> HeaderMap<'h> {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn get_one<'a>(&'a self, name: &str) -> Option<&'a str> {
|
||||
self.headers.get(name).and_then(|values| {
|
||||
if values.len() >= 1 { Some(values[0].borrow()) }
|
||||
else { None }
|
||||
})
|
||||
self.headers.get(name.into() : &UncasedAsciiRef)
|
||||
.and_then(|values| {
|
||||
if values.len() >= 1 { Some(values[0].borrow()) }
|
||||
else { None }
|
||||
})
|
||||
}
|
||||
|
||||
/// Replace any header that matches the name of `header.name` with `header`.
|
||||
|
@ -287,7 +290,7 @@ impl<'h> HeaderMap<'h> {
|
|||
pub fn replace_all<'n, 'v: 'h, H>(&mut self, name: H, values: Vec<Cow<'v, str>>)
|
||||
where 'n: 'h, H: Into<Cow<'n, str>>
|
||||
{
|
||||
self.headers.insert(name.into(), values);
|
||||
self.headers.insert(UncasedAscii::new(name), values);
|
||||
}
|
||||
|
||||
/// Adds `header` into the map. If a header with `header.name` was
|
||||
|
@ -365,7 +368,9 @@ impl<'h> HeaderMap<'h> {
|
|||
pub fn add_all<'n, H>(&mut self, name: H, values: &mut Vec<Cow<'h, str>>)
|
||||
where 'n:'h, H: Into<Cow<'n, str>>
|
||||
{
|
||||
self.headers.entry(name.into()).or_insert(vec![]).append(values)
|
||||
self.headers.entry(UncasedAscii::new(name))
|
||||
.or_insert(vec![])
|
||||
.append(values)
|
||||
}
|
||||
|
||||
/// Remove all of the values for header with name `name`.
|
||||
|
@ -386,7 +391,7 @@ impl<'h> HeaderMap<'h> {
|
|||
/// assert_eq!(map.len(), 1);
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, name: &str) {
|
||||
self.headers.remove(name);
|
||||
self.headers.remove(name.into() : &UncasedAsciiRef);
|
||||
}
|
||||
|
||||
/// Removes all of the headers stored in this map and returns a vector
|
||||
|
@ -436,7 +441,7 @@ impl<'h> HeaderMap<'h> {
|
|||
pub fn iter<'s>(&'s self) -> impl Iterator<Item=Header<'s>> {
|
||||
self.headers.iter().flat_map(|(key, values)| {
|
||||
values.iter().map(move |val| {
|
||||
Header::new(key.borrow(), val.borrow())
|
||||
Header::new(key.as_str(), val.borrow())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -464,7 +469,35 @@ impl<'h> HeaderMap<'h> {
|
|||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub fn into_iter_raw(self)
|
||||
-> impl Iterator<Item=(Cow<'h, str>, Vec<Cow<'h, str>>)> {
|
||||
-> impl Iterator<Item=(UncasedAscii<'h>, Vec<Cow<'h, str>>)> {
|
||||
self.headers.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::HeaderMap;
|
||||
|
||||
#[test]
|
||||
fn case_insensitive_add_get() {
|
||||
let mut map = HeaderMap::new();
|
||||
map.add_raw("content-type", "application/json");
|
||||
|
||||
let ct = map.get_one("Content-Type");
|
||||
assert_eq!(ct, Some("application/json"));
|
||||
|
||||
let ct2 = map.get_one("CONTENT-TYPE");
|
||||
assert_eq!(ct2, Some("application/json"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_insensitive_multiadd() {
|
||||
let mut map = HeaderMap::new();
|
||||
map.add_raw("x-custom", "a");
|
||||
map.add_raw("X-Custom", "b");
|
||||
map.add_raw("x-CUSTOM", "c");
|
||||
|
||||
let vals: Vec<_> = map.get("x-CuStOm").collect();
|
||||
assert_eq!(vals, vec!["a", "b", "c"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
use error::Error;
|
||||
use http::hyper;
|
||||
use http::ascii;
|
||||
|
||||
use self::Method::*;
|
||||
|
||||
// TODO: Support non-standard methods, here and in codegen.
|
||||
|
@ -51,30 +54,10 @@ impl Method {
|
|||
Get | Head | Connect | Trace | Options => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Method {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Method, Error> {
|
||||
match s {
|
||||
"GET" | "get" => Ok(Get),
|
||||
"PUT" | "put" => Ok(Put),
|
||||
"POST" | "post" => Ok(Post),
|
||||
"DELETE" | "delete" => Ok(Delete),
|
||||
"OPTIONS" | "options" => Ok(Options),
|
||||
"HEAD" | "head" => Ok(Head),
|
||||
"TRACE" | "trace" => Ok(Trace),
|
||||
"CONNECT" | "connect" => Ok(Connect),
|
||||
"PATCH" | "patch" => Ok(Patch),
|
||||
_ => Err(Error::BadMethod),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str(match *self {
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Get => "GET",
|
||||
Put => "PUT",
|
||||
Post => "POST",
|
||||
|
@ -84,6 +67,33 @@ impl fmt::Display for Method {
|
|||
Trace => "TRACE",
|
||||
Connect => "CONNECT",
|
||||
Patch => "PATCH",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Method {
|
||||
type Err = Error;
|
||||
|
||||
// According to the RFC, method names are case-sensitive. But some old
|
||||
// clients don't follow this, so we just do a case-insensitive match here.
|
||||
fn from_str(s: &str) -> Result<Method, Error> {
|
||||
match s {
|
||||
x if ascii::uncased_eq(x, Get.as_str()) => Ok(Get),
|
||||
x if ascii::uncased_eq(x, Put.as_str()) => Ok(Put),
|
||||
x if ascii::uncased_eq(x, Post.as_str()) => Ok(Post),
|
||||
x if ascii::uncased_eq(x, Delete.as_str()) => Ok(Delete),
|
||||
x if ascii::uncased_eq(x, Options.as_str()) => Ok(Options),
|
||||
x if ascii::uncased_eq(x, Head.as_str()) => Ok(Head),
|
||||
x if ascii::uncased_eq(x, Trace.as_str()) => Ok(Trace),
|
||||
x if ascii::uncased_eq(x, Connect.as_str()) => Ok(Connect),
|
||||
x if ascii::uncased_eq(x, Patch.as_str()) => Ok(Patch),
|
||||
_ => Err(Error::BadMethod),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ mod method;
|
|||
mod content_type;
|
||||
mod status;
|
||||
mod header;
|
||||
mod ascii;
|
||||
|
||||
pub use self::method::Method;
|
||||
pub use self::content_type::ContentType;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(drop_types_in_const)]
|
||||
#![feature(associated_consts)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(type_ascription)]
|
||||
|
||||
//! # Rocket - Core API Documentation
|
||||
//!
|
||||
|
|
|
@ -1023,7 +1023,7 @@ impl<'r> Response<'r> {
|
|||
}
|
||||
|
||||
for (name, values) in other.headers.into_iter_raw() {
|
||||
self.headers.replace_all(name, values);
|
||||
self.headers.replace_all(name.into_cow(), values);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1072,7 @@ impl<'r> Response<'r> {
|
|||
}
|
||||
|
||||
for (name, mut values) in other.headers.into_iter_raw() {
|
||||
self.headers.add_all(name, &mut values);
|
||||
self.headers.add_all(name.into_cow(), &mut values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ impl Rocket {
|
|||
*hyp_res.status_mut() = hyper::StatusCode::from_u16(response.status().code);
|
||||
|
||||
for header in response.headers() {
|
||||
let name = header.name.into_owned();
|
||||
let name = header.name.into_string();
|
||||
let value = vec![header.value.into_owned().into()];
|
||||
hyp_res.headers_mut().set_raw(name, value);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue