mirror of https://github.com/fafhrd91/actix-web
change internal rep of Value
This commit is contained in:
parent
b5b99d3909
commit
0c601c7f1b
|
@ -830,8 +830,8 @@ mod tests {
|
||||||
.get_all(SET_COOKIE)
|
.get_all(SET_COOKIE)
|
||||||
.map(|v| v.to_str().unwrap().to_owned())
|
.map(|v| v.to_str().unwrap().to_owned())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(val[1], "c1=cookie1");
|
assert_eq!(val[0], "c1=cookie1");
|
||||||
assert_eq!(val[0], "c2=cookie2");
|
assert_eq!(val[1], "c2=cookie2");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -144,60 +144,8 @@ pub(crate) trait MessageType: Sized {
|
||||||
let k = key.as_str().as_bytes();
|
let k = key.as_str().as_bytes();
|
||||||
let k_len = k.len();
|
let k_len = k.len();
|
||||||
|
|
||||||
match value {
|
// TODO: drain?
|
||||||
Value::One(ref val) => {
|
for val in value.iter() {
|
||||||
let v = val.as_ref();
|
|
||||||
let v_len = v.len();
|
|
||||||
|
|
||||||
// key length + value length + colon + space + \r\n
|
|
||||||
let len = k_len + v_len + 4;
|
|
||||||
|
|
||||||
if len > remaining {
|
|
||||||
// not enough room in buffer for this header; reserve more space
|
|
||||||
|
|
||||||
// SAFETY: all the bytes written up to position "pos" are initialized
|
|
||||||
// the written byte count and pointer advancement are kept in sync
|
|
||||||
unsafe {
|
|
||||||
dst.advance_mut(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
dst.reserve(len * 2);
|
|
||||||
remaining = dst.capacity() - dst.len();
|
|
||||||
|
|
||||||
// re-assign buf raw pointer since it's possible that the buffer was
|
|
||||||
// reallocated and/or resized
|
|
||||||
buf = dst.chunk_mut().as_mut_ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: on each write, it is enough to ensure that the advancement of the
|
|
||||||
// cursor matches the number of bytes written
|
|
||||||
unsafe {
|
|
||||||
// use upper Camel-Case
|
|
||||||
if camel_case {
|
|
||||||
write_camel_case(k, from_raw_parts_mut(buf, k_len))
|
|
||||||
} else {
|
|
||||||
write_data(k, buf, k_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = buf.add(k_len);
|
|
||||||
|
|
||||||
write_data(b": ", buf, 2);
|
|
||||||
buf = buf.add(2);
|
|
||||||
|
|
||||||
write_data(v, buf, v_len);
|
|
||||||
buf = buf.add(v_len);
|
|
||||||
|
|
||||||
write_data(b"\r\n", buf, 2);
|
|
||||||
buf = buf.add(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += len;
|
|
||||||
remaining -= len;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Multi(ref vec) => {
|
|
||||||
for val in vec {
|
|
||||||
let v = val.as_ref();
|
let v = val.as_ref();
|
||||||
let v_len = v.len();
|
let v_len = v.len();
|
||||||
let len = k_len + v_len + 4;
|
let len = k_len + v_len + 4;
|
||||||
|
@ -241,8 +189,6 @@ pub(crate) trait MessageType: Sized {
|
||||||
pos += len;
|
pos += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// final cursor synchronization with the bytes container
|
// final cursor synchronization with the bytes container
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
use std::{borrow::Cow, str::FromStr};
|
||||||
|
|
||||||
|
use http::header::{HeaderName, InvalidHeaderName};
|
||||||
|
|
||||||
|
pub trait AsHeaderName: Sealed {}
|
||||||
|
|
||||||
|
pub trait Sealed {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for HeaderName {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
|
Ok(Cow::Borrowed(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsHeaderName for HeaderName {}
|
||||||
|
|
||||||
|
impl Sealed for &HeaderName {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
|
Ok(Cow::Borrowed(*self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsHeaderName for &HeaderName {}
|
||||||
|
|
||||||
|
impl Sealed for &str {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsHeaderName for &str {}
|
||||||
|
|
||||||
|
impl Sealed for String {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsHeaderName for String {}
|
||||||
|
|
||||||
|
impl Sealed for &String {
|
||||||
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsHeaderName for &String {}
|
|
@ -1,58 +1,71 @@
|
||||||
//! A multi-value [`HeaderMap`], its iterators, and a helper trait for types that can be effectively
|
//! A multi-value [`HeaderMap`], its iterators, and a helper trait for types that can be effectively
|
||||||
//! borrowed as, or converted to, a [HeaderValue].
|
//! borrowed as, or converted to, a [HeaderValue].
|
||||||
|
|
||||||
use std::{borrow::Cow, collections::hash_map, mem, str::FromStr};
|
use std::{borrow::Cow, collections::hash_map, ops};
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use http::header::{HeaderName, HeaderValue, InvalidHeaderName};
|
use http::header::{HeaderName, HeaderValue};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
pub use as_header_name::AsHeaderName;
|
use crate::header::AsHeaderName;
|
||||||
|
|
||||||
/// A multi-map of HTTP headers.
|
/// A multi-map of HTTP headers.
|
||||||
///
|
///
|
||||||
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s.
|
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
///
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// map.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain"));
|
||||||
|
/// map.insert(header::ORIGIN, HeaderValue::from_static("example.com"));
|
||||||
|
///
|
||||||
|
/// assert!(map.contains_key(header::CONTENT_TYPE));
|
||||||
|
/// assert!(map.contains_key(header::ORIGIN));
|
||||||
|
///
|
||||||
|
/// let mut removed = map.remove(header::ORIGIN);
|
||||||
|
/// assert_eq!(removed.next().unwrap(), "example.com");
|
||||||
|
///
|
||||||
|
/// assert!(!map.contains_key(header::ORIGIN));
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct HeaderMap {
|
pub struct HeaderMap {
|
||||||
pub(crate) inner: AHashMap<HeaderName, Value>,
|
pub(crate) inner: AHashMap<HeaderName, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A bespoke non-empty list for HeaderMap values.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) enum Value {
|
pub(crate) struct Value {
|
||||||
One(HeaderValue),
|
inner: SmallVec<[HeaderValue; 4]>,
|
||||||
Multi(SmallVec<[HeaderValue; 4]>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
fn len(&self) -> usize {
|
fn one(val: HeaderValue) -> Self {
|
||||||
match self {
|
Self {
|
||||||
Value::One(_) => 1,
|
inner: smallvec![val],
|
||||||
Value::Multi(vals) => vals.len(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first(&self) -> &HeaderValue {
|
fn first(&self) -> &HeaderValue {
|
||||||
match self {
|
&self.inner[0]
|
||||||
Value::One(ref val) => val,
|
|
||||||
Value::Multi(ref val) => &val[0],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_mut(&mut self) -> &mut HeaderValue {
|
fn first_mut(&mut self) -> &mut HeaderValue {
|
||||||
match self {
|
&mut self.inner[0]
|
||||||
Value::One(ref mut val) => val,
|
}
|
||||||
Value::Multi(ref mut val) => &mut val[0],
|
|
||||||
|
fn append(&mut self, new_val: HeaderValue) {
|
||||||
|
self.inner.push(new_val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append(&mut self, val: HeaderValue) {
|
impl ops::Deref for Value {
|
||||||
match self {
|
type Target = SmallVec<[HeaderValue; 4]>;
|
||||||
Value::One(_) => match mem::replace(self, Value::Multi(smallvec![val])) {
|
|
||||||
Value::One(val) => self.append(val),
|
fn deref(&self) -> &Self::Target {
|
||||||
Value::Multi(_) => unreachable!(),
|
&self.inner
|
||||||
},
|
|
||||||
Value::Multi(ref mut vals) => vals.push(val),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +73,15 @@ impl HeaderMap {
|
||||||
/// Create an empty `HeaderMap`.
|
/// Create an empty `HeaderMap`.
|
||||||
///
|
///
|
||||||
/// The map will be created without any capacity; this function will not allocate.
|
/// The map will be created without any capacity; this function will not allocate.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::HeaderMap;
|
||||||
|
/// let map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// assert!(map.is_empty());
|
||||||
|
/// assert_eq!(0, map.capacity());
|
||||||
|
/// ```
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
HeaderMap::default()
|
HeaderMap::default()
|
||||||
}
|
}
|
||||||
|
@ -68,6 +90,15 @@ impl HeaderMap {
|
||||||
///
|
///
|
||||||
/// The map will be able to hold at least `capacity` elements without needing to reallocate.
|
/// The map will be able to hold at least `capacity` elements without needing to reallocate.
|
||||||
/// If `capacity` is 0, the map will be created without allocating.
|
/// If `capacity` is 0, the map will be created without allocating.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::HeaderMap;
|
||||||
|
/// let map = HeaderMap::with_capacity(16);
|
||||||
|
///
|
||||||
|
/// assert!(map.is_empty());
|
||||||
|
/// assert!(map.capacity() >= 16);
|
||||||
|
/// ```
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
HeaderMap {
|
HeaderMap {
|
||||||
inner: AHashMap::with_capacity(capacity),
|
inner: AHashMap::with_capacity(capacity),
|
||||||
|
@ -105,7 +136,21 @@ impl HeaderMap {
|
||||||
|
|
||||||
/// Returns the number of values stored in the map.
|
/// Returns the number of values stored in the map.
|
||||||
///
|
///
|
||||||
/// Also see [`len_keys`](Self::len_keys).
|
/// See also: [`len_keys`](Self::len_keys).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
/// assert_eq!(map.len(), 0);
|
||||||
|
///
|
||||||
|
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
/// assert_eq!(map.len(), 2);
|
||||||
|
///
|
||||||
|
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
/// assert_eq!(map.len(), 3);
|
||||||
|
/// ```
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.inner
|
self.inner
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -114,12 +159,36 @@ impl HeaderMap {
|
||||||
|
|
||||||
/// Returns the number of _keys_ stored in the map.
|
/// Returns the number of _keys_ stored in the map.
|
||||||
///
|
///
|
||||||
/// The number of _values_ stored will be at least this number. Also see [`Self::len`].
|
/// The number of values stored will be at least this number. See also: [`Self::len`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
/// assert_eq!(map.len_keys(), 0);
|
||||||
|
///
|
||||||
|
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
/// assert_eq!(map.len_keys(), 2);
|
||||||
|
///
|
||||||
|
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
/// assert_eq!(map.len_keys(), 2);
|
||||||
|
/// ```
|
||||||
pub fn len_keys(&self) -> usize {
|
pub fn len_keys(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the map contains no elements.
|
/// Returns true if the map contains no elements.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
/// assert!(map.is_empty());
|
||||||
|
///
|
||||||
|
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
|
||||||
|
/// assert!(!map.is_empty());
|
||||||
|
/// ```
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.inner.len() == 0
|
self.inner.len() == 0
|
||||||
}
|
}
|
||||||
|
@ -127,6 +196,19 @@ impl HeaderMap {
|
||||||
/// Clears the map, removing all name-value pairs.
|
/// Clears the map, removing all name-value pairs.
|
||||||
///
|
///
|
||||||
/// Keeps the allocated memory for reuse.
|
/// Keeps the allocated memory for reuse.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
/// assert_eq!(map.len(), 2);
|
||||||
|
///
|
||||||
|
/// map.clear();
|
||||||
|
/// assert!(map.is_empty());
|
||||||
|
/// ```
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.inner.clear();
|
self.inner.clear();
|
||||||
}
|
}
|
||||||
|
@ -140,18 +222,63 @@ impl HeaderMap {
|
||||||
|
|
||||||
/// Returns a reference to the _first_ value associated with a header name.
|
/// Returns a reference to the _first_ value associated with a header name.
|
||||||
///
|
///
|
||||||
/// Even when multiple values associated with the key, the "first" one is returned but is not
|
/// Returns `None` if there is no value associated with the key.
|
||||||
/// guaranteed to be chosen in with particular order. Use `get_all` to get all values associated
|
///
|
||||||
/// with a given key. Returns `None` if there are no values associated with the key.
|
/// Even when multiple values are associated with the key, the "first" one is returned but is
|
||||||
|
/// not guaranteed to be chosen with any particular order; though, the returned item will be
|
||||||
|
/// consistent for each call to `get` if the map has not changed.
|
||||||
|
///
|
||||||
|
/// See also: [`get_all`](Self::get_all).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
///
|
||||||
|
/// let cookie = map.get(header::SET_COOKIE).unwrap();
|
||||||
|
/// assert_eq!(cookie, "one=1");
|
||||||
|
///
|
||||||
|
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
/// assert_eq!(map.get(header::SET_COOKIE).unwrap(), "one=1");
|
||||||
|
///
|
||||||
|
/// assert_eq!(map.get(header::SET_COOKIE), map.get("set-cookie"));
|
||||||
|
/// assert_eq!(map.get(header::SET_COOKIE), map.get("Set-Cookie"));
|
||||||
|
///
|
||||||
|
/// assert!(map.get(header::HOST).is_none());
|
||||||
|
/// assert!(map.get("INVALID HEADER NAME").is_none());
|
||||||
|
/// ```
|
||||||
pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {
|
pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {
|
||||||
self.get_value(key).map(|val| val.first())
|
self.get_value(key).map(|val| val.first())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the _first_ value associated a header name.
|
/// Returns a mutable reference to the _first_ value associated a header name.
|
||||||
///
|
///
|
||||||
/// Even when multiple values associated with the key, the "first" one is returned but is not
|
/// Returns `None` if there is no value associated with the key.
|
||||||
/// guaranteed to be chosen in with particular order. Use `get_all` to get all values associated
|
///
|
||||||
/// with a given key. Returns `None` if there are no values associated with the key.
|
/// Even when multiple values are associated with the key, the "first" one is returned but is
|
||||||
|
/// not guaranteed to be chosen with any particular order; though, the returned item will be
|
||||||
|
/// consistent for each call to `get_mut` if the map has not changed.
|
||||||
|
///
|
||||||
|
/// See also: [`get_all`](Self::get_all).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
///
|
||||||
|
/// let mut cookie = map.get_mut(header::SET_COOKIE).unwrap();
|
||||||
|
/// assert_eq!(cookie, "one=1");
|
||||||
|
///
|
||||||
|
/// *cookie = HeaderValue::from_static("three=3");
|
||||||
|
/// assert_eq!(map.get(header::SET_COOKIE).unwrap(), "three=3");
|
||||||
|
///
|
||||||
|
/// assert!(map.get(header::HOST).is_none());
|
||||||
|
/// assert!(map.get("INVALID HEADER NAME").is_none());
|
||||||
|
/// ```
|
||||||
pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {
|
pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {
|
||||||
match key.try_as_name().ok()? {
|
match key.try_as_name().ok()? {
|
||||||
Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
|
Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
|
||||||
|
@ -164,6 +291,23 @@ impl HeaderMap {
|
||||||
/// The returned iterator does not incur any allocations and will yield no items if there are no
|
/// The returned iterator does not incur any allocations and will yield no items if there are no
|
||||||
/// values associated with the key. Iteration order is **not** guaranteed to be the same as
|
/// values associated with the key. Iteration order is **not** guaranteed to be the same as
|
||||||
/// insertion order.
|
/// insertion order.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
|
||||||
|
/// let mut map = HeaderMap::new();
|
||||||
|
///
|
||||||
|
/// let mut none_iter = map.get_all(header::ORIGIN);
|
||||||
|
/// assert!(none_iter.next().is_none());
|
||||||
|
///
|
||||||
|
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
///
|
||||||
|
/// let mut set_cookies_iter = map.get_all(header::SET_COOKIE);
|
||||||
|
/// assert_eq!(set_cookies_iter.next().unwrap(), "one=1");
|
||||||
|
/// assert_eq!(set_cookies_iter.next().unwrap(), "two=2");
|
||||||
|
/// assert!(set_cookies_iter.next().is_none());
|
||||||
|
/// ```
|
||||||
pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> {
|
pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> {
|
||||||
GetAll::new(self.get_value(key))
|
GetAll::new(self.get_value(key))
|
||||||
}
|
}
|
||||||
|
@ -185,7 +329,8 @@ impl HeaderMap {
|
||||||
/// previous values are removed and returned as a `Removed` iterator. The key is not updated;
|
/// previous values are removed and returned as a `Removed` iterator. The key is not updated;
|
||||||
/// this matters for types that can be `==` without being identical.
|
/// this matters for types that can be `==` without being identical.
|
||||||
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) -> Removed {
|
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) -> Removed {
|
||||||
Removed::new(self.inner.insert(key, Value::One(val)))
|
let value = self.inner.insert(key, Value::one(val));
|
||||||
|
Removed::new(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a name-value pair into the map.
|
/// Inserts a name-value pair into the map.
|
||||||
|
@ -199,7 +344,7 @@ impl HeaderMap {
|
||||||
entry.get_mut().append(value);
|
entry.get_mut().append(value);
|
||||||
}
|
}
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(Value::One(value));
|
entry.insert(Value::one(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -282,51 +427,6 @@ impl<'a> IntoIterator for &'a HeaderMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod as_header_name {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub trait AsHeaderName: Sealed {}
|
|
||||||
|
|
||||||
pub trait Sealed {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sealed for HeaderName {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
|
||||||
Ok(Cow::Borrowed(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsHeaderName for HeaderName {}
|
|
||||||
|
|
||||||
impl Sealed for &HeaderName {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
|
||||||
Ok(Cow::Borrowed(*self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsHeaderName for &HeaderName {}
|
|
||||||
|
|
||||||
impl Sealed for &str {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
|
||||||
HeaderName::from_str(self).map(Cow::Owned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsHeaderName for &str {}
|
|
||||||
|
|
||||||
impl Sealed for String {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
|
||||||
HeaderName::from_str(self).map(Cow::Owned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsHeaderName for String {}
|
|
||||||
|
|
||||||
impl Sealed for &String {
|
|
||||||
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
|
||||||
HeaderName::from_str(self).map(Cow::Owned)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl AsHeaderName for &String {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for all values with the same header name.
|
/// Iterator for all values with the same header name.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GetAll<'a> {
|
pub struct GetAll<'a> {
|
||||||
|
@ -346,13 +446,7 @@ impl<'a> Iterator for GetAll<'a> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let val = self.value?;
|
let val = self.value?;
|
||||||
|
|
||||||
match val {
|
match val.get(self.idx) {
|
||||||
Value::One(ref val) => {
|
|
||||||
// remove value to fast-path future next calls
|
|
||||||
self.value = None;
|
|
||||||
Some(val)
|
|
||||||
}
|
|
||||||
Value::Multi(ref vals) => match vals.get(self.idx) {
|
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
Some(val)
|
Some(val)
|
||||||
|
@ -362,7 +456,6 @@ impl<'a> Iterator for GetAll<'a> {
|
||||||
self.value = None;
|
self.value = None;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,17 +471,12 @@ impl<'a> Iterator for GetAll<'a> {
|
||||||
/// on [`HeaderMap`] that remove or replace items.
|
/// on [`HeaderMap`] that remove or replace items.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Removed {
|
pub struct Removed {
|
||||||
inner: smallvec::IntoIter<[HeaderValue; 4]>,
|
inner: Option<smallvec::IntoIter<[HeaderValue; 4]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Removed {
|
impl<'a> Removed {
|
||||||
fn new(value: Option<Value>) -> Self {
|
fn new(value: Option<Value>) -> Self {
|
||||||
let inner = match value {
|
let inner = value.map(|value| value.inner.into_iter());
|
||||||
Some(Value::One(val)) => smallvec![val].into_iter(),
|
|
||||||
Some(Value::Multi(vals)) => vals.into_iter(),
|
|
||||||
None => smallvec![].into_iter(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,12 +486,15 @@ impl Iterator for Removed {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.inner.next()
|
self.inner.as_mut()?.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
self.inner.size_hint()
|
match self.inner {
|
||||||
|
Some(ref iter) => iter.size_hint(),
|
||||||
|
None => (0, None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,15 +554,10 @@ impl<'a> Iterator for Iter<'a> {
|
||||||
|
|
||||||
let (name, value) = self.inner.next()?;
|
let (name, value) = self.inner.next()?;
|
||||||
|
|
||||||
match value {
|
// set up new inner iter and recurse into it
|
||||||
Value::One(ref value) => Some((name, value)),
|
self.multi_inner = Some((name, &value.inner));
|
||||||
Value::Multi(ref vals) => {
|
|
||||||
// set up new multi value inner iter and recurse into it
|
|
||||||
self.multi_inner = Some((name, vals));
|
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
@ -488,7 +574,7 @@ impl<'a> Iterator for Iter<'a> {
|
||||||
pub struct Drain<'a> {
|
pub struct Drain<'a> {
|
||||||
inner: hash_map::Drain<'a, HeaderName, Value>,
|
inner: hash_map::Drain<'a, HeaderName, Value>,
|
||||||
multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>,
|
multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>,
|
||||||
multi_inner_idx: usize,
|
multi_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drain<'a> {
|
impl<'a> Drain<'a> {
|
||||||
|
@ -496,7 +582,7 @@ impl<'a> Drain<'a> {
|
||||||
Self {
|
Self {
|
||||||
inner: iter,
|
inner: iter,
|
||||||
multi_inner: None,
|
multi_inner: None,
|
||||||
multi_inner_idx: 0,
|
multi_idx: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -508,26 +594,21 @@ impl<'a> Iterator for Drain<'a> {
|
||||||
// handle in-progress multi value iterators first
|
// handle in-progress multi value iterators first
|
||||||
if let Some((ref mut name, ref mut vals)) = self.multi_inner {
|
if let Some((ref mut name, ref mut vals)) = self.multi_inner {
|
||||||
if vals.len() > 0 {
|
if vals.len() > 0 {
|
||||||
// OPTIMISE: array removals
|
// OPTIMIZE: array removals
|
||||||
return Some((name.take(), vals.remove(0)));
|
return Some((name.take(), vals.remove(0)));
|
||||||
} else {
|
} else {
|
||||||
// no more items in value iterator; reset state
|
// no more items in value iterator; reset state
|
||||||
self.multi_inner = None;
|
self.multi_inner = None;
|
||||||
self.multi_inner_idx = 0;
|
self.multi_idx = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, mut value) = self.inner.next()?;
|
let (name, value) = self.inner.next()?;
|
||||||
|
|
||||||
match value {
|
// set up new inner iter and recurse into it
|
||||||
Value::One(value) => Some((Some(name), value)),
|
self.multi_inner = Some((Some(name), value.inner));
|
||||||
Value::Multi(ref mut vals) => {
|
|
||||||
// set up new multi value inner iter and recurse into it
|
|
||||||
self.multi_inner = Some((Some(name), mem::take(vals)));
|
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
@ -574,15 +655,10 @@ impl Iterator for IntoIter {
|
||||||
|
|
||||||
let (name, value) = self.inner.next()?;
|
let (name, value) = self.inner.next()?;
|
||||||
|
|
||||||
match value {
|
// set up new inner iter and recurse into it
|
||||||
Value::One(value) => Some((name, value)),
|
self.multi_inner = Some((name, value.inner.into_iter()));
|
||||||
Value::Multi(vals) => {
|
|
||||||
// set up new multi value inner iter and recurse into it
|
|
||||||
self.multi_inner = Some((name, vals.into_iter()));
|
|
||||||
self.next()
|
self.next()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub use http::header::*;
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
use crate::httpmessage::HttpMessage;
|
use crate::httpmessage::HttpMessage;
|
||||||
|
|
||||||
|
mod as_name;
|
||||||
mod into_pair;
|
mod into_pair;
|
||||||
mod into_value;
|
mod into_value;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -23,6 +24,7 @@ pub use self::common::*;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use self::shared::*;
|
pub use self::shared::*;
|
||||||
|
|
||||||
|
pub use self::as_name::AsHeaderName;
|
||||||
pub use self::into_pair::IntoHeaderPair;
|
pub use self::into_pair::IntoHeaderPair;
|
||||||
pub use self::into_value::IntoHeaderValue;
|
pub use self::into_value::IntoHeaderValue;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -97,26 +99,3 @@ pub(crate) const HTTP_VALUE: &AsciiSet = &CONTROLS
|
||||||
.add(b']')
|
.add(b']')
|
||||||
.add(b'{')
|
.add(b'{')
|
||||||
.add(b'}');
|
.add(b'}');
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::header;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_http_header_map_to_ours() {
|
|
||||||
let mut http_map = http::HeaderMap::new();
|
|
||||||
let map = HeaderMap::from_drain(http_map.drain());
|
|
||||||
assert!(map.is_empty());
|
|
||||||
|
|
||||||
let mut http_map = http::HeaderMap::new();
|
|
||||||
http_map.append(header::HOST, HeaderValue::from_static("duck.com"));
|
|
||||||
http_map.append(header::COOKIE, HeaderValue::from_static("one=1"));
|
|
||||||
http_map.append(header::COOKIE, HeaderValue::from_static("two=2"));
|
|
||||||
|
|
||||||
let map = HeaderMap::from_drain(http_map.drain());
|
|
||||||
assert_eq!(map.len(), 3);
|
|
||||||
assert!(map.contains_key(header::HOST));
|
|
||||||
assert!(map.contains_key(header::COOKIE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -897,16 +897,20 @@ mod tests {
|
||||||
.max_age(time::Duration::days(1))
|
.max_age(time::Duration::days(1))
|
||||||
.finish(),
|
.finish(),
|
||||||
)
|
)
|
||||||
.del_cookie(&cookies[1])
|
.del_cookie(&cookies[0])
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let mut val: Vec<_> = resp
|
let mut val = resp
|
||||||
.headers()
|
.headers()
|
||||||
.get_all(SET_COOKIE)
|
.get_all(SET_COOKIE)
|
||||||
.map(|v| v.to_str().unwrap().to_owned())
|
.map(|v| v.to_str().unwrap())
|
||||||
.collect();
|
.collect::<Vec<_>>();
|
||||||
val.sort();
|
val.sort();
|
||||||
|
|
||||||
|
// the .del_cookie call
|
||||||
assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
|
assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
|
||||||
|
|
||||||
|
// the .cookie call
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
val[1],
|
val[1],
|
||||||
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
|
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
|
||||||
|
@ -931,9 +935,9 @@ mod tests {
|
||||||
|
|
||||||
let mut iter = r.cookies();
|
let mut iter = r.cookies();
|
||||||
let v = iter.next().unwrap();
|
let v = iter.next().unwrap();
|
||||||
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
|
|
||||||
let v = iter.next().unwrap();
|
|
||||||
assert_eq!((v.name(), v.value()), ("original", "val100"));
|
assert_eq!((v.name(), v.value()), ("original", "val100"));
|
||||||
|
let v = iter.next().unwrap();
|
||||||
|
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue