use std::any::Any;
use std::borrow::Cow::{Borrowed};
use std::borrow::ToOwned;
use std::fmt;
use std::raw::TraitObject;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Entry};
use std::iter::{FromIterator, IntoIterator};
use std::borrow::{Cow, IntoCow};
use std::{mem, raw};
use httparse;
use unicase::UniCase;
use self::internals::Item;
use error::HttpResult;
pub use self::shared::{Charset, Encoding, EntityTag, Quality, QualityItem, qitem, q};
pub use self::common::*;
mod common;
mod internals;
mod shared;
pub mod parsing;
type HeaderName = UniCase<Cow<'static, str>>;
pub trait Header: Clone + Any + Send + Sync {
fn header_name() -> &'static str;
fn parse_header(raw: &[Vec<u8>]) -> Option<Self>;
}
pub trait HeaderFormat: fmt::Debug + HeaderClone + Any + Send + Sync {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result;
}
#[doc(hidden)]
pub trait HeaderClone {
fn clone_box(&self) -> Box<HeaderFormat + Sync + Send>;
}
impl<T: HeaderFormat + Send + Sync + Clone> HeaderClone for T {
#[inline]
fn clone_box(&self) -> Box<HeaderFormat + Sync + Send> {
Box::new(self.clone())
}
}
impl HeaderFormat + Send + Sync {
#[inline]
unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
mem::transmute(mem::transmute::<&HeaderFormat, raw::TraitObject>(self).data)
}
#[inline]
unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
mem::transmute(mem::transmute::<&mut HeaderFormat, raw::TraitObject>(self).data)
}
}
impl Clone for Box<HeaderFormat + Send + Sync> {
#[inline]
fn clone(&self) -> Box<HeaderFormat + Send + Sync> {
self.clone_box()
}
}
#[inline]
fn header_name<T: Header>() -> &'static str {
let name = <T as Header>::header_name();
name
}
#[derive(Clone)]
pub struct Headers {
data: HashMap<HeaderName, Item>
}
impl Headers {
pub fn new() -> Headers {
Headers {
data: HashMap::new()
}
}
#[doc(hidden)]
pub fn from_raw<'a>(raw: &[httparse::Header<'a>]) -> HttpResult<Headers> {
let mut headers = Headers::new();
for header in raw {
debug!("raw header: {:?}={:?}", header.name, &header.value[..]);
let name = UniCase(header.name.to_owned().into_cow());
let mut item = match headers.data.entry(name) {
Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])),
Entry::Occupied(entry) => entry.into_mut()
};
let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count();
let value = &header.value[.. header.value.len() - trim];
item.mut_raw().push(value.to_vec());
}
Ok(headers)
}
pub fn set<H: Header + HeaderFormat>(&mut self, value: H) {
self.data.insert(UniCase(Borrowed(header_name::<H>())),
Item::new_typed(Box::new(value)));
}
pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.data
.get(&UniCase(Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))
.map(Item::raw)
}
pub fn set_raw<K: IntoCow<'static, str>>(&mut self, name: K, value: Vec<Vec<u8>>) {
self.data.insert(UniCase(name.into_cow()), Item::new_raw(value));
}
pub fn remove_raw(&mut self, name: &str) {
self.data.remove(&UniCase(name.into_cow()));
}
pub fn get<H: Header + HeaderFormat>(&self) -> Option<&H> {
self.data.get(&UniCase(Borrowed(header_name::<H>()))).and_then(Item::typed::<H>)
}
pub fn get_mut<H: Header + HeaderFormat>(&mut self) -> Option<&mut H> {
self.data.get_mut(&UniCase(Borrowed(header_name::<H>()))).and_then(Item::typed_mut::<H>)
}
pub fn has<H: Header + HeaderFormat>(&self) -> bool {
self.data.contains_key(&UniCase(Borrowed(header_name::<H>())))
}
pub fn remove<H: Header + HeaderFormat>(&mut self) -> bool {
self.data.remove(&UniCase(Borrowed(header_name::<H>()))).is_some()
}
pub fn iter<'a>(&'a self) -> HeadersItems<'a> {
HeadersItems {
inner: self.data.iter()
}
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn clear(&mut self) {
self.data.clear()
}
}
impl fmt::Display for Headers {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for header in self.iter() {
try!(write!(fmt, "{}\r\n", header));
}
Ok(())
}
}
impl fmt::Debug for Headers {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
try!(fmt.write_str("Headers {{ "));
for header in self.iter() {
try!(write!(fmt, "{:?}, ", header));
}
try!(fmt.write_str("}}"));
Ok(())
}
}
pub struct HeadersItems<'a> {
inner: Iter<'a, HeaderName, Item>
}
impl<'a> Iterator for HeadersItems<'a> {
type Item = HeaderView<'a>;
fn next(&mut self) -> Option<HeaderView<'a>> {
match self.inner.next() {
Some((k, v)) => Some(HeaderView(k, v)),
None => None
}
}
}
pub struct HeaderView<'a>(&'a HeaderName, &'a Item);
impl<'a> HeaderView<'a> {
#[inline]
pub fn is<H: Header>(&self) -> bool {
UniCase(header_name::<H>().into_cow()) == *self.0
}
#[inline]
pub fn name(&self) -> &'a str {
self.0.as_slice()
}
#[inline]
pub fn value<H: Header + HeaderFormat>(&self) -> Option<&'a H> {
self.1.typed::<H>()
}
#[inline]
pub fn value_string(&self) -> String {
(*self.1).to_string()
}
}
impl<'a> fmt::Display for HeaderView<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.0, *self.1)
}
}
impl<'a> fmt::Debug for HeaderView<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, fmt)
}
}
impl<'a> Extend<HeaderView<'a>> for Headers {
fn extend<I: IntoIterator<Item=HeaderView<'a>>>(&mut self, iter: I) {
for header in iter {
self.data.insert((*header.0).clone(), (*header.1).clone());
}
}
}
impl<'a> FromIterator<HeaderView<'a>> for Headers {
fn from_iter<I: IntoIterator<Item=HeaderView<'a>>>(iter: I) -> Headers {
let mut headers = Headers::new();
headers.extend(iter);
headers
}
}
impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
(**self).fmt_header(fmt)
}
}
pub struct HeaderFormatter<'a, H: HeaderFormat>(pub &'a H);
impl<'a, H: HeaderFormat> fmt::Display for HeaderFormatter<'a, H> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f)
}
}
impl<'a, H: HeaderFormat> fmt::Debug for HeaderFormatter<'a, H> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f)
}
}
#[cfg(test)]
mod tests {
use std::fmt;
use mime::Mime;
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
use super::{Headers, Header, HeaderFormat, ContentLength, ContentType,
Accept, Host, qitem};
use httparse;
use test::Bencher;
macro_rules! raw {
($($line:expr),*) => ({
[$({
let line = $line;
let pos = line.position_elem(&b':').expect("raw splits on :, not found");
httparse::Header {
name: ::std::str::from_utf8(&line[..pos]).unwrap(),
value: &line[pos + 2..]
}
}),*]
})
}
#[test]
fn test_from_raw() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
assert_eq!(headers.get(), Some(&ContentLength(10)));
}
#[test]
fn test_content_type() {
let content_type = Header::parse_header([b"text/plain".to_vec()].as_slice());
assert_eq!(content_type, 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".to_vec()].as_slice());
assert_eq!(accept, Some(Accept(vec![text_plain.clone()])));
let accept = Header::parse_header([b"application/vnd.github.v3.full+json; q=0.5, text/plain".to_vec()].as_slice());
assert_eq!(accept, Some(Accept(vec![application_vendor, text_plain])));
}
#[derive(Clone, PartialEq, Debug)]
struct CrazyLength(Option<bool>, usize);
impl Header for CrazyLength {
fn header_name() -> &'static str {
"content-length"
}
fn parse_header(raw: &[Vec<u8>]) -> Option<CrazyLength> {
use std::str::from_utf8;
use std::str::FromStr;
if raw.len() != 1 {
return None;
}
match from_utf8(unsafe { &raw.get_unchecked(0)[..] }) {
Ok(s) => FromStr::from_str(s).ok(),
Err(_) => None
}.map(|u| CrazyLength(Some(false), u))
}
}
impl HeaderFormat for CrazyLength {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let CrazyLength(ref opt, ref value) = *self;
write!(fmt, "{:?}, {:?}", opt, value)
}
}
#[test]
fn test_different_structs_for_same_header() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10)));
assert_eq!(headers.get::<CrazyLength>(), Some(&CrazyLength(Some(false), 10)));
}
#[test]
fn test_trailing_whitespace() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10 ")).unwrap();
assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(10)));
}
#[test]
fn test_multiple_reads() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
let ContentLength(one) = *headers.get::<ContentLength>().unwrap();
let ContentLength(two) = *headers.get::<ContentLength>().unwrap();
assert_eq!(one, two);
}
#[test]
fn test_different_reads() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10", b"Content-Type: text/plain")).unwrap();
let ContentLength(_) = *headers.get::<ContentLength>().unwrap();
let ContentType(_) = *headers.get::<ContentType>().unwrap();
}
#[test]
fn test_get_mutable() {
let mut headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
*headers.get_mut::<ContentLength>().unwrap() = ContentLength(20);
assert_eq!(*headers.get::<ContentLength>().unwrap(), ContentLength(20));
}
#[test]
fn test_headers_show() {
let mut headers = Headers::new();
headers.set(ContentLength(15));
headers.set(Host { hostname: "foo.bar".to_string(), port: None });
let s = headers.to_string();
let mut pieces = s.split("\r\n").collect::<Vec<&str>>();
pieces.sort();
let s = pieces.into_iter().rev().collect::<Vec<&str>>().connect("\r\n");
assert_eq!(s, "Host: foo.bar\r\nContent-Length: 15\r\n");
}
#[test]
fn test_headers_show_raw() {
let headers = Headers::from_raw(&raw!(b"Content-Length: 10")).unwrap();
let s = headers.to_string();
assert_eq!(s, "Content-Length: 10\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_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_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);
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::<ContentLength>());
assert_eq!(header.name(), <ContentLength as Header>::header_name());
assert_eq!(header.value(), Some(&ContentLength(11)));
assert_eq!(header.value_string(), "11".to_string());
}
}
#[bench]
fn bench_headers_new(b: &mut Bencher) {
b.iter(|| {
let mut h = Headers::new();
h.set(ContentLength(11));
h
})
}
#[bench]
fn bench_headers_from_raw(b: &mut Bencher) {
let raw = raw!(b"Content-Length: 10");
b.iter(|| Headers::from_raw(&raw).unwrap())
}
#[bench]
fn bench_headers_get(b: &mut Bencher) {
let mut headers = Headers::new();
headers.set(ContentLength(11));
b.iter(|| assert_eq!(headers.get::<ContentLength>(), Some(&ContentLength(11))))
}
#[bench]
fn bench_headers_get_miss(b: &mut Bencher) {
let headers = Headers::new();
b.iter(|| assert!(headers.get::<ContentLength>().is_none()))
}
#[bench]
fn bench_headers_set(b: &mut Bencher) {
let mut headers = Headers::new();
b.iter(|| headers.set(ContentLength(12)))
}
#[bench]
fn bench_headers_has(b: &mut Bencher) {
let mut headers = Headers::new();
headers.set(ContentLength(11));
b.iter(|| assert!(headers.has::<ContentLength>()))
}
#[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::<ContentLength>()))
}
#[bench]
fn bench_headers_fmt(b: &mut Bencher) {
let mut headers = Headers::new();
headers.set(ContentLength(11));
b.iter(|| headers.to_string())
}
}