//! Headers container, and common header fields. //! //! hyper has the opinion that Headers should be strongly-typed, because that's //! why we're using Rust in the first place. To set or get any header, an object //! must implement the `Header` trait from this module. Several common headers //! are already provided, such as `Host`, `ContentType`, `UserAgent`, and others. //! //! # Why Typed? //! //! Or, why not stringly-typed? Types give the following advantages: //! //! - More difficult to typo, since typos in types should be caught by the compiler //! - Parsing to a proper type by default //! //! # Defining Custom Headers //! //! Hyper provides many of the most commonly used headers in HTTP. If //! you need to define a custom header, it's easy to do while still taking //! advantage of the type system. Hyper includes a `header!` macro for defining //! many wrapper-style headers. //! //! ``` //! #[macro_use] extern crate hyper; //! use hyper::header::Headers; //! header! { (XRequestGuid, "X-Request-Guid") => [String] } //! //! fn main () { //! let mut headers = Headers::new(); //! //! headers.set(XRequestGuid("a proper guid".to_owned())) //! } //! ``` //! //! This works well for simple "string" headers. If you need more control, //! you can implement the trait directly. //! //! ## Implementing the `Header` trait //! //! Consider a Do Not Track header. It can be true or false, but it represents //! that via the numerals `1` and `0`. //! //! ``` //! use std::fmt; //! use hyper::header::{self, Header, Raw}; //! //! #[derive(Debug, Clone, Copy)] //! struct Dnt(bool); //! //! impl Header for Dnt { //! fn header_name() -> &'static str { //! "DNT" //! } //! //! fn parse_header(raw: &Raw) -> hyper::Result { //! if raw.len() == 1 { //! let line = &raw[0]; //! if line.len() == 1 { //! let byte = line[0]; //! match byte { //! b'0' => return Ok(Dnt(true)), //! b'1' => return Ok(Dnt(false)), //! _ => () //! } //! } //! } //! Err(hyper::Error::Header) //! } //! //! fn fmt_header(&self, f: &mut header::Formatter) -> fmt::Result { //! let value = if self.0 { //! "1" //! } else { //! "0" //! }; //! f.fmt_line(&value) //! } //! } //! ``` use std::borrow::{Cow, ToOwned}; use std::iter::{FromIterator, IntoIterator}; use std::{mem, fmt}; use unicase::UniCase; use self::internals::{Item, VecMap, Entry}; use self::sealed::{GetType, HeaderClone}; pub use self::shared::*; pub use self::common::*; pub use self::raw::Raw; use bytes::Bytes; mod common; mod internals; mod raw; mod shared; pub mod parsing; /// A trait for any object that will represent a header field and value. /// /// This trait represents the construction and identification of headers, /// and contains trait-object unsafe methods. pub trait Header: HeaderClone + GetType + Send + Sync { /// Returns the name of the header field this belongs to. /// /// This will become an associated constant once available. fn header_name() -> &'static str where Self: Sized; /// Parse a header from a raw stream of bytes. /// /// It's possible that a request can include a header field more than once, /// and in that case, the slice will have a length greater than 1. However, /// it's not necessarily the case that a Header is *allowed* to have more /// than one field value. If that's the case, you **should** return `None` /// if `raw.len() > 1`. fn parse_header(raw: &Raw) -> ::Result where Self: Sized; /// Format a header to outgoing stream. /// /// Most headers should be formatted on one line, and so a common pattern /// would be to implement `std::fmt::Display` for this type as well, and /// then just call `f.fmt_line(self)`. /// /// ## Note /// /// This has the ability to format a header over multiple lines. /// /// The main example here is `Set-Cookie`, which requires that every /// cookie being set be specified in a separate line. Almost every other /// case should only format as 1 single line. #[inline] fn fmt_header(&self, f: &mut Formatter) -> fmt::Result; } mod sealed { use std::any::{Any, TypeId}; use super::Header; #[doc(hidden)] pub trait GetType: Any { #[inline(always)] fn get_type(&self) -> TypeId { TypeId::of::() } } impl GetType for T {} #[doc(hidden)] pub trait HeaderClone { fn clone_box(&self) -> Box
; } impl HeaderClone for T { #[inline] fn clone_box(&self) -> Box
{ Box::new(self.clone()) } } #[test] fn test_get_type() { use ::header::{ContentLength, UserAgent}; let len = ContentLength(5); let agent = UserAgent::new("hyper"); assert_eq!(TypeId::of::(), len.get_type()); assert_eq!(TypeId::of::(), agent.get_type()); let len: Box
= Box::new(len); let agent: Box
= Box::new(agent); assert_eq!(TypeId::of::(), (*len).get_type()); assert_eq!(TypeId::of::(), (*agent).get_type()); } } /// A formatter used to serialize headers to an output stream. #[allow(missing_debug_implementations)] pub struct Formatter<'a, 'b: 'a>(Multi<'a, 'b>); enum Multi<'a, 'b: 'a> { Line(&'a str, &'a mut fmt::Formatter<'b>), Join(bool, &'a mut fmt::Formatter<'b>), Raw(&'a mut Raw), } impl<'a, 'b> Formatter<'a, 'b> { /// Format one 'line' of a header. /// /// This writes the header name plus the `Display` value as a single line. /// /// ## Note /// /// This has the ability to format a header over multiple lines. /// /// The main example here is `Set-Cookie`, which requires that every /// cookie being set be specified in a separate line. Almost every other /// case should only format as 1 single line. pub fn fmt_line(&mut self, line: &fmt::Display) -> fmt::Result { use std::fmt::Write; match self.0 { Multi::Line(ref name, ref mut f) => { try!(f.write_str(*name)); try!(f.write_str(": ")); try!(write!(NewlineReplacer(*f), "{}", line)); f.write_str("\r\n") }, Multi::Join(ref mut first, ref mut f) => { if !*first { try!(f.write_str(", ")); } else { *first = false; } write!(NewlineReplacer(*f), "{}", line) } Multi::Raw(ref mut raw) => { let mut s = String::new(); try!(write!(NewlineReplacer(&mut s), "{}", line)); raw.push(s); Ok(()) } } } } struct ValueString<'a>(&'a Item); impl<'a> fmt::Debug for ValueString<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(f.write_str("\"")); try!(self.0.write_h1(&mut Formatter(Multi::Join(true, f)))); f.write_str("\"") } } impl<'a> fmt::Display for ValueString<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.write_h1(&mut Formatter(Multi::Join(true, f))) } } struct HeaderValueString<'a, H: Header + 'a>(&'a H); impl<'a, H: Header> fmt::Debug for HeaderValueString<'a, H> { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { try!(f.write_str("\"")); try!(self.0.fmt_header(&mut Formatter(Multi::Join(true, f)))); f.write_str("\"") } } struct NewlineReplacer<'a, F: fmt::Write + 'a>(&'a mut F); impl<'a, F: fmt::Write + 'a> fmt::Write for NewlineReplacer<'a, F> { fn write_str(&mut self, s: &str) -> fmt::Result { let mut since = 0; for (i, &byte) in s.as_bytes().iter().enumerate() { if byte == b'\r' || byte == b'\n' { try!(self.0.write_str(&s[since..i])); try!(self.0.write_str(" ")); since = i + 1; } } if since < s.len() { self.0.write_str(&s[since..]) } else { Ok(()) } } } impl Header + Send + Sync { // A trait object looks like this: // // TraitObject { data: *mut (), vtable: *mut () } // // So, we transmute &Trait into a (*mut (), *mut ()). This depends on the // order the compiler has chosen to represent a TraitObject. // // It has been assured that this order will be stable. #[inline] unsafe fn downcast_ref_unchecked(&self) -> &T { &*(mem::transmute::<*const _, (*const (), *const ())>(self).0 as *const T) } #[inline] unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { &mut *(mem::transmute::<*mut _, (*mut (), *mut ())>(self).0 as *mut T) } #[inline] unsafe fn downcast_unchecked(self: Box) -> T { *Box::from_raw(mem::transmute::<*mut _, (*mut (), *mut ())>(Box::into_raw(self)).0 as *mut T) } } impl Clone for Box
{ #[inline] fn clone(&self) -> Box
{ self.clone_box() } } #[inline] fn header_name() -> &'static str { ::header_name() } /// A map of header fields on requests and responses. #[derive(Clone)] pub struct Headers { data: VecMap, } impl Default for Headers { fn default() -> Headers { Headers::new() } } 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, 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. #[inline] pub fn new() -> Headers { Headers::with_capacity(0) } /// Creates a new `Headers` struct with space reserved for `len` headers. #[inline] pub fn with_capacity(len: usize) -> Headers { Headers { data: VecMap::with_capacity(len) } } /// Set a header field to the corresponding value. /// /// The field is determined by the type of the value being set. pub fn set(&mut self, value: H) { trace!("Headers.set( {:?}, {:?} )", header_name::(), HeaderValueString(&value)); self.data.insert(HeaderName(UniCase(Cow::Borrowed(header_name::()))), Item::new_typed(Box::new(value))); } /// Get a reference to the header field's value, if it exists. pub fn get(&self) -> Option<&H> { self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) .and_then(Item::typed::) } /// Get a mutable reference to the header field's value, if it exists. pub fn get_mut(&mut self) -> Option<&mut H> { self.data.get_mut(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) .and_then(Item::typed_mut::) } /// Returns a boolean of whether a certain header is in the map. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # use hyper::header::ContentType; /// # let mut headers = Headers::new(); /// headers.set(ContentType::json()); /// assert!(headers.has::()); /// ``` pub fn has(&self) -> bool { self.data.contains_key(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) } /// Removes a header from the map, if one existed. /// Returns the header, if one has been removed and could be parsed. /// /// Note that this function may return `None` even though a header was removed. If you want to /// know whether a header exists, rather rely on `has`. pub fn remove(&mut self) -> Option { trace!("Headers.remove( {:?} )", header_name::()); self.data.remove(&HeaderName(UniCase(Cow::Borrowed(header_name::())))) .and_then(Item::into_typed::) } /// Returns an iterator over the header fields. pub fn iter(&self) -> HeadersItems { HeadersItems { inner: self.data.iter() } } /// Returns the number of headers in the map. pub fn len(&self) -> usize { self.data.len() } /// Remove all headers from the map. pub fn clear(&mut self) { self.data.clear() } /// Access the raw value of a header. /// /// Prefer to use the typed getters instead. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # let mut headers = Headers::new(); /// # headers.set_raw("content-type", "text/plain"); /// let raw = headers.get_raw("content-type").unwrap(); /// assert_eq!(raw, "text/plain"); /// ``` pub fn get_raw(&self, name: &str) -> Option<&Raw> { self.data .get(name) .map(Item::raw) } /// Set the raw value of a header, bypassing any typed headers. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # let mut headers = Headers::new(); /// headers.set_raw("content-length", b"1".as_ref()); /// headers.set_raw("content-length", "2"); /// headers.set_raw("content-length", "3".to_string()); /// headers.set_raw("content-length", vec![vec![b'4']]); /// ``` pub fn set_raw>, V: Into>(&mut self, name: K, value: V) { let name = name.into(); let value = value.into(); trace!("Headers.set_raw( {:?}, {:?} )", name, value); self.data.insert(HeaderName(UniCase(name)), Item::new_raw(value)); } /// Append a value to raw value of this header. /// /// If a header already contains a value, this will add another line to it. /// /// If a header does not exist for this name, a new one will be created with /// the value. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # let mut headers = Headers::new(); /// headers.append_raw("x-foo", b"bar".to_vec()); /// headers.append_raw("x-foo", b"quux".to_vec()); /// ``` pub fn append_raw>, V: Into>(&mut self, name: K, value: V) { let name = name.into(); let value = value.into(); trace!("Headers.append_raw( {:?}, {:?} )", name, value); let name = HeaderName(UniCase(name)); if let Some(item) = self.data.get_mut(&name) { item.raw_mut().push(value); return; } self.data.insert(name, Item::new_raw(value)); } /// Remove a header by name. pub fn remove_raw(&mut self, name: &str) { trace!("Headers.remove_raw( {:?} )", name); self.data.remove(name); } } impl PartialEq for Headers { fn eq(&self, other: &Headers) -> bool { if self.len() != other.len() { return false; } for header in self.iter() { match other.get_raw(header.name()) { Some(val) if val == self.get_raw(header.name()).unwrap() => {}, _ => { return false; } } } true } } impl fmt::Display for Headers { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for header in self.iter() { try!(fmt::Display::fmt(&header, f)); } Ok(()) } } impl fmt::Debug for Headers { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_map() .entries(self.iter().map(|view| (view.0.as_ref(), ValueString(view.1)))) .finish() } } /// An `Iterator` over the fields in a `Headers` map. #[allow(missing_debug_implementations)] pub struct HeadersItems<'a> { inner: ::std::slice::Iter<'a, (HeaderName, Item)> } impl<'a> Iterator for HeadersItems<'a> { type Item = HeaderView<'a>; fn next(&mut self) -> Option> { self.inner.next().map(|&(ref k, ref v)| HeaderView(k, v)) } } /// Returned with the `HeadersItems` iterator. pub struct HeaderView<'a>(&'a HeaderName, &'a Item); impl<'a> HeaderView<'a> { /// Check if a HeaderView is a certain Header. #[inline] pub fn is(&self) -> bool { HeaderName(UniCase(Cow::Borrowed(header_name::()))) == *self.0 } /// Get the Header name as a slice. #[inline] pub fn name(&self) -> &'a str { self.0.as_ref() } /// Cast the value to a certain Header type. #[inline] pub fn value(&self) -> Option<&'a H> { self.1.typed::() } /// Get just the header value as a String. /// /// This will join multiple values of this header with a `, `. /// /// **Warning:** This may not be the format that should be used to send /// a Request or Response. #[inline] pub fn value_string(&self) -> String { ValueString(self.1).to_string() } /// Access the raw value of the header. #[inline] pub fn raw(&self) -> &Raw { self.1.raw() } } impl<'a> fmt::Display for HeaderView<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.1.write_h1(&mut Formatter(Multi::Line(self.0.as_ref(), f))) } } impl<'a> fmt::Debug for HeaderView<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } impl<'a> Extend> for Headers { fn extend>>(&mut self, iter: I) { for header in iter { self.data.insert((*header.0).clone(), (*header.1).clone()); } } } impl<'a> Extend<(&'a str, Bytes)> for Headers { fn extend>(&mut self, iter: I) { for (name, value) in iter { let name = HeaderName(UniCase(maybe_literal(name))); //let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count(); match self.data.entry(name) { Entry::Vacant(entry) => { entry.insert(Item::new_raw(self::raw::parsed(value))); } Entry::Occupied(entry) => { self::raw::push(entry.into_mut().raw_mut(), value); } }; } } } impl<'a> FromIterator> for Headers { fn from_iter>>(iter: I) -> Headers { let mut headers = Headers::new(); headers.extend(iter); headers } } #[derive(Clone, Debug)] struct HeaderName(UniCase>); impl fmt::Display for HeaderName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl AsRef for HeaderName { fn as_ref(&self) -> &str { ((self.0).0).as_ref() } } 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 } } } impl PartialEq for str { fn eq(&self, other: &HeaderName) -> bool { let k = other.as_ref(); if self.len() == k.len() && self.as_ptr() == k.as_ptr() { true } else { other.0 == self } } } #[cfg(test)] mod tests { use std::fmt; use mime::Mime; use mime::TopLevel::Text; use mime::SubLevel::Plain; use super::{Headers, Header, Raw, ContentLength, ContentType, Accept, Host, qitem, SetCookie}; #[cfg(feature = "nightly")] use test::Bencher; macro_rules! make_header { ($name:expr, $value:expr) => ({ let mut headers = Headers::new(); headers.set_raw(String::from_utf8($name.to_vec()).unwrap(), $value.to_vec()); headers }); ($text:expr) => ({ let bytes = $text; let colon = bytes.iter().position(|&x| x == b':').unwrap(); make_header!(&bytes[..colon], &bytes[colon + 2..]) }) } #[test] fn test_from_raw() { let headers = make_header!(b"Content-Length", b"10"); assert_eq!(headers.get(), Some(&ContentLength(10))); } #[test] fn test_content_type() { let content_type = Header::parse_header(&b"text/plain".as_ref().into()); assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![])))); } #[test] fn test_accept() { let text_plain = qitem(Mime(Text, Plain, vec![])); let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap(); let accept = Header::parse_header(&b"text/plain".as_ref().into()); assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()]))); let bytevec = b"application/vnd.github.v3.full+json; q=0.5, text/plain".as_ref().into(); let accept = Header::parse_header(&bytevec); assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain]))); } #[derive(Clone, PartialEq, Debug)] struct CrazyLength(Option, usize); impl Header for CrazyLength { fn header_name() -> &'static str { "content-length" } fn parse_header(raw: &Raw) -> ::Result { use std::str::from_utf8; use std::str::FromStr; if let Some(line) = raw.one() { let s = try!(from_utf8(line).map(|s| FromStr::from_str(s).map_err(|_| ::Error::Header))); s.map(|u| CrazyLength(Some(false), u)) } else { Err(::Error::Header) } } fn fmt_header(&self, f: &mut super::Formatter) -> fmt::Result { f.fmt_line(self) } } impl fmt::Display for CrazyLength { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let CrazyLength(ref opt, ref value) = *self; write!(f, "{:?}, {:?}", opt, value) } } #[test] fn test_different_structs_for_same_header() { let headers = make_header!(b"Content-Length: 10"); assert_eq!(headers.get::(), Some(&ContentLength(10))); assert_eq!(headers.get::(), Some(&CrazyLength(Some(false), 10))); } #[test] fn test_trailing_whitespace() { let headers = make_header!(b"Content-Length: 10 "); assert_eq!(headers.get::(), Some(&ContentLength(10))); } #[test] fn test_multiple_reads() { let headers = make_header!(b"Content-Length: 10"); let ContentLength(one) = *headers.get::().unwrap(); let ContentLength(two) = *headers.get::().unwrap(); assert_eq!(one, two); } #[test] fn test_different_reads() { let mut headers = Headers::new(); headers.set_raw("Content-Length", "10"); headers.set_raw("Content-Type", "text/plain"); let ContentLength(_) = *headers.get::().unwrap(); let ContentType(_) = *headers.get::().unwrap(); } #[test] fn test_typed_get_raw() { let mut headers = Headers::new(); headers.set(ContentLength(15)); assert_eq!(headers.get_raw("content-length").unwrap(), "15"); headers.set(SetCookie(vec![ "foo=bar".to_string(), "baz=quux; Path=/path".to_string() ])); assert_eq!(headers.get_raw("set-cookie").unwrap(), &["foo=bar", "baz=quux; Path=/path"][..]); } #[test] fn test_get_mutable() { let mut headers = make_header!(b"Content-Length: 10"); *headers.get_mut::().unwrap() = ContentLength(20); assert_eq!(headers.get_raw("content-length").unwrap(), &[b"20".to_vec()][..]); assert_eq!(*headers.get::().unwrap(), ContentLength(20)); } #[test] fn test_headers_to_string() { let mut headers = Headers::new(); headers.set(ContentLength(15)); headers.set(Host::new("foo.bar", None)); let s = headers.to_string(); assert!(s.contains("Host: foo.bar\r\n")); assert!(s.contains("Content-Length: 15\r\n")); } #[test] fn test_headers_to_string_raw() { let mut headers = make_header!(b"Content-Length: 10"); headers.set_raw("x-foo", vec![b"foo".to_vec(), b"bar".to_vec()]); let s = headers.to_string(); assert_eq!(s, "Content-Length: 10\r\nx-foo: foo\r\nx-foo: bar\r\n"); } #[test] fn test_set_raw() { let mut headers = Headers::new(); headers.set(ContentLength(10)); headers.set_raw("content-LENGTH", vec![b"20".to_vec()]); assert_eq!(headers.get_raw("Content-length").unwrap(), &[b"20".to_vec()][..]); assert_eq!(headers.get(), Some(&ContentLength(20))); } #[test] fn test_append_raw() { let mut headers = Headers::new(); headers.set(ContentLength(10)); headers.append_raw("content-LENGTH", b"20".to_vec()); assert_eq!(headers.get_raw("Content-length").unwrap(), &[b"10".to_vec(), b"20".to_vec()][..]); headers.append_raw("x-foo", "bar"); assert_eq!(headers.get_raw("x-foo").unwrap(), &[b"bar".to_vec()][..]); } #[test] fn test_remove_raw() { let mut headers = Headers::new(); headers.set_raw("content-LENGTH", vec![b"20".to_vec()]); headers.remove_raw("content-LENGTH"); assert_eq!(headers.get_raw("Content-length"), None); } #[test] fn test_remove() { let mut headers = Headers::new(); headers.set(ContentLength(10)); assert_eq!(headers.remove(), Some(ContentLength(10))); } #[test] fn test_len() { let mut headers = Headers::new(); headers.set(ContentLength(10)); assert_eq!(headers.len(), 1); headers.set(ContentType(Mime(Text, Plain, vec![]))); assert_eq!(headers.len(), 2); // Redundant, should not increase count. headers.set(ContentLength(20)); assert_eq!(headers.len(), 2); } #[test] fn test_clear() { let mut headers = Headers::new(); headers.set(ContentLength(10)); headers.set(ContentType(Mime(Text, Plain, vec![]))); assert_eq!(headers.len(), 2); headers.clear(); assert_eq!(headers.len(), 0); } #[test] fn test_iter() { let mut headers = Headers::new(); headers.set(ContentLength(11)); for header in headers.iter() { assert!(header.is::()); assert_eq!(header.name(), ::header_name()); assert_eq!(header.value(), Some(&ContentLength(11))); assert_eq!(header.value_string(), "11".to_owned()); } } #[test] fn test_header_view_value_string() { let mut headers = Headers::new(); headers.set_raw("foo", vec![b"one".to_vec(), b"two".to_vec()]); for header in headers.iter() { assert_eq!(header.name(), "foo"); assert_eq!(header.value_string(), "one, two"); } } #[test] fn test_header_view_raw() { let mut headers = Headers::new(); headers.set_raw("foo", vec![b"one".to_vec(), b"two".to_vec()]); for header in headers.iter() { assert_eq!(header.name(), "foo"); let values: Vec<&[u8]> = header.raw().iter().collect(); assert_eq!(values, vec![b"one", b"two"]); } } #[test] fn test_eq() { let mut headers1 = Headers::new(); let mut headers2 = Headers::new(); assert_eq!(headers1, headers2); headers1.set(ContentLength(11)); headers2.set(Host::new("foo.bar", None)); assert!(headers1 != headers2); headers1 = Headers::new(); headers2 = Headers::new(); headers1.set(ContentLength(11)); headers2.set(ContentLength(11)); assert_eq!(headers1, headers2); headers1.set(ContentLength(10)); assert!(headers1 != headers2); headers1 = Headers::new(); headers2 = Headers::new(); headers1.set(Host::new("foo.bar", None)); headers1.set(ContentLength(11)); headers2.set(ContentLength(11)); assert!(headers1 != headers2); } #[cfg(feature = "nightly")] #[bench] fn bench_headers_new(b: &mut Bencher) { b.iter(|| { let mut h = Headers::new(); h.set(ContentLength(11)); h }) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_get(b: &mut Bencher) { let mut headers = Headers::new(); headers.set(ContentLength(11)); b.iter(|| assert_eq!(headers.get::(), Some(&ContentLength(11)))) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_get_miss(b: &mut Bencher) { let headers = Headers::new(); b.iter(|| assert!(headers.get::().is_none())) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_set(b: &mut Bencher) { let mut headers = Headers::new(); b.iter(|| headers.set(ContentLength(12))) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_has(b: &mut Bencher) { let mut headers = Headers::new(); headers.set(ContentLength(11)); b.iter(|| assert!(headers.has::())) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_view_is(b: &mut Bencher) { let mut headers = Headers::new(); headers.set(ContentLength(11)); let mut iter = headers.iter(); let view = iter.next().unwrap(); b.iter(|| assert!(view.is::())) } #[cfg(feature = "nightly")] #[bench] fn bench_headers_fmt(b: &mut Bencher) { let mut headers = Headers::new(); headers.set(ContentLength(11)); b.iter(|| headers.to_string()) } }