mirror of https://github.com/fafhrd91/actix-web
clean up header map
This commit is contained in:
parent
dddb623a11
commit
5b20bf5446
|
@ -53,7 +53,6 @@ bytes = "1"
|
||||||
bytestring = "1"
|
bytestring = "1"
|
||||||
cookie = { version = "0.14.1", features = ["percent-encode"] }
|
cookie = { version = "0.14.1", features = ["percent-encode"] }
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
either = "1.5.3"
|
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-channel = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-channel = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
use std::{
|
use std::{borrow::Cow, collections::hash_map, str::FromStr};
|
||||||
collections::hash_map::{self, Entry},
|
|
||||||
convert::TryFrom,
|
|
||||||
};
|
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use either::Either;
|
use http::header::{HeaderName, HeaderValue, InvalidHeaderName};
|
||||||
use http::header::{HeaderName, HeaderValue};
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
/// A multi-map of HTTP headers.
|
/// A multi-map of HTTP headers.
|
||||||
///
|
///
|
||||||
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more values.
|
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s.
|
||||||
#[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>,
|
||||||
|
@ -54,33 +50,29 @@ impl Value {
|
||||||
impl HeaderMap {
|
impl HeaderMap {
|
||||||
/// Create an empty `HeaderMap`.
|
/// Create an empty `HeaderMap`.
|
||||||
///
|
///
|
||||||
/// The map will be created without any capacity. This function will not
|
/// The map will be created without any capacity. This function will not allocate.
|
||||||
/// allocate.
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
HeaderMap {
|
HeaderMap::default()
|
||||||
inner: AHashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an empty `HeaderMap` with the specified capacity.
|
/// Create an empty `HeaderMap` with the specified capacity.
|
||||||
///
|
///
|
||||||
/// The returned map will allocate internal storage in order to hold about
|
/// The returned map will allocate internal storage in order to hold about `capacity` elements
|
||||||
/// `capacity` elements without reallocating. However, this is a "best
|
/// without reallocating. However, this is a "best effort" as there are usage patterns that
|
||||||
/// effort" as there are usage patterns that could cause additional
|
/// could cause additional allocations before `capacity` headers are stored in the map.
|
||||||
/// allocations before `capacity` headers are stored in the map.
|
|
||||||
///
|
///
|
||||||
/// More capacity than requested may be allocated.
|
/// More capacity than requested may be allocated.
|
||||||
pub fn with_capacity(capacity: usize) -> HeaderMap {
|
pub fn with_capacity(capacity: usize) -> HeaderMap {
|
||||||
HeaderMap {
|
HeaderMap {
|
||||||
inner: AHashMap::with_capacity_and_hasher(capacity, Default::default()),
|
inner: AHashMap::with_capacity(capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of keys stored in the map.
|
/// Returns the number of keys stored in the map.
|
||||||
///
|
///
|
||||||
/// This number could be be less than or equal to actual headers stored in
|
/// This number could be be less than or equal to actual headers stored in the map.
|
||||||
/// the map.
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
|
// TODO: wat!? that's messed up
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,206 +81,210 @@ impl HeaderMap {
|
||||||
self.inner.len() == 0
|
self.inner.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the map, removing all key-value pairs. Keeps the allocated memory
|
/// Clears the map, removing all name-value pairs. Keeps the allocated memory for reuse.
|
||||||
/// for reuse.
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.inner.clear();
|
self.inner.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of headers the map can hold without reallocating.
|
/// Returns the number of headers the map can hold without reallocating.
|
||||||
///
|
///
|
||||||
/// This number is an approximation as certain usage patterns could cause
|
/// This number is an approximation as certain usage patterns could cause additional allocations
|
||||||
/// additional allocations before the returned capacity is filled.
|
/// before the returned capacity is filled.
|
||||||
pub fn capacity(&self) -> usize {
|
pub fn capacity(&self) -> usize {
|
||||||
self.inner.capacity()
|
self.inner.capacity()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reserves capacity for at least `additional` more headers to be inserted
|
/// Reserves capacity for at least `additional` more headers to be inserted in the map.
|
||||||
/// into the `HeaderMap`.
|
|
||||||
///
|
///
|
||||||
/// The header map may reserve more space to avoid frequent reallocations.
|
/// The header map may reserve more space to avoid frequent reallocations. Like with
|
||||||
/// Like with `with_capacity`, this will be a "best effort" to avoid
|
/// `with_capacity`, this will be a "best effort" to avoid allocations until `additional` more
|
||||||
/// allocations until `additional` more headers are inserted. Certain usage
|
/// headers are inserted. Certain usage patterns could cause additional allocations before the
|
||||||
/// patterns could cause additional allocations before the number is
|
/// number is reached.
|
||||||
/// reached.
|
|
||||||
pub fn reserve(&mut self, additional: usize) {
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
self.inner.reserve(additional)
|
self.inner.reserve(additional)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the value associated with the key.
|
fn get_value(&self, key: impl AsHeaderName) -> Option<&Value> {
|
||||||
///
|
match key.try_as_name().ok()? {
|
||||||
/// If there are multiple values associated with the key, then the first one
|
Cow::Borrowed(name) => self.inner.get(name),
|
||||||
/// is returned. Use `get_all` to get all values associated with a given
|
Cow::Owned(name) => self.inner.get(&name),
|
||||||
/// key. Returns `None` if there are no values associated with the key.
|
|
||||||
pub fn get<N: AsName>(&self, name: N) -> Option<&HeaderValue> {
|
|
||||||
self.get2(name).map(|v| v.first())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get2<N: AsName>(&self, name: N) -> Option<&Value> {
|
|
||||||
match name.as_name() {
|
|
||||||
Either::Left(name) => self.inner.get(name),
|
|
||||||
Either::Right(s) => {
|
|
||||||
if let Ok(name) = HeaderName::try_from(s) {
|
|
||||||
self.inner.get(&name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator of all values associated with a key.
|
/// Returns a reference to the first value associated with a header name.
|
||||||
|
///
|
||||||
|
/// If there are multiple values associated with the key, then the first one is returned.
|
||||||
|
/// Use `get_all` to get all values associated with a given key. Returns `None` if there are no
|
||||||
|
/// values associated with the key.
|
||||||
|
pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {
|
||||||
|
self.get_value(key).map(|val| val.first())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the first value associated a header name.
|
||||||
|
///
|
||||||
|
/// If there are multiple values associated with the key, then the first one is returned.
|
||||||
|
/// Use `get_all` to get all values associated with a given key. Returns `None` if there are no
|
||||||
|
/// values associated with the key.
|
||||||
|
pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {
|
||||||
|
match key.try_as_name().ok()? {
|
||||||
|
Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
|
||||||
|
Cow::Owned(name) => self.inner.get_mut(&name).map(|v| v.first_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of all values associated with a header name.
|
||||||
///
|
///
|
||||||
/// The returned view does not incur any allocations and allows iterating the values associated
|
/// The returned view does not incur any allocations and allows iterating the values associated
|
||||||
/// with the key. Returns `None` if there are no values associated with the key. Iteration order
|
/// with the key. Iterator will yield no items if there are no values associated with the key.
|
||||||
/// is not guaranteed to be the same as insertion order.
|
/// Iteration order is not guaranteed to be the same as insertion order.
|
||||||
pub fn get_all<N: AsName>(&self, name: N) -> GetAll<'_> {
|
pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> {
|
||||||
GetAll {
|
GetAll::new(self.get_value(key))
|
||||||
idx: 0,
|
|
||||||
item: self.get2(name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the value associated with the key.
|
|
||||||
///
|
|
||||||
/// If there are multiple values associated with the key, then the first one
|
|
||||||
/// is returned. Use `entry` to get all values associated with a given
|
|
||||||
/// key. Returns `None` if there are no values associated with the key.
|
|
||||||
pub fn get_mut<N: AsName>(&mut self, name: N) -> Option<&mut HeaderValue> {
|
|
||||||
match name.as_name() {
|
|
||||||
Either::Left(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
|
|
||||||
Either::Right(s) => {
|
|
||||||
if let Ok(name) = HeaderName::try_from(s) {
|
|
||||||
self.inner.get_mut(&name).map(|v| v.first_mut())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the map contains a value for the specified key.
|
/// Returns true if the map contains a value for the specified key.
|
||||||
pub fn contains_key<N: AsName>(&self, key: N) -> bool {
|
///
|
||||||
match key.as_name() {
|
/// Invalid header names will simply return false.
|
||||||
Either::Left(name) => self.inner.contains_key(name),
|
pub fn contains_key(&self, key: impl AsHeaderName) -> bool {
|
||||||
Either::Right(s) => {
|
match key.try_as_name() {
|
||||||
if let Ok(name) = HeaderName::try_from(s) {
|
Ok(Cow::Borrowed(name)) => self.inner.contains_key(name),
|
||||||
self.inner.contains_key(&name)
|
Ok(Cow::Owned(name)) => self.inner.contains_key(&name),
|
||||||
} else {
|
Err(_) => false,
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator visiting all key-value pairs.
|
/// An iterator visiting all name-value pairs.
|
||||||
///
|
///
|
||||||
/// The iteration order is arbitrary, but consistent across platforms for
|
/// The iteration order is arbitrary but consistent across platforms for the same crate version.
|
||||||
/// the same crate version. Each key will be yielded once per associated
|
/// Each key will be yielded once per associated value. So, if a key has 3 associated values, it
|
||||||
/// value. So, if a key has 3 associated values, it will be yielded 3 times.
|
/// will be yielded 3 times.
|
||||||
pub fn iter(&self) -> Iter<'_> {
|
pub fn iter(&self) -> Iter<'_> {
|
||||||
Iter::new(self.inner.iter())
|
Iter::new(self.inner.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator visiting all keys.
|
/// An iterator visiting all keys.
|
||||||
///
|
///
|
||||||
/// The iteration order is arbitrary, but consistent across platforms for
|
/// The iteration order is arbitrary but consistent across platforms for the same crate version.
|
||||||
/// the same crate version. Each key will be yielded only once even if it
|
/// Each key will be yielded only once even if it has multiple associated values.
|
||||||
/// has multiple associated values.
|
|
||||||
pub fn keys(&self) -> Keys<'_> {
|
pub fn keys(&self) -> Keys<'_> {
|
||||||
Keys(self.inner.keys())
|
Keys(self.inner.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a key-value pair into the map.
|
/// Inserts a name-value pair into the map.
|
||||||
///
|
///
|
||||||
/// If the map did not previously have this key present, then `None` is
|
/// If the map did have this key present, the new value is associated with the key and all
|
||||||
/// returned.
|
/// previous values are removed. **Note** that only a single one of the previous values
|
||||||
///
|
/// is returned. If there are multiple values that have been previously associated with the key,
|
||||||
/// If the map did have this key present, the new value is associated with
|
/// then the first one is returned. See `insert_mult` on `OccupiedEntry` for an API that returns
|
||||||
/// the key and all previous values are removed. **Note** that only a single
|
|
||||||
/// one of the previous values is returned. If there are multiple values
|
|
||||||
/// that have been previously associated with the key, then the first one is
|
|
||||||
/// returned. See `insert_mult` on `OccupiedEntry` for an API that returns
|
|
||||||
/// all values.
|
/// all values.
|
||||||
///
|
///
|
||||||
/// The key is not updated, though; this matters for types that can be `==`
|
/// The key is not updated, though; this matters for types that can be `==`
|
||||||
/// without being identical.
|
/// without being identical.
|
||||||
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) {
|
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) {
|
||||||
let _ = self.inner.insert(key, Value::One(val));
|
self.inner.insert(key, Value::One(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a key-value pair into the map.
|
/// Inserts a name-value pair into the map.
|
||||||
///
|
///
|
||||||
/// If the map did not previously have this key present, then `false` is
|
/// If the map did have this key present, the new value is pushed to the end of the list of
|
||||||
/// returned.
|
/// values currently associated with the key. The key is not updated, though; this matters for
|
||||||
///
|
/// types that can be `==` without being identical.
|
||||||
/// If the map did have this key present, the new value is pushed to the end
|
|
||||||
/// of the list of values currently associated with the key. The key is not
|
|
||||||
/// updated, though; this matters for types that can be `==` without being
|
|
||||||
/// identical.
|
|
||||||
pub fn append(&mut self, key: HeaderName, value: HeaderValue) {
|
pub fn append(&mut self, key: HeaderName, value: HeaderValue) {
|
||||||
match self.inner.entry(key) {
|
match self.inner.entry(key) {
|
||||||
Entry::Occupied(mut entry) => entry.get_mut().append(value),
|
hash_map::Entry::Occupied(mut entry) => {
|
||||||
Entry::Vacant(entry) => {
|
entry.get_mut().append(value);
|
||||||
|
}
|
||||||
|
hash_map::Entry::Vacant(entry) => {
|
||||||
entry.insert(Value::One(value));
|
entry.insert(Value::One(value));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all headers for a particular header name from the map.
|
/// Removes all headers for a particular header name from the map.
|
||||||
pub fn remove<N: AsName>(&mut self, key: N) {
|
// TODO: return value somehow
|
||||||
match key.as_name() {
|
pub fn remove(&mut self, key: impl AsHeaderName) {
|
||||||
Either::Left(name) => {
|
match key.try_as_name() {
|
||||||
let _ = self.inner.remove(name);
|
Ok(Cow::Borrowed(name)) => self.inner.remove(name),
|
||||||
}
|
Ok(Cow::Owned(name)) => self.inner.remove(&name),
|
||||||
Either::Right(s) => {
|
Err(_) => None,
|
||||||
if let Ok(name) = HeaderName::try_from(s) {
|
};
|
||||||
let _ = self.inner.remove(&name);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
impl IntoIterator for HeaderMap {
|
||||||
|
type Item = (HeaderName, HeaderValue);
|
||||||
|
type IntoIter = IntoIter;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
IntoIter::new(self.inner.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a HeaderMap {
|
||||||
|
type Item = (&'a HeaderName, &'a HeaderValue);
|
||||||
|
type IntoIter = Iter<'a>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
Iter::new(self.inner.iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AsHeaderName: sealed::Sealed {}
|
||||||
|
|
||||||
|
mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
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 {}
|
||||||
|
|
||||||
#[doc(hidden)]
|
impl Sealed for &HeaderName {
|
||||||
pub trait AsName {
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str>;
|
Ok(Cow::Borrowed(*self))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsName for HeaderName {
|
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
|
||||||
Either::Left(self)
|
|
||||||
}
|
}
|
||||||
}
|
impl AsHeaderName for &HeaderName {}
|
||||||
|
|
||||||
impl<'a> AsName for &'a HeaderName {
|
impl Sealed for &str {
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
Either::Left(self)
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
impl AsHeaderName for &str {}
|
||||||
|
|
||||||
impl<'a> AsName for &'a str {
|
impl Sealed for String {
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
Either::Right(self)
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
impl AsHeaderName for String {}
|
||||||
|
|
||||||
impl AsName for String {
|
impl Sealed for &String {
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
|
||||||
Either::Right(self.as_str())
|
HeaderName::from_str(self).map(Cow::Owned)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AsName for &'a String {
|
|
||||||
fn as_name(&self) -> Either<&HeaderName, &str> {
|
|
||||||
Either::Right(self.as_str())
|
|
||||||
}
|
}
|
||||||
|
impl AsHeaderName for &String {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator for all values in a `HeaderMap` with the same name.
|
/// Iterator for all values in a `HeaderMap` with the same name.
|
||||||
pub struct GetAll<'a> {
|
pub struct GetAll<'a> {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
item: Option<&'a Value>,
|
value: Option<&'a Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GetAll<'a> {
|
||||||
|
fn new(value: Option<&'a Value>) -> Self {
|
||||||
|
Self { idx: 0, value }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for GetAll<'a> {
|
impl<'a> Iterator for GetAll<'a> {
|
||||||
|
@ -296,25 +292,25 @@ impl<'a> Iterator for GetAll<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<&'a HeaderValue> {
|
fn next(&mut self) -> Option<&'a HeaderValue> {
|
||||||
if let Some(ref val) = self.item {
|
let val = self.value?;
|
||||||
match val {
|
|
||||||
Value::One(ref val) => {
|
match val {
|
||||||
self.item.take();
|
Value::One(ref val) => {
|
||||||
|
// remove value to fast-path future next calls
|
||||||
|
self.value = None;
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
Value::Multi(ref vec) => match vec.get(self.idx) {
|
||||||
|
Some(val) => {
|
||||||
|
self.idx += 1;
|
||||||
Some(val)
|
Some(val)
|
||||||
}
|
}
|
||||||
Value::Multi(ref vec) => {
|
None => {
|
||||||
if self.idx < vec.len() {
|
// current index is none; remove value to fast-path future next calls
|
||||||
let item = Some(&vec[self.idx]);
|
self.value = None;
|
||||||
self.idx += 1;
|
None
|
||||||
item
|
|
||||||
} else {
|
|
||||||
self.item.take();
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,27 +326,18 @@ impl<'a> Iterator for Keys<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a HeaderMap {
|
|
||||||
type Item = (&'a HeaderName, &'a HeaderValue);
|
|
||||||
type IntoIter = Iter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Iter<'a> {
|
pub struct Iter<'a> {
|
||||||
idx: usize,
|
|
||||||
current: Option<(&'a HeaderName, &'a SmallVec<[HeaderValue; 4]>)>,
|
|
||||||
iter: hash_map::Iter<'a, HeaderName, Value>,
|
iter: hash_map::Iter<'a, HeaderName, Value>,
|
||||||
|
multi_inner: Option<(&'a HeaderName, &'a SmallVec<[HeaderValue; 4]>)>,
|
||||||
|
multi_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iter<'a> {
|
impl<'a> Iter<'a> {
|
||||||
fn new(iter: hash_map::Iter<'a, HeaderName, Value>) -> Self {
|
fn new(iter: hash_map::Iter<'a, HeaderName, Value>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
iter,
|
iter,
|
||||||
idx: 0,
|
multi_idx: 0,
|
||||||
current: None,
|
multi_inner: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,26 +347,171 @@ impl<'a> Iterator for Iter<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
|
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
|
||||||
if let Some(ref mut item) = self.current {
|
// handle in-progress multi value lists first
|
||||||
if self.idx < item.1.len() {
|
if let Some((ref name, ref mut vals)) = self.multi_inner {
|
||||||
let item = (item.0, &item.1[self.idx]);
|
match vals.get(self.multi_idx) {
|
||||||
self.idx += 1;
|
Some(val) => {
|
||||||
return Some(item);
|
self.multi_idx += 1;
|
||||||
} else {
|
return Some((name, val));
|
||||||
self.idx = 0;
|
}
|
||||||
self.current.take();
|
None => {
|
||||||
}
|
// no more items in value list; reset state
|
||||||
}
|
self.multi_idx = 0;
|
||||||
if let Some(item) = self.iter.next() {
|
self.multi_inner = None;
|
||||||
match item.1 {
|
|
||||||
Value::One(ref value) => Some((item.0, value)),
|
|
||||||
Value::Multi(ref vec) => {
|
|
||||||
self.current = Some((item.0, vec));
|
|
||||||
self.next()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
|
let (name, value) = self.iter.next()?;
|
||||||
|
|
||||||
|
match value {
|
||||||
|
Value::One(ref value) => Some((name, value)),
|
||||||
|
Value::Multi(ref vals) => {
|
||||||
|
// set up new multi value inner iter and recurse into it
|
||||||
|
self.multi_inner = Some((name, vals));
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct IntoIter {
|
||||||
|
iter: hash_map::IntoIter<HeaderName, Value>,
|
||||||
|
multi_inner: Option<(HeaderName, smallvec::IntoIter<[HeaderValue; 4]>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIter {
|
||||||
|
fn new(iter: hash_map::IntoIter<HeaderName, Value>) -> Self {
|
||||||
|
Self {
|
||||||
|
iter,
|
||||||
|
multi_inner: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for IntoIter {
|
||||||
|
type Item = (HeaderName, HeaderValue);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<(HeaderName, HeaderValue)> {
|
||||||
|
// handle in-progress multi value iterators first
|
||||||
|
if let Some((ref name, ref mut vals)) = self.multi_inner {
|
||||||
|
match vals.next() {
|
||||||
|
Some(val) => {
|
||||||
|
return Some((name.clone(), val));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// no more items in value iterator; reset state
|
||||||
|
self.multi_inner = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (name, value) = self.iter.next()?;
|
||||||
|
|
||||||
|
match value {
|
||||||
|
Value::One(value) => Some((name, value)),
|
||||||
|
Value::Multi(vals) => {
|
||||||
|
// set up new multi value inner iter and recurse into it
|
||||||
|
self.multi_inner = Some((name, vals.into_iter()));
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use http::header;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new() {
|
||||||
|
let map = HeaderMap::new();
|
||||||
|
assert_eq!(map.len(), 0);
|
||||||
|
assert_eq!(map.capacity(), 0);
|
||||||
|
|
||||||
|
let map = HeaderMap::with_capacity(16);
|
||||||
|
assert_eq!(map.len(), 0);
|
||||||
|
assert!(map.capacity() >= 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert() {
|
||||||
|
let mut map = HeaderMap::new();
|
||||||
|
|
||||||
|
map.insert(header::LOCATION, HeaderValue::from_static("/test"));
|
||||||
|
assert_eq!(map.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contains() {
|
||||||
|
let mut map = HeaderMap::new();
|
||||||
|
assert!(!map.contains_key(header::LOCATION));
|
||||||
|
|
||||||
|
map.insert(header::LOCATION, HeaderValue::from_static("/test"));
|
||||||
|
assert!(map.contains_key(header::LOCATION));
|
||||||
|
assert!(map.contains_key("Location"));
|
||||||
|
assert!(map.contains_key("Location".to_owned()));
|
||||||
|
assert!(map.contains_key("location"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entries_iter() {
|
||||||
|
let mut map = HeaderMap::new();
|
||||||
|
|
||||||
|
map.append(header::HOST, HeaderValue::from_static("duck.com"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
|
||||||
|
let mut iter = map.iter();
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
|
||||||
|
let pairs = map.iter().collect::<Vec<_>>();
|
||||||
|
assert!(pairs.contains(&(&header::HOST, &HeaderValue::from_static("duck.com"))));
|
||||||
|
assert!(pairs.contains(&(&header::COOKIE, &HeaderValue::from_static("one=1"))));
|
||||||
|
assert!(pairs.contains(&(&header::COOKIE, &HeaderValue::from_static("two=2"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entries_into_iter() {
|
||||||
|
let mut map = HeaderMap::new();
|
||||||
|
|
||||||
|
map.append(header::HOST, HeaderValue::from_static("duck.com"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
|
||||||
|
let mut iter = map.into_iter();
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entries_iter_and_into_iter_same_order() {
|
||||||
|
let mut map = HeaderMap::new();
|
||||||
|
|
||||||
|
map.append(header::HOST, HeaderValue::from_static("duck.com"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("one=1"));
|
||||||
|
map.append(header::COOKIE, HeaderValue::from_static("two=2"));
|
||||||
|
|
||||||
|
let mut iter = map.iter();
|
||||||
|
let mut into_iter = map.clone().into_iter();
|
||||||
|
|
||||||
|
assert_eq!(iter.next().map(owned_pair), into_iter.next());
|
||||||
|
assert_eq!(iter.next().map(owned_pair), into_iter.next());
|
||||||
|
assert_eq!(iter.next().map(owned_pair), into_iter.next());
|
||||||
|
assert_eq!(iter.next().map(owned_pair), into_iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn owned_pair<'a>(
|
||||||
|
(name, val): (&'a HeaderName, &'a HeaderValue),
|
||||||
|
) -> (HeaderName, HeaderValue) {
|
||||||
|
(name.clone(), val.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -177,13 +177,17 @@ impl<P> fmt::Debug for Request<P> {
|
||||||
self.method(),
|
self.method(),
|
||||||
self.path()
|
self.path()
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(q) = self.uri().query().as_ref() {
|
if let Some(q) = self.uri().query().as_ref() {
|
||||||
writeln!(f, " query: ?{:?}", q)?;
|
writeln!(f, " query: ?{:?}", q)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(f, " headers:")?;
|
writeln!(f, " headers:")?;
|
||||||
for (key, val) in self.headers() {
|
|
||||||
|
for (key, val) in self.headers().iter() {
|
||||||
writeln!(f, " {:?}: {:?}", key, val)?;
|
writeln!(f, " {:?}: {:?}", key, val)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -752,9 +752,11 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
|
||||||
let mut msg = BoxedResponseHead::new(head.status);
|
let mut msg = BoxedResponseHead::new(head.status);
|
||||||
msg.version = head.version;
|
msg.version = head.version;
|
||||||
msg.reason = head.reason;
|
msg.reason = head.reason;
|
||||||
for (k, v) in &head.headers {
|
|
||||||
|
for (k, v) in head.headers.iter() {
|
||||||
msg.headers.append(k.clone(), v.clone());
|
msg.headers.append(k.clone(), v.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.no_chunking(!head.chunked());
|
msg.no_chunking(!head.chunked());
|
||||||
|
|
||||||
ResponseBuilder {
|
ResponseBuilder {
|
||||||
|
|
Loading…
Reference in New Issue