diff --git a/src/header/common/preference_applied.rs b/src/header/common/preference_applied.rs index 328a7d7b78..7c0196feb8 100644 --- a/src/header/common/preference_applied.rs +++ b/src/header/common/preference_applied.rs @@ -86,17 +86,17 @@ impl fmt::Display for PreferenceApplied { #[cfg(test)] mod tests { - use header::{Header, Preference}; + use header::Preference; use super::*; #[test] fn test_format_ignore_parameters() { assert_eq!( - format!("{}", &PreferenceApplied(vec![Preference::Extension( + format!("{}", PreferenceApplied(vec![Preference::Extension( "foo".to_owned(), "bar".to_owned(), vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())] - )]) as &(Header + Send + Sync)), + )])), "foo=bar".to_owned() ); } diff --git a/src/header/internals/item.rs b/src/header/internals/item.rs index a28f162b01..74935bfdc2 100644 --- a/src/header/internals/item.rs +++ b/src/header/internals/item.rs @@ -4,7 +4,7 @@ use std::fmt; use std::str::from_utf8; use super::cell::{OptCell, PtrMapCell}; -use header::{Header, MultilineFormatter, Raw}; +use header::{Header, MultilineFormatter, Multi, raw, Raw}; #[derive(Clone)] @@ -46,7 +46,8 @@ impl Item { return raw; } - let raw = unsafe { self.typed.one() }.to_string().into_bytes().into(); + let mut raw = raw::new(); + self.write_h1(&mut MultilineFormatter(Multi::Raw(&mut raw))).expect("fmt failed"); self.raw.set(raw); self.raw.as_ref().unwrap() diff --git a/src/header/mod.rs b/src/header/mod.rs index af5c1ba76a..891e27527f 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -167,6 +167,7 @@ pub struct MultilineFormatter<'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> MultilineFormatter<'a, 'b> { @@ -187,6 +188,12 @@ impl<'a, 'b> MultilineFormatter<'a, 'b> { } write!(NewlineReplacer(*f), "{}", line) } + Multi::Raw(ref mut raw) => { + let mut s = String::new(); + try!(write!(NewlineReplacer(&mut s), "{}", line)); + raw.push(s); + Ok(()) + } } } } @@ -227,9 +234,9 @@ impl<'a, H: Header> fmt::Debug for HeaderValueString<'a, H> { } } -struct NewlineReplacer<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>); +struct NewlineReplacer<'a, F: fmt::Write + 'a>(&'a mut F); -impl<'a, 'b> fmt::Write for NewlineReplacer<'a, 'b> { +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() { @@ -643,45 +650,6 @@ impl<'a> FromIterator> for Headers { } } -deprecated! { - #[deprecated(note="The semantics of formatting a HeaderFormat directly are not clear")] - impl<'a> fmt::Display for &'a (Header + Send + Sync) { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut multi = MultilineFormatter(Multi::Join(true, f)); - self.fmt_multi_header(&mut multi) - } - } -} - -deprecated! { - #[deprecated(note="The semantics of formatting a HeaderFormat directly are not clear")] - /// A wrapper around any Header with a Display impl that calls `fmt_header`. - /// - /// This can be used like so: `format!("{}", HeaderFormatter(&header))` to - /// get the 'value string' representation of this Header. - /// - /// Note: This may not necessarily be the value written to stream, such - /// as with the SetCookie header. - pub struct HeaderFormatter<'a, H: Header>(pub &'a H); -} - -#[allow(deprecated)] -impl<'a, H: Header> fmt::Display for HeaderFormatter<'a, H> { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&HeaderValueString(self.0), f) - } -} - -#[allow(deprecated)] -impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - #[derive(Clone, Debug)] struct HeaderName(UniCase>); @@ -717,7 +685,7 @@ mod tests { use mime::TopLevel::Text; use mime::SubLevel::Plain; use super::{Headers, Header, Raw, ContentLength, ContentType, - Accept, Host, qitem}; + Accept, Host, qitem, SetCookie}; #[cfg(feature = "nightly")] use test::Bencher; @@ -814,6 +782,19 @@ mod tests { 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"); diff --git a/src/header/raw.rs b/src/header/raw.rs index 2b03701c0b..9401dd2b14 100644 --- a/src/header/raw.rs +++ b/src/header/raw.rs @@ -3,7 +3,7 @@ use std::fmt; use bytes::Bytes; /// A raw header value. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug)] pub struct Raw(Lines); impl Raw { @@ -11,6 +11,7 @@ impl Raw { #[inline] pub fn len(&self) -> usize { match self.0 { + Lines::Empty => 0, Lines::One(..) => 1, Lines::Many(ref lines) => lines.len() } @@ -39,6 +40,7 @@ impl Raw { pub fn push>(&mut self, val: V) { let raw = val.into(); match raw.0 { + Lines::Empty => (), Lines::One(one) => self.push_line(one), Lines::Many(lines) => { for line in lines { @@ -48,9 +50,12 @@ impl Raw { } } - fn push_line(&mut self, line: Line) { - let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new())); + fn push_line(&mut self, line: Bytes) { + let lines = ::std::mem::replace(&mut self.0, Lines::Empty); match lines { + Lines::Empty => { + self.0 = Lines::One(line); + } Lines::One(one) => { self.0 = Lines::Many(vec![one, line]); } @@ -62,20 +67,14 @@ impl Raw { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Clone)] enum Lines { - One(Line), - Many(Vec), -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum Line { - Static(&'static [u8]), - Owned(Vec), - Shared(Bytes), + Empty, + One(Bytes), + Many(Vec), } -fn eq, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { +fn eq_many, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { if a.len() != b.len() { false } else { @@ -88,18 +87,54 @@ fn eq, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { } } +fn eq>(raw: &Raw, b: &[B]) -> bool { + match raw.0 { + Lines::Empty => b.is_empty(), + Lines::One(ref line) => eq_many(&[line], b), + Lines::Many(ref lines) => eq_many(lines, b) + } +} + +impl PartialEq for Raw { + fn eq(&self, other: &Raw) -> bool { + match other.0 { + Lines::Empty => eq(self, &[] as &[Bytes]), + Lines::One(ref line) => eq(self, &[line]), + Lines::Many(ref lines) => eq(self, lines), + } + } +} + +impl Eq for Raw {} + impl PartialEq<[Vec]> for Raw { fn eq(&self, bytes: &[Vec]) -> bool { - match self.0 { - Lines::One(ref line) => eq(&[line], bytes), - Lines::Many(ref lines) => eq(lines, bytes) - } + eq(self, bytes) + } +} + +impl<'a> PartialEq<[&'a [u8]]> for Raw { + fn eq(&self, bytes: &[&[u8]]) -> bool { + eq(self, bytes) + } +} + +impl PartialEq<[String]> for Raw { + fn eq(&self, bytes: &[String]) -> bool { + eq(self, bytes) + } +} + +impl<'a> PartialEq<[&'a str]> for Raw { + fn eq(&self, bytes: &[&'a str]) -> bool { + eq(self, bytes) } } impl PartialEq<[u8]> for Raw { fn eq(&self, bytes: &[u8]) -> bool { match self.0 { + Lines::Empty => bytes.is_empty(), Lines::One(ref line) => line.as_ref() == bytes, Lines::Many(..) => false } @@ -108,10 +143,7 @@ impl PartialEq<[u8]> for Raw { impl PartialEq for Raw { fn eq(&self, s: &str) -> bool { - match self.0 { - Lines::One(ref line) => line.as_ref() == s.as_bytes(), - Lines::Many(..) => false - } + self == s.as_bytes() } } @@ -155,31 +187,7 @@ impl<'a> From<&'a [u8]> for Raw { impl From for Raw { #[inline] fn from(val: Bytes) -> Raw { - Raw(Lines::One(Line::Shared(val))) - } -} - -impl From> for Line { - #[inline] - fn from(val: Vec) -> Line { - Line::Owned(val) - } -} - -impl From for Line { - #[inline] - fn from(val: Bytes) -> Line { - Line::Shared(val) - } -} - -impl AsRef<[u8]> for Line { - fn as_ref(&self) -> &[u8] { - match *self { - Line::Static(ref s) => s, - Line::Owned(ref v) => v.as_ref(), - Line::Shared(ref m) => m.as_ref(), - } + Raw(Lines::One(val)) } } @@ -188,12 +196,17 @@ pub fn parsed(val: Bytes) -> Raw { } pub fn push(raw: &mut Raw, val: Bytes) { - raw.push_line(Line::from(val)); + raw.push_line(val); } -impl fmt::Debug for Raw { +pub fn new() -> Raw { + Raw(Lines::Empty) +} + +impl fmt::Debug for Lines { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { + match *self { + Lines::Empty => f.pad("[]"), Lines::One(ref line) => fmt::Debug::fmt(&[line], f), Lines::Many(ref lines) => fmt::Debug::fmt(lines, f) } @@ -205,6 +218,7 @@ impl ::std::ops::Index for Raw { fn index(&self, idx: usize) -> &[u8] { match self.0 { + Lines::Empty => panic!("index of out of bounds: {}", idx), Lines::One(ref line) => if idx == 0 { line.as_ref() } else { @@ -217,12 +231,12 @@ impl ::std::ops::Index for Raw { macro_rules! literals { ($($len:expr => $($value:expr),+;)+) => ( - fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Line { + fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Bytes { match s.len() { $($len => { $( if s.as_ref() == $value { - return Line::Static($value); + return Bytes::from_static($value); } )+ })+ @@ -230,7 +244,7 @@ macro_rules! literals { _ => () } - Line::from(s.into_owned()) + Bytes::from(s.into_owned()) } #[test] @@ -263,12 +277,19 @@ impl<'a> IntoIterator for &'a Raw { } } -#[derive(Debug)] pub struct RawLines<'a> { inner: &'a Lines, pos: usize, } +impl<'a> fmt::Debug for RawLines<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("RawLines") + .field(&self.inner) + .finish() + } +} + impl<'a> Iterator for RawLines<'a> { type Item = &'a [u8]; @@ -277,6 +298,7 @@ impl<'a> Iterator for RawLines<'a> { let current_pos = self.pos; self.pos += 1; match *self.inner { + Lines::Empty => None, Lines::One(ref line) => { if current_pos == 0 { Some(line.as_ref())