From 00bc001e59820c02f67d6ae7b19b2827b3da0838 Mon Sep 17 00:00:00 2001 From: Jonathan Reem Date: Fri, 31 Oct 2014 15:09:42 -0700 Subject: [PATCH 1/2] Split Header into Header and HeaderFormat Header contains all of the trait-object unsafe methods including the name of the header and parsing. HeaderFormat contains fmt_header, which is the only trait-object safe method. --- src/header/mod.rs | 70 ++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/header/mod.rs b/src/header/mod.rs index 764f9ec3..fe744e02 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -6,24 +6,26 @@ //! are already provided, such as `Host`, `ContentType`, `UserAgent`, and others. use std::ascii::{AsciiExt, ASCII_LOWER_MAP}; use std::fmt::{mod, Show}; -use std::hash; use std::intrinsics::TypeId; -use std::mem::{transmute, transmute_copy}; use std::raw::TraitObject; use std::str::{SendStr, Slice, Owned}; use std::collections::hashmap::{HashMap, Entries, Occupied, Vacant}; use std::sync::RWLock; +use std::{hash, mem}; use uany::{UncheckedAnyDowncast, UncheckedAnyMutDowncast}; use typeable::Typeable; -use http::{read_header, LineEnding}; +use http::{mod, LineEnding}; use {HttpResult}; /// Common Headers pub mod common; /// 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: Typeable + Send + Sync { /// Returns the name of the header field this belongs to. /// @@ -38,7 +40,16 @@ pub trait Header: Typeable + Send + Sync { /// than one field value. If that's the case, you **should** return `None` /// if `raw.len() > 1`. fn parse_header(raw: &[Vec]) -> Option; +} + +/// A trait for any object that will represent a header field and value. +/// +/// This trait represents the formatting of a Header for output to a TcpStream. +pub trait HeaderFormat: Typeable + Send + Sync { /// Format a header to be output into a TcpStream. + /// + /// This method is not allowed to introduce an Err not produced + /// by the passed-in Formatter. fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result; } @@ -47,29 +58,29 @@ trait Is { fn is(self) -> bool; } -impl<'a> Is for &'a Header { +impl<'a> Is for &'a HeaderFormat { fn is(self) -> bool { self.get_type() == TypeId::of::() } } -impl<'a> UncheckedAnyDowncast<'a> for &'a Header { +impl<'a> UncheckedAnyDowncast<'a> for &'a HeaderFormat { #[inline] unsafe fn downcast_ref_unchecked(self) -> &'a T { - let to: TraitObject = transmute_copy(&self); - transmute(to.data) + let to: TraitObject = mem::transmute_copy(&self); + mem::transmute(to.data) } } -impl<'a> UncheckedAnyMutDowncast<'a> for &'a mut Header { +impl<'a> UncheckedAnyMutDowncast<'a> for &'a mut HeaderFormat { #[inline] unsafe fn downcast_mut_unchecked(self) -> &'a mut T { - let to: TraitObject = transmute_copy(&self); - transmute(to.data) + let to: TraitObject = mem::transmute_copy(&self); + mem::transmute(to.data) } } -fn header_name() -> &'static str { +fn header_name() -> &'static str { let name = Header::header_name(None::); name } @@ -92,7 +103,7 @@ impl Headers { pub fn from_raw(rdr: &mut R) -> HttpResult { let mut headers = Headers::new(); loop { - match try!(read_header(rdr)) { + match try!(http::read_header(rdr)) { Some((name, value)) => { let name = CaseInsensitive(Owned(name)); let item = match headers.data.entry(name) { @@ -115,9 +126,9 @@ impl 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) { + pub fn set(&mut self, value: H) { self.data.insert(CaseInsensitive(Slice(header_name::())), - RWLock::new(Item::typed(box value as Box
))); + RWLock::new(Item::typed(box value as Box))); } /// Access the raw value of a header. @@ -135,13 +146,13 @@ impl Headers { self.data.find_equiv(&CaseInsensitive(name)).and_then(|item| { let lock = item.read(); if let Some(ref raw) = lock.raw { - return unsafe { transmute(Some(raw[])) }; + return unsafe { mem::transmute(Some(raw[])) }; } let mut lock = item.write(); let raw = vec![lock.typed.as_ref().unwrap().to_string().into_bytes()]; lock.raw = Some(raw); - unsafe { transmute(Some(lock.raw.as_ref().unwrap()[])) } + unsafe { mem::transmute(Some(lock.raw.as_ref().unwrap()[])) } }) } @@ -159,7 +170,7 @@ impl Headers { } /// Get a reference to the header field's value, if it exists. - pub fn get(&self) -> Option<&H> { + pub fn get(&self) -> Option<&H> { self.get_or_parse::().map(|item| { let read = item.read(); debug!("downcasting {}", *read); @@ -167,12 +178,12 @@ impl Headers { Some(ref val) => unsafe { val.downcast_ref_unchecked() }, _ => unreachable!() }; - unsafe { transmute::<&H, &H>(ret) } + unsafe { mem::transmute::<&H, &H>(ret) } }) } /// Get a mutable reference to the header field's value, if it exists. - pub fn get_mut(&mut self) -> Option<&mut H> { + pub fn get_mut(&mut self) -> Option<&mut H> { self.get_or_parse::().map(|item| { let mut write = item.write(); debug!("downcasting {}", *write); @@ -180,11 +191,11 @@ impl Headers { Some(ref mut val) => unsafe { val.downcast_mut_unchecked() }, _ => unreachable!() }; - unsafe { transmute::<&mut H, &mut H>(ret) } + unsafe { mem::transmute::<&mut H, &mut H>(ret) } }) } - fn get_or_parse(&self) -> Option<&RWLock> { + fn get_or_parse(&self) -> Option<&RWLock> { self.data.find(&CaseInsensitive(Slice(header_name::()))).and_then(|item| { match item.read().typed { Some(ref typed) if typed.is::() => return Some(item), @@ -226,7 +237,7 @@ impl Headers { }; // Mutate! - write.typed = Some(box header as Box
); + write.typed = Some(box header as Box); Some(item) }) } @@ -241,13 +252,13 @@ impl Headers { /// # let mut headers = Headers::new(); /// let has_type = headers.has::(); /// ``` - pub fn has(&self) -> bool { + pub fn has(&self) -> bool { self.data.contains_key(&CaseInsensitive(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 { + pub fn remove(&mut self) -> bool { self.data.remove(&CaseInsensitive(Slice(Header::header_name(None::)))) } @@ -306,7 +317,7 @@ impl Mutable for Headers { struct Item { raw: Option>>, - typed: Option> + typed: Option> } impl Item { @@ -317,7 +328,7 @@ impl Item { } } - fn typed(ty: Box
) -> Item { + fn typed(ty: Box) -> Item { Item { raw: None, typed: Some(ty), @@ -342,7 +353,7 @@ impl fmt::Show for Item { } } -impl fmt::Show for Box
{ +impl fmt::Show for Box { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { (**self).fmt_header(fmt) } @@ -432,7 +443,7 @@ mod tests { 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]))); } @@ -457,6 +468,9 @@ mod tests { None => 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) From 0652858dbf2aa34f88dc48a9f675a6936834a3d3 Mon Sep 17 00:00:00 2001 From: Jonathan Reem Date: Fri, 31 Oct 2014 15:10:34 -0700 Subject: [PATCH 2/2] Update all common headers for the new Header trait split --- src/header/common/accept.rs | 4 +++- src/header/common/connection.rs | 4 +++- src/header/common/content_length.rs | 4 +++- src/header/common/content_type.rs | 4 +++- src/header/common/date.rs | 4 +++- src/header/common/host.rs | 4 +++- src/header/common/location.rs | 14 ++++++++------ src/header/common/server.rs | 4 +++- src/header/common/transfer_encoding.rs | 4 +++- src/header/common/upgrade.rs | 4 +++- src/header/common/user_agent.rs | 4 +++- 11 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/header/common/accept.rs b/src/header/common/accept.rs index 89c0bd8b..0fcb1819 100644 --- a/src/header/common/accept.rs +++ b/src/header/common/accept.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use std::str::from_utf8; use mime::Mime; @@ -49,7 +49,9 @@ impl Header for Accept { None } } +} +impl HeaderFormat for Accept { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Accept(ref value) = *self; let last = value.len() - 1; diff --git a/src/header/common/connection.rs b/src/header/common/connection.rs index cb20fe4b..049fede5 100644 --- a/src/header/common/connection.rs +++ b/src/header/common/connection.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::{from_comma_delimited, fmt_comma_delimited}; use std::from_str::FromStr; @@ -53,7 +53,9 @@ impl Header for Connection { fn parse_header(raw: &[Vec]) -> Option { from_comma_delimited(raw).map(|vec| Connection(vec)) } +} +impl HeaderFormat for Connection { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Connection(ref parts) = *self; fmt_comma_delimited(fmt, parts[]) diff --git a/src/header/common/content_length.rs b/src/header/common/content_length.rs index f0a072fe..8e553b33 100644 --- a/src/header/common/content_length.rs +++ b/src/header/common/content_length.rs @@ -1,6 +1,6 @@ use std::fmt::{mod, Show}; -use header::Header; +use header::{Header, HeaderFormat}; use super::util::from_one_raw_str; /// The `Content-Length` header. @@ -17,7 +17,9 @@ impl Header for ContentLength { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw).map(|u| ContentLength(u)) } +} +impl HeaderFormat for ContentLength { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let ContentLength(ref value) = *self; value.fmt(fmt) diff --git a/src/header/common/content_type.rs b/src/header/common/content_type.rs index 5562a0b7..01c830b3 100644 --- a/src/header/common/content_type.rs +++ b/src/header/common/content_type.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; use mime::Mime; @@ -18,7 +18,9 @@ impl Header for ContentType { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw).map(|mime| ContentType(mime)) } +} +impl HeaderFormat for ContentType { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let ContentType(ref value) = *self; value.fmt(fmt) diff --git a/src/header/common/date.rs b/src/header/common/date.rs index 4e8ad878..9342be34 100644 --- a/src/header/common/date.rs +++ b/src/header/common/date.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; use std::from_str::FromStr; @@ -17,7 +17,9 @@ impl Header for Date { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw) } +} +impl HeaderFormat for Date { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { self.fmt(fmt) } diff --git a/src/header/common/host.rs b/src/header/common/host.rs index d1d13888..5b40a9e4 100644 --- a/src/header/common/host.rs +++ b/src/header/common/host.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use Port; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; @@ -61,7 +61,9 @@ impl Header for Host { }) }) } +} +impl HeaderFormat for Host { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self.port { None | Some(80) | Some(443) => self.hostname.fmt(fmt), diff --git a/src/header/common/location.rs b/src/header/common/location.rs index d6d48772..8864353b 100644 --- a/src/header/common/location.rs +++ b/src/header/common/location.rs @@ -1,14 +1,14 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; /// The `Location` header. /// -/// The Location response-header field is used to redirect the recipient to -/// a location other than the Request-URI for completion of the request or identification -/// of a new resource. For 201 (Created) responses, the Location is that of the new -/// resource which was created by the request. For 3xx responses, the location SHOULD -/// indicate the server's preferred URI for automatic redirection to the resource. +/// The Location response-header field is used to redirect the recipient to +/// a location other than the Request-URI for completion of the request or identification +/// of a new resource. For 201 (Created) responses, the Location is that of the new +/// resource which was created by the request. For 3xx responses, the location SHOULD +/// indicate the server's preferred URI for automatic redirection to the resource. /// The field value consists of a single absolute URI. /// /// Currently is just a String, but it should probably become a better type, @@ -24,7 +24,9 @@ impl Header for Location { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw).map(|s| Location(s)) } +} +impl HeaderFormat for Location { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Location(ref value) = *self; value.fmt(fmt) diff --git a/src/header/common/server.rs b/src/header/common/server.rs index e0084d69..f48cbb0b 100644 --- a/src/header/common/server.rs +++ b/src/header/common/server.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; @@ -16,7 +16,9 @@ impl Header for Server { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw).map(|s| Server(s)) } +} +impl HeaderFormat for Server { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Server(ref value) = *self; value.fmt(fmt) diff --git a/src/header/common/transfer_encoding.rs b/src/header/common/transfer_encoding.rs index 703eb1fc..edce4fd2 100644 --- a/src/header/common/transfer_encoding.rs +++ b/src/header/common/transfer_encoding.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt; use std::from_str::FromStr; use super::{from_comma_delimited, fmt_comma_delimited}; @@ -75,7 +75,9 @@ impl Header for TransferEncoding { fn parse_header(raw: &[Vec]) -> Option { from_comma_delimited(raw).map(|vec| TransferEncoding(vec)) } +} +impl HeaderFormat for TransferEncoding { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let TransferEncoding(ref parts) = *self; fmt_comma_delimited(fmt, parts[]) diff --git a/src/header/common/upgrade.rs b/src/header/common/upgrade.rs index fab3bc2c..26c754b0 100644 --- a/src/header/common/upgrade.rs +++ b/src/header/common/upgrade.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::{from_comma_delimited, fmt_comma_delimited}; use std::from_str::FromStr; @@ -42,7 +42,9 @@ impl Header for Upgrade { fn parse_header(raw: &[Vec]) -> Option { from_comma_delimited(raw).map(|vec| Upgrade(vec)) } +} +impl HeaderFormat for Upgrade { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Upgrade(ref parts) = *self; fmt_comma_delimited(fmt, parts[]) diff --git a/src/header/common/user_agent.rs b/src/header/common/user_agent.rs index c46d2920..a4cd8d6c 100644 --- a/src/header/common/user_agent.rs +++ b/src/header/common/user_agent.rs @@ -1,4 +1,4 @@ -use header::Header; +use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; use super::util::from_one_raw_str; @@ -16,7 +16,9 @@ impl Header for UserAgent { fn parse_header(raw: &[Vec]) -> Option { from_one_raw_str(raw).map(|s| UserAgent(s)) } +} +impl HeaderFormat for UserAgent { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let UserAgent(ref value) = *self; value.fmt(fmt)