add Headers.set_raw, remove unsafe from .get_raw
Internal representation was changed from an enum back to a Struct again. The raw representation *has* to stick around, even if parsed as a proper typed header. The reason is that internally, hyper will access some headers to know which parts of the http protocol to follow (such as if the response has a length or is chunked). The raw value may still be needed afterwards, such as for a DOM binding of .getAllResponseHeaders(). Since the raw is kept around, the unsafety of get_raw is no longer true, and so that is removed. It's still more ergonomic to access the types, and safer as well, so that is recommended when possible.
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