Merge pull request #70 from hyperium/raw
add Headers.set_raw, remove unsafe from .get_raw
This commit is contained in:
@@ -76,7 +76,7 @@ fn header_name<T: Header>() -> &'static str {
|
|||||||
|
|
||||||
/// A map of header fields on requests and responses.
|
/// A map of header fields on requests and responses.
|
||||||
pub struct Headers {
|
pub struct Headers {
|
||||||
data: HashMap<CaseInsensitive, RWLock<Item>>
|
data: HashMap<CaseInsensitive<SendStr>, RWLock<Item>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Headers {
|
impl Headers {
|
||||||
@@ -96,12 +96,12 @@ impl Headers {
|
|||||||
Some((name, value)) => {
|
Some((name, value)) => {
|
||||||
let name = CaseInsensitive(Owned(name));
|
let name = CaseInsensitive(Owned(name));
|
||||||
let item = match headers.data.entry(name) {
|
let item = match headers.data.entry(name) {
|
||||||
Vacant(entry) => entry.set(RWLock::new(Raw(vec![]))),
|
Vacant(entry) => entry.set(RWLock::new(Item::raw(vec![]))),
|
||||||
Occupied(entry) => entry.into_mut()
|
Occupied(entry) => entry.into_mut()
|
||||||
};
|
};
|
||||||
|
|
||||||
match &mut *item.write() {
|
match &mut item.write().raw {
|
||||||
&Raw(ref mut raw) => raw.push(value),
|
&Some(ref mut raw) => raw.push(value),
|
||||||
// Unreachable
|
// Unreachable
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
@@ -116,41 +116,55 @@ impl Headers {
|
|||||||
///
|
///
|
||||||
/// The field is determined by the type of the value being set.
|
/// The field is determined by the type of the value being set.
|
||||||
pub fn set<H: Header>(&mut self, value: H) {
|
pub fn set<H: Header>(&mut self, value: H) {
|
||||||
self.data.insert(CaseInsensitive(Slice(header_name::<H>())), RWLock::new(Typed(box value as Box<Header + Send + Sync>)));
|
self.data.insert(CaseInsensitive(Slice(header_name::<H>())),
|
||||||
|
RWLock::new(Item::typed(box value as Box<Header + Send + Sync>)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the raw value of a header, if it exists and has not
|
/// Access the raw value of a header.
|
||||||
/// been already parsed.
|
|
||||||
///
|
///
|
||||||
/// If the header field has already been parsed into a typed header,
|
/// Prefer to use the typed getters instead.
|
||||||
/// then you *must* access it through that representation.
|
|
||||||
///
|
|
||||||
/// This operation is unsafe because the raw representation can be
|
|
||||||
/// invalidated by lasting too long or by the header being parsed
|
|
||||||
/// while you still have a reference to the data.
|
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use hyper::header::Headers;
|
/// # use hyper::header::Headers;
|
||||||
/// # let mut headers = Headers::new();
|
/// # let mut headers = Headers::new();
|
||||||
/// let raw_content_type = unsafe { headers.get_raw("content-type") };
|
/// let raw_content_type = headers.get_raw("content-type");
|
||||||
/// ```
|
/// ```
|
||||||
pub unsafe fn get_raw(&self, name: &'static str) -> Option<*const [Vec<u8>]> {
|
pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
|
||||||
self.data.find(&CaseInsensitive(Slice(name))).and_then(|item| {
|
self.data.find_equiv(&CaseInsensitive(name)).and_then(|item| {
|
||||||
match *item.read() {
|
let lock = item.read();
|
||||||
Raw(ref raw) => Some(raw.as_slice() as *const [Vec<u8>]),
|
if let Some(ref raw) = lock.raw {
|
||||||
_ => None
|
return unsafe { 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()[])) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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", vec!["5".as_bytes().to_vec()]);
|
||||||
|
/// ```
|
||||||
|
pub fn set_raw<K: IntoMaybeOwned<'static>>(&mut self, name: K, value: Vec<Vec<u8>>) {
|
||||||
|
self.data.insert(CaseInsensitive(name.into_maybe_owned()), RWLock::new(Item::raw(value)));
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to the header field's value, if it exists.
|
/// Get a reference to the header field's value, if it exists.
|
||||||
pub fn get<H: Header>(&self) -> Option<&H> {
|
pub fn get<H: Header>(&self) -> Option<&H> {
|
||||||
self.get_or_parse::<H>().map(|item| {
|
self.get_or_parse::<H>().map(|item| {
|
||||||
let read = item.read();
|
let read = item.read();
|
||||||
debug!("downcasting {}", *read);
|
debug!("downcasting {}", *read);
|
||||||
let ret = match *read {
|
let ret = match read.typed {
|
||||||
Typed(ref val) => unsafe { val.downcast_ref_unchecked() },
|
Some(ref val) => unsafe { val.downcast_ref_unchecked() },
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
unsafe { transmute::<&H, &H>(ret) }
|
unsafe { transmute::<&H, &H>(ret) }
|
||||||
@@ -162,8 +176,8 @@ impl Headers {
|
|||||||
self.get_or_parse::<H>().map(|item| {
|
self.get_or_parse::<H>().map(|item| {
|
||||||
let mut write = item.write();
|
let mut write = item.write();
|
||||||
debug!("downcasting {}", *write);
|
debug!("downcasting {}", *write);
|
||||||
let ret = match *&mut *write {
|
let ret = match *&mut write.typed {
|
||||||
Typed(ref mut val) => unsafe { val.downcast_mut_unchecked() },
|
Some(ref mut val) => unsafe { val.downcast_mut_unchecked() },
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
unsafe { transmute::<&mut H, &mut H>(ret) }
|
unsafe { transmute::<&mut H, &mut H>(ret) }
|
||||||
@@ -172,44 +186,47 @@ impl Headers {
|
|||||||
|
|
||||||
fn get_or_parse<H: Header>(&self) -> Option<&RWLock<Item>> {
|
fn get_or_parse<H: Header>(&self) -> Option<&RWLock<Item>> {
|
||||||
self.data.find(&CaseInsensitive(Slice(header_name::<H>()))).and_then(|item| {
|
self.data.find(&CaseInsensitive(Slice(header_name::<H>()))).and_then(|item| {
|
||||||
let done = match *item.read() {
|
match item.read().typed {
|
||||||
// Huge borrowck hack here, should be refactored to just return here.
|
Some(ref typed) if typed.is::<H>() => return Some(item),
|
||||||
Typed(ref typed) if typed.is::<H>() => true,
|
Some(ref typed) => {
|
||||||
|
warn!("attempted to access {} as wrong type", typed);
|
||||||
// Typed, wrong type.
|
return None;
|
||||||
Typed(_) => return None,
|
}
|
||||||
|
_ => ()
|
||||||
// Raw, work to do.
|
}
|
||||||
Raw(_) => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// borrowck hack continued
|
|
||||||
if done { return Some(item); }
|
|
||||||
|
|
||||||
// Take out a write lock to do the parsing and mutation.
|
// Take out a write lock to do the parsing and mutation.
|
||||||
let mut write = item.write();
|
let mut write = item.write();
|
||||||
|
|
||||||
let header = match *write {
|
// Since this lock can queue, it's possible another thread just
|
||||||
// Since this lock can queue, it's possible another thread just
|
// did the work for us.
|
||||||
// did the work for us.
|
match write.typed {
|
||||||
//
|
|
||||||
// Check they inserted the correct type and move on.
|
// Check they inserted the correct type and move on.
|
||||||
Typed(ref typed) if typed.is::<H>() => return Some(item),
|
Some(ref typed) if typed.is::<H>() => return Some(item),
|
||||||
|
|
||||||
// Wrong type, another thread got here before us and parsed
|
// Wrong type, another thread got here before us and parsed
|
||||||
// as a different representation.
|
// as a different representation.
|
||||||
Typed(_) => return None,
|
Some(ref typed) => {
|
||||||
|
debug!("other thread was here first?")
|
||||||
|
warn!("attempted to access {} as wrong type", typed);
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
|
||||||
// We are first in the queue or the only ones, so do the actual
|
// We are first in the queue or the only ones, so do the actual
|
||||||
// work of parsing and mutation.
|
// work of parsing and mutation.
|
||||||
Raw(ref raw) => match Header::parse_header(raw.as_slice()) {
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = match write.raw {
|
||||||
|
Some(ref raw) => match Header::parse_header(raw[]) {
|
||||||
Some::<H>(h) => h,
|
Some::<H>(h) => h,
|
||||||
None => return None
|
None => return None
|
||||||
}
|
},
|
||||||
|
None => unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mutate in the raw case.
|
// Mutate!
|
||||||
*write = Typed(box header as Box<Header + Send + Sync>);
|
write.typed = Some(box header as Box<Header + Send + Sync>);
|
||||||
Some(item)
|
Some(item)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -253,7 +270,7 @@ impl fmt::Show for Headers {
|
|||||||
|
|
||||||
/// An `Iterator` over the fields in a `Headers` map.
|
/// An `Iterator` over the fields in a `Headers` map.
|
||||||
pub struct HeadersItems<'a> {
|
pub struct HeadersItems<'a> {
|
||||||
inner: Entries<'a, CaseInsensitive, RWLock<Item>>
|
inner: Entries<'a, CaseInsensitive<SendStr>, RWLock<Item>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator<(&'a str, HeaderView<'a>)> for HeadersItems<'a> {
|
impl<'a> Iterator<(&'a str, HeaderView<'a>)> for HeadersItems<'a> {
|
||||||
@@ -287,28 +304,53 @@ impl Mutable for Headers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Item {
|
struct Item {
|
||||||
Raw(Vec<Vec<u8>>),
|
raw: Option<Vec<Vec<u8>>>,
|
||||||
Typed(Box<Header + Send + Sync>)
|
typed: Option<Box<Header + Send + Sync>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for Item {
|
impl Item {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn raw(data: Vec<Vec<u8>>) -> Item {
|
||||||
match *self {
|
Item {
|
||||||
Typed(ref h) => h.fmt_header(fmt),
|
raw: Some(data),
|
||||||
Raw(ref raw) => {
|
typed: None,
|
||||||
for part in raw.iter() {
|
}
|
||||||
try!(fmt.write(part.as_slice()));
|
}
|
||||||
}
|
|
||||||
Ok(())
|
fn typed(ty: Box<Header + Send + Sync>) -> Item {
|
||||||
},
|
Item {
|
||||||
|
raw: None,
|
||||||
|
typed: Some(ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CaseInsensitive(SendStr);
|
impl fmt::Show for Item {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self.typed {
|
||||||
|
Some(ref h) => h.fmt_header(fmt),
|
||||||
|
None => match self.raw {
|
||||||
|
Some(ref raw) => {
|
||||||
|
for part in raw.iter() {
|
||||||
|
try!(fmt.write(part.as_slice()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
None => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Str for CaseInsensitive {
|
impl fmt::Show for Box<Header + Send + Sync> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
(**self).fmt_header(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CaseInsensitive<S: Str>(S);
|
||||||
|
|
||||||
|
impl<S: Str> Str for CaseInsensitive<S> {
|
||||||
fn as_slice(&self) -> &str {
|
fn as_slice(&self) -> &str {
|
||||||
let CaseInsensitive(ref s) = *self;
|
let CaseInsensitive(ref s) = *self;
|
||||||
s.as_slice()
|
s.as_slice()
|
||||||
@@ -316,21 +358,29 @@ impl Str for CaseInsensitive {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for CaseInsensitive {
|
impl<S: Str> fmt::Show for CaseInsensitive<S> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.as_slice().fmt(fmt)
|
self.as_slice().fmt(fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for CaseInsensitive {
|
impl<S: Str> PartialEq for CaseInsensitive<S> {
|
||||||
fn eq(&self, other: &CaseInsensitive) -> bool {
|
fn eq(&self, other: &CaseInsensitive<S>) -> bool {
|
||||||
self.as_slice().eq_ignore_ascii_case(other.as_slice())
|
self.as_slice().eq_ignore_ascii_case(other.as_slice())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for CaseInsensitive {}
|
impl<S: Str> Eq for CaseInsensitive<S> {}
|
||||||
|
|
||||||
impl<H: hash::Writer> hash::Hash<H> for CaseInsensitive {
|
impl<S: Str, S2: Str> Equiv<CaseInsensitive<S2>> for CaseInsensitive<S> {
|
||||||
|
fn equiv(&self, other: &CaseInsensitive<S2>) -> bool {
|
||||||
|
let left = CaseInsensitive(self.as_slice());
|
||||||
|
let right = CaseInsensitive(other.as_slice());
|
||||||
|
left == right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Str, H: hash::Writer> hash::Hash<H> for CaseInsensitive<S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash(&self, hasher: &mut H) {
|
fn hash(&self, hasher: &mut H) {
|
||||||
for byte in self.as_slice().bytes() {
|
for byte in self.as_slice().bytes() {
|
||||||
@@ -387,7 +437,7 @@ mod tests {
|
|||||||
assert_eq!(accept, Some(Accept(vec![application_vendor, text_plain])));
|
assert_eq!(accept, Some(Accept(vec![application_vendor, text_plain])));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone, Show)]
|
||||||
struct CrazyLength(Option<bool>, uint);
|
struct CrazyLength(Option<bool>, uint);
|
||||||
|
|
||||||
impl Header for CrazyLength {
|
impl Header for CrazyLength {
|
||||||
@@ -408,9 +458,8 @@ mod tests {
|
|||||||
}.map(|u| CrazyLength(Some(false), u))
|
}.map(|u| CrazyLength(Some(false), u))
|
||||||
}
|
}
|
||||||
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use std::fmt::Show;
|
let CrazyLength(ref opt, ref value) = *self;
|
||||||
let CrazyLength(_, ref value) = *self;
|
write!(fmt, "{}, {}", opt, value)
|
||||||
value.fmt(fmt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +504,15 @@ mod tests {
|
|||||||
pieces.sort();
|
pieces.sort();
|
||||||
let s = pieces.into_iter().rev().collect::<Vec<&str>>().connect("\r\n");
|
let s = pieces.into_iter().rev().collect::<Vec<&str>>().connect("\r\n");
|
||||||
assert_eq!(s[], "Host: foo.bar\r\nContent-Length: 15\r\n");
|
assert_eq!(s[], "Host: foo.bar\r\nContent-Length: 15\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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user