perf(headers): check for header literals before allocating name
This commit is contained in:
		| @@ -42,7 +42,8 @@ const ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE: UniCase<&'static str> = UniCase("tr | ||||
|  | ||||
| impl Header for AccessControlAllowCredentials { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Access-Control-Allow-Credentials" | ||||
|         static NAME: &'static str = "Access-Control-Allow-Credentials"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> { | ||||
|   | ||||
| @@ -73,7 +73,8 @@ impl<S: Scheme> DerefMut for Authorization<S> { | ||||
|  | ||||
| impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'static { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Authorization" | ||||
|         static NAME: &'static str = "Authorization"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> { | ||||
|   | ||||
| @@ -51,7 +51,8 @@ __hyper__deref!(CacheControl => Vec<CacheDirective>); | ||||
|  | ||||
| impl Header for CacheControl { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Cache-Control" | ||||
|         static NAME: &'static str = "Cache-Control"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> { | ||||
|   | ||||
| @@ -90,7 +90,8 @@ pub struct ContentDisposition { | ||||
|  | ||||
| impl Header for ContentDisposition { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Content-Disposition" | ||||
|         static NAME: &'static str = "Content-Disposition"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentDisposition> { | ||||
|   | ||||
| @@ -32,10 +32,13 @@ use header::{Header, parsing}; | ||||
| #[derive(Clone, Copy, Debug, PartialEq)] | ||||
| pub struct ContentLength(pub u64); | ||||
|  | ||||
| //static NAME: &'static str = "Content-Length"; | ||||
|  | ||||
| impl Header for ContentLength { | ||||
|     #[inline] | ||||
|     fn header_name() -> &'static str { | ||||
|         "Content-Length" | ||||
|         static NAME: &'static str = "Content-Length"; | ||||
|         NAME | ||||
|     } | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentLength> { | ||||
|         // If multiple Content-Length headers were sent, everything can still | ||||
|   | ||||
| @@ -39,7 +39,8 @@ __hyper__deref!(Cookie => Vec<CookiePair>); | ||||
|  | ||||
| impl Header for Cookie { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Cookie" | ||||
|         static NAME: &'static str = "Cookie"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> { | ||||
|   | ||||
| @@ -30,7 +30,8 @@ const EXPECT_CONTINUE: UniCase<&'static str> = UniCase("100-continue"); | ||||
|  | ||||
| impl Header for Expect { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Expect" | ||||
|         static NAME: &'static str = "Expect"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> { | ||||
|   | ||||
| @@ -43,7 +43,8 @@ pub struct Host { | ||||
|  | ||||
| impl Header for Host { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Host" | ||||
|         static NAME: &'static str = "Host"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> { | ||||
|   | ||||
| @@ -55,7 +55,8 @@ pub enum IfRange { | ||||
|  | ||||
| impl Header for IfRange { | ||||
|     fn header_name() -> &'static str { | ||||
|         "If-Range" | ||||
|         static NAME: &'static str = "If-Range"; | ||||
|         NAME | ||||
|     } | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> { | ||||
|         let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); | ||||
|   | ||||
| @@ -217,7 +217,8 @@ macro_rules! header { | ||||
|         __hyper__deref!($id => Vec<$item>); | ||||
|         impl $crate::header::Header for $id { | ||||
|             fn header_name() -> &'static str { | ||||
|                 $n | ||||
|                 static NAME: &'static str = $n; | ||||
|                 NAME | ||||
|             } | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||
| @@ -243,7 +244,8 @@ macro_rules! header { | ||||
|         __hyper__deref!($id => Vec<$item>); | ||||
|         impl $crate::header::Header for $id { | ||||
|             fn header_name() -> &'static str { | ||||
|                 $n | ||||
|                 static NAME: &'static str = $n; | ||||
|                 NAME | ||||
|             } | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||
| @@ -268,7 +270,8 @@ macro_rules! header { | ||||
|         __hyper__deref!($id => $value); | ||||
|         impl $crate::header::Header for $id { | ||||
|             fn header_name() -> &'static str { | ||||
|                 $n | ||||
|                 static NAME: &'static str = $n; | ||||
|                 NAME | ||||
|             } | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 $crate::header::parsing::from_one_raw_str(raw).map($id) | ||||
| @@ -296,7 +299,8 @@ macro_rules! header { | ||||
|         } | ||||
|         impl $crate::header::Header for $id { | ||||
|             fn header_name() -> &'static str { | ||||
|                 $n | ||||
|                 static NAME: &'static str = $n; | ||||
|                 NAME | ||||
|             } | ||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||
|                 // FIXME: Return None if no item is in $id::Only | ||||
|   | ||||
| @@ -40,7 +40,8 @@ pub enum Pragma { | ||||
|  | ||||
| impl Header for Pragma { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Pragma" | ||||
|         static NAME: &'static str = "Pragma"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Pragma> { | ||||
|   | ||||
| @@ -52,7 +52,8 @@ __hyper__deref!(Prefer => Vec<Preference>); | ||||
|  | ||||
| impl Header for Prefer { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Prefer" | ||||
|         static NAME: &'static str = "Prefer"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> { | ||||
|   | ||||
| @@ -50,7 +50,8 @@ __hyper__deref!(PreferenceApplied => Vec<Preference>); | ||||
|  | ||||
| impl Header for PreferenceApplied { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Preference-Applied" | ||||
|         static NAME: &'static str = "Preference-Applied"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> { | ||||
|   | ||||
| @@ -176,7 +176,8 @@ impl FromStr for ByteRangeSpec { | ||||
| impl Header for Range { | ||||
|  | ||||
|     fn header_name() -> &'static str { | ||||
|         "Range" | ||||
|         static NAME: &'static str = "Range"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> { | ||||
|   | ||||
| @@ -84,7 +84,8 @@ __hyper__deref!(SetCookie => Vec<CookiePair>); | ||||
|  | ||||
| impl Header for SetCookie { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Set-Cookie" | ||||
|         static NAME: &'static str = "Set-Cookie"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<SetCookie> { | ||||
|   | ||||
| @@ -121,7 +121,8 @@ impl FromStr for StrictTransportSecurity { | ||||
|  | ||||
| impl Header for StrictTransportSecurity { | ||||
|     fn header_name() -> &'static str { | ||||
|         "Strict-Transport-Security" | ||||
|         static NAME: &'static str = "Strict-Transport-Security"; | ||||
|         NAME | ||||
|     } | ||||
|  | ||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> { | ||||
|   | ||||
| @@ -80,7 +80,6 @@ use std::borrow::{Cow, ToOwned}; | ||||
| //use std::collections::HashMap; | ||||
| //use std::collections::hash_map::{Iter, Entry}; | ||||
| use std::iter::{FromIterator, IntoIterator}; | ||||
| use std::ops::{Deref, DerefMut}; | ||||
| use std::{mem, fmt}; | ||||
|  | ||||
| use {httparse, traitobject}; | ||||
| @@ -104,7 +103,6 @@ mod internals; | ||||
| mod shared; | ||||
| pub mod parsing; | ||||
|  | ||||
| type HeaderName = UniCase<CowStr>; | ||||
|  | ||||
| /// A trait for any object that will represent a header field and value. | ||||
| /// | ||||
| @@ -179,6 +177,52 @@ impl Default for Headers { | ||||
|     } | ||||
| } | ||||
|  | ||||
| macro_rules! literals { | ||||
|     ($($len:expr => $($header:path),+;)+) => ( | ||||
|         fn maybe_literal(s: &str) -> Cow<'static, str> { | ||||
|             match s.len() { | ||||
|                 $($len => { | ||||
|                     $( | ||||
|                     if UniCase(<$header>::header_name()) == s { | ||||
|                         return Cow::Borrowed(<$header>::header_name()); | ||||
|                     } | ||||
|                     )+ | ||||
|                 })+ | ||||
|  | ||||
|                 _ => () | ||||
|             } | ||||
|  | ||||
|             Cow::Owned(s.to_owned()) | ||||
|         } | ||||
|  | ||||
|         #[test] | ||||
|         fn test_literal_lens() { | ||||
|             $( | ||||
|             $({ | ||||
|                 let s = <$header>::header_name(); | ||||
|                 assert!(s.len() == $len, "{:?} has len of {}, listed as {}", s, s.len(), $len); | ||||
|             })+ | ||||
|             )+ | ||||
|         } | ||||
|     ); | ||||
| } | ||||
|  | ||||
| literals! { | ||||
|     4  => Host, Date, ETag; | ||||
|     5  => Allow, Range; | ||||
|     6  => Accept, Cookie, Server, Expect; | ||||
|     7  => Upgrade, Referer, Expires; | ||||
|     8  => Location, IfMatch, IfRange; | ||||
|     10 => UserAgent, Connection, SetCookie; | ||||
|     12 => ContentType; | ||||
|     13 => Authorization<String>, CacheControl, LastModified, IfNoneMatch, AcceptRanges, ContentRange; | ||||
|     14 => ContentLength, AcceptCharset; | ||||
|     15 => AcceptEncoding, AcceptLanguage; | ||||
|     17 => TransferEncoding; | ||||
|     25 => StrictTransportSecurity; | ||||
|     27 => AccessControlAllowOrigin; | ||||
| } | ||||
|  | ||||
| impl Headers { | ||||
|  | ||||
|     /// Creates a new, empty headers map. | ||||
| @@ -193,7 +237,7 @@ impl Headers { | ||||
|         let mut headers = Headers::new(); | ||||
|         for header in raw { | ||||
|             trace!("raw header: {:?}={:?}", header.name, &header.value[..]); | ||||
|             let name = UniCase(CowStr(Cow::Owned(header.name.to_owned()))); | ||||
|             let name = HeaderName(UniCase(maybe_literal(header.name))); | ||||
|             let mut item = match headers.data.entry(name) { | ||||
|                 Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])), | ||||
|                 Entry::Occupied(entry) => entry.into_mut() | ||||
| @@ -210,7 +254,7 @@ impl Headers { | ||||
|     /// The field is determined by the type of the value being set. | ||||
|     pub fn set<H: Header>(&mut self, value: H) { | ||||
|         trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value)); | ||||
|         self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))), | ||||
|         self.data.insert(HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))), | ||||
|                          Item::new_typed(Box::new(value))); | ||||
|     } | ||||
|  | ||||
| @@ -227,7 +271,7 @@ impl Headers { | ||||
|     /// ``` | ||||
|     pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> { | ||||
|         self.data | ||||
|             .get(&UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) | ||||
|             .get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) | ||||
|             .map(Item::raw) | ||||
|     } | ||||
|  | ||||
| @@ -243,26 +287,26 @@ impl Headers { | ||||
|     pub fn set_raw<K: Into<Cow<'static, str>> + fmt::Debug>(&mut self, name: K, | ||||
|             value: Vec<Vec<u8>>) { | ||||
|         trace!("Headers.set_raw( {:?}, {:?} )", name, value); | ||||
|         self.data.insert(UniCase(CowStr(name.into())), Item::new_raw(value)); | ||||
|         self.data.insert(HeaderName(UniCase(name.into())), Item::new_raw(value)); | ||||
|     } | ||||
|  | ||||
|     /// Remove a header set by set_raw | ||||
|     pub fn remove_raw(&mut self, name: &str) { | ||||
|         trace!("Headers.remove_raw( {:?} )", name); | ||||
|         self.data.remove( | ||||
|             &UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) | ||||
|             &HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /// Get a reference to the header field's value, if it exists. | ||||
|     pub fn get<H: Header>(&self) -> Option<&H> { | ||||
|         self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|         self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))) | ||||
|         .and_then(Item::typed::<H>) | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the header field's value, if it exists. | ||||
|     pub fn get_mut<H: Header>(&mut self) -> Option<&mut H> { | ||||
|         self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|         self.data.get_mut(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))) | ||||
|         .and_then(Item::typed_mut::<H>) | ||||
|     } | ||||
|  | ||||
| @@ -277,14 +321,14 @@ impl Headers { | ||||
|     /// let has_type = headers.has::<ContentType>(); | ||||
|     /// ``` | ||||
|     pub fn has<H: Header>(&self) -> bool { | ||||
|         self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) | ||||
|         self.data.contains_key(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))) | ||||
|     } | ||||
|  | ||||
|     /// Removes a header from the map, if one existed. | ||||
|     /// Returns true if a header has been removed. | ||||
|     pub fn remove<H: Header>(&mut self) -> bool { | ||||
|         trace!("Headers.remove( {:?} )", header_name::<H>()); | ||||
|         self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))).is_some() | ||||
|         self.data.remove(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))).is_some() | ||||
|     } | ||||
|  | ||||
|     /// Returns an iterator over the header fields. | ||||
| @@ -396,7 +440,7 @@ impl<'a> HeaderView<'a> { | ||||
|     /// Check if a HeaderView is a certain Header. | ||||
|     #[inline] | ||||
|     pub fn is<H: Header>(&self) -> bool { | ||||
|         UniCase(CowStr(Cow::Borrowed(header_name::<H>()))) == *self.0 | ||||
|         HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))) == *self.0 | ||||
|     } | ||||
|  | ||||
|     /// Get the Header name as a slice. | ||||
| @@ -474,38 +518,30 @@ impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] | ||||
| struct CowStr(Cow<'static, str>); | ||||
| #[derive(Clone, Debug)] | ||||
| struct HeaderName(UniCase<Cow<'static, str>>); | ||||
|  | ||||
| impl Deref for CowStr { | ||||
|     type Target = Cow<'static, str>; | ||||
|  | ||||
|     fn deref(&self) -> &Cow<'static, str> { | ||||
|         &self.0 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for CowStr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Debug::fmt(&self.0, f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for CowStr { | ||||
| impl fmt::Display for HeaderName { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Display::fmt(&self.0, f) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl DerefMut for CowStr { | ||||
|     fn deref_mut(&mut self) -> &mut Cow<'static, str> { | ||||
|         &mut self.0 | ||||
| impl AsRef<str> for HeaderName { | ||||
|     fn as_ref(&self) -> &str { | ||||
|         ((self.0).0).as_ref() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsRef<str> for CowStr { | ||||
|     fn as_ref(&self) -> &str { | ||||
|         self | ||||
| impl PartialEq for HeaderName { | ||||
|     fn eq(&self, other: &HeaderName) -> bool { | ||||
|         let s = self.as_ref(); | ||||
|         let k = other.as_ref(); | ||||
|         if s.len() == k.len() && s.as_ptr() == k.as_ptr() { | ||||
|             true | ||||
|         } else { | ||||
|             self.0 == other.0 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user