//! 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. use std::ascii::OwnedAsciiExt; use std::char::is_lowercase; use std::fmt::{mod, Show}; use std::mem::{transmute, transmute_copy}; use std::raw::TraitObject; use std::str::{from_utf8, SendStr, Slice, Owned}; use std::string::raw; use std::collections::hashmap::{HashMap, Entries}; use uany::UncheckedAnyDowncast; use http::read_header; use {HttpResult}; /// Common Headers pub mod common; /// A trait for any object that will represent a header field and value. pub trait Header: 'static { /// Returns the name of the header field this belongs to. /// /// The market `Option` is to hint to the type system which implementation /// to call. This can be done away with once UFCS arrives. fn header_name(marker: Option) -> &'static str; /// 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: &[Vec]) -> Option; /// Format a header to be output into a TcpStream. fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result; } impl<'a> UncheckedAnyDowncast<'a> for &'a Header + 'a { #[inline] unsafe fn downcast_ref_unchecked(self) -> &'a T { let to: TraitObject = transmute_copy(&self); transmute(to.data) } } fn header_name() -> &'static str { let name = Header::header_name(None::); debug_assert!(name.as_slice().chars().all(|c| c == '-' || is_lowercase(c)), "Header names should be lowercase: {}", name); name } /// A map of header fields on requests and responses. pub struct Headers { data: HashMap } impl Headers { /// Creates a new, empty headers map. pub fn new() -> Headers { Headers { data: HashMap::new() } } #[doc(hidden)] pub fn from_raw(rdr: &mut R) -> HttpResult { let mut headers = Headers::new(); loop { match try!(read_header(rdr)) { Some((name, value)) => { // read_header already checks that name is a token, which // means its safe utf8 let name = unsafe { raw::from_utf8(name) }.into_ascii_lower(); match headers.data.find_or_insert(Owned(name), Raw(vec![])) { &Raw(ref mut pieces) => pieces.push(value), // at this point, Raw is the only thing that has been inserted _ => unreachable!() } }, None => break, } } Ok(headers) } /// 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) { self.data.insert(Slice(header_name::()), Typed(box value)); } /// Get a clone of the header field's value, if it exists. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # use hyper::header::common::ContentType; /// # let mut headers = Headers::new(); /// let content_type = headers.get::(); /// ``` pub fn get(&mut self) -> Option { self.get_ref().map(|v: &H| v.clone()) } /// Access the raw value of a header, if it exists and has not /// been already parsed. /// /// If the header field has already been parsed into a typed header, /// then you *must* access it through that representation. /// /// Example: /// ``` /// # use hyper::header::Headers; /// # let mut headers = Headers::new(); /// let raw_content_type = unsafe { headers.get_raw("content-type") }; /// ``` pub unsafe fn get_raw(&self, name: &'static str) -> Option<&[Vec]> { self.data.find(&Slice(name)).and_then(|item| { match *item { Raw(ref raw) => Some(raw.as_slice()), _ => None } }) } /// Get a reference to the header field's value, if it exists. pub fn get_ref(&mut self) -> Option<&H> { self.data.find_mut(&Slice(header_name::())).and_then(|item| { debug!("get_ref, name={}, val={}", header_name::(), item); let header = match *item { Raw(ref raw) => match Header::parse_header(raw.as_slice()) { Some::(h) => { h }, None => return None }, Typed(..) => return Some(item) }; *item = Typed(box header as Box
); Some(item) }).and_then(|item| { debug!("downcasting {}", item); let ret = match *item { Typed(ref val) => { unsafe { Some(val.downcast_ref_unchecked()) } }, Raw(..) => unreachable!() }; debug!("returning {}", ret.is_some()); ret }) } /// Returns a boolean of whether a certain header is in the map. /// /// Example: /// /// ``` /// # use hyper::header::Headers; /// # use hyper::header::common::ContentType; /// # let mut headers = Headers::new(); /// let has_type = headers.has::(); /// ``` pub fn has(&self) -> bool { self.data.contains_key(&Slice(header_name::())) } /// Removes a header from the map, if one existed. /// Returns true if a header has been removed. pub fn remove(&mut self) -> bool { self.data.pop_equiv(&Header::header_name(None::)).is_some() } /// Returns an iterator over the header fields. pub fn iter<'a>(&'a self) -> HeadersItems<'a> { HeadersItems { inner: self.data.iter() } } } impl fmt::Show for Headers { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { try!("Headers {\n".fmt(fmt)); for (k, v) in self.iter() { try!(write!(fmt, "\t{}: {}\n", k, v)); } "}".fmt(fmt) } } /// An `Iterator` over the fields in a `Headers` map. pub struct HeadersItems<'a> { inner: Entries<'a, SendStr, Item> } impl<'a> Iterator<(&'a str, HeaderView<'a>)> for HeadersItems<'a> { fn next(&mut self) -> Option<(&'a str, HeaderView<'a>)> { match self.inner.next() { Some((k, v)) => Some((k.as_slice(), HeaderView(v))), None => None } } } /// Returned with the `HeadersItems` iterator. pub struct HeaderView<'a>(&'a Item); impl<'a> fmt::Show for HeaderView<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let HeaderView(item) = *self; item.fmt(fmt) } } impl Collection for Headers { fn len(&self) -> uint { self.data.len() } } impl Mutable for Headers { fn clear(&mut self) { self.data.clear() } } enum Item { Raw(Vec>), Typed(Box
) } impl fmt::Show for Item { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Raw(ref v) => { for part in v.iter() { try!(fmt.write(part.as_slice())); } Ok(()) }, Typed(ref h) => h.fmt_header(fmt) } } } #[cfg(test)] mod tests { use std::io::MemReader; use mime::{Mime, Text, Plain}; use super::{Headers, Header}; use super::common::{ContentLength, ContentType}; fn mem(s: &str) -> MemReader { MemReader::new(s.as_bytes().to_vec()) } #[test] fn test_from_raw() { let mut headers = Headers::from_raw(&mut mem("Content-Length: 10\r\n\r\n")).unwrap(); assert_eq!(headers.get_ref(), Some(&ContentLength(10))); } #[test] fn test_content_type() { let content_type = Header::parse_header(["text/plain".as_bytes().to_vec()].as_slice()); assert_eq!(content_type, Some(ContentType(Mime(Text, Plain, vec![])))); } }