diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-15 16:37:08 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-17 16:30:22 -0600 |
| commit | 45df4d0d9b577fecee798d672695fe24ff57fb1b (patch) | |
| tree | 1b99bf645035b58e0d6db08c7a83521f41f7a75b /vendor/http/src/header | |
| parent | f94f79608393d4ab127db63cc41668445ef6b243 (diff) | |
feat: migrate from Cedar to SpiceDB authorization system
This is a major architectural change that replaces the Cedar policy-based
authorization system with SpiceDB's relation-based authorization.
Key changes:
- Migrate from Rust to Go implementation
- Replace Cedar policies with SpiceDB schema and relationships
- Switch from envoy `ext_authz` with Cedar to SpiceDB permission checks
- Update build system and dependencies for Go ecosystem
- Maintain Envoy integration for external authorization
This change enables more flexible permission modeling through SpiceDB's
Google Zanzibar inspired relation-based system, supporting complex
hierarchical permissions that were difficult to express in Cedar.
Breaking change: Existing Cedar policies and Rust-based configuration
will no longer work and need to be migrated to SpiceDB schema.
Diffstat (limited to 'vendor/http/src/header')
| -rw-r--r-- | vendor/http/src/header/map.rs | 3903 | ||||
| -rw-r--r-- | vendor/http/src/header/mod.rs | 176 | ||||
| -rw-r--r-- | vendor/http/src/header/name.rs | 1922 | ||||
| -rw-r--r-- | vendor/http/src/header/value.rs | 798 |
4 files changed, 0 insertions, 6799 deletions
diff --git a/vendor/http/src/header/map.rs b/vendor/http/src/header/map.rs deleted file mode 100644 index ebbc5937..00000000 --- a/vendor/http/src/header/map.rs +++ /dev/null @@ -1,3903 +0,0 @@ -use std::collections::hash_map::RandomState; -use std::collections::HashMap; -use std::convert::TryFrom; -use std::hash::{BuildHasher, Hash, Hasher}; -use std::iter::{FromIterator, FusedIterator}; -use std::marker::PhantomData; -use std::{fmt, mem, ops, ptr, vec}; - -use crate::Error; - -use super::name::{HdrName, HeaderName, InvalidHeaderName}; -use super::HeaderValue; - -pub use self::as_header_name::AsHeaderName; -pub use self::into_header_name::IntoHeaderName; - -/// A set of HTTP headers -/// -/// `HeaderMap` is a multimap of [`HeaderName`] to values. -/// -/// [`HeaderName`]: struct.HeaderName.html -/// -/// # Examples -/// -/// Basic usage -/// -/// ``` -/// # use http::HeaderMap; -/// # use http::header::{CONTENT_LENGTH, HOST, LOCATION}; -/// let mut headers = HeaderMap::new(); -/// -/// headers.insert(HOST, "example.com".parse().unwrap()); -/// headers.insert(CONTENT_LENGTH, "123".parse().unwrap()); -/// -/// assert!(headers.contains_key(HOST)); -/// assert!(!headers.contains_key(LOCATION)); -/// -/// assert_eq!(headers[HOST], "example.com"); -/// -/// headers.remove(HOST); -/// -/// assert!(!headers.contains_key(HOST)); -/// ``` -#[derive(Clone)] -pub struct HeaderMap<T = HeaderValue> { - // Used to mask values to get an index - mask: Size, - indices: Box<[Pos]>, - entries: Vec<Bucket<T>>, - extra_values: Vec<ExtraValue<T>>, - danger: Danger, -} - -// # Implementation notes -// -// Below, you will find a fairly large amount of code. Most of this is to -// provide the necessary functions to efficiently manipulate the header -// multimap. The core hashing table is based on robin hood hashing [1]. While -// this is the same hashing algorithm used as part of Rust's `HashMap` in -// stdlib, many implementation details are different. The two primary reasons -// for this divergence are that `HeaderMap` is a multimap and the structure has -// been optimized to take advantage of the characteristics of HTTP headers. -// -// ## Structure Layout -// -// Most of the data contained by `HeaderMap` is *not* stored in the hash table. -// Instead, pairs of header name and *first* associated header value are stored -// in the `entries` vector. If the header name has more than one associated -// header value, then additional values are stored in `extra_values`. The actual -// hash table (`indices`) only maps hash codes to indices in `entries`. This -// means that, when an eviction happens, the actual header name and value stay -// put and only a tiny amount of memory has to be copied. -// -// Extra values associated with a header name are tracked using a linked list. -// Links are formed with offsets into `extra_values` and not pointers. -// -// [1]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing - -/// `HeaderMap` entry iterator. -/// -/// Yields `(&HeaderName, &value)` tuples. The same header name may be yielded -/// more than once if it has more than one associated value. -#[derive(Debug)] -pub struct Iter<'a, T> { - map: &'a HeaderMap<T>, - entry: usize, - cursor: Option<Cursor>, -} - -/// `HeaderMap` mutable entry iterator -/// -/// Yields `(&HeaderName, &mut value)` tuples. The same header name may be -/// yielded more than once if it has more than one associated value. -#[derive(Debug)] -pub struct IterMut<'a, T> { - map: *mut HeaderMap<T>, - entry: usize, - cursor: Option<Cursor>, - lt: PhantomData<&'a mut HeaderMap<T>>, -} - -/// An owning iterator over the entries of a `HeaderMap`. -/// -/// This struct is created by the `into_iter` method on `HeaderMap`. -#[derive(Debug)] -pub struct IntoIter<T> { - // If None, pull from `entries` - next: Option<usize>, - entries: vec::IntoIter<Bucket<T>>, - extra_values: Vec<ExtraValue<T>>, -} - -/// An iterator over `HeaderMap` keys. -/// -/// Each header name is yielded only once, even if it has more than one -/// associated value. -#[derive(Debug)] -pub struct Keys<'a, T> { - inner: ::std::slice::Iter<'a, Bucket<T>>, -} - -/// `HeaderMap` value iterator. -/// -/// Each value contained in the `HeaderMap` will be yielded. -#[derive(Debug)] -pub struct Values<'a, T> { - inner: Iter<'a, T>, -} - -/// `HeaderMap` mutable value iterator -#[derive(Debug)] -pub struct ValuesMut<'a, T> { - inner: IterMut<'a, T>, -} - -/// A drain iterator for `HeaderMap`. -#[derive(Debug)] -pub struct Drain<'a, T> { - idx: usize, - len: usize, - entries: *mut [Bucket<T>], - // If None, pull from `entries` - next: Option<usize>, - extra_values: *mut Vec<ExtraValue<T>>, - lt: PhantomData<&'a mut HeaderMap<T>>, -} - -/// A view to all values stored in a single entry. -/// -/// This struct is returned by `HeaderMap::get_all`. -#[derive(Debug)] -pub struct GetAll<'a, T> { - map: &'a HeaderMap<T>, - index: Option<usize>, -} - -/// A view into a single location in a `HeaderMap`, which may be vacant or occupied. -#[derive(Debug)] -pub enum Entry<'a, T: 'a> { - /// An occupied entry - Occupied(OccupiedEntry<'a, T>), - - /// A vacant entry - Vacant(VacantEntry<'a, T>), -} - -/// A view into a single empty location in a `HeaderMap`. -/// -/// This struct is returned as part of the `Entry` enum. -#[derive(Debug)] -pub struct VacantEntry<'a, T> { - map: &'a mut HeaderMap<T>, - key: HeaderName, - hash: HashValue, - probe: usize, - danger: bool, -} - -/// A view into a single occupied location in a `HeaderMap`. -/// -/// This struct is returned as part of the `Entry` enum. -#[derive(Debug)] -pub struct OccupiedEntry<'a, T> { - map: &'a mut HeaderMap<T>, - probe: usize, - index: usize, -} - -/// An iterator of all values associated with a single header name. -#[derive(Debug)] -pub struct ValueIter<'a, T> { - map: &'a HeaderMap<T>, - index: usize, - front: Option<Cursor>, - back: Option<Cursor>, -} - -/// A mutable iterator of all values associated with a single header name. -#[derive(Debug)] -pub struct ValueIterMut<'a, T> { - map: *mut HeaderMap<T>, - index: usize, - front: Option<Cursor>, - back: Option<Cursor>, - lt: PhantomData<&'a mut HeaderMap<T>>, -} - -/// An drain iterator of all values associated with a single header name. -#[derive(Debug)] -pub struct ValueDrain<'a, T> { - first: Option<T>, - next: Option<::std::vec::IntoIter<T>>, - lt: PhantomData<&'a mut HeaderMap<T>>, -} - -/// Error returned when max capacity of `HeaderMap` is exceeded -pub struct MaxSizeReached { - _priv: (), -} - -/// Tracks the value iterator state -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum Cursor { - Head, - Values(usize), -} - -/// Type used for representing the size of a HeaderMap value. -/// -/// 32,768 is more than enough entries for a single header map. Setting this -/// limit enables using `u16` to represent all offsets, which takes 2 bytes -/// instead of 8 on 64 bit processors. -/// -/// Setting this limit is especially beneficial for `indices`, making it more -/// cache friendly. More hash codes can fit in a cache line. -/// -/// You may notice that `u16` may represent more than 32,768 values. This is -/// true, but 32,768 should be plenty and it allows us to reserve the top bit -/// for future usage. -type Size = u16; - -/// This limit falls out from above. -const MAX_SIZE: usize = 1 << 15; - -/// An entry in the hash table. This represents the full hash code for an entry -/// as well as the position of the entry in the `entries` vector. -#[derive(Copy, Clone)] -struct Pos { - // Index in the `entries` vec - index: Size, - // Full hash value for the entry. - hash: HashValue, -} - -/// Hash values are limited to u16 as well. While `fast_hash` and `Hasher` -/// return `usize` hash codes, limiting the effective hash code to the lower 16 -/// bits is fine since we know that the `indices` vector will never grow beyond -/// that size. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -struct HashValue(u16); - -/// Stores the data associated with a `HeaderMap` entry. Only the first value is -/// included in this struct. If a header name has more than one associated -/// value, all extra values are stored in the `extra_values` vector. A doubly -/// linked list of entries is maintained. The doubly linked list is used so that -/// removing a value is constant time. This also has the nice property of -/// enabling double ended iteration. -#[derive(Debug, Clone)] -struct Bucket<T> { - hash: HashValue, - key: HeaderName, - value: T, - links: Option<Links>, -} - -/// The head and tail of the value linked list. -#[derive(Debug, Copy, Clone)] -struct Links { - next: usize, - tail: usize, -} - -/// Access to the `links` value in a slice of buckets. -/// -/// It's important that no other field is accessed, since it may have been -/// freed in a `Drain` iterator. -#[derive(Debug)] -struct RawLinks<T>(*mut [Bucket<T>]); - -/// Node in doubly-linked list of header value entries -#[derive(Debug, Clone)] -struct ExtraValue<T> { - value: T, - prev: Link, - next: Link, -} - -/// A header value node is either linked to another node in the `extra_values` -/// list or it points to an entry in `entries`. The entry in `entries` is the -/// start of the list and holds the associated header name. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum Link { - Entry(usize), - Extra(usize), -} - -/// Tracks the header map danger level! This relates to the adaptive hashing -/// algorithm. A HeaderMap starts in the "green" state, when a large number of -/// collisions are detected, it transitions to the yellow state. At this point, -/// the header map will either grow and switch back to the green state OR it -/// will transition to the red state. -/// -/// When in the red state, a safe hashing algorithm is used and all values in -/// the header map have to be rehashed. -#[derive(Clone)] -enum Danger { - Green, - Yellow, - Red(RandomState), -} - -// Constants related to detecting DOS attacks. -// -// Displacement is the number of entries that get shifted when inserting a new -// value. Forward shift is how far the entry gets stored from the ideal -// position. -// -// The current constant values were picked from another implementation. It could -// be that there are different values better suited to the header map case. -const DISPLACEMENT_THRESHOLD: usize = 128; -const FORWARD_SHIFT_THRESHOLD: usize = 512; - -// The default strategy for handling the yellow danger state is to increase the -// header map capacity in order to (hopefully) reduce the number of collisions. -// If growing the hash map would cause the load factor to drop bellow this -// threshold, then instead of growing, the headermap is switched to the red -// danger state and safe hashing is used instead. -const LOAD_FACTOR_THRESHOLD: f32 = 0.2; - -// Macro used to iterate the hash table starting at a given point, looping when -// the end is hit. -macro_rules! probe_loop { - ($label:tt: $probe_var: ident < $len: expr, $body: expr) => { - debug_assert!($len > 0); - $label: - loop { - if $probe_var < $len { - $body - $probe_var += 1; - } else { - $probe_var = 0; - } - } - }; - ($probe_var: ident < $len: expr, $body: expr) => { - debug_assert!($len > 0); - loop { - if $probe_var < $len { - $body - $probe_var += 1; - } else { - $probe_var = 0; - } - } - }; -} - -// First part of the robinhood algorithm. Given a key, find the slot in which it -// will be inserted. This is done by starting at the "ideal" spot. Then scanning -// until the destination slot is found. A destination slot is either the next -// empty slot or the next slot that is occupied by an entry that has a lower -// displacement (displacement is the distance from the ideal spot). -// -// This is implemented as a macro instead of a function that takes a closure in -// order to guarantee that it is "inlined". There is no way to annotate closures -// to guarantee inlining. -macro_rules! insert_phase_one { - ($map:ident, - $key:expr, - $probe:ident, - $pos:ident, - $hash:ident, - $danger:ident, - $vacant:expr, - $occupied:expr, - $robinhood:expr) => - {{ - let $hash = hash_elem_using(&$map.danger, &$key); - let mut $probe = desired_pos($map.mask, $hash); - let mut dist = 0; - let ret; - - // Start at the ideal position, checking all slots - probe_loop!('probe: $probe < $map.indices.len(), { - if let Some(($pos, entry_hash)) = $map.indices[$probe].resolve() { - // The slot is already occupied, but check if it has a lower - // displacement. - let their_dist = probe_distance($map.mask, entry_hash, $probe); - - if their_dist < dist { - // The new key's distance is larger, so claim this spot and - // displace the current entry. - // - // Check if this insertion is above the danger threshold. - let $danger = - dist >= FORWARD_SHIFT_THRESHOLD && !$map.danger.is_red(); - - ret = $robinhood; - break 'probe; - } else if entry_hash == $hash && $map.entries[$pos].key == $key { - // There already is an entry with the same key. - ret = $occupied; - break 'probe; - } - } else { - // The entry is vacant, use it for this key. - let $danger = - dist >= FORWARD_SHIFT_THRESHOLD && !$map.danger.is_red(); - - ret = $vacant; - break 'probe; - } - - dist += 1; - }); - - ret - }} -} - -// ===== impl HeaderMap ===== - -impl HeaderMap { - /// Create an empty `HeaderMap`. - /// - /// The map will be created without any capacity. This function will not - /// allocate. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let map = HeaderMap::new(); - /// - /// assert!(map.is_empty()); - /// assert_eq!(0, map.capacity()); - /// ``` - pub fn new() -> Self { - HeaderMap::try_with_capacity(0).unwrap() - } -} - -impl<T> HeaderMap<T> { - /// Create an empty `HeaderMap` with the specified capacity. - /// - /// The returned map will allocate internal storage in order to hold about - /// `capacity` elements without reallocating. However, this is a "best - /// effort" as there are usage patterns that could cause additional - /// allocations before `capacity` headers are stored in the map. - /// - /// More capacity than requested may be allocated. - /// - /// # Panics - /// - /// This method panics if capacity exceeds max `HeaderMap` capacity. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let map: HeaderMap<u32> = HeaderMap::with_capacity(10); - /// - /// assert!(map.is_empty()); - /// assert_eq!(12, map.capacity()); - /// ``` - pub fn with_capacity(capacity: usize) -> HeaderMap<T> { - Self::try_with_capacity(capacity).expect("size overflows MAX_SIZE") - } - - /// Create an empty `HeaderMap` with the specified capacity. - /// - /// The returned map will allocate internal storage in order to hold about - /// `capacity` elements without reallocating. However, this is a "best - /// effort" as there are usage patterns that could cause additional - /// allocations before `capacity` headers are stored in the map. - /// - /// More capacity than requested may be allocated. - /// - /// # Errors - /// - /// This function may return an error if `HeaderMap` exceeds max capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let map: HeaderMap<u32> = HeaderMap::try_with_capacity(10).unwrap(); - /// - /// assert!(map.is_empty()); - /// assert_eq!(12, map.capacity()); - /// ``` - pub fn try_with_capacity(capacity: usize) -> Result<HeaderMap<T>, MaxSizeReached> { - if capacity == 0 { - Ok(HeaderMap { - mask: 0, - indices: Box::new([]), // as a ZST, this doesn't actually allocate anything - entries: Vec::new(), - extra_values: Vec::new(), - danger: Danger::Green, - }) - } else { - let raw_cap = match to_raw_capacity(capacity).checked_next_power_of_two() { - Some(c) => c, - None => return Err(MaxSizeReached { _priv: () }), - }; - if raw_cap > MAX_SIZE { - return Err(MaxSizeReached { _priv: () }); - } - debug_assert!(raw_cap > 0); - - Ok(HeaderMap { - mask: (raw_cap - 1) as Size, - indices: vec![Pos::none(); raw_cap].into_boxed_slice(), - entries: Vec::with_capacity(usable_capacity(raw_cap)), - extra_values: Vec::new(), - danger: Danger::Green, - }) - } - } - - /// Returns the number of headers stored in the map. - /// - /// This number represents the total number of **values** stored in the map. - /// This number can be greater than or equal to the number of **keys** - /// stored given that a single key may have more than one associated value. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{ACCEPT, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// assert_eq!(0, map.len()); - /// - /// map.insert(ACCEPT, "text/plain".parse().unwrap()); - /// map.insert(HOST, "localhost".parse().unwrap()); - /// - /// assert_eq!(2, map.len()); - /// - /// map.append(ACCEPT, "text/html".parse().unwrap()); - /// - /// assert_eq!(3, map.len()); - /// ``` - pub fn len(&self) -> usize { - self.entries.len() + self.extra_values.len() - } - - /// Returns the number of keys stored in the map. - /// - /// This number will be less than or equal to `len()` as each key may have - /// more than one associated value. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{ACCEPT, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// assert_eq!(0, map.keys_len()); - /// - /// map.insert(ACCEPT, "text/plain".parse().unwrap()); - /// map.insert(HOST, "localhost".parse().unwrap()); - /// - /// assert_eq!(2, map.keys_len()); - /// - /// map.insert(ACCEPT, "text/html".parse().unwrap()); - /// - /// assert_eq!(2, map.keys_len()); - /// ``` - pub fn keys_len(&self) -> usize { - self.entries.len() - } - - /// Returns true if the map contains no elements. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// - /// assert!(map.is_empty()); - /// - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// assert!(!map.is_empty()); - /// ``` - pub fn is_empty(&self) -> bool { - self.entries.len() == 0 - } - - /// Clears the map, removing all key-value pairs. Keeps the allocated memory - /// for reuse. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// map.clear(); - /// assert!(map.is_empty()); - /// assert!(map.capacity() > 0); - /// ``` - pub fn clear(&mut self) { - self.entries.clear(); - self.extra_values.clear(); - self.danger = Danger::Green; - - for e in self.indices.iter_mut() { - *e = Pos::none(); - } - } - - /// Returns the number of headers the map can hold without reallocating. - /// - /// This number is an approximation as certain usage patterns could cause - /// additional allocations before the returned capacity is filled. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// - /// assert_eq!(0, map.capacity()); - /// - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// assert_eq!(6, map.capacity()); - /// ``` - pub fn capacity(&self) -> usize { - usable_capacity(self.indices.len()) - } - - /// Reserves capacity for at least `additional` more headers to be inserted - /// into the `HeaderMap`. - /// - /// The header map may reserve more space to avoid frequent reallocations. - /// Like with `with_capacity`, this will be a "best effort" to avoid - /// allocations until `additional` more headers are inserted. Certain usage - /// patterns could cause additional allocations before the number is - /// reached. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows `HeaderMap` `MAX_SIZE`. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.reserve(10); - /// # map.insert(HOST, "bar".parse().unwrap()); - /// ``` - pub fn reserve(&mut self, additional: usize) { - self.try_reserve(additional) - .expect("size overflows MAX_SIZE") - } - - /// Reserves capacity for at least `additional` more headers to be inserted - /// into the `HeaderMap`. - /// - /// The header map may reserve more space to avoid frequent reallocations. - /// Like with `with_capacity`, this will be a "best effort" to avoid - /// allocations until `additional` more headers are inserted. Certain usage - /// patterns could cause additional allocations before the number is - /// reached. - /// - /// # Errors - /// - /// This method differs from `reserve` by returning an error instead of - /// panicking if the value is too large. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.try_reserve(10).unwrap(); - /// # map.try_insert(HOST, "bar".parse().unwrap()).unwrap(); - /// ``` - pub fn try_reserve(&mut self, additional: usize) -> Result<(), MaxSizeReached> { - // TODO: This can't overflow if done properly... since the max # of - // elements is u16::MAX. - let cap = self - .entries - .len() - .checked_add(additional) - .ok_or_else(MaxSizeReached::new)?; - - let raw_cap = to_raw_capacity(cap); - - if raw_cap > self.indices.len() { - let raw_cap = raw_cap - .checked_next_power_of_two() - .ok_or_else(MaxSizeReached::new)?; - if raw_cap > MAX_SIZE { - return Err(MaxSizeReached::new()); - } - - if self.entries.is_empty() { - self.mask = raw_cap as Size - 1; - self.indices = vec![Pos::none(); raw_cap].into_boxed_slice(); - self.entries = Vec::with_capacity(usable_capacity(raw_cap)); - } else { - self.try_grow(raw_cap)?; - } - } - - Ok(()) - } - - /// Returns a reference to the value associated with the key. - /// - /// 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. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(map.get("host").is_none()); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// assert_eq!(map.get(HOST).unwrap(), &"hello"); - /// assert_eq!(map.get("host").unwrap(), &"hello"); - /// - /// map.append(HOST, "world".parse().unwrap()); - /// assert_eq!(map.get("host").unwrap(), &"hello"); - /// ``` - pub fn get<K>(&self, key: K) -> Option<&T> - where - K: AsHeaderName, - { - self.get2(&key) - } - - fn get2<K>(&self, key: &K) -> Option<&T> - where - K: AsHeaderName, - { - match key.find(self) { - Some((_, found)) => { - let entry = &self.entries[found]; - Some(&entry.value) - } - None => None, - } - } - - /// 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. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::default(); - /// map.insert(HOST, "hello".to_string()); - /// map.get_mut("host").unwrap().push_str("-world"); - /// - /// assert_eq!(map.get(HOST).unwrap(), &"hello-world"); - /// ``` - pub fn get_mut<K>(&mut self, key: K) -> Option<&mut T> - where - K: AsHeaderName, - { - match key.find(self) { - Some((_, found)) => { - let entry = &mut self.entries[found]; - Some(&mut entry.value) - } - None => None, - } - } - - /// Returns a view of all values associated with a key. - /// - /// The returned view does not incur any allocations and allows iterating - /// the values associated with the key. See [`GetAll`] for more details. - /// Returns `None` if there are no values associated with the key. - /// - /// [`GetAll`]: struct.GetAll.html - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// map.append(HOST, "goodbye".parse().unwrap()); - /// - /// let view = map.get_all("host"); - /// - /// let mut iter = view.iter(); - /// assert_eq!(&"hello", iter.next().unwrap()); - /// assert_eq!(&"goodbye", iter.next().unwrap()); - /// assert!(iter.next().is_none()); - /// ``` - pub fn get_all<K>(&self, key: K) -> GetAll<'_, T> - where - K: AsHeaderName, - { - GetAll { - map: self, - index: key.find(self).map(|(_, i)| i), - } - } - - /// Returns true if the map contains a value for the specified key. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(!map.contains_key(HOST)); - /// - /// map.insert(HOST, "world".parse().unwrap()); - /// assert!(map.contains_key("host")); - /// ``` - pub fn contains_key<K>(&self, key: K) -> bool - where - K: AsHeaderName, - { - key.find(self).is_some() - } - - /// An iterator visiting all key-value pairs. - /// - /// The iteration order is arbitrary, but consistent across platforms for - /// the same crate version. Each key will be yielded once per associated - /// value. So, if a key has 3 associated values, it will be yielded 3 times. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// map.append(HOST, "goodbye".parse().unwrap()); - /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); - /// - /// for (key, value) in map.iter() { - /// println!("{:?}: {:?}", key, value); - /// } - /// ``` - pub fn iter(&self) -> Iter<'_, T> { - Iter { - map: self, - entry: 0, - cursor: self.entries.first().map(|_| Cursor::Head), - } - } - - /// An iterator visiting all key-value pairs, with mutable value references. - /// - /// The iterator order is arbitrary, but consistent across platforms for the - /// same crate version. Each key will be yielded once per associated value, - /// so if a key has 3 associated values, it will be yielded 3 times. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::default(); - /// - /// map.insert(HOST, "hello".to_string()); - /// map.append(HOST, "goodbye".to_string()); - /// map.insert(CONTENT_LENGTH, "123".to_string()); - /// - /// for (key, value) in map.iter_mut() { - /// value.push_str("-boop"); - /// } - /// ``` - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - IterMut { - map: self as *mut _, - entry: 0, - cursor: self.entries.first().map(|_| Cursor::Head), - lt: PhantomData, - } - } - - /// An iterator visiting all keys. - /// - /// The iteration order is arbitrary, but consistent across platforms for - /// the same crate version. Each key will be yielded only once even if it - /// has multiple associated values. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// map.append(HOST, "goodbye".parse().unwrap()); - /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); - /// - /// for key in map.keys() { - /// println!("{:?}", key); - /// } - /// ``` - pub fn keys(&self) -> Keys<'_, T> { - Keys { - inner: self.entries.iter(), - } - } - - /// An iterator visiting all values. - /// - /// The iteration order is arbitrary, but consistent across platforms for - /// the same crate version. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// map.append(HOST, "goodbye".parse().unwrap()); - /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); - /// - /// for value in map.values() { - /// println!("{:?}", value); - /// } - /// ``` - pub fn values(&self) -> Values<'_, T> { - Values { inner: self.iter() } - } - - /// An iterator visiting all values mutably. - /// - /// The iteration order is arbitrary, but consistent across platforms for - /// the same crate version. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::default(); - /// - /// map.insert(HOST, "hello".to_string()); - /// map.append(HOST, "goodbye".to_string()); - /// map.insert(CONTENT_LENGTH, "123".to_string()); - /// - /// for value in map.values_mut() { - /// value.push_str("-boop"); - /// } - /// ``` - pub fn values_mut(&mut self) -> ValuesMut<'_, T> { - ValuesMut { - inner: self.iter_mut(), - } - } - - /// Clears the map, returning all entries as an iterator. - /// - /// The internal memory is kept for reuse. - /// - /// For each yielded item that has `None` provided for the `HeaderName`, - /// then the associated header name is the same as that of the previously - /// yielded item. The first yielded item will have `HeaderName` set. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::{CONTENT_LENGTH, HOST}; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(HOST, "hello".parse().unwrap()); - /// map.append(HOST, "goodbye".parse().unwrap()); - /// map.insert(CONTENT_LENGTH, "123".parse().unwrap()); - /// - /// let mut drain = map.drain(); - /// - /// - /// assert_eq!(drain.next(), Some((Some(HOST), "hello".parse().unwrap()))); - /// assert_eq!(drain.next(), Some((None, "goodbye".parse().unwrap()))); - /// - /// assert_eq!(drain.next(), Some((Some(CONTENT_LENGTH), "123".parse().unwrap()))); - /// - /// assert_eq!(drain.next(), None); - /// ``` - pub fn drain(&mut self) -> Drain<'_, T> { - for i in self.indices.iter_mut() { - *i = Pos::none(); - } - - // Memory safety - // - // When the Drain is first created, it shortens the length of - // the source vector to make sure no uninitialized or moved-from - // elements are accessible at all if the Drain's destructor never - // gets to run. - - let entries = &mut self.entries[..] as *mut _; - let extra_values = &mut self.extra_values as *mut _; - let len = self.entries.len(); - unsafe { - self.entries.set_len(0); - } - - Drain { - idx: 0, - len, - entries, - extra_values, - next: None, - lt: PhantomData, - } - } - - fn value_iter(&self, idx: Option<usize>) -> ValueIter<'_, T> { - use self::Cursor::*; - - if let Some(idx) = idx { - let back = { - let entry = &self.entries[idx]; - - entry.links.map(|l| Values(l.tail)).unwrap_or(Head) - }; - - ValueIter { - map: self, - index: idx, - front: Some(Head), - back: Some(back), - } - } else { - ValueIter { - map: self, - index: usize::MAX, - front: None, - back: None, - } - } - } - - fn value_iter_mut(&mut self, idx: usize) -> ValueIterMut<'_, T> { - use self::Cursor::*; - - let back = { - let entry = &self.entries[idx]; - - entry.links.map(|l| Values(l.tail)).unwrap_or(Head) - }; - - ValueIterMut { - map: self as *mut _, - index: idx, - front: Some(Head), - back: Some(back), - lt: PhantomData, - } - } - - /// Gets the given key's corresponding entry in the map for in-place - /// manipulation. - /// - /// # Panics - /// - /// This method panics if capacity exceeds max `HeaderMap` capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map: HeaderMap<u32> = HeaderMap::default(); - /// - /// let headers = &[ - /// "content-length", - /// "x-hello", - /// "Content-Length", - /// "x-world", - /// ]; - /// - /// for &header in headers { - /// let counter = map.entry(header).or_insert(0); - /// *counter += 1; - /// } - /// - /// assert_eq!(map["content-length"], 2); - /// assert_eq!(map["x-hello"], 1); - /// ``` - pub fn entry<K>(&mut self, key: K) -> Entry<'_, T> - where - K: IntoHeaderName, - { - key.try_entry(self).expect("size overflows MAX_SIZE") - } - - /// Gets the given key's corresponding entry in the map for in-place - /// manipulation. - /// - /// # Errors - /// - /// This method differs from `entry` by allowing types that may not be - /// valid `HeaderName`s to passed as the key (such as `String`). If they - /// do not parse as a valid `HeaderName`, this returns an - /// `InvalidHeaderName` error. - /// - /// If reserving space goes over the maximum, this will also return an - /// error. However, to prevent breaking changes to the return type, the - /// error will still say `InvalidHeaderName`, unlike other `try_*` methods - /// which return a `MaxSizeReached` error. - pub fn try_entry<K>(&mut self, key: K) -> Result<Entry<'_, T>, InvalidHeaderName> - where - K: AsHeaderName, - { - key.try_entry(self).map_err(|err| match err { - as_header_name::TryEntryError::InvalidHeaderName(e) => e, - as_header_name::TryEntryError::MaxSizeReached(_e) => { - // Unfortunately, we cannot change the return type of this - // method, so the max size reached error needs to be converted - // into an InvalidHeaderName. Yay. - InvalidHeaderName::new() - } - }) - } - - fn try_entry2<K>(&mut self, key: K) -> Result<Entry<'_, T>, MaxSizeReached> - where - K: Hash + Into<HeaderName>, - HeaderName: PartialEq<K>, - { - // Ensure that there is space in the map - self.try_reserve_one()?; - - Ok(insert_phase_one!( - self, - key, - probe, - pos, - hash, - danger, - Entry::Vacant(VacantEntry { - map: self, - hash, - key: key.into(), - probe, - danger, - }), - Entry::Occupied(OccupiedEntry { - map: self, - index: pos, - probe, - }), - Entry::Vacant(VacantEntry { - map: self, - hash, - key: key.into(), - probe, - danger, - }) - )) - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not previously have this key present, then `None` is - /// returned. - /// - /// If the map did have this key present, the new value is associated with - /// 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. - /// - /// The key is not updated, though; this matters for types that can be `==` - /// without being identical. - /// - /// # Panics - /// - /// This method panics if capacity exceeds max `HeaderMap` capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(map.insert(HOST, "world".parse().unwrap()).is_none()); - /// assert!(!map.is_empty()); - /// - /// let mut prev = map.insert(HOST, "earth".parse().unwrap()).unwrap(); - /// assert_eq!("world", prev); - /// ``` - pub fn insert<K>(&mut self, key: K, val: T) -> Option<T> - where - K: IntoHeaderName, - { - self.try_insert(key, val).expect("size overflows MAX_SIZE") - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not previously have this key present, then `None` is - /// returned. - /// - /// If the map did have this key present, the new value is associated with - /// 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. - /// - /// The key is not updated, though; this matters for types that can be `==` - /// without being identical. - /// - /// # Errors - /// - /// This function may return an error if `HeaderMap` exceeds max capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(map.try_insert(HOST, "world".parse().unwrap()).unwrap().is_none()); - /// assert!(!map.is_empty()); - /// - /// let mut prev = map.try_insert(HOST, "earth".parse().unwrap()).unwrap().unwrap(); - /// assert_eq!("world", prev); - /// ``` - pub fn try_insert<K>(&mut self, key: K, val: T) -> Result<Option<T>, MaxSizeReached> - where - K: IntoHeaderName, - { - key.try_insert(self, val) - } - - #[inline] - fn try_insert2<K>(&mut self, key: K, value: T) -> Result<Option<T>, MaxSizeReached> - where - K: Hash + Into<HeaderName>, - HeaderName: PartialEq<K>, - { - self.try_reserve_one()?; - - Ok(insert_phase_one!( - self, - key, - probe, - pos, - hash, - danger, - // Vacant - { - let _ = danger; // Make lint happy - let index = self.entries.len(); - self.try_insert_entry(hash, key.into(), value)?; - self.indices[probe] = Pos::new(index, hash); - None - }, - // Occupied - Some(self.insert_occupied(pos, value)), - // Robinhood - { - self.try_insert_phase_two(key.into(), value, hash, probe, danger)?; - None - } - )) - } - - /// Set an occupied bucket to the given value - #[inline] - fn insert_occupied(&mut self, index: usize, value: T) -> T { - if let Some(links) = self.entries[index].links { - self.remove_all_extra_values(links.next); - } - - let entry = &mut self.entries[index]; - mem::replace(&mut entry.value, value) - } - - fn insert_occupied_mult(&mut self, index: usize, value: T) -> ValueDrain<'_, T> { - let old; - let links; - - { - let entry = &mut self.entries[index]; - - old = mem::replace(&mut entry.value, value); - links = entry.links.take(); - } - - let raw_links = self.raw_links(); - let extra_values = &mut self.extra_values; - - let next = - links.map(|l| drain_all_extra_values(raw_links, extra_values, l.next).into_iter()); - - ValueDrain { - first: Some(old), - next, - lt: PhantomData, - } - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not previously have this key present, then `false` is - /// returned. - /// - /// 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. - /// - /// # Panics - /// - /// This method panics if capacity exceeds max `HeaderMap` capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(map.insert(HOST, "world".parse().unwrap()).is_none()); - /// assert!(!map.is_empty()); - /// - /// map.append(HOST, "earth".parse().unwrap()); - /// - /// let values = map.get_all("host"); - /// let mut i = values.iter(); - /// assert_eq!("world", *i.next().unwrap()); - /// assert_eq!("earth", *i.next().unwrap()); - /// ``` - pub fn append<K>(&mut self, key: K, value: T) -> bool - where - K: IntoHeaderName, - { - self.try_append(key, value) - .expect("size overflows MAX_SIZE") - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not previously have this key present, then `false` is - /// returned. - /// - /// 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. - /// - /// # Errors - /// - /// This function may return an error if `HeaderMap` exceeds max capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// assert!(map.try_insert(HOST, "world".parse().unwrap()).unwrap().is_none()); - /// assert!(!map.is_empty()); - /// - /// map.try_append(HOST, "earth".parse().unwrap()).unwrap(); - /// - /// let values = map.get_all("host"); - /// let mut i = values.iter(); - /// assert_eq!("world", *i.next().unwrap()); - /// assert_eq!("earth", *i.next().unwrap()); - /// ``` - pub fn try_append<K>(&mut self, key: K, value: T) -> Result<bool, MaxSizeReached> - where - K: IntoHeaderName, - { - key.try_append(self, value) - } - - #[inline] - fn try_append2<K>(&mut self, key: K, value: T) -> Result<bool, MaxSizeReached> - where - K: Hash + Into<HeaderName>, - HeaderName: PartialEq<K>, - { - self.try_reserve_one()?; - - Ok(insert_phase_one!( - self, - key, - probe, - pos, - hash, - danger, - // Vacant - { - let _ = danger; - let index = self.entries.len(); - self.try_insert_entry(hash, key.into(), value)?; - self.indices[probe] = Pos::new(index, hash); - false - }, - // Occupied - { - append_value(pos, &mut self.entries[pos], &mut self.extra_values, value); - true - }, - // Robinhood - { - self.try_insert_phase_two(key.into(), value, hash, probe, danger)?; - - false - } - )) - } - - #[inline] - fn find<K>(&self, key: &K) -> Option<(usize, usize)> - where - K: Hash + Into<HeaderName> + ?Sized, - HeaderName: PartialEq<K>, - { - if self.entries.is_empty() { - return None; - } - - let hash = hash_elem_using(&self.danger, key); - let mask = self.mask; - let mut probe = desired_pos(mask, hash); - let mut dist = 0; - - probe_loop!(probe < self.indices.len(), { - if let Some((i, entry_hash)) = self.indices[probe].resolve() { - if dist > probe_distance(mask, entry_hash, probe) { - // give up when probe distance is too long - return None; - } else if entry_hash == hash && self.entries[i].key == *key { - return Some((probe, i)); - } - } else { - return None; - } - - dist += 1; - }); - } - - /// phase 2 is post-insert where we forward-shift `Pos` in the indices. - #[inline] - fn try_insert_phase_two( - &mut self, - key: HeaderName, - value: T, - hash: HashValue, - probe: usize, - danger: bool, - ) -> Result<usize, MaxSizeReached> { - // Push the value and get the index - let index = self.entries.len(); - self.try_insert_entry(hash, key, value)?; - - let num_displaced = do_insert_phase_two(&mut self.indices, probe, Pos::new(index, hash)); - - if danger || num_displaced >= DISPLACEMENT_THRESHOLD { - // Increase danger level - self.danger.set_yellow(); - } - - Ok(index) - } - - /// Removes a key from the map, returning the value associated with the key. - /// - /// Returns `None` if the map does not contain the key. If there are - /// multiple values associated with the key, then the first one is returned. - /// See `remove_entry_mult` on `OccupiedEntry` for an API that yields all - /// values. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// let prev = map.remove(HOST).unwrap(); - /// assert_eq!("hello.world", prev); - /// - /// assert!(map.remove(HOST).is_none()); - /// ``` - pub fn remove<K>(&mut self, key: K) -> Option<T> - where - K: AsHeaderName, - { - match key.find(self) { - Some((probe, idx)) => { - if let Some(links) = self.entries[idx].links { - self.remove_all_extra_values(links.next); - } - - let entry = self.remove_found(probe, idx); - - Some(entry.value) - } - None => None, - } - } - - /// Remove an entry from the map. - /// - /// Warning: To avoid inconsistent state, extra values _must_ be removed - /// for the `found` index (via `remove_all_extra_values` or similar) - /// _before_ this method is called. - #[inline] - fn remove_found(&mut self, probe: usize, found: usize) -> Bucket<T> { - // index `probe` and entry `found` is to be removed - // use swap_remove, but then we need to update the index that points - // to the other entry that has to move - self.indices[probe] = Pos::none(); - let entry = self.entries.swap_remove(found); - - // correct index that points to the entry that had to swap places - if let Some(entry) = self.entries.get(found) { - // was not last element - // examine new element in `found` and find it in indices - let mut probe = desired_pos(self.mask, entry.hash); - - probe_loop!(probe < self.indices.len(), { - if let Some((i, _)) = self.indices[probe].resolve() { - if i >= self.entries.len() { - // found it - self.indices[probe] = Pos::new(found, entry.hash); - break; - } - } - }); - - // Update links - if let Some(links) = entry.links { - self.extra_values[links.next].prev = Link::Entry(found); - self.extra_values[links.tail].next = Link::Entry(found); - } - } - - // backward shift deletion in self.indices - // after probe, shift all non-ideally placed indices backward - if !self.entries.is_empty() { - let mut last_probe = probe; - let mut probe = probe + 1; - - probe_loop!(probe < self.indices.len(), { - if let Some((_, entry_hash)) = self.indices[probe].resolve() { - if probe_distance(self.mask, entry_hash, probe) > 0 { - self.indices[last_probe] = self.indices[probe]; - self.indices[probe] = Pos::none(); - } else { - break; - } - } else { - break; - } - - last_probe = probe; - }); - } - - entry - } - - /// Removes the `ExtraValue` at the given index. - #[inline] - fn remove_extra_value(&mut self, idx: usize) -> ExtraValue<T> { - let raw_links = self.raw_links(); - remove_extra_value(raw_links, &mut self.extra_values, idx) - } - - fn remove_all_extra_values(&mut self, mut head: usize) { - loop { - let extra = self.remove_extra_value(head); - - if let Link::Extra(idx) = extra.next { - head = idx; - } else { - break; - } - } - } - - #[inline] - fn try_insert_entry( - &mut self, - hash: HashValue, - key: HeaderName, - value: T, - ) -> Result<(), MaxSizeReached> { - if self.entries.len() >= MAX_SIZE { - return Err(MaxSizeReached::new()); - } - - self.entries.push(Bucket { - hash, - key, - value, - links: None, - }); - - Ok(()) - } - - fn rebuild(&mut self) { - // Loop over all entries and re-insert them into the map - 'outer: for (index, entry) in self.entries.iter_mut().enumerate() { - let hash = hash_elem_using(&self.danger, &entry.key); - let mut probe = desired_pos(self.mask, hash); - let mut dist = 0; - - // Update the entry's hash code - entry.hash = hash; - - probe_loop!(probe < self.indices.len(), { - if let Some((_, entry_hash)) = self.indices[probe].resolve() { - // if existing element probed less than us, swap - let their_dist = probe_distance(self.mask, entry_hash, probe); - - if their_dist < dist { - // Robinhood - break; - } - } else { - // Vacant slot - self.indices[probe] = Pos::new(index, hash); - continue 'outer; - } - - dist += 1; - }); - - do_insert_phase_two(&mut self.indices, probe, Pos::new(index, hash)); - } - } - - fn reinsert_entry_in_order(&mut self, pos: Pos) { - if let Some((_, entry_hash)) = pos.resolve() { - // Find first empty bucket and insert there - let mut probe = desired_pos(self.mask, entry_hash); - - probe_loop!(probe < self.indices.len(), { - if self.indices[probe].resolve().is_none() { - // empty bucket, insert here - self.indices[probe] = pos; - return; - } - }); - } - } - - fn try_reserve_one(&mut self) -> Result<(), MaxSizeReached> { - let len = self.entries.len(); - - if self.danger.is_yellow() { - let load_factor = self.entries.len() as f32 / self.indices.len() as f32; - - if load_factor >= LOAD_FACTOR_THRESHOLD { - // Transition back to green danger level - self.danger.set_green(); - - // Double the capacity - let new_cap = self.indices.len() * 2; - - // Grow the capacity - self.try_grow(new_cap)?; - } else { - self.danger.set_red(); - - // Rebuild hash table - for index in self.indices.iter_mut() { - *index = Pos::none(); - } - - self.rebuild(); - } - } else if len == self.capacity() { - if len == 0 { - let new_raw_cap = 8; - self.mask = 8 - 1; - self.indices = vec![Pos::none(); new_raw_cap].into_boxed_slice(); - self.entries = Vec::with_capacity(usable_capacity(new_raw_cap)); - } else { - let raw_cap = self.indices.len(); - self.try_grow(raw_cap << 1)?; - } - } - - Ok(()) - } - - #[inline] - fn try_grow(&mut self, new_raw_cap: usize) -> Result<(), MaxSizeReached> { - if new_raw_cap > MAX_SIZE { - return Err(MaxSizeReached::new()); - } - - // find first ideally placed element -- start of cluster - let mut first_ideal = 0; - - for (i, pos) in self.indices.iter().enumerate() { - if let Some((_, entry_hash)) = pos.resolve() { - if 0 == probe_distance(self.mask, entry_hash, i) { - first_ideal = i; - break; - } - } - } - - // visit the entries in an order where we can simply reinsert them - // into self.indices without any bucket stealing. - let old_indices = mem::replace( - &mut self.indices, - vec![Pos::none(); new_raw_cap].into_boxed_slice(), - ); - self.mask = new_raw_cap.wrapping_sub(1) as Size; - - for &pos in &old_indices[first_ideal..] { - self.reinsert_entry_in_order(pos); - } - - for &pos in &old_indices[..first_ideal] { - self.reinsert_entry_in_order(pos); - } - - // Reserve additional entry slots - let more = self.capacity() - self.entries.len(); - self.entries.reserve_exact(more); - Ok(()) - } - - #[inline] - fn raw_links(&mut self) -> RawLinks<T> { - RawLinks(&mut self.entries[..] as *mut _) - } -} - -/// Removes the `ExtraValue` at the given index. -#[inline] -fn remove_extra_value<T>( - mut raw_links: RawLinks<T>, - extra_values: &mut Vec<ExtraValue<T>>, - idx: usize, -) -> ExtraValue<T> { - let prev; - let next; - - { - debug_assert!(extra_values.len() > idx); - let extra = &extra_values[idx]; - prev = extra.prev; - next = extra.next; - } - - // First unlink the extra value - match (prev, next) { - (Link::Entry(prev), Link::Entry(next)) => { - debug_assert_eq!(prev, next); - - raw_links[prev] = None; - } - (Link::Entry(prev), Link::Extra(next)) => { - debug_assert!(raw_links[prev].is_some()); - - raw_links[prev].as_mut().unwrap().next = next; - - debug_assert!(extra_values.len() > next); - extra_values[next].prev = Link::Entry(prev); - } - (Link::Extra(prev), Link::Entry(next)) => { - debug_assert!(raw_links[next].is_some()); - - raw_links[next].as_mut().unwrap().tail = prev; - - debug_assert!(extra_values.len() > prev); - extra_values[prev].next = Link::Entry(next); - } - (Link::Extra(prev), Link::Extra(next)) => { - debug_assert!(extra_values.len() > next); - debug_assert!(extra_values.len() > prev); - - extra_values[prev].next = Link::Extra(next); - extra_values[next].prev = Link::Extra(prev); - } - } - - // Remove the extra value - let mut extra = extra_values.swap_remove(idx); - - // This is the index of the value that was moved (possibly `extra`) - let old_idx = extra_values.len(); - - // Update the links - if extra.prev == Link::Extra(old_idx) { - extra.prev = Link::Extra(idx); - } - - if extra.next == Link::Extra(old_idx) { - extra.next = Link::Extra(idx); - } - - // Check if another entry was displaced. If it was, then the links - // need to be fixed. - if idx != old_idx { - let next; - let prev; - - { - debug_assert!(extra_values.len() > idx); - let moved = &extra_values[idx]; - next = moved.next; - prev = moved.prev; - } - - // An entry was moved, we have to the links - match prev { - Link::Entry(entry_idx) => { - // It is critical that we do not attempt to read the - // header name or value as that memory may have been - // "released" already. - debug_assert!(raw_links[entry_idx].is_some()); - - let links = raw_links[entry_idx].as_mut().unwrap(); - links.next = idx; - } - Link::Extra(extra_idx) => { - debug_assert!(extra_values.len() > extra_idx); - extra_values[extra_idx].next = Link::Extra(idx); - } - } - - match next { - Link::Entry(entry_idx) => { - debug_assert!(raw_links[entry_idx].is_some()); - - let links = raw_links[entry_idx].as_mut().unwrap(); - links.tail = idx; - } - Link::Extra(extra_idx) => { - debug_assert!(extra_values.len() > extra_idx); - extra_values[extra_idx].prev = Link::Extra(idx); - } - } - } - - debug_assert!({ - for v in &*extra_values { - assert!(v.next != Link::Extra(old_idx)); - assert!(v.prev != Link::Extra(old_idx)); - } - - true - }); - - extra -} - -fn drain_all_extra_values<T>( - raw_links: RawLinks<T>, - extra_values: &mut Vec<ExtraValue<T>>, - mut head: usize, -) -> Vec<T> { - let mut vec = Vec::new(); - loop { - let extra = remove_extra_value(raw_links, extra_values, head); - vec.push(extra.value); - - if let Link::Extra(idx) = extra.next { - head = idx; - } else { - break; - } - } - vec -} - -impl<'a, T> IntoIterator for &'a HeaderMap<T> { - type Item = (&'a HeaderName, &'a T); - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -impl<'a, T> IntoIterator for &'a mut HeaderMap<T> { - type Item = (&'a HeaderName, &'a mut T); - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -impl<T> IntoIterator for HeaderMap<T> { - type Item = (Option<HeaderName>, T); - type IntoIter = IntoIter<T>; - - /// Creates a consuming iterator, that is, one that moves keys and values - /// out of the map in arbitrary order. The map cannot be used after calling - /// this. - /// - /// For each yielded item that has `None` provided for the `HeaderName`, - /// then the associated header name is the same as that of the previously - /// yielded item. The first yielded item will have `HeaderName` set. - /// - /// # Examples - /// - /// Basic usage. - /// - /// ``` - /// # use http::header; - /// # use http::header::*; - /// let mut map = HeaderMap::new(); - /// map.insert(header::CONTENT_LENGTH, "123".parse().unwrap()); - /// map.insert(header::CONTENT_TYPE, "json".parse().unwrap()); - /// - /// let mut iter = map.into_iter(); - /// assert_eq!(iter.next(), Some((Some(header::CONTENT_LENGTH), "123".parse().unwrap()))); - /// assert_eq!(iter.next(), Some((Some(header::CONTENT_TYPE), "json".parse().unwrap()))); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// Multiple values per key. - /// - /// ``` - /// # use http::header; - /// # use http::header::*; - /// let mut map = HeaderMap::new(); - /// - /// map.append(header::CONTENT_LENGTH, "123".parse().unwrap()); - /// map.append(header::CONTENT_LENGTH, "456".parse().unwrap()); - /// - /// map.append(header::CONTENT_TYPE, "json".parse().unwrap()); - /// map.append(header::CONTENT_TYPE, "html".parse().unwrap()); - /// map.append(header::CONTENT_TYPE, "xml".parse().unwrap()); - /// - /// let mut iter = map.into_iter(); - /// - /// assert_eq!(iter.next(), Some((Some(header::CONTENT_LENGTH), "123".parse().unwrap()))); - /// assert_eq!(iter.next(), Some((None, "456".parse().unwrap()))); - /// - /// assert_eq!(iter.next(), Some((Some(header::CONTENT_TYPE), "json".parse().unwrap()))); - /// assert_eq!(iter.next(), Some((None, "html".parse().unwrap()))); - /// assert_eq!(iter.next(), Some((None, "xml".parse().unwrap()))); - /// assert!(iter.next().is_none()); - /// ``` - fn into_iter(self) -> IntoIter<T> { - IntoIter { - next: None, - entries: self.entries.into_iter(), - extra_values: self.extra_values, - } - } -} - -impl<T> FromIterator<(HeaderName, T)> for HeaderMap<T> { - fn from_iter<I>(iter: I) -> Self - where - I: IntoIterator<Item = (HeaderName, T)>, - { - let mut map = HeaderMap::default(); - map.extend(iter); - map - } -} - -/// Try to convert a `HashMap` into a `HeaderMap`. -/// -/// # Examples -/// -/// ``` -/// use std::collections::HashMap; -/// use std::convert::TryInto; -/// use http::HeaderMap; -/// -/// let mut map = HashMap::new(); -/// map.insert("X-Custom-Header".to_string(), "my value".to_string()); -/// -/// let headers: HeaderMap = (&map).try_into().expect("valid headers"); -/// assert_eq!(headers["X-Custom-Header"], "my value"); -/// ``` -impl<'a, K, V, S, T> TryFrom<&'a HashMap<K, V, S>> for HeaderMap<T> -where - K: Eq + Hash, - HeaderName: TryFrom<&'a K>, - <HeaderName as TryFrom<&'a K>>::Error: Into<crate::Error>, - T: TryFrom<&'a V>, - T::Error: Into<crate::Error>, -{ - type Error = Error; - - fn try_from(c: &'a HashMap<K, V, S>) -> Result<Self, Self::Error> { - c.iter() - .map(|(k, v)| -> crate::Result<(HeaderName, T)> { - let name = TryFrom::try_from(k).map_err(Into::into)?; - let value = TryFrom::try_from(v).map_err(Into::into)?; - Ok((name, value)) - }) - .collect() - } -} - -impl<T> Extend<(Option<HeaderName>, T)> for HeaderMap<T> { - /// Extend a `HeaderMap` with the contents of another `HeaderMap`. - /// - /// This function expects the yielded items to follow the same structure as - /// `IntoIter`. - /// - /// # Panics - /// - /// This panics if the first yielded item does not have a `HeaderName`. - /// - /// # Examples - /// - /// ``` - /// # use http::header::*; - /// let mut map = HeaderMap::new(); - /// - /// map.insert(ACCEPT, "text/plain".parse().unwrap()); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// let mut extra = HeaderMap::new(); - /// - /// extra.insert(HOST, "foo.bar".parse().unwrap()); - /// extra.insert(COOKIE, "hello".parse().unwrap()); - /// extra.append(COOKIE, "world".parse().unwrap()); - /// - /// map.extend(extra); - /// - /// assert_eq!(map["host"], "foo.bar"); - /// assert_eq!(map["accept"], "text/plain"); - /// assert_eq!(map["cookie"], "hello"); - /// - /// let v = map.get_all("host"); - /// assert_eq!(1, v.iter().count()); - /// - /// let v = map.get_all("cookie"); - /// assert_eq!(2, v.iter().count()); - /// ``` - fn extend<I: IntoIterator<Item = (Option<HeaderName>, T)>>(&mut self, iter: I) { - let mut iter = iter.into_iter(); - - // The structure of this is a bit weird, but it is mostly to make the - // borrow checker happy. - let (mut key, mut val) = match iter.next() { - Some((Some(key), val)) => (key, val), - Some((None, _)) => panic!("expected a header name, but got None"), - None => return, - }; - - 'outer: loop { - let mut entry = match self.try_entry2(key).expect("size overflows MAX_SIZE") { - Entry::Occupied(mut e) => { - // Replace all previous values while maintaining a handle to - // the entry. - e.insert(val); - e - } - Entry::Vacant(e) => e.insert_entry(val), - }; - - // As long as `HeaderName` is none, keep inserting the value into - // the current entry - loop { - match iter.next() { - Some((Some(k), v)) => { - key = k; - val = v; - continue 'outer; - } - Some((None, v)) => { - entry.append(v); - } - None => { - return; - } - } - } - } - } -} - -impl<T> Extend<(HeaderName, T)> for HeaderMap<T> { - fn extend<I: IntoIterator<Item = (HeaderName, T)>>(&mut self, iter: I) { - // Keys may be already present or show multiple times in the iterator. - // Reserve the entire hint lower bound if the map is empty. - // Otherwise reserve half the hint (rounded up), so the map - // will only resize twice in the worst case. - let iter = iter.into_iter(); - - let reserve = if self.is_empty() { - iter.size_hint().0 - } else { - (iter.size_hint().0 + 1) / 2 - }; - - self.reserve(reserve); - - for (k, v) in iter { - self.append(k, v); - } - } -} - -impl<T: PartialEq> PartialEq for HeaderMap<T> { - fn eq(&self, other: &HeaderMap<T>) -> bool { - if self.len() != other.len() { - return false; - } - - self.keys() - .all(|key| self.get_all(key) == other.get_all(key)) - } -} - -impl<T: Eq> Eq for HeaderMap<T> {} - -impl<T: fmt::Debug> fmt::Debug for HeaderMap<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -impl<T> Default for HeaderMap<T> { - fn default() -> Self { - HeaderMap::try_with_capacity(0).expect("zero capacity should never fail") - } -} - -impl<K, T> ops::Index<K> for HeaderMap<T> -where - K: AsHeaderName, -{ - type Output = T; - - /// # Panics - /// Using the index operator will cause a panic if the header you're querying isn't set. - #[inline] - fn index(&self, index: K) -> &T { - match self.get2(&index) { - Some(val) => val, - None => panic!("no entry found for key {:?}", index.as_str()), - } - } -} - -/// phase 2 is post-insert where we forward-shift `Pos` in the indices. -/// -/// returns the number of displaced elements -#[inline] -fn do_insert_phase_two(indices: &mut [Pos], mut probe: usize, mut old_pos: Pos) -> usize { - let mut num_displaced = 0; - - probe_loop!(probe < indices.len(), { - let pos = &mut indices[probe]; - - if pos.is_none() { - *pos = old_pos; - break; - } else { - num_displaced += 1; - old_pos = mem::replace(pos, old_pos); - } - }); - - num_displaced -} - -#[inline] -fn append_value<T>( - entry_idx: usize, - entry: &mut Bucket<T>, - extra: &mut Vec<ExtraValue<T>>, - value: T, -) { - match entry.links { - Some(links) => { - let idx = extra.len(); - extra.push(ExtraValue { - value, - prev: Link::Extra(links.tail), - next: Link::Entry(entry_idx), - }); - - extra[links.tail].next = Link::Extra(idx); - - entry.links = Some(Links { tail: idx, ..links }); - } - None => { - let idx = extra.len(); - extra.push(ExtraValue { - value, - prev: Link::Entry(entry_idx), - next: Link::Entry(entry_idx), - }); - - entry.links = Some(Links { - next: idx, - tail: idx, - }); - } - } -} - -// ===== impl Iter ===== - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = (&'a HeaderName, &'a T); - - fn next(&mut self) -> Option<Self::Item> { - use self::Cursor::*; - - if self.cursor.is_none() { - if (self.entry + 1) >= self.map.entries.len() { - return None; - } - - self.entry += 1; - self.cursor = Some(Cursor::Head); - } - - let entry = &self.map.entries[self.entry]; - - match self.cursor.unwrap() { - Head => { - self.cursor = entry.links.map(|l| Values(l.next)); - Some((&entry.key, &entry.value)) - } - Values(idx) => { - let extra = &self.map.extra_values[idx]; - - match extra.next { - Link::Entry(_) => self.cursor = None, - Link::Extra(i) => self.cursor = Some(Values(i)), - } - - Some((&entry.key, &extra.value)) - } - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let map = self.map; - debug_assert!(map.entries.len() >= self.entry); - - let lower = map.entries.len() - self.entry; - // We could pessimistically guess at the upper bound, saying - // that its lower + map.extra_values.len(). That could be - // way over though, such as if we're near the end, and have - // already gone through several extra values... - (lower, None) - } -} - -impl<'a, T> FusedIterator for Iter<'a, T> {} - -unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} -unsafe impl<'a, T: Sync> Send for Iter<'a, T> {} - -// ===== impl IterMut ===== - -impl<'a, T> IterMut<'a, T> { - fn next_unsafe(&mut self) -> Option<(&'a HeaderName, *mut T)> { - use self::Cursor::*; - - if self.cursor.is_none() { - if (self.entry + 1) >= unsafe { &*self.map }.entries.len() { - return None; - } - - self.entry += 1; - self.cursor = Some(Cursor::Head); - } - - let entry = unsafe { &mut (*self.map).entries[self.entry] }; - - match self.cursor.unwrap() { - Head => { - self.cursor = entry.links.map(|l| Values(l.next)); - Some((&entry.key, &mut entry.value as *mut _)) - } - Values(idx) => { - let extra = unsafe { &mut (*self.map).extra_values[idx] }; - - match extra.next { - Link::Entry(_) => self.cursor = None, - Link::Extra(i) => self.cursor = Some(Values(i)), - } - - Some((&entry.key, &mut extra.value as *mut _)) - } - } - } -} - -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = (&'a HeaderName, &'a mut T); - - fn next(&mut self) -> Option<Self::Item> { - self.next_unsafe() - .map(|(key, ptr)| (key, unsafe { &mut *ptr })) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let map = unsafe { &*self.map }; - debug_assert!(map.entries.len() >= self.entry); - - let lower = map.entries.len() - self.entry; - // We could pessimistically guess at the upper bound, saying - // that its lower + map.extra_values.len(). That could be - // way over though, such as if we're near the end, and have - // already gone through several extra values... - (lower, None) - } -} - -impl<'a, T> FusedIterator for IterMut<'a, T> {} - -unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} -unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} - -// ===== impl Keys ===== - -impl<'a, T> Iterator for Keys<'a, T> { - type Item = &'a HeaderName; - - fn next(&mut self) -> Option<Self::Item> { - self.inner.next().map(|b| &b.key) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() - } - - fn nth(&mut self, n: usize) -> Option<Self::Item> { - self.inner.nth(n).map(|b| &b.key) - } - - fn count(self) -> usize { - self.inner.count() - } - - fn last(self) -> Option<Self::Item> { - self.inner.last().map(|b| &b.key) - } -} - -impl<'a, T> ExactSizeIterator for Keys<'a, T> {} -impl<'a, T> FusedIterator for Keys<'a, T> {} - -// ===== impl Values ==== - -impl<'a, T> Iterator for Values<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<Self::Item> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() - } -} - -impl<'a, T> FusedIterator for Values<'a, T> {} - -// ===== impl ValuesMut ==== - -impl<'a, T> Iterator for ValuesMut<'a, T> { - type Item = &'a mut T; - - fn next(&mut self) -> Option<Self::Item> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - self.inner.size_hint() - } -} - -impl<'a, T> FusedIterator for ValuesMut<'a, T> {} - -// ===== impl Drain ===== - -impl<'a, T> Iterator for Drain<'a, T> { - type Item = (Option<HeaderName>, T); - - fn next(&mut self) -> Option<Self::Item> { - if let Some(next) = self.next { - // Remove the extra value - - let raw_links = RawLinks(self.entries); - let extra = unsafe { remove_extra_value(raw_links, &mut *self.extra_values, next) }; - - match extra.next { - Link::Extra(idx) => self.next = Some(idx), - Link::Entry(_) => self.next = None, - } - - return Some((None, extra.value)); - } - - let idx = self.idx; - - if idx == self.len { - return None; - } - - self.idx += 1; - - unsafe { - let entry = &(*self.entries)[idx]; - - // Read the header name - let key = ptr::read(&entry.key as *const _); - let value = ptr::read(&entry.value as *const _); - self.next = entry.links.map(|l| l.next); - - Some((Some(key), value)) - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - // At least this many names... It's unknown if the user wants - // to count the extra_values on top. - // - // For instance, extending a new `HeaderMap` wouldn't need to - // reserve the upper-bound in `entries`, only the lower-bound. - let lower = self.len - self.idx; - let upper = unsafe { (*self.extra_values).len() } + lower; - (lower, Some(upper)) - } -} - -impl<'a, T> FusedIterator for Drain<'a, T> {} - -impl<'a, T> Drop for Drain<'a, T> { - fn drop(&mut self) { - for _ in self {} - } -} - -unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {} -unsafe impl<'a, T: Send> Send for Drain<'a, T> {} - -// ===== impl Entry ===== - -impl<'a, T> Entry<'a, T> { - /// Ensures a value is in the entry by inserting the default if empty. - /// - /// Returns a mutable reference to the **first** value in the entry. - /// - /// # Panics - /// - /// This method panics if capacity exceeds max `HeaderMap` capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map: HeaderMap<u32> = HeaderMap::default(); - /// - /// let headers = &[ - /// "content-length", - /// "x-hello", - /// "Content-Length", - /// "x-world", - /// ]; - /// - /// for &header in headers { - /// let counter = map.entry(header) - /// .or_insert(0); - /// *counter += 1; - /// } - /// - /// assert_eq!(map["content-length"], 2); - /// assert_eq!(map["x-hello"], 1); - /// ``` - pub fn or_insert(self, default: T) -> &'a mut T { - self.or_try_insert(default) - .expect("size overflows MAX_SIZE") - } - - /// Ensures a value is in the entry by inserting the default if empty. - /// - /// Returns a mutable reference to the **first** value in the entry. - /// - /// # Errors - /// - /// This function may return an error if `HeaderMap` exceeds max capacity - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map: HeaderMap<u32> = HeaderMap::default(); - /// - /// let headers = &[ - /// "content-length", - /// "x-hello", - /// "Content-Length", - /// "x-world", - /// ]; - /// - /// for &header in headers { - /// let counter = map.entry(header) - /// .or_try_insert(0) - /// .unwrap(); - /// *counter += 1; - /// } - /// - /// assert_eq!(map["content-length"], 2); - /// assert_eq!(map["x-hello"], 1); - /// ``` - pub fn or_try_insert(self, default: T) -> Result<&'a mut T, MaxSizeReached> { - use self::Entry::*; - - match self { - Occupied(e) => Ok(e.into_mut()), - Vacant(e) => e.try_insert(default), - } - } - - /// Ensures a value is in the entry by inserting the result of the default - /// function if empty. - /// - /// The default function is not called if the entry exists in the map. - /// Returns a mutable reference to the **first** value in the entry. - /// - /// # Examples - /// - /// Basic usage. - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map = HeaderMap::new(); - /// - /// let res = map.entry("x-hello") - /// .or_insert_with(|| "world".parse().unwrap()); - /// - /// assert_eq!(res, "world"); - /// ``` - /// - /// The default function is not called if the entry exists in the map. - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.try_insert(HOST, "world".parse().unwrap()).unwrap(); - /// - /// let res = map.try_entry("host") - /// .unwrap() - /// .or_try_insert_with(|| unreachable!()) - /// .unwrap(); - /// - /// - /// assert_eq!(res, "world"); - /// ``` - pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T { - self.or_try_insert_with(default) - .expect("size overflows MAX_SIZE") - } - - /// Ensures a value is in the entry by inserting the result of the default - /// function if empty. - /// - /// The default function is not called if the entry exists in the map. - /// Returns a mutable reference to the **first** value in the entry. - /// - /// # Examples - /// - /// Basic usage. - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map = HeaderMap::new(); - /// - /// let res = map.entry("x-hello") - /// .or_insert_with(|| "world".parse().unwrap()); - /// - /// assert_eq!(res, "world"); - /// ``` - /// - /// The default function is not called if the entry exists in the map. - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.try_insert(HOST, "world".parse().unwrap()).unwrap(); - /// - /// let res = map.try_entry("host") - /// .unwrap() - /// .or_try_insert_with(|| unreachable!()) - /// .unwrap(); - /// - /// - /// assert_eq!(res, "world"); - /// ``` - pub fn or_try_insert_with<F: FnOnce() -> T>( - self, - default: F, - ) -> Result<&'a mut T, MaxSizeReached> { - use self::Entry::*; - - match self { - Occupied(e) => Ok(e.into_mut()), - Vacant(e) => e.try_insert(default()), - } - } - - /// Returns a reference to the entry's key - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map = HeaderMap::new(); - /// - /// assert_eq!(map.entry("x-hello").key(), "x-hello"); - /// ``` - pub fn key(&self) -> &HeaderName { - use self::Entry::*; - - match *self { - Vacant(ref e) => e.key(), - Occupied(ref e) => e.key(), - } - } -} - -// ===== impl VacantEntry ===== - -impl<'a, T> VacantEntry<'a, T> { - /// Returns a reference to the entry's key - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// let mut map = HeaderMap::new(); - /// - /// assert_eq!(map.entry("x-hello").key().as_str(), "x-hello"); - /// ``` - pub fn key(&self) -> &HeaderName { - &self.key - } - - /// Take ownership of the key - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry}; - /// let mut map = HeaderMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("x-hello") { - /// assert_eq!(v.into_key().as_str(), "x-hello"); - /// } - /// ``` - pub fn into_key(self) -> HeaderName { - self.key - } - - /// Insert the value into the entry. - /// - /// The value will be associated with this entry's key. A mutable reference - /// to the inserted value will be returned. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry}; - /// let mut map = HeaderMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("x-hello") { - /// v.insert("world".parse().unwrap()); - /// } - /// - /// assert_eq!(map["x-hello"], "world"); - /// ``` - pub fn insert(self, value: T) -> &'a mut T { - self.try_insert(value).expect("size overflows MAX_SIZE") - } - - /// Insert the value into the entry. - /// - /// The value will be associated with this entry's key. A mutable reference - /// to the inserted value will be returned. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry}; - /// let mut map = HeaderMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("x-hello") { - /// v.insert("world".parse().unwrap()); - /// } - /// - /// assert_eq!(map["x-hello"], "world"); - /// ``` - pub fn try_insert(self, value: T) -> Result<&'a mut T, MaxSizeReached> { - // Ensure that there is space in the map - let index = - self.map - .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?; - - Ok(&mut self.map.entries[index].value) - } - - /// Insert the value into the entry. - /// - /// The value will be associated with this entry's key. The new - /// `OccupiedEntry` is returned, allowing for further manipulation. - /// - /// # Examples - /// - /// ``` - /// # use http::header::*; - /// let mut map = HeaderMap::new(); - /// - /// if let Entry::Vacant(v) = map.try_entry("x-hello").unwrap() { - /// let mut e = v.try_insert_entry("world".parse().unwrap()).unwrap(); - /// e.insert("world2".parse().unwrap()); - /// } - /// - /// assert_eq!(map["x-hello"], "world2"); - /// ``` - pub fn insert_entry(self, value: T) -> OccupiedEntry<'a, T> { - self.try_insert_entry(value) - .expect("size overflows MAX_SIZE") - } - - /// Insert the value into the entry. - /// - /// The value will be associated with this entry's key. The new - /// `OccupiedEntry` is returned, allowing for further manipulation. - /// - /// # Examples - /// - /// ``` - /// # use http::header::*; - /// let mut map = HeaderMap::new(); - /// - /// if let Entry::Vacant(v) = map.try_entry("x-hello").unwrap() { - /// let mut e = v.try_insert_entry("world".parse().unwrap()).unwrap(); - /// e.insert("world2".parse().unwrap()); - /// } - /// - /// assert_eq!(map["x-hello"], "world2"); - /// ``` - pub fn try_insert_entry(self, value: T) -> Result<OccupiedEntry<'a, T>, MaxSizeReached> { - // Ensure that there is space in the map - let index = - self.map - .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?; - - Ok(OccupiedEntry { - map: self.map, - index, - probe: self.probe, - }) - } -} - -// ===== impl GetAll ===== - -impl<'a, T: 'a> GetAll<'a, T> { - /// Returns an iterator visiting all values associated with the entry. - /// - /// Values are iterated in insertion order. - /// - /// # Examples - /// - /// ``` - /// # use http::HeaderMap; - /// # use http::header::HOST; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// map.append(HOST, "hello.earth".parse().unwrap()); - /// - /// let values = map.get_all("host"); - /// let mut iter = values.iter(); - /// assert_eq!(&"hello.world", iter.next().unwrap()); - /// assert_eq!(&"hello.earth", iter.next().unwrap()); - /// assert!(iter.next().is_none()); - /// ``` - pub fn iter(&self) -> ValueIter<'a, T> { - // This creates a new GetAll struct so that the lifetime - // isn't bound to &self. - GetAll { - map: self.map, - index: self.index, - } - .into_iter() - } -} - -impl<'a, T: PartialEq> PartialEq for GetAll<'a, T> { - fn eq(&self, other: &Self) -> bool { - self.iter().eq(other.iter()) - } -} - -impl<'a, T> IntoIterator for GetAll<'a, T> { - type Item = &'a T; - type IntoIter = ValueIter<'a, T>; - - fn into_iter(self) -> ValueIter<'a, T> { - self.map.value_iter(self.index) - } -} - -impl<'a, 'b: 'a, T> IntoIterator for &'b GetAll<'a, T> { - type Item = &'a T; - type IntoIter = ValueIter<'a, T>; - - fn into_iter(self) -> ValueIter<'a, T> { - self.map.value_iter(self.index) - } -} - -// ===== impl ValueIter ===== - -impl<'a, T: 'a> Iterator for ValueIter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<Self::Item> { - use self::Cursor::*; - - match self.front { - Some(Head) => { - let entry = &self.map.entries[self.index]; - - if self.back == Some(Head) { - self.front = None; - self.back = None; - } else { - // Update the iterator state - match entry.links { - Some(links) => { - self.front = Some(Values(links.next)); - } - None => unreachable!(), - } - } - - Some(&entry.value) - } - Some(Values(idx)) => { - let extra = &self.map.extra_values[idx]; - - if self.front == self.back { - self.front = None; - self.back = None; - } else { - match extra.next { - Link::Entry(_) => self.front = None, - Link::Extra(i) => self.front = Some(Values(i)), - } - } - - Some(&extra.value) - } - None => None, - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - match (self.front, self.back) { - // Exactly 1 value... - (Some(Cursor::Head), Some(Cursor::Head)) => (1, Some(1)), - // At least 1... - (Some(_), _) => (1, None), - // No more values... - (None, _) => (0, Some(0)), - } - } -} - -impl<'a, T: 'a> DoubleEndedIterator for ValueIter<'a, T> { - fn next_back(&mut self) -> Option<Self::Item> { - use self::Cursor::*; - - match self.back { - Some(Head) => { - self.front = None; - self.back = None; - Some(&self.map.entries[self.index].value) - } - Some(Values(idx)) => { - let extra = &self.map.extra_values[idx]; - - if self.front == self.back { - self.front = None; - self.back = None; - } else { - match extra.prev { - Link::Entry(_) => self.back = Some(Head), - Link::Extra(idx) => self.back = Some(Values(idx)), - } - } - - Some(&extra.value) - } - None => None, - } - } -} - -impl<'a, T> FusedIterator for ValueIter<'a, T> {} - -// ===== impl ValueIterMut ===== - -impl<'a, T: 'a> Iterator for ValueIterMut<'a, T> { - type Item = &'a mut T; - - fn next(&mut self) -> Option<Self::Item> { - use self::Cursor::*; - - let entry = unsafe { &mut (*self.map).entries[self.index] }; - - match self.front { - Some(Head) => { - if self.back == Some(Head) { - self.front = None; - self.back = None; - } else { - // Update the iterator state - match entry.links { - Some(links) => { - self.front = Some(Values(links.next)); - } - None => unreachable!(), - } - } - - Some(&mut entry.value) - } - Some(Values(idx)) => { - let extra = unsafe { &mut (*self.map).extra_values[idx] }; - - if self.front == self.back { - self.front = None; - self.back = None; - } else { - match extra.next { - Link::Entry(_) => self.front = None, - Link::Extra(i) => self.front = Some(Values(i)), - } - } - - Some(&mut extra.value) - } - None => None, - } - } -} - -impl<'a, T: 'a> DoubleEndedIterator for ValueIterMut<'a, T> { - fn next_back(&mut self) -> Option<Self::Item> { - use self::Cursor::*; - - let entry = unsafe { &mut (*self.map).entries[self.index] }; - - match self.back { - Some(Head) => { - self.front = None; - self.back = None; - Some(&mut entry.value) - } - Some(Values(idx)) => { - let extra = unsafe { &mut (*self.map).extra_values[idx] }; - - if self.front == self.back { - self.front = None; - self.back = None; - } else { - match extra.prev { - Link::Entry(_) => self.back = Some(Head), - Link::Extra(idx) => self.back = Some(Values(idx)), - } - } - - Some(&mut extra.value) - } - None => None, - } - } -} - -impl<'a, T> FusedIterator for ValueIterMut<'a, T> {} - -unsafe impl<'a, T: Sync> Sync for ValueIterMut<'a, T> {} -unsafe impl<'a, T: Send> Send for ValueIterMut<'a, T> {} - -// ===== impl IntoIter ===== - -impl<T> Iterator for IntoIter<T> { - type Item = (Option<HeaderName>, T); - - fn next(&mut self) -> Option<Self::Item> { - if let Some(next) = self.next { - self.next = match self.extra_values[next].next { - Link::Entry(_) => None, - Link::Extra(v) => Some(v), - }; - - let value = unsafe { ptr::read(&self.extra_values[next].value) }; - - return Some((None, value)); - } - - if let Some(bucket) = self.entries.next() { - self.next = bucket.links.map(|l| l.next); - let name = Some(bucket.key); - let value = bucket.value; - - return Some((name, value)); - } - - None - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let (lower, _) = self.entries.size_hint(); - // There could be more than just the entries upper, as there - // could be items in the `extra_values`. We could guess, saying - // `upper + extra_values.len()`, but that could overestimate by a lot. - (lower, None) - } -} - -impl<T> FusedIterator for IntoIter<T> {} - -impl<T> Drop for IntoIter<T> { - fn drop(&mut self) { - // Ensure the iterator is consumed - for _ in self.by_ref() {} - - // All the values have already been yielded out. - unsafe { - self.extra_values.set_len(0); - } - } -} - -// ===== impl OccupiedEntry ===== - -impl<'a, T> OccupiedEntry<'a, T> { - /// Returns a reference to the entry's key. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// - /// if let Entry::Occupied(e) = map.entry("host") { - /// assert_eq!("host", e.key()); - /// } - /// ``` - pub fn key(&self) -> &HeaderName { - &self.map.entries[self.index].key - } - - /// Get a reference to the first value in the entry. - /// - /// Values are stored in insertion order. - /// - /// # Panics - /// - /// `get` panics if there are no values associated with the entry. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// assert_eq!(e.get(), &"hello.world"); - /// - /// e.append("hello.earth".parse().unwrap()); - /// - /// assert_eq!(e.get(), &"hello.world"); - /// } - /// ``` - pub fn get(&self) -> &T { - &self.map.entries[self.index].value - } - - /// Get a mutable reference to the first value in the entry. - /// - /// Values are stored in insertion order. - /// - /// # Panics - /// - /// `get_mut` panics if there are no values associated with the entry. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::default(); - /// map.insert(HOST, "hello.world".to_string()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// e.get_mut().push_str("-2"); - /// assert_eq!(e.get(), &"hello.world-2"); - /// } - /// ``` - pub fn get_mut(&mut self) -> &mut T { - &mut self.map.entries[self.index].value - } - - /// Converts the `OccupiedEntry` into a mutable reference to the **first** - /// value. - /// - /// The lifetime of the returned reference is bound to the original map. - /// - /// # Panics - /// - /// `into_mut` panics if there are no values associated with the entry. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::default(); - /// map.insert(HOST, "hello.world".to_string()); - /// map.append(HOST, "hello.earth".to_string()); - /// - /// if let Entry::Occupied(e) = map.entry("host") { - /// e.into_mut().push_str("-2"); - /// } - /// - /// assert_eq!("hello.world-2", map["host"]); - /// ``` - pub fn into_mut(self) -> &'a mut T { - &mut self.map.entries[self.index].value - } - - /// Sets the value of the entry. - /// - /// All previous values associated with the entry are removed and the first - /// one is returned. See `insert_mult` for an API that returns all values. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "hello.world".parse().unwrap()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// let mut prev = e.insert("earth".parse().unwrap()); - /// assert_eq!("hello.world", prev); - /// } - /// - /// assert_eq!("earth", map["host"]); - /// ``` - pub fn insert(&mut self, value: T) -> T { - self.map.insert_occupied(self.index, value) - } - - /// Sets the value of the entry. - /// - /// This function does the same as `insert` except it returns an iterator - /// that yields all values previously associated with the key. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// map.append(HOST, "world2".parse().unwrap()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// let mut prev = e.insert_mult("earth".parse().unwrap()); - /// assert_eq!("world", prev.next().unwrap()); - /// assert_eq!("world2", prev.next().unwrap()); - /// assert!(prev.next().is_none()); - /// } - /// - /// assert_eq!("earth", map["host"]); - /// ``` - pub fn insert_mult(&mut self, value: T) -> ValueDrain<'_, T> { - self.map.insert_occupied_mult(self.index, value) - } - - /// Insert the value into the entry. - /// - /// The new value is appended to the end of the entry's value list. All - /// previous values associated with the entry are retained. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// e.append("earth".parse().unwrap()); - /// } - /// - /// let values = map.get_all("host"); - /// let mut i = values.iter(); - /// assert_eq!("world", *i.next().unwrap()); - /// assert_eq!("earth", *i.next().unwrap()); - /// ``` - pub fn append(&mut self, value: T) { - let idx = self.index; - let entry = &mut self.map.entries[idx]; - append_value(idx, entry, &mut self.map.extra_values, value); - } - - /// Remove the entry from the map. - /// - /// All values associated with the entry are removed and the first one is - /// returned. See `remove_entry_mult` for an API that returns all values. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// - /// if let Entry::Occupied(e) = map.entry("host") { - /// let mut prev = e.remove(); - /// assert_eq!("world", prev); - /// } - /// - /// assert!(!map.contains_key("host")); - /// ``` - pub fn remove(self) -> T { - self.remove_entry().1 - } - - /// Remove the entry from the map. - /// - /// The key and all values associated with the entry are removed and the - /// first one is returned. See `remove_entry_mult` for an API that returns - /// all values. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// - /// if let Entry::Occupied(e) = map.entry("host") { - /// let (key, mut prev) = e.remove_entry(); - /// assert_eq!("host", key.as_str()); - /// assert_eq!("world", prev); - /// } - /// - /// assert!(!map.contains_key("host")); - /// ``` - pub fn remove_entry(self) -> (HeaderName, T) { - if let Some(links) = self.map.entries[self.index].links { - self.map.remove_all_extra_values(links.next); - } - - let entry = self.map.remove_found(self.probe, self.index); - - (entry.key, entry.value) - } - - /// Remove the entry from the map. - /// - /// The key and all values associated with the entry are removed and - /// returned. - pub fn remove_entry_mult(self) -> (HeaderName, ValueDrain<'a, T>) { - let raw_links = self.map.raw_links(); - let extra_values = &mut self.map.extra_values; - - let next = self.map.entries[self.index] - .links - .map(|l| drain_all_extra_values(raw_links, extra_values, l.next).into_iter()); - - let entry = self.map.remove_found(self.probe, self.index); - - let drain = ValueDrain { - first: Some(entry.value), - next, - lt: PhantomData, - }; - (entry.key, drain) - } - - /// Returns an iterator visiting all values associated with the entry. - /// - /// Values are iterated in insertion order. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::new(); - /// map.insert(HOST, "world".parse().unwrap()); - /// map.append(HOST, "earth".parse().unwrap()); - /// - /// if let Entry::Occupied(e) = map.entry("host") { - /// let mut iter = e.iter(); - /// assert_eq!(&"world", iter.next().unwrap()); - /// assert_eq!(&"earth", iter.next().unwrap()); - /// assert!(iter.next().is_none()); - /// } - /// ``` - pub fn iter(&self) -> ValueIter<'_, T> { - self.map.value_iter(Some(self.index)) - } - - /// Returns an iterator mutably visiting all values associated with the - /// entry. - /// - /// Values are iterated in insertion order. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderMap, Entry, HOST}; - /// let mut map = HeaderMap::default(); - /// map.insert(HOST, "world".to_string()); - /// map.append(HOST, "earth".to_string()); - /// - /// if let Entry::Occupied(mut e) = map.entry("host") { - /// for e in e.iter_mut() { - /// e.push_str("-boop"); - /// } - /// } - /// - /// let mut values = map.get_all("host"); - /// let mut i = values.iter(); - /// assert_eq!(&"world-boop", i.next().unwrap()); - /// assert_eq!(&"earth-boop", i.next().unwrap()); - /// ``` - pub fn iter_mut(&mut self) -> ValueIterMut<'_, T> { - self.map.value_iter_mut(self.index) - } -} - -impl<'a, T> IntoIterator for OccupiedEntry<'a, T> { - type Item = &'a mut T; - type IntoIter = ValueIterMut<'a, T>; - - fn into_iter(self) -> ValueIterMut<'a, T> { - self.map.value_iter_mut(self.index) - } -} - -impl<'a, 'b: 'a, T> IntoIterator for &'b OccupiedEntry<'a, T> { - type Item = &'a T; - type IntoIter = ValueIter<'a, T>; - - fn into_iter(self) -> ValueIter<'a, T> { - self.iter() - } -} - -impl<'a, 'b: 'a, T> IntoIterator for &'b mut OccupiedEntry<'a, T> { - type Item = &'a mut T; - type IntoIter = ValueIterMut<'a, T>; - - fn into_iter(self) -> ValueIterMut<'a, T> { - self.iter_mut() - } -} - -// ===== impl ValueDrain ===== - -impl<'a, T> Iterator for ValueDrain<'a, T> { - type Item = T; - - fn next(&mut self) -> Option<T> { - if self.first.is_some() { - self.first.take() - } else if let Some(ref mut extras) = self.next { - extras.next() - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option<usize>) { - match (&self.first, &self.next) { - // Exactly 1 - (&Some(_), &None) => (1, Some(1)), - // 1 + extras - (&Some(_), Some(extras)) => { - let (l, u) = extras.size_hint(); - (l + 1, u.map(|u| u + 1)) - } - // Extras only - (&None, Some(extras)) => extras.size_hint(), - // No more - (&None, &None) => (0, Some(0)), - } - } -} - -impl<'a, T> FusedIterator for ValueDrain<'a, T> {} - -impl<'a, T> Drop for ValueDrain<'a, T> { - fn drop(&mut self) { - for _ in self.by_ref() {} - } -} - -unsafe impl<'a, T: Sync> Sync for ValueDrain<'a, T> {} -unsafe impl<'a, T: Send> Send for ValueDrain<'a, T> {} - -// ===== impl RawLinks ===== - -impl<T> Clone for RawLinks<T> { - fn clone(&self) -> RawLinks<T> { - *self - } -} - -impl<T> Copy for RawLinks<T> {} - -impl<T> ops::Index<usize> for RawLinks<T> { - type Output = Option<Links>; - - fn index(&self, idx: usize) -> &Self::Output { - unsafe { &(*self.0)[idx].links } - } -} - -impl<T> ops::IndexMut<usize> for RawLinks<T> { - fn index_mut(&mut self, idx: usize) -> &mut Self::Output { - unsafe { &mut (*self.0)[idx].links } - } -} - -// ===== impl Pos ===== - -impl Pos { - #[inline] - fn new(index: usize, hash: HashValue) -> Self { - debug_assert!(index < MAX_SIZE); - Pos { - index: index as Size, - hash, - } - } - - #[inline] - fn none() -> Self { - Pos { - index: !0, - hash: HashValue(0), - } - } - - #[inline] - fn is_some(&self) -> bool { - !self.is_none() - } - - #[inline] - fn is_none(&self) -> bool { - self.index == !0 - } - - #[inline] - fn resolve(&self) -> Option<(usize, HashValue)> { - if self.is_some() { - Some((self.index as usize, self.hash)) - } else { - None - } - } -} - -impl Danger { - fn is_red(&self) -> bool { - matches!(*self, Danger::Red(_)) - } - - fn set_red(&mut self) { - debug_assert!(self.is_yellow()); - *self = Danger::Red(RandomState::new()); - } - - fn is_yellow(&self) -> bool { - matches!(*self, Danger::Yellow) - } - - fn set_yellow(&mut self) { - if let Danger::Green = *self { - *self = Danger::Yellow; - } - } - - fn set_green(&mut self) { - debug_assert!(self.is_yellow()); - *self = Danger::Green; - } -} - -// ===== impl MaxSizeReached ===== - -impl MaxSizeReached { - fn new() -> Self { - MaxSizeReached { _priv: () } - } -} - -impl fmt::Debug for MaxSizeReached { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("MaxSizeReached") - // skip _priv noise - .finish() - } -} - -impl fmt::Display for MaxSizeReached { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("max size reached") - } -} - -impl std::error::Error for MaxSizeReached {} - -// ===== impl Utils ===== - -#[inline] -fn usable_capacity(cap: usize) -> usize { - cap - cap / 4 -} - -#[inline] -fn to_raw_capacity(n: usize) -> usize { - match n.checked_add(n / 3) { - Some(n) => n, - None => panic!( - "requested capacity {} too large: overflow while converting to raw capacity", - n - ), - } -} - -#[inline] -fn desired_pos(mask: Size, hash: HashValue) -> usize { - (hash.0 & mask) as usize -} - -/// The number of steps that `current` is forward of the desired position for hash -#[inline] -fn probe_distance(mask: Size, hash: HashValue, current: usize) -> usize { - current.wrapping_sub(desired_pos(mask, hash)) & mask as usize -} - -fn hash_elem_using<K>(danger: &Danger, k: &K) -> HashValue -where - K: Hash + ?Sized, -{ - use fnv::FnvHasher; - - const MASK: u64 = (MAX_SIZE as u64) - 1; - - let hash = match *danger { - // Safe hash - Danger::Red(ref hasher) => { - let mut h = hasher.build_hasher(); - k.hash(&mut h); - h.finish() - } - // Fast hash - _ => { - let mut h = FnvHasher::default(); - k.hash(&mut h); - h.finish() - } - }; - - HashValue((hash & MASK) as u16) -} - -/* - * - * ===== impl IntoHeaderName / AsHeaderName ===== - * - */ - -mod into_header_name { - use super::{Entry, HdrName, HeaderMap, HeaderName, MaxSizeReached}; - - /// A marker trait used to identify values that can be used as insert keys - /// to a `HeaderMap`. - pub trait IntoHeaderName: Sealed {} - - // All methods are on this pub(super) trait, instead of `IntoHeaderName`, - // so that they aren't publicly exposed to the world. - // - // Being on the `IntoHeaderName` trait would mean users could call - // `"host".insert(&mut map, "localhost")`. - // - // Ultimately, this allows us to adjust the signatures of these methods - // without breaking any external crate. - pub trait Sealed { - #[doc(hidden)] - fn try_insert<T>(self, map: &mut HeaderMap<T>, val: T) - -> Result<Option<T>, MaxSizeReached>; - - #[doc(hidden)] - fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached>; - - #[doc(hidden)] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached>; - } - - // ==== impls ==== - - impl Sealed for HeaderName { - #[inline] - fn try_insert<T>( - self, - map: &mut HeaderMap<T>, - val: T, - ) -> Result<Option<T>, MaxSizeReached> { - map.try_insert2(self, val) - } - - #[inline] - fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached> { - map.try_append2(self, val) - } - - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> { - map.try_entry2(self) - } - } - - impl IntoHeaderName for HeaderName {} - - impl<'a> Sealed for &'a HeaderName { - #[inline] - fn try_insert<T>( - self, - map: &mut HeaderMap<T>, - val: T, - ) -> Result<Option<T>, MaxSizeReached> { - map.try_insert2(self, val) - } - #[inline] - fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached> { - map.try_append2(self, val) - } - - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> { - map.try_entry2(self) - } - } - - impl<'a> IntoHeaderName for &'a HeaderName {} - - impl Sealed for &'static str { - #[inline] - fn try_insert<T>( - self, - map: &mut HeaderMap<T>, - val: T, - ) -> Result<Option<T>, MaxSizeReached> { - HdrName::from_static(self, move |hdr| map.try_insert2(hdr, val)) - } - #[inline] - fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached> { - HdrName::from_static(self, move |hdr| map.try_append2(hdr, val)) - } - - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> { - HdrName::from_static(self, move |hdr| map.try_entry2(hdr)) - } - } - - impl IntoHeaderName for &'static str {} -} - -mod as_header_name { - use super::{Entry, HdrName, HeaderMap, HeaderName, InvalidHeaderName, MaxSizeReached}; - - /// A marker trait used to identify values that can be used as search keys - /// to a `HeaderMap`. - pub trait AsHeaderName: Sealed {} - - // Debug not currently needed, save on compiling it - #[allow(missing_debug_implementations)] - pub enum TryEntryError { - InvalidHeaderName(InvalidHeaderName), - MaxSizeReached(MaxSizeReached), - } - - impl From<InvalidHeaderName> for TryEntryError { - fn from(e: InvalidHeaderName) -> TryEntryError { - TryEntryError::InvalidHeaderName(e) - } - } - - impl From<MaxSizeReached> for TryEntryError { - fn from(e: MaxSizeReached) -> TryEntryError { - TryEntryError::MaxSizeReached(e) - } - } - - // All methods are on this pub(super) trait, instead of `AsHeaderName`, - // so that they aren't publicly exposed to the world. - // - // Being on the `AsHeaderName` trait would mean users could call - // `"host".find(&map)`. - // - // Ultimately, this allows us to adjust the signatures of these methods - // without breaking any external crate. - pub trait Sealed { - #[doc(hidden)] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError>; - - #[doc(hidden)] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)>; - - #[doc(hidden)] - fn as_str(&self) -> &str; - } - - // ==== impls ==== - - impl Sealed for HeaderName { - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> { - Ok(map.try_entry2(self)?) - } - - #[inline] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)> { - map.find(self) - } - - fn as_str(&self) -> &str { - <HeaderName>::as_str(self) - } - } - - impl AsHeaderName for HeaderName {} - - impl<'a> Sealed for &'a HeaderName { - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> { - Ok(map.try_entry2(self)?) - } - - #[inline] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)> { - map.find(*self) - } - - fn as_str(&self) -> &str { - <HeaderName>::as_str(self) - } - } - - impl<'a> AsHeaderName for &'a HeaderName {} - - impl<'a> Sealed for &'a str { - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> { - Ok(HdrName::from_bytes(self.as_bytes(), move |hdr| { - map.try_entry2(hdr) - })??) - } - - #[inline] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)> { - HdrName::from_bytes(self.as_bytes(), move |hdr| map.find(&hdr)).unwrap_or(None) - } - - fn as_str(&self) -> &str { - self - } - } - - impl<'a> AsHeaderName for &'a str {} - - impl Sealed for String { - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> { - self.as_str().try_entry(map) - } - - #[inline] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)> { - Sealed::find(&self.as_str(), map) - } - - fn as_str(&self) -> &str { - self - } - } - - impl AsHeaderName for String {} - - impl<'a> Sealed for &'a String { - #[inline] - fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> { - self.as_str().try_entry(map) - } - - #[inline] - fn find<T>(&self, map: &HeaderMap<T>) -> Option<(usize, usize)> { - Sealed::find(*self, map) - } - - fn as_str(&self) -> &str { - self - } - } - - impl<'a> AsHeaderName for &'a String {} -} - -#[test] -fn test_bounds() { - fn check_bounds<T: Send + Send>() {} - - check_bounds::<HeaderMap<()>>(); - check_bounds::<Iter<'static, ()>>(); - check_bounds::<IterMut<'static, ()>>(); - check_bounds::<Keys<'static, ()>>(); - check_bounds::<Values<'static, ()>>(); - check_bounds::<ValuesMut<'static, ()>>(); - check_bounds::<Drain<'static, ()>>(); - check_bounds::<GetAll<'static, ()>>(); - check_bounds::<Entry<'static, ()>>(); - check_bounds::<VacantEntry<'static, ()>>(); - check_bounds::<OccupiedEntry<'static, ()>>(); - check_bounds::<ValueIter<'static, ()>>(); - check_bounds::<ValueIterMut<'static, ()>>(); - check_bounds::<ValueDrain<'static, ()>>(); -} - -#[test] -fn skip_duplicates_during_key_iteration() { - let mut map = HeaderMap::new(); - map.try_append("a", HeaderValue::from_static("a")).unwrap(); - map.try_append("a", HeaderValue::from_static("b")).unwrap(); - assert_eq!(map.keys().count(), map.keys_len()); -} diff --git a/vendor/http/src/header/mod.rs b/vendor/http/src/header/mod.rs deleted file mode 100644 index 5d405767..00000000 --- a/vendor/http/src/header/mod.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! HTTP header types -//! -//! The module provides [`HeaderName`], [`HeaderMap`], and a number of types -//! used for interacting with `HeaderMap`. These types allow representing both -//! HTTP/1 and HTTP/2 headers. -//! -//! # `HeaderName` -//! -//! The `HeaderName` type represents both standard header names as well as -//! custom header names. The type handles the case insensitive nature of header -//! names and is used as the key portion of `HeaderMap`. Header names are -//! normalized to lower case. In other words, when creating a `HeaderName` with -//! a string, even if upper case characters are included, when getting a string -//! representation of the `HeaderName`, it will be all lower case. This allows -//! for faster `HeaderMap` comparison operations. -//! -//! The internal representation is optimized to efficiently handle the cases -//! most commonly encountered when working with HTTP. Standard header names are -//! special cased and are represented internally as an enum. Short custom -//! headers will be stored directly in the `HeaderName` struct and will not -//! incur any allocation overhead, however longer strings will require an -//! allocation for storage. -//! -//! ## Limitations -//! -//! `HeaderName` has a max length of 32,768 for header names. Attempting to -//! parse longer names will result in a panic. -//! -//! # `HeaderMap` -//! -//! `HeaderMap` is a map structure of header names highly optimized for use -//! cases common with HTTP. It is a [multimap] structure, where each header name -//! may have multiple associated header values. Given this, some of the APIs -//! diverge from [`HashMap`]. -//! -//! ## Overview -//! -//! Just like `HashMap` in Rust's stdlib, `HeaderMap` is based on [Robin Hood -//! hashing]. This algorithm tends to reduce the worst case search times in the -//! table and enables high load factors without seriously affecting performance. -//! Internally, keys and values are stored in vectors. As such, each insertion -//! will not incur allocation overhead. However, once the underlying vector -//! storage is full, a larger vector must be allocated and all values copied. -//! -//! ## Deterministic ordering -//! -//! Unlike Rust's `HashMap`, values in `HeaderMap` are deterministically -//! ordered. Roughly, values are ordered by insertion. This means that a -//! function that deterministically operates on a header map can rely on the -//! iteration order to remain consistent across processes and platforms. -//! -//! ## Adaptive hashing -//! -//! `HeaderMap` uses an adaptive hashing strategy in order to efficiently handle -//! most common cases. All standard headers have statically computed hash values -//! which removes the need to perform any hashing of these headers at runtime. -//! The default hash function emphasizes performance over robustness. However, -//! `HeaderMap` detects high collision rates and switches to a secure hash -//! function in those events. The threshold is set such that only denial of -//! service attacks should trigger it. -//! -//! ## Limitations -//! -//! `HeaderMap` can store a maximum of 32,768 headers (header name / value -//! pairs). Attempting to insert more will result in a panic. -//! -//! [`HeaderName`]: struct.HeaderName.html -//! [`HeaderMap`]: struct.HeaderMap.html -//! [multimap]: https://en.wikipedia.org/wiki/Multimap -//! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html -//! [Robin Hood hashing]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing - -mod map; -mod name; -mod value; - -pub use self::map::{ - AsHeaderName, Drain, Entry, GetAll, HeaderMap, IntoHeaderName, IntoIter, Iter, IterMut, Keys, - MaxSizeReached, OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values, - ValuesMut, -}; -pub use self::name::{HeaderName, InvalidHeaderName}; -pub use self::value::{HeaderValue, InvalidHeaderValue, ToStrError}; - -// Use header name constants -#[rustfmt::skip] -pub use self::name::{ - ACCEPT, - ACCEPT_CHARSET, - ACCEPT_ENCODING, - ACCEPT_LANGUAGE, - ACCEPT_RANGES, - ACCESS_CONTROL_ALLOW_CREDENTIALS, - ACCESS_CONTROL_ALLOW_HEADERS, - ACCESS_CONTROL_ALLOW_METHODS, - ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_EXPOSE_HEADERS, - ACCESS_CONTROL_MAX_AGE, - ACCESS_CONTROL_REQUEST_HEADERS, - ACCESS_CONTROL_REQUEST_METHOD, - AGE, - ALLOW, - ALT_SVC, - AUTHORIZATION, - CACHE_CONTROL, - CACHE_STATUS, - CDN_CACHE_CONTROL, - CONNECTION, - CONTENT_DISPOSITION, - CONTENT_ENCODING, - CONTENT_LANGUAGE, - CONTENT_LENGTH, - CONTENT_LOCATION, - CONTENT_RANGE, - CONTENT_SECURITY_POLICY, - CONTENT_SECURITY_POLICY_REPORT_ONLY, - CONTENT_TYPE, - COOKIE, - DNT, - DATE, - ETAG, - EXPECT, - EXPIRES, - FORWARDED, - FROM, - HOST, - IF_MATCH, - IF_MODIFIED_SINCE, - IF_NONE_MATCH, - IF_RANGE, - IF_UNMODIFIED_SINCE, - LAST_MODIFIED, - LINK, - LOCATION, - MAX_FORWARDS, - ORIGIN, - PRAGMA, - PROXY_AUTHENTICATE, - PROXY_AUTHORIZATION, - PUBLIC_KEY_PINS, - PUBLIC_KEY_PINS_REPORT_ONLY, - RANGE, - REFERER, - REFERRER_POLICY, - REFRESH, - RETRY_AFTER, - SEC_WEBSOCKET_ACCEPT, - SEC_WEBSOCKET_EXTENSIONS, - SEC_WEBSOCKET_KEY, - SEC_WEBSOCKET_PROTOCOL, - SEC_WEBSOCKET_VERSION, - SERVER, - SET_COOKIE, - STRICT_TRANSPORT_SECURITY, - TE, - TRAILER, - TRANSFER_ENCODING, - UPGRADE, - UPGRADE_INSECURE_REQUESTS, - USER_AGENT, - VARY, - VIA, - WARNING, - WWW_AUTHENTICATE, - X_CONTENT_TYPE_OPTIONS, - X_DNS_PREFETCH_CONTROL, - X_FRAME_OPTIONS, - X_XSS_PROTECTION, -}; - -/// Maximum length of a header name -/// -/// Generally, 64kb for a header name is WAY too much than would ever be needed -/// in practice. Restricting it to this size enables using `u16` values to -/// represent offsets when dealing with header names. -const MAX_HEADER_NAME_LEN: usize = (1 << 16) - 1; diff --git a/vendor/http/src/header/name.rs b/vendor/http/src/header/name.rs deleted file mode 100644 index 122b96f9..00000000 --- a/vendor/http/src/header/name.rs +++ /dev/null @@ -1,1922 +0,0 @@ -use crate::byte_str::ByteStr; -use bytes::{Bytes, BytesMut}; - -use std::borrow::Borrow; -use std::convert::TryFrom; -use std::error::Error; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::mem::MaybeUninit; -use std::str::FromStr; - -/// Represents an HTTP header field name -/// -/// Header field names identify the header. Header sets may include multiple -/// headers with the same name. The HTTP specification defines a number of -/// standard headers, but HTTP messages may include non-standard header names as -/// well as long as they adhere to the specification. -/// -/// `HeaderName` is used as the [`HeaderMap`] key. Constants are available for -/// all standard header names in the [`header`] module. -/// -/// # Representation -/// -/// `HeaderName` represents standard header names using an `enum`, as such they -/// will not require an allocation for storage. All custom header names are -/// lower cased upon conversion to a `HeaderName` value. This avoids the -/// overhead of dynamically doing lower case conversion during the hash code -/// computation and the comparison operation. -/// -/// [`HeaderMap`]: struct.HeaderMap.html -/// [`header`]: index.html -#[derive(Clone, Eq, PartialEq, Hash)] -pub struct HeaderName { - inner: Repr<Custom>, -} - -// Almost a full `HeaderName` -#[derive(Debug, Hash)] -pub struct HdrName<'a> { - inner: Repr<MaybeLower<'a>>, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -enum Repr<T> { - Standard(StandardHeader), - Custom(T), -} - -// Used to hijack the Hash impl -#[derive(Debug, Clone, Eq, PartialEq)] -struct Custom(ByteStr); - -#[derive(Debug, Clone)] -// Invariant: If lower then buf is valid UTF-8. -struct MaybeLower<'a> { - buf: &'a [u8], - lower: bool, -} - -/// A possible error when converting a `HeaderName` from another type. -pub struct InvalidHeaderName { - _priv: (), -} - -macro_rules! standard_headers { - ( - $( - $(#[$docs:meta])* - ($konst:ident, $upcase:ident, $name_bytes:literal); - )+ - ) => { - #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] - enum StandardHeader { - $( - $konst, - )+ - } - - $( - $(#[$docs])* - pub const $upcase: HeaderName = HeaderName { - inner: Repr::Standard(StandardHeader::$konst), - }; - )+ - - impl StandardHeader { - #[inline] - fn as_str(&self) -> &'static str { - match *self { - // Safety: test_parse_standard_headers ensures these &[u8]s are &str-safe. - $( - StandardHeader::$konst => unsafe { std::str::from_utf8_unchecked( $name_bytes ) }, - )+ - } - } - - const fn from_bytes(name_bytes: &[u8]) -> Option<StandardHeader> { - match name_bytes { - $( - $name_bytes => Some(StandardHeader::$konst), - )+ - _ => None, - } - } - } - - #[cfg(test)] - const TEST_HEADERS: &'static [(StandardHeader, &'static [u8])] = &[ - $( - (StandardHeader::$konst, $name_bytes), - )+ - ]; - - #[test] - fn test_parse_standard_headers() { - for &(std, name_bytes) in TEST_HEADERS { - // Test lower case - assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), HeaderName::from(std)); - - // Test upper case - let upper = std::str::from_utf8(name_bytes).expect("byte string constants are all utf-8").to_uppercase(); - assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), HeaderName::from(std)); - } - } - - #[test] - fn test_standard_headers_into_bytes() { - for &(std, name_bytes) in TEST_HEADERS { - let name = std::str::from_utf8(name_bytes).unwrap(); - let std = HeaderName::from(std); - // Test lower case - let bytes: Bytes = - HeaderName::from_bytes(name_bytes).unwrap().inner.into(); - assert_eq!(bytes, name); - assert_eq!(HeaderName::from_bytes(name_bytes).unwrap(), std); - - // Test upper case - let upper = name.to_uppercase(); - let bytes: Bytes = - HeaderName::from_bytes(upper.as_bytes()).unwrap().inner.into(); - assert_eq!(bytes, name_bytes); - assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), - std); - } - - } - } -} - -// Generate constants for all standard HTTP headers. This includes a static hash -// code for the "fast hash" path. The hash code for static headers *do not* have -// to match the text representation of those headers. This is because header -// strings are always converted to the static values (when they match) before -// being hashed. This means that it is impossible to compare the static hash -// code of CONTENT_LENGTH with "content-length". -standard_headers! { - /// Advertises which content types the client is able to understand. - /// - /// The Accept request HTTP header advertises which content types, expressed - /// as MIME types, the client is able to understand. Using content - /// negotiation, the server then selects one of the proposals, uses it and - /// informs the client of its choice with the Content-Type response header. - /// Browsers set adequate values for this header depending of the context - /// where the request is done: when fetching a CSS stylesheet a different - /// value is set for the request than when fetching an image, video or a - /// script. - (Accept, ACCEPT, b"accept"); - - /// Advertises which character set the client is able to understand. - /// - /// The Accept-Charset request HTTP header advertises which character set - /// the client is able to understand. Using content negotiation, the server - /// then selects one of the proposals, uses it and informs the client of its - /// choice within the Content-Type response header. Browsers usually don't - /// set this header as the default value for each content type is usually - /// correct and transmitting it would allow easier fingerprinting. - /// - /// If the server cannot serve any matching character set, it can - /// theoretically send back a 406 (Not Acceptable) error code. But, for a - /// better user experience, this is rarely done and the more common way is - /// to ignore the Accept-Charset header in this case. - (AcceptCharset, ACCEPT_CHARSET, b"accept-charset"); - - /// Advertises which content encoding the client is able to understand. - /// - /// The Accept-Encoding request HTTP header advertises which content - /// encoding, usually a compression algorithm, the client is able to - /// understand. Using content negotiation, the server selects one of the - /// proposals, uses it and informs the client of its choice with the - /// Content-Encoding response header. - /// - /// Even if both the client and the server supports the same compression - /// algorithms, the server may choose not to compress the body of a - /// response, if the identity value is also acceptable. Two common cases - /// lead to this: - /// - /// * The data to be sent is already compressed and a second compression - /// won't lead to smaller data to be transmitted. This may the case with - /// some image formats; - /// - /// * The server is overloaded and cannot afford the computational overhead - /// induced by the compression requirement. Typically, Microsoft recommends - /// not to compress if a server use more than 80 % of its computational - /// power. - /// - /// As long as the identity value, meaning no compression, is not explicitly - /// forbidden, by an identity;q=0 or a *;q=0 without another explicitly set - /// value for identity, the server must never send back a 406 Not Acceptable - /// error. - (AcceptEncoding, ACCEPT_ENCODING, b"accept-encoding"); - - /// Advertises which languages the client is able to understand. - /// - /// The Accept-Language request HTTP header advertises which languages the - /// client is able to understand, and which locale variant is preferred. - /// Using content negotiation, the server then selects one of the proposals, - /// uses it and informs the client of its choice with the Content-Language - /// response header. Browsers set adequate values for this header according - /// their user interface language and even if a user can change it, this - /// happens rarely (and is frown upon as it leads to fingerprinting). - /// - /// This header is a hint to be used when the server has no way of - /// determining the language via another way, like a specific URL, that is - /// controlled by an explicit user decision. It is recommended that the - /// server never overrides an explicit decision. The content of the - /// Accept-Language is often out of the control of the user (like when - /// traveling and using an Internet Cafe in a different country); the user - /// may also want to visit a page in another language than the locale of - /// their user interface. - /// - /// If the server cannot serve any matching language, it can theoretically - /// send back a 406 (Not Acceptable) error code. But, for a better user - /// experience, this is rarely done and more common way is to ignore the - /// Accept-Language header in this case. - (AcceptLanguage, ACCEPT_LANGUAGE, b"accept-language"); - - /// Marker used by the server to advertise partial request support. - /// - /// The Accept-Ranges response HTTP header is a marker used by the server to - /// advertise its support of partial requests. The value of this field - /// indicates the unit that can be used to define a range. - /// - /// In presence of an Accept-Ranges header, the browser may try to resume an - /// interrupted download, rather than to start it from the start again. - (AcceptRanges, ACCEPT_RANGES, b"accept-ranges"); - - /// Preflight response indicating if the response to the request can be - /// exposed to the page. - /// - /// The Access-Control-Allow-Credentials response header indicates whether - /// or not the response to the request can be exposed to the page. It can be - /// exposed when the true value is returned; it can't in other cases. - /// - /// Credentials are cookies, authorization headers or TLS client - /// certificates. - /// - /// When used as part of a response to a preflight request, this indicates - /// whether or not the actual request can be made using credentials. Note - /// that simple GET requests are not preflighted, and so if a request is - /// made for a resource with credentials, if this header is not returned - /// with the resource, the response is ignored by the browser and not - /// returned to web content. - /// - /// The Access-Control-Allow-Credentials header works in conjunction with - /// the XMLHttpRequest.withCredentials property or with the credentials - /// option in the Request() constructor of the Fetch API. Credentials must - /// be set on both sides (the Access-Control-Allow-Credentials header and in - /// the XHR or Fetch request) in order for the CORS request with credentials - /// to succeed. - (AccessControlAllowCredentials, ACCESS_CONTROL_ALLOW_CREDENTIALS, b"access-control-allow-credentials"); - - /// Preflight response indicating permitted HTTP headers. - /// - /// The Access-Control-Allow-Headers response header is used in response to - /// a preflight request to indicate which HTTP headers will be available via - /// Access-Control-Expose-Headers when making the actual request. - /// - /// The simple headers, Accept, Accept-Language, Content-Language, - /// Content-Type (but only with a MIME type of its parsed value (ignoring - /// parameters) of either application/x-www-form-urlencoded, - /// multipart/form-data, or text/plain), are always available and don't need - /// to be listed by this header. - /// - /// This header is required if the request has an - /// Access-Control-Request-Headers header. - (AccessControlAllowHeaders, ACCESS_CONTROL_ALLOW_HEADERS, b"access-control-allow-headers"); - - /// Preflight header response indicating permitted access methods. - /// - /// The Access-Control-Allow-Methods response header specifies the method or - /// methods allowed when accessing the resource in response to a preflight - /// request. - (AccessControlAllowMethods, ACCESS_CONTROL_ALLOW_METHODS, b"access-control-allow-methods"); - - /// Indicates whether the response can be shared with resources with the - /// given origin. - (AccessControlAllowOrigin, ACCESS_CONTROL_ALLOW_ORIGIN, b"access-control-allow-origin"); - - /// Indicates which headers can be exposed as part of the response by - /// listing their names. - (AccessControlExposeHeaders, ACCESS_CONTROL_EXPOSE_HEADERS, b"access-control-expose-headers"); - - /// Indicates how long the results of a preflight request can be cached. - (AccessControlMaxAge, ACCESS_CONTROL_MAX_AGE, b"access-control-max-age"); - - /// Informs the server which HTTP headers will be used when an actual - /// request is made. - (AccessControlRequestHeaders, ACCESS_CONTROL_REQUEST_HEADERS, b"access-control-request-headers"); - - /// Informs the server know which HTTP method will be used when the actual - /// request is made. - (AccessControlRequestMethod, ACCESS_CONTROL_REQUEST_METHOD, b"access-control-request-method"); - - /// Indicates the time in seconds the object has been in a proxy cache. - /// - /// The Age header is usually close to zero. If it is Age: 0, it was - /// probably just fetched from the origin server; otherwise It is usually - /// calculated as a difference between the proxy's current date and the Date - /// general header included in the HTTP response. - (Age, AGE, b"age"); - - /// Lists the set of methods support by a resource. - /// - /// This header must be sent if the server responds with a 405 Method Not - /// Allowed status code to indicate which request methods can be used. An - /// empty Allow header indicates that the resource allows no request - /// methods, which might occur temporarily for a given resource, for - /// example. - (Allow, ALLOW, b"allow"); - - /// Advertises the availability of alternate services to clients. - (AltSvc, ALT_SVC, b"alt-svc"); - - /// Contains the credentials to authenticate a user agent with a server. - /// - /// Usually this header is included after the server has responded with a - /// 401 Unauthorized status and the WWW-Authenticate header. - (Authorization, AUTHORIZATION, b"authorization"); - - /// Specifies directives for caching mechanisms in both requests and - /// responses. - /// - /// Caching directives are unidirectional, meaning that a given directive in - /// a request is not implying that the same directive is to be given in the - /// response. - (CacheControl, CACHE_CONTROL, b"cache-control"); - - /// Indicates how caches have handled a response and its corresponding request. - /// - /// See [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211.html). - (CacheStatus, CACHE_STATUS, b"cache-status"); - - /// Specifies directives that allow origin servers to control the behavior of CDN caches - /// interposed between them and clients separately from other caches that might handle the - /// response. - /// - /// See [RFC 9213](https://www.rfc-editor.org/rfc/rfc9213.html). - (CdnCacheControl, CDN_CACHE_CONTROL, b"cdn-cache-control"); - - /// Controls whether or not the network connection stays open after the - /// current transaction finishes. - /// - /// If the value sent is keep-alive, the connection is persistent and not - /// closed, allowing for subsequent requests to the same server to be done. - /// - /// Except for the standard hop-by-hop headers (Keep-Alive, - /// Transfer-Encoding, TE, Connection, Trailer, Upgrade, Proxy-Authorization - /// and Proxy-Authenticate), any hop-by-hop headers used by the message must - /// be listed in the Connection header, so that the first proxy knows he has - /// to consume them and not to forward them further. Standard hop-by-hop - /// headers can be listed too (it is often the case of Keep-Alive, but this - /// is not mandatory. - (Connection, CONNECTION, b"connection"); - - /// Indicates if the content is expected to be displayed inline. - /// - /// In a regular HTTP response, the Content-Disposition response header is a - /// header indicating if the content is expected to be displayed inline in - /// the browser, that is, as a Web page or as part of a Web page, or as an - /// attachment, that is downloaded and saved locally. - /// - /// In a multipart/form-data body, the HTTP Content-Disposition general - /// header is a header that can be used on the subpart of a multipart body - /// to give information about the field it applies to. The subpart is - /// delimited by the boundary defined in the Content-Type header. Used on - /// the body itself, Content-Disposition has no effect. - /// - /// The Content-Disposition header is defined in the larger context of MIME - /// messages for e-mail, but only a subset of the possible parameters apply - /// to HTTP forms and POST requests. Only the value form-data, as well as - /// the optional directive name and filename, can be used in the HTTP - /// context. - (ContentDisposition, CONTENT_DISPOSITION, b"content-disposition"); - - /// Used to compress the media-type. - /// - /// When present, its value indicates what additional content encoding has - /// been applied to the entity-body. It lets the client know, how to decode - /// in order to obtain the media-type referenced by the Content-Type header. - /// - /// It is recommended to compress data as much as possible and therefore to - /// use this field, but some types of resources, like jpeg images, are - /// already compressed. Sometimes using additional compression doesn't - /// reduce payload size and can even make the payload longer. - (ContentEncoding, CONTENT_ENCODING, b"content-encoding"); - - /// Used to describe the languages intended for the audience. - /// - /// This header allows a user to differentiate according to the users' own - /// preferred language. For example, if "Content-Language: de-DE" is set, it - /// says that the document is intended for German language speakers - /// (however, it doesn't indicate the document is written in German. For - /// example, it might be written in English as part of a language course for - /// German speakers). - /// - /// If no Content-Language is specified, the default is that the content is - /// intended for all language audiences. Multiple language tags are also - /// possible, as well as applying the Content-Language header to various - /// media types and not only to textual documents. - (ContentLanguage, CONTENT_LANGUAGE, b"content-language"); - - /// Indicates the size of the entity-body. - /// - /// The header value must be a decimal indicating the number of octets sent - /// to the recipient. - (ContentLength, CONTENT_LENGTH, b"content-length"); - - /// Indicates an alternate location for the returned data. - /// - /// The principal use case is to indicate the URL of the resource - /// transmitted as the result of content negotiation. - /// - /// Location and Content-Location are different: Location indicates the - /// target of a redirection (or the URL of a newly created document), while - /// Content-Location indicates the direct URL to use to access the resource, - /// without the need of further content negotiation. Location is a header - /// associated with the response, while Content-Location is associated with - /// the entity returned. - (ContentLocation, CONTENT_LOCATION, b"content-location"); - - /// Indicates where in a full body message a partial message belongs. - (ContentRange, CONTENT_RANGE, b"content-range"); - - /// Allows controlling resources the user agent is allowed to load for a - /// given page. - /// - /// With a few exceptions, policies mostly involve specifying server origins - /// and script endpoints. This helps guard against cross-site scripting - /// attacks (XSS). - (ContentSecurityPolicy, CONTENT_SECURITY_POLICY, b"content-security-policy"); - - /// Allows experimenting with policies by monitoring their effects. - /// - /// The HTTP Content-Security-Policy-Report-Only response header allows web - /// developers to experiment with policies by monitoring (but not enforcing) - /// their effects. These violation reports consist of JSON documents sent - /// via an HTTP POST request to the specified URI. - (ContentSecurityPolicyReportOnly, CONTENT_SECURITY_POLICY_REPORT_ONLY, b"content-security-policy-report-only"); - - /// Used to indicate the media type of the resource. - /// - /// In responses, a Content-Type header tells the client what the content - /// type of the returned content actually is. Browsers will do MIME sniffing - /// in some cases and will not necessarily follow the value of this header; - /// to prevent this behavior, the header X-Content-Type-Options can be set - /// to nosniff. - /// - /// In requests, (such as POST or PUT), the client tells the server what - /// type of data is actually sent. - (ContentType, CONTENT_TYPE, b"content-type"); - - /// Contains stored HTTP cookies previously sent by the server with the - /// Set-Cookie header. - /// - /// The Cookie header might be omitted entirely, if the privacy setting of - /// the browser are set to block them, for example. - (Cookie, COOKIE, b"cookie"); - - /// Indicates the client's tracking preference. - /// - /// This header lets users indicate whether they would prefer privacy rather - /// than personalized content. - (Dnt, DNT, b"dnt"); - - /// Contains the date and time at which the message was originated. - (Date, DATE, b"date"); - - /// Identifier for a specific version of a resource. - /// - /// This header allows caches to be more efficient, and saves bandwidth, as - /// a web server does not need to send a full response if the content has - /// not changed. On the other side, if the content has changed, etags are - /// useful to help prevent simultaneous updates of a resource from - /// overwriting each other ("mid-air collisions"). - /// - /// If the resource at a given URL changes, a new Etag value must be - /// generated. Etags are therefore similar to fingerprints and might also be - /// used for tracking purposes by some servers. A comparison of them allows - /// to quickly determine whether two representations of a resource are the - /// same, but they might also be set to persist indefinitely by a tracking - /// server. - (Etag, ETAG, b"etag"); - - /// Indicates expectations that need to be fulfilled by the server in order - /// to properly handle the request. - /// - /// The only expectation defined in the specification is Expect: - /// 100-continue, to which the server shall respond with: - /// - /// * 100 if the information contained in the header is sufficient to cause - /// an immediate success, - /// - /// * 417 (Expectation Failed) if it cannot meet the expectation; or any - /// other 4xx status otherwise. - /// - /// For example, the server may reject a request if its Content-Length is - /// too large. - /// - /// No common browsers send the Expect header, but some other clients such - /// as cURL do so by default. - (Expect, EXPECT, b"expect"); - - /// Contains the date/time after which the response is considered stale. - /// - /// Invalid dates, like the value 0, represent a date in the past and mean - /// that the resource is already expired. - /// - /// If there is a Cache-Control header with the "max-age" or "s-max-age" - /// directive in the response, the Expires header is ignored. - (Expires, EXPIRES, b"expires"); - - /// Contains information from the client-facing side of proxy servers that - /// is altered or lost when a proxy is involved in the path of the request. - /// - /// The alternative and de-facto standard versions of this header are the - /// X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Proto headers. - /// - /// This header is used for debugging, statistics, and generating - /// location-dependent content and by design it exposes privacy sensitive - /// information, such as the IP address of the client. Therefore the user's - /// privacy must be kept in mind when deploying this header. - (Forwarded, FORWARDED, b"forwarded"); - - /// Contains an Internet email address for a human user who controls the - /// requesting user agent. - /// - /// If you are running a robotic user agent (e.g. a crawler), the From - /// header should be sent, so you can be contacted if problems occur on - /// servers, such as if the robot is sending excessive, unwanted, or invalid - /// requests. - (From, FROM, b"from"); - - /// Specifies the domain name of the server and (optionally) the TCP port - /// number on which the server is listening. - /// - /// If no port is given, the default port for the service requested (e.g., - /// "80" for an HTTP URL) is implied. - /// - /// A Host header field must be sent in all HTTP/1.1 request messages. A 400 - /// (Bad Request) status code will be sent to any HTTP/1.1 request message - /// that lacks a Host header field or contains more than one. - (Host, HOST, b"host"); - - /// Makes a request conditional based on the E-Tag. - /// - /// For GET and HEAD methods, the server will send back the requested - /// resource only if it matches one of the listed ETags. For PUT and other - /// non-safe methods, it will only upload the resource in this case. - /// - /// The comparison with the stored ETag uses the strong comparison - /// algorithm, meaning two files are considered identical byte to byte only. - /// This is weakened when the W/ prefix is used in front of the ETag. - /// - /// There are two common use cases: - /// - /// * For GET and HEAD methods, used in combination with an Range header, it - /// can guarantee that the new ranges requested comes from the same resource - /// than the previous one. If it doesn't match, then a 416 (Range Not - /// Satisfiable) response is returned. - /// - /// * For other methods, and in particular for PUT, If-Match can be used to - /// prevent the lost update problem. It can check if the modification of a - /// resource that the user wants to upload will not override another change - /// that has been done since the original resource was fetched. If the - /// request cannot be fulfilled, the 412 (Precondition Failed) response is - /// returned. - (IfMatch, IF_MATCH, b"if-match"); - - /// Makes a request conditional based on the modification date. - /// - /// The If-Modified-Since request HTTP header makes the request conditional: - /// the server will send back the requested resource, with a 200 status, - /// only if it has been last modified after the given date. If the request - /// has not been modified since, the response will be a 304 without any - /// body; the Last-Modified header will contain the date of last - /// modification. Unlike If-Unmodified-Since, If-Modified-Since can only be - /// used with a GET or HEAD. - /// - /// When used in combination with If-None-Match, it is ignored, unless the - /// server doesn't support If-None-Match. - /// - /// The most common use case is to update a cached entity that has no - /// associated ETag. - (IfModifiedSince, IF_MODIFIED_SINCE, b"if-modified-since"); - - /// Makes a request conditional based on the E-Tag. - /// - /// The If-None-Match HTTP request header makes the request conditional. For - /// GET and HEAD methods, the server will send back the requested resource, - /// with a 200 status, only if it doesn't have an ETag matching the given - /// ones. For other methods, the request will be processed only if the - /// eventually existing resource's ETag doesn't match any of the values - /// listed. - /// - /// When the condition fails for GET and HEAD methods, then the server must - /// return HTTP status code 304 (Not Modified). For methods that apply - /// server-side changes, the status code 412 (Precondition Failed) is used. - /// Note that the server generating a 304 response MUST generate any of the - /// following header fields that would have been sent in a 200 (OK) response - /// to the same request: Cache-Control, Content-Location, Date, ETag, - /// Expires, and Vary. - /// - /// The comparison with the stored ETag uses the weak comparison algorithm, - /// meaning two files are considered identical not only if they are - /// identical byte to byte, but if the content is equivalent. For example, - /// two pages that would differ only by the date of generation in the footer - /// would be considered as identical. - /// - /// When used in combination with If-Modified-Since, it has precedence (if - /// the server supports it). - /// - /// There are two common use cases: - /// - /// * For `GET` and `HEAD` methods, to update a cached entity that has an associated ETag. - /// * For other methods, and in particular for `PUT`, `If-None-Match` used with - /// the `*` value can be used to save a file not known to exist, - /// guaranteeing that another upload didn't happen before, losing the data - /// of the previous put; this problems is the variation of the lost update - /// problem. - (IfNoneMatch, IF_NONE_MATCH, b"if-none-match"); - - /// Makes a request conditional based on range. - /// - /// The If-Range HTTP request header makes a range request conditional: if - /// the condition is fulfilled, the range request will be issued and the - /// server sends back a 206 Partial Content answer with the appropriate - /// body. If the condition is not fulfilled, the full resource is sent back, - /// with a 200 OK status. - /// - /// This header can be used either with a Last-Modified validator, or with - /// an ETag, but not with both. - /// - /// The most common use case is to resume a download, to guarantee that the - /// stored resource has not been modified since the last fragment has been - /// received. - (IfRange, IF_RANGE, b"if-range"); - - /// Makes the request conditional based on the last modification date. - /// - /// The If-Unmodified-Since request HTTP header makes the request - /// conditional: the server will send back the requested resource, or accept - /// it in the case of a POST or another non-safe method, only if it has not - /// been last modified after the given date. If the request has been - /// modified after the given date, the response will be a 412 (Precondition - /// Failed) error. - /// - /// There are two common use cases: - /// - /// * In conjunction non-safe methods, like POST, it can be used to - /// implement an optimistic concurrency control, like done by some wikis: - /// editions are rejected if the stored document has been modified since the - /// original has been retrieved. - /// - /// * In conjunction with a range request with a If-Range header, it can be - /// used to ensure that the new fragment requested comes from an unmodified - /// document. - (IfUnmodifiedSince, IF_UNMODIFIED_SINCE, b"if-unmodified-since"); - - /// The Last-Modified header contains the date and time when the origin believes - /// the resource was last modified. - /// - /// The value is a valid Date/Time string defined in [RFC9910](https://datatracker.ietf.org/doc/html/rfc9110#section-5.6.7) - (LastModified, LAST_MODIFIED, b"last-modified"); - - /// Allows the server to point an interested client to another resource - /// containing metadata about the requested resource. - (Link, LINK, b"link"); - - /// Indicates the URL to redirect a page to. - /// - /// The Location response header indicates the URL to redirect a page to. It - /// only provides a meaning when served with a 3xx status response. - /// - /// The HTTP method used to make the new request to fetch the page pointed - /// to by Location depends of the original method and of the kind of - /// redirection: - /// - /// * If 303 (See Also) responses always lead to the use of a GET method, - /// 307 (Temporary Redirect) and 308 (Permanent Redirect) don't change the - /// method used in the original request; - /// - /// * 301 (Permanent Redirect) and 302 (Found) doesn't change the method - /// most of the time, though older user-agents may (so you basically don't - /// know). - /// - /// All responses with one of these status codes send a Location header. - /// - /// Beside redirect response, messages with 201 (Created) status also - /// include the Location header. It indicates the URL to the newly created - /// resource. - /// - /// Location and Content-Location are different: Location indicates the - /// target of a redirection (or the URL of a newly created resource), while - /// Content-Location indicates the direct URL to use to access the resource - /// when content negotiation happened, without the need of further content - /// negotiation. Location is a header associated with the response, while - /// Content-Location is associated with the entity returned. - (Location, LOCATION, b"location"); - - /// Indicates the max number of intermediaries the request should be sent - /// through. - (MaxForwards, MAX_FORWARDS, b"max-forwards"); - - /// Indicates where a fetch originates from. - /// - /// It doesn't include any path information, but only the server name. It is - /// sent with CORS requests, as well as with POST requests. It is similar to - /// the Referer header, but, unlike this header, it doesn't disclose the - /// whole path. - (Origin, ORIGIN, b"origin"); - - /// HTTP/1.0 header usually used for backwards compatibility. - /// - /// The Pragma HTTP/1.0 general header is an implementation-specific header - /// that may have various effects along the request-response chain. It is - /// used for backwards compatibility with HTTP/1.0 caches where the - /// Cache-Control HTTP/1.1 header is not yet present. - (Pragma, PRAGMA, b"pragma"); - - /// Defines the authentication method that should be used to gain access to - /// a proxy. - /// - /// Unlike `www-authenticate`, the `proxy-authenticate` header field applies - /// only to the next outbound client on the response chain. This is because - /// only the client that chose a given proxy is likely to have the - /// credentials necessary for authentication. However, when multiple proxies - /// are used within the same administrative domain, such as office and - /// regional caching proxies within a large corporate network, it is common - /// for credentials to be generated by the user agent and passed through the - /// hierarchy until consumed. Hence, in such a configuration, it will appear - /// as if Proxy-Authenticate is being forwarded because each proxy will send - /// the same challenge set. - /// - /// The `proxy-authenticate` header is sent along with a `407 Proxy - /// Authentication Required`. - (ProxyAuthenticate, PROXY_AUTHENTICATE, b"proxy-authenticate"); - - /// Contains the credentials to authenticate a user agent to a proxy server. - /// - /// This header is usually included after the server has responded with a - /// 407 Proxy Authentication Required status and the Proxy-Authenticate - /// header. - (ProxyAuthorization, PROXY_AUTHORIZATION, b"proxy-authorization"); - - /// Associates a specific cryptographic public key with a certain server. - /// - /// This decreases the risk of MITM attacks with forged certificates. If one - /// or several keys are pinned and none of them are used by the server, the - /// browser will not accept the response as legitimate, and will not display - /// it. - (PublicKeyPins, PUBLIC_KEY_PINS, b"public-key-pins"); - - /// Sends reports of pinning violation to the report-uri specified in the - /// header. - /// - /// Unlike `Public-Key-Pins`, this header still allows browsers to connect - /// to the server if the pinning is violated. - (PublicKeyPinsReportOnly, PUBLIC_KEY_PINS_REPORT_ONLY, b"public-key-pins-report-only"); - - /// Indicates the part of a document that the server should return. - /// - /// Several parts can be requested with one Range header at once, and the - /// server may send back these ranges in a multipart document. If the server - /// sends back ranges, it uses the 206 Partial Content for the response. If - /// the ranges are invalid, the server returns the 416 Range Not Satisfiable - /// error. The server can also ignore the Range header and return the whole - /// document with a 200 status code. - (Range, RANGE, b"range"); - - /// Contains the address of the previous web page from which a link to the - /// currently requested page was followed. - /// - /// The Referer header allows servers to identify where people are visiting - /// them from and may use that data for analytics, logging, or optimized - /// caching, for example. - (Referer, REFERER, b"referer"); - - /// Governs which referrer information should be included with requests - /// made. - (ReferrerPolicy, REFERRER_POLICY, b"referrer-policy"); - - /// Informs the web browser that the current page or frame should be - /// refreshed. - (Refresh, REFRESH, b"refresh"); - - /// The Retry-After response HTTP header indicates how long the user agent - /// should wait before making a follow-up request. There are two main cases - /// this header is used: - /// - /// * When sent with a 503 (Service Unavailable) response, it indicates how - /// long the service is expected to be unavailable. - /// - /// * When sent with a redirect response, such as 301 (Moved Permanently), - /// it indicates the minimum time that the user agent is asked to wait - /// before issuing the redirected request. - (RetryAfter, RETRY_AFTER, b"retry-after"); - - /// The |Sec-WebSocket-Accept| header field is used in the WebSocket - /// opening handshake. It is sent from the server to the client to - /// confirm that the server is willing to initiate the WebSocket - /// connection. - (SecWebSocketAccept, SEC_WEBSOCKET_ACCEPT, b"sec-websocket-accept"); - - /// The |Sec-WebSocket-Extensions| header field is used in the WebSocket - /// opening handshake. It is initially sent from the client to the - /// server, and then subsequently sent from the server to the client, to - /// agree on a set of protocol-level extensions to use for the duration - /// of the connection. - (SecWebSocketExtensions, SEC_WEBSOCKET_EXTENSIONS, b"sec-websocket-extensions"); - - /// The |Sec-WebSocket-Key| header field is used in the WebSocket opening - /// handshake. It is sent from the client to the server to provide part - /// of the information used by the server to prove that it received a - /// valid WebSocket opening handshake. This helps ensure that the server - /// does not accept connections from non-WebSocket clients (e.g., HTTP - /// clients) that are being abused to send data to unsuspecting WebSocket - /// servers. - (SecWebSocketKey, SEC_WEBSOCKET_KEY, b"sec-websocket-key"); - - /// The |Sec-WebSocket-Protocol| header field is used in the WebSocket - /// opening handshake. It is sent from the client to the server and back - /// from the server to the client to confirm the subprotocol of the - /// connection. This enables scripts to both select a subprotocol and be - /// sure that the server agreed to serve that subprotocol. - (SecWebSocketProtocol, SEC_WEBSOCKET_PROTOCOL, b"sec-websocket-protocol"); - - /// The |Sec-WebSocket-Version| header field is used in the WebSocket - /// opening handshake. It is sent from the client to the server to - /// indicate the protocol version of the connection. This enables - /// servers to correctly interpret the opening handshake and subsequent - /// data being sent from the data, and close the connection if the server - /// cannot interpret that data in a safe manner. - (SecWebSocketVersion, SEC_WEBSOCKET_VERSION, b"sec-websocket-version"); - - /// Contains information about the software used by the origin server to - /// handle the request. - /// - /// Overly long and detailed Server values should be avoided as they - /// potentially reveal internal implementation details that might make it - /// (slightly) easier for attackers to find and exploit known security - /// holes. - (Server, SERVER, b"server"); - - /// Used to send cookies from the server to the user agent. - (SetCookie, SET_COOKIE, b"set-cookie"); - - /// Tells the client to communicate with HTTPS instead of using HTTP. - (StrictTransportSecurity, STRICT_TRANSPORT_SECURITY, b"strict-transport-security"); - - /// Informs the server of transfer encodings willing to be accepted as part - /// of the response. - /// - /// See also the Transfer-Encoding response header for more details on - /// transfer encodings. Note that chunked is always acceptable for HTTP/1.1 - /// recipients and you that don't have to specify "chunked" using the TE - /// header. However, it is useful for setting if the client is accepting - /// trailer fields in a chunked transfer coding using the "trailers" value. - (Te, TE, b"te"); - - /// Allows the sender to include additional fields at the end of chunked - /// messages. - (Trailer, TRAILER, b"trailer"); - - /// Specifies the form of encoding used to safely transfer the entity to the - /// client. - /// - /// `transfer-encoding` is a hop-by-hop header, that is applying to a - /// message between two nodes, not to a resource itself. Each segment of a - /// multi-node connection can use different `transfer-encoding` values. If - /// you want to compress data over the whole connection, use the end-to-end - /// header `content-encoding` header instead. - /// - /// When present on a response to a `HEAD` request that has no body, it - /// indicates the value that would have applied to the corresponding `GET` - /// message. - (TransferEncoding, TRANSFER_ENCODING, b"transfer-encoding"); - - /// Contains a string that allows identifying the requesting client's - /// software. - (UserAgent, USER_AGENT, b"user-agent"); - - /// Used as part of the exchange to upgrade the protocol. - (Upgrade, UPGRADE, b"upgrade"); - - /// Sends a signal to the server expressing the client’s preference for an - /// encrypted and authenticated response. - (UpgradeInsecureRequests, UPGRADE_INSECURE_REQUESTS, b"upgrade-insecure-requests"); - - /// Determines how to match future requests with cached responses. - /// - /// The `vary` HTTP response header determines how to match future request - /// headers to decide whether a cached response can be used rather than - /// requesting a fresh one from the origin server. It is used by the server - /// to indicate which headers it used when selecting a representation of a - /// resource in a content negotiation algorithm. - /// - /// The `vary` header should be set on a 304 Not Modified response exactly - /// like it would have been set on an equivalent 200 OK response. - (Vary, VARY, b"vary"); - - /// Added by proxies to track routing. - /// - /// The `via` general header is added by proxies, both forward and reverse - /// proxies, and can appear in the request headers and the response headers. - /// It is used for tracking message forwards, avoiding request loops, and - /// identifying the protocol capabilities of senders along the - /// request/response chain. - (Via, VIA, b"via"); - - /// General HTTP header contains information about possible problems with - /// the status of the message. - /// - /// More than one `warning` header may appear in a response. Warning header - /// fields can in general be applied to any message, however some warn-codes - /// are specific to caches and can only be applied to response messages. - (Warning, WARNING, b"warning"); - - /// Defines the authentication method that should be used to gain access to - /// a resource. - (WwwAuthenticate, WWW_AUTHENTICATE, b"www-authenticate"); - - /// Marker used by the server to indicate that the MIME types advertised in - /// the `content-type` headers should not be changed and be followed. - /// - /// This allows to opt-out of MIME type sniffing, or, in other words, it is - /// a way to say that the webmasters knew what they were doing. - /// - /// This header was introduced by Microsoft in IE 8 as a way for webmasters - /// to block content sniffing that was happening and could transform - /// non-executable MIME types into executable MIME types. Since then, other - /// browsers have introduced it, even if their MIME sniffing algorithms were - /// less aggressive. - /// - /// Site security testers usually expect this header to be set. - (XContentTypeOptions, X_CONTENT_TYPE_OPTIONS, b"x-content-type-options"); - - /// Controls DNS prefetching. - /// - /// The `x-dns-prefetch-control` HTTP response header controls DNS - /// prefetching, a feature by which browsers proactively perform domain name - /// resolution on both links that the user may choose to follow as well as - /// URLs for items referenced by the document, including images, CSS, - /// JavaScript, and so forth. - /// - /// This prefetching is performed in the background, so that the DNS is - /// likely to have been resolved by the time the referenced items are - /// needed. This reduces latency when the user clicks a link. - (XDnsPrefetchControl, X_DNS_PREFETCH_CONTROL, b"x-dns-prefetch-control"); - - /// Indicates whether or not a browser should be allowed to render a page in - /// a frame. - /// - /// Sites can use this to avoid clickjacking attacks, by ensuring that their - /// content is not embedded into other sites. - /// - /// The added security is only provided if the user accessing the document - /// is using a browser supporting `x-frame-options`. - (XFrameOptions, X_FRAME_OPTIONS, b"x-frame-options"); - - /// Stop pages from loading when an XSS attack is detected. - /// - /// The HTTP X-XSS-Protection response header is a feature of Internet - /// Explorer, Chrome and Safari that stops pages from loading when they - /// detect reflected cross-site scripting (XSS) attacks. Although these - /// protections are largely unnecessary in modern browsers when sites - /// implement a strong Content-Security-Policy that disables the use of - /// inline JavaScript ('unsafe-inline'), they can still provide protections - /// for users of older web browsers that don't yet support CSP. - (XXssProtection, X_XSS_PROTECTION, b"x-xss-protection"); -} - -/// Valid header name characters -/// -/// ```not_rust -/// field-name = token -/// separators = "(" | ")" | "<" | ">" | "@" -/// | "," | ";" | ":" | "\" | <"> -/// | "/" | "[" | "]" | "?" | "=" -/// | "{" | "}" | SP | HT -/// token = 1*tchar -/// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" -/// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" -/// / DIGIT / ALPHA -/// ; any VCHAR, except delimiters -/// ``` -// HEADER_CHARS maps every byte that is 128 or larger to 0 so everything that is -// mapped by HEADER_CHARS, maps to a valid single-byte UTF-8 codepoint. -#[rustfmt::skip] -const HEADER_CHARS: [u8; 256] = [ - // 0 1 2 3 4 5 6 7 8 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 0, 0, 0, b'!', 0, b'#', b'$', b'%', b'&', b'\'', // 3x - 0, 0, b'*', b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x - b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, // 5x - 0, 0, 0, 0, 0, b'a', b'b', b'c', b'd', b'e', // 6x - b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', // 7x - b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', // 8x - b'z', 0, 0, 0, b'^', b'_', b'`', b'a', b'b', b'c', // 9x - b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x - b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x - b'x', b'y', b'z', 0, b'|', 0, b'~', 0, 0, 0, // 12x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x - 0, 0, 0, 0, 0, 0 // 25x -]; - -/// Valid header name characters for HTTP/2.0 and HTTP/3.0 -// HEADER_CHARS_H2 maps every byte that is 128 or larger to 0 so everything that is -// mapped by HEADER_CHARS_H2, maps to a valid single-byte UTF-8 codepoint. -#[rustfmt::skip] -const HEADER_CHARS_H2: [u8; 256] = [ - // 0 1 2 3 4 5 6 7 8 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 0, 0, 0, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', // 3x - 0, 0, b'*', b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x - b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, // 5x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, b'^', b'_', b'`', b'a', b'b', b'c', // 9x - b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x - b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x - b'x', b'y', b'z', 0, b'|', 0, b'~', 0, 0, 0, // 12x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x - 0, 0, 0, 0, 0, 0 // 25x -]; - -fn parse_hdr<'a>( - data: &'a [u8], - b: &'a mut [MaybeUninit<u8>; SCRATCH_BUF_SIZE], - table: &[u8; 256], -) -> Result<HdrName<'a>, InvalidHeaderName> { - match data.len() { - 0 => Err(InvalidHeaderName::new()), - len @ 1..=SCRATCH_BUF_SIZE => { - // Read from data into the buffer - transforming using `table` as we go - data.iter() - .zip(b.iter_mut()) - .for_each(|(index, out)| *out = MaybeUninit::new(table[*index as usize])); - // Safety: len bytes of b were just initialized. - let name: &'a [u8] = unsafe { slice_assume_init(&b[0..len]) }; - match StandardHeader::from_bytes(name) { - Some(sh) => Ok(sh.into()), - None => { - if name.contains(&0) { - Err(InvalidHeaderName::new()) - } else { - Ok(HdrName::custom(name, true)) - } - } - } - } - SCRATCH_BUF_OVERFLOW..=super::MAX_HEADER_NAME_LEN => Ok(HdrName::custom(data, false)), - _ => Err(InvalidHeaderName::new()), - } -} - -impl<'a> From<StandardHeader> for HdrName<'a> { - fn from(hdr: StandardHeader) -> HdrName<'a> { - HdrName { - inner: Repr::Standard(hdr), - } - } -} - -impl HeaderName { - /// Converts a slice of bytes to an HTTP header name. - /// - /// This function normalizes the input. - pub fn from_bytes(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { - let mut buf = uninit_u8_array(); - // Precondition: HEADER_CHARS is a valid table for parse_hdr(). - match parse_hdr(src, &mut buf, &HEADER_CHARS)?.inner { - Repr::Standard(std) => Ok(std.into()), - Repr::Custom(MaybeLower { buf, lower: true }) => { - let buf = Bytes::copy_from_slice(buf); - // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. - let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; - Ok(Custom(val).into()) - } - Repr::Custom(MaybeLower { buf, lower: false }) => { - use bytes::BufMut; - let mut dst = BytesMut::with_capacity(buf.len()); - - for b in buf.iter() { - // HEADER_CHARS maps all bytes to valid single-byte UTF-8 - let b = HEADER_CHARS[*b as usize]; - - if b == 0 { - return Err(InvalidHeaderName::new()); - } - - dst.put_u8(b); - } - - // Safety: the loop above maps all bytes in buf to valid single byte - // UTF-8 before copying them into dst. This means that dst (and hence - // dst.freeze()) is valid UTF-8. - let val = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; - - Ok(Custom(val).into()) - } - } - } - - /// Converts a slice of bytes to an HTTP header name. - /// - /// This function expects the input to only contain lowercase characters. - /// This is useful when decoding HTTP/2.0 or HTTP/3.0 headers. Both - /// require that all headers be represented in lower case. - /// - /// # Examples - /// - /// ``` - /// # use http::header::*; - /// - /// // Parsing a lower case header - /// let hdr = HeaderName::from_lowercase(b"content-length").unwrap(); - /// assert_eq!(CONTENT_LENGTH, hdr); - /// - /// // Parsing a header that contains uppercase characters - /// assert!(HeaderName::from_lowercase(b"Content-Length").is_err()); - /// ``` - pub fn from_lowercase(src: &[u8]) -> Result<HeaderName, InvalidHeaderName> { - let mut buf = uninit_u8_array(); - // Precondition: HEADER_CHARS_H2 is a valid table for parse_hdr() - match parse_hdr(src, &mut buf, &HEADER_CHARS_H2)?.inner { - Repr::Standard(std) => Ok(std.into()), - Repr::Custom(MaybeLower { buf, lower: true }) => { - let buf = Bytes::copy_from_slice(buf); - // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. - let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; - Ok(Custom(val).into()) - } - Repr::Custom(MaybeLower { buf, lower: false }) => { - for &b in buf.iter() { - // HEADER_CHARS_H2 maps all bytes that are not valid single-byte - // UTF-8 to 0 so this check returns an error for invalid UTF-8. - if HEADER_CHARS_H2[b as usize] == 0 { - return Err(InvalidHeaderName::new()); - } - } - - let buf = Bytes::copy_from_slice(buf); - // Safety: the loop above checks that each byte of buf (either - // version) is valid UTF-8. - let val = unsafe { ByteStr::from_utf8_unchecked(buf) }; - Ok(Custom(val).into()) - } - } - } - - /// Converts a static string to a HTTP header name. - /// - /// This function requires the static string to only contain lowercase - /// characters, numerals and symbols, as per the HTTP/2.0 specification - /// and header names internal representation within this library. - /// - /// # Panics - /// - /// This function panics when the static string is a invalid header. - /// - /// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345) - /// makes its way into stable, the panic message at compile-time is - /// going to look cryptic, but should at least point at your header value: - /// - /// ```text - /// error: any use of this value will cause an error - /// --> http/src/header/name.rs:1241:13 - /// | - /// 1241 | ([] as [u8; 0])[0]; // Invalid header name - /// | ^^^^^^^^^^^^^^^^^^ - /// | | - /// | index out of bounds: the length is 0 but the index is 0 - /// | inside `http::HeaderName::from_static` at http/src/header/name.rs:1241:13 - /// | inside `INVALID_NAME` at src/main.rs:3:34 - /// | - /// ::: src/main.rs:3:1 - /// | - /// 3 | const INVALID_NAME: HeaderName = HeaderName::from_static("Capitalized"); - /// | ------------------------------------------------------------------------ - /// ``` - /// - /// # Examples - /// - /// ``` - /// # use http::header::*; - /// // Parsing a standard header - /// let hdr = HeaderName::from_static("content-length"); - /// assert_eq!(CONTENT_LENGTH, hdr); - /// - /// // Parsing a custom header - /// let CUSTOM_HEADER: &'static str = "custom-header"; - /// - /// let a = HeaderName::from_lowercase(b"custom-header").unwrap(); - /// let b = HeaderName::from_static(CUSTOM_HEADER); - /// assert_eq!(a, b); - /// ``` - /// - /// ```should_panic - /// # use http::header::*; - /// # - /// // Parsing a header that contains invalid symbols(s): - /// HeaderName::from_static("content{}{}length"); // This line panics! - /// - /// // Parsing a header that contains invalid uppercase characters. - /// let a = HeaderName::from_static("foobar"); - /// let b = HeaderName::from_static("FOOBAR"); // This line panics! - /// ``` - #[allow(unconditional_panic)] // required for the panic circumvention - pub const fn from_static(src: &'static str) -> HeaderName { - let name_bytes = src.as_bytes(); - if let Some(standard) = StandardHeader::from_bytes(name_bytes) { - return HeaderName { - inner: Repr::Standard(standard), - }; - } - - if name_bytes.is_empty() || name_bytes.len() > super::MAX_HEADER_NAME_LEN || { - let mut i = 0; - loop { - if i >= name_bytes.len() { - break false; - } else if HEADER_CHARS_H2[name_bytes[i] as usize] == 0 { - break true; - } - i += 1; - } - } { - // TODO: When msrv is bumped to larger than 1.57, this should be - // replaced with `panic!` macro. - // https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts - // - // See the panics section of this method's document for details. - #[allow(clippy::no_effect, clippy::out_of_bounds_indexing)] - ([] as [u8; 0])[0]; // Invalid header name - } - - HeaderName { - inner: Repr::Custom(Custom(ByteStr::from_static(src))), - } - } - - /// Returns a `str` representation of the header. - /// - /// The returned string will always be lower case. - #[inline] - pub fn as_str(&self) -> &str { - match self.inner { - Repr::Standard(v) => v.as_str(), - Repr::Custom(ref v) => &v.0, - } - } - - pub(super) fn into_bytes(self) -> Bytes { - self.inner.into() - } -} - -impl FromStr for HeaderName { - type Err = InvalidHeaderName; - - fn from_str(s: &str) -> Result<HeaderName, InvalidHeaderName> { - HeaderName::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName { _priv: () }) - } -} - -impl AsRef<str> for HeaderName { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl AsRef<[u8]> for HeaderName { - fn as_ref(&self) -> &[u8] { - self.as_str().as_bytes() - } -} - -impl Borrow<str> for HeaderName { - fn borrow(&self) -> &str { - self.as_str() - } -} - -impl fmt::Debug for HeaderName { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), fmt) - } -} - -impl fmt::Display for HeaderName { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), fmt) - } -} - -impl InvalidHeaderName { - pub(super) fn new() -> InvalidHeaderName { - InvalidHeaderName { _priv: () } - } -} - -impl<'a> From<&'a HeaderName> for HeaderName { - fn from(src: &'a HeaderName) -> HeaderName { - src.clone() - } -} - -#[doc(hidden)] -impl<T> From<Repr<T>> for Bytes -where - T: Into<Bytes>, -{ - fn from(repr: Repr<T>) -> Bytes { - match repr { - Repr::Standard(header) => Bytes::from_static(header.as_str().as_bytes()), - Repr::Custom(header) => header.into(), - } - } -} - -impl From<Custom> for Bytes { - #[inline] - fn from(Custom(inner): Custom) -> Bytes { - Bytes::from(inner) - } -} - -impl<'a> TryFrom<&'a str> for HeaderName { - type Error = InvalidHeaderName; - #[inline] - fn try_from(s: &'a str) -> Result<Self, Self::Error> { - Self::from_bytes(s.as_bytes()) - } -} - -impl<'a> TryFrom<&'a String> for HeaderName { - type Error = InvalidHeaderName; - #[inline] - fn try_from(s: &'a String) -> Result<Self, Self::Error> { - Self::from_bytes(s.as_bytes()) - } -} - -impl<'a> TryFrom<&'a [u8]> for HeaderName { - type Error = InvalidHeaderName; - #[inline] - fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> { - Self::from_bytes(s) - } -} - -impl TryFrom<String> for HeaderName { - type Error = InvalidHeaderName; - - #[inline] - fn try_from(s: String) -> Result<Self, Self::Error> { - Self::from_bytes(s.as_bytes()) - } -} - -impl TryFrom<Vec<u8>> for HeaderName { - type Error = InvalidHeaderName; - - #[inline] - fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> { - Self::from_bytes(&vec) - } -} - -#[doc(hidden)] -impl From<StandardHeader> for HeaderName { - fn from(src: StandardHeader) -> HeaderName { - HeaderName { - inner: Repr::Standard(src), - } - } -} - -#[doc(hidden)] -impl From<Custom> for HeaderName { - fn from(src: Custom) -> HeaderName { - HeaderName { - inner: Repr::Custom(src), - } - } -} - -impl<'a> PartialEq<&'a HeaderName> for HeaderName { - #[inline] - fn eq(&self, other: &&'a HeaderName) -> bool { - *self == **other - } -} - -impl<'a> PartialEq<HeaderName> for &'a HeaderName { - #[inline] - fn eq(&self, other: &HeaderName) -> bool { - *other == *self - } -} - -impl PartialEq<str> for HeaderName { - /// Performs a case-insensitive comparison of the string against the header - /// name - /// - /// # Examples - /// - /// ``` - /// use http::header::CONTENT_LENGTH; - /// - /// assert_eq!(CONTENT_LENGTH, "content-length"); - /// assert_eq!(CONTENT_LENGTH, "Content-Length"); - /// assert_ne!(CONTENT_LENGTH, "content length"); - /// ``` - #[inline] - fn eq(&self, other: &str) -> bool { - eq_ignore_ascii_case(self.as_ref(), other.as_bytes()) - } -} - -impl PartialEq<HeaderName> for str { - /// Performs a case-insensitive comparison of the string against the header - /// name - /// - /// # Examples - /// - /// ``` - /// use http::header::CONTENT_LENGTH; - /// - /// assert_eq!(CONTENT_LENGTH, "content-length"); - /// assert_eq!(CONTENT_LENGTH, "Content-Length"); - /// assert_ne!(CONTENT_LENGTH, "content length"); - /// ``` - #[inline] - fn eq(&self, other: &HeaderName) -> bool { - *other == *self - } -} - -impl<'a> PartialEq<&'a str> for HeaderName { - /// Performs a case-insensitive comparison of the string against the header - /// name - #[inline] - fn eq(&self, other: &&'a str) -> bool { - *self == **other - } -} - -impl<'a> PartialEq<HeaderName> for &'a str { - /// Performs a case-insensitive comparison of the string against the header - /// name - #[inline] - fn eq(&self, other: &HeaderName) -> bool { - *other == *self - } -} - -impl fmt::Debug for InvalidHeaderName { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("InvalidHeaderName") - // skip _priv noise - .finish() - } -} - -impl fmt::Display for InvalidHeaderName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid HTTP header name") - } -} - -impl Error for InvalidHeaderName {} - -// ===== HdrName ===== - -impl<'a> HdrName<'a> { - // Precondition: if lower then buf is valid UTF-8 - fn custom(buf: &'a [u8], lower: bool) -> HdrName<'a> { - HdrName { - // Invariant (on MaybeLower): follows from the precondition - inner: Repr::Custom(MaybeLower { buf, lower }), - } - } - - pub fn from_bytes<F, U>(hdr: &[u8], f: F) -> Result<U, InvalidHeaderName> - where - F: FnOnce(HdrName<'_>) -> U, - { - let mut buf = uninit_u8_array(); - // Precondition: HEADER_CHARS is a valid table for parse_hdr(). - let hdr = parse_hdr(hdr, &mut buf, &HEADER_CHARS)?; - Ok(f(hdr)) - } - - pub fn from_static<F, U>(hdr: &'static str, f: F) -> U - where - F: FnOnce(HdrName<'_>) -> U, - { - let mut buf = uninit_u8_array(); - let hdr = - // Precondition: HEADER_CHARS is a valid table for parse_hdr(). - parse_hdr(hdr.as_bytes(), &mut buf, &HEADER_CHARS).expect("static str is invalid name"); - f(hdr) - } -} - -#[doc(hidden)] -impl<'a> From<HdrName<'a>> for HeaderName { - fn from(src: HdrName<'a>) -> HeaderName { - match src.inner { - Repr::Standard(s) => HeaderName { - inner: Repr::Standard(s), - }, - Repr::Custom(maybe_lower) => { - if maybe_lower.lower { - let buf = Bytes::copy_from_slice(maybe_lower.buf); - // Safety: the invariant on MaybeLower ensures buf is valid UTF-8. - let byte_str = unsafe { ByteStr::from_utf8_unchecked(buf) }; - - HeaderName { - inner: Repr::Custom(Custom(byte_str)), - } - } else { - use bytes::BufMut; - let mut dst = BytesMut::with_capacity(maybe_lower.buf.len()); - - for b in maybe_lower.buf.iter() { - // HEADER_CHARS maps each byte to a valid single-byte UTF-8 - // codepoint. - dst.put_u8(HEADER_CHARS[*b as usize]); - } - - // Safety: the loop above maps each byte of maybe_lower.buf to a - // valid single-byte UTF-8 codepoint before copying it into dst. - // dst (and hence dst.freeze()) is thus valid UTF-8. - let buf = unsafe { ByteStr::from_utf8_unchecked(dst.freeze()) }; - - HeaderName { - inner: Repr::Custom(Custom(buf)), - } - } - } - } - } -} - -#[doc(hidden)] -impl<'a> PartialEq<HdrName<'a>> for HeaderName { - #[inline] - fn eq(&self, other: &HdrName<'a>) -> bool { - match self.inner { - Repr::Standard(a) => match other.inner { - Repr::Standard(b) => a == b, - _ => false, - }, - Repr::Custom(Custom(ref a)) => match other.inner { - Repr::Custom(ref b) => { - if b.lower { - a.as_bytes() == b.buf - } else { - eq_ignore_ascii_case(a.as_bytes(), b.buf) - } - } - _ => false, - }, - } - } -} - -// ===== Custom ===== - -impl Hash for Custom { - #[inline] - fn hash<H: Hasher>(&self, hasher: &mut H) { - hasher.write(self.0.as_bytes()) - } -} - -// ===== MaybeLower ===== - -impl<'a> Hash for MaybeLower<'a> { - #[inline] - fn hash<H: Hasher>(&self, hasher: &mut H) { - if self.lower { - hasher.write(self.buf); - } else { - for &b in self.buf { - hasher.write(&[HEADER_CHARS[b as usize]]); - } - } - } -} - -// Assumes that the left hand side is already lower case -#[inline] -fn eq_ignore_ascii_case(lower: &[u8], s: &[u8]) -> bool { - if lower.len() != s.len() { - return false; - } - - lower - .iter() - .zip(s) - .all(|(a, b)| *a == HEADER_CHARS[*b as usize]) -} - -// Utility functions for MaybeUninit<>. These are drawn from unstable API's on -// MaybeUninit<> itself. -const SCRATCH_BUF_SIZE: usize = 64; -const SCRATCH_BUF_OVERFLOW: usize = SCRATCH_BUF_SIZE + 1; - -fn uninit_u8_array() -> [MaybeUninit<u8>; SCRATCH_BUF_SIZE] { - let arr = MaybeUninit::<[MaybeUninit<u8>; SCRATCH_BUF_SIZE]>::uninit(); - // Safety: assume_init() is claiming that an array of MaybeUninit<> - // has been initialized, but MaybeUninit<>'s do not require initialization. - unsafe { arr.assume_init() } -} - -// Assuming all the elements are initialized, get a slice of them. -// -// Safety: All elements of `slice` must be initialized to prevent -// undefined behavior. -unsafe fn slice_assume_init<T>(slice: &[MaybeUninit<T>]) -> &[T] { - &*(slice as *const [MaybeUninit<T>] as *const [T]) -} - -#[cfg(test)] -mod tests { - use self::StandardHeader::Vary; - use super::*; - - #[test] - fn test_bounds() { - fn check_bounds<T: Sync + Send>() {} - check_bounds::<HeaderName>(); - } - - #[test] - fn test_parse_invalid_headers() { - for i in 0..128 { - let hdr = vec![1u8; i]; - assert!( - HeaderName::from_bytes(&hdr).is_err(), - "{} invalid header chars did not fail", - i - ); - } - } - - const ONE_TOO_LONG: &[u8] = &[b'a'; super::super::MAX_HEADER_NAME_LEN + 1]; - - #[test] - fn test_invalid_name_lengths() { - assert!( - HeaderName::from_bytes(&[]).is_err(), - "zero-length header name is an error", - ); - - let long = &ONE_TOO_LONG[0..super::super::MAX_HEADER_NAME_LEN]; - - let long_str = std::str::from_utf8(long).unwrap(); - assert_eq!(HeaderName::from_static(long_str), long_str); // shouldn't panic! - - assert!( - HeaderName::from_bytes(long).is_ok(), - "max header name length is ok", - ); - assert!( - HeaderName::from_bytes(ONE_TOO_LONG).is_err(), - "longer than max header name length is an error", - ); - } - - #[test] - #[should_panic] - fn test_static_invalid_name_lengths() { - // Safety: ONE_TOO_LONG contains only the UTF-8 safe, single-byte codepoint b'a'. - let _ = HeaderName::from_static(unsafe { std::str::from_utf8_unchecked(ONE_TOO_LONG) }); - } - - #[test] - fn test_from_hdr_name() { - use self::StandardHeader::Vary; - - let name = HeaderName::from(HdrName { - inner: Repr::Standard(Vary), - }); - - assert_eq!(name.inner, Repr::Standard(Vary)); - - let name = HeaderName::from(HdrName { - inner: Repr::Custom(MaybeLower { - buf: b"hello-world", - lower: true, - }), - }); - - assert_eq!( - name.inner, - Repr::Custom(Custom(ByteStr::from_static("hello-world"))) - ); - - let name = HeaderName::from(HdrName { - inner: Repr::Custom(MaybeLower { - buf: b"Hello-World", - lower: false, - }), - }); - - assert_eq!( - name.inner, - Repr::Custom(Custom(ByteStr::from_static("hello-world"))) - ); - } - - #[test] - fn test_eq_hdr_name() { - use self::StandardHeader::Vary; - - let a = HeaderName { - inner: Repr::Standard(Vary), - }; - let b = HdrName { - inner: Repr::Standard(Vary), - }; - - assert_eq!(a, b); - - let a = HeaderName { - inner: Repr::Custom(Custom(ByteStr::from_static("vaary"))), - }; - assert_ne!(a, b); - - let b = HdrName { - inner: Repr::Custom(MaybeLower { - buf: b"vaary", - lower: true, - }), - }; - - assert_eq!(a, b); - - let b = HdrName { - inner: Repr::Custom(MaybeLower { - buf: b"vaary", - lower: false, - }), - }; - - assert_eq!(a, b); - - let b = HdrName { - inner: Repr::Custom(MaybeLower { - buf: b"VAARY", - lower: false, - }), - }; - - assert_eq!(a, b); - - let a = HeaderName { - inner: Repr::Standard(Vary), - }; - assert_ne!(a, b); - } - - #[test] - fn test_from_static_std() { - let a = HeaderName { - inner: Repr::Standard(Vary), - }; - - let b = HeaderName::from_static("vary"); - assert_eq!(a, b); - - let b = HeaderName::from_static("vaary"); - assert_ne!(a, b); - } - - #[test] - #[should_panic] - fn test_from_static_std_uppercase() { - HeaderName::from_static("Vary"); - } - - #[test] - #[should_panic] - fn test_from_static_std_symbol() { - HeaderName::from_static("vary{}"); - } - - // MaybeLower { lower: true } - #[test] - fn test_from_static_custom_short() { - let a = HeaderName { - inner: Repr::Custom(Custom(ByteStr::from_static("customheader"))), - }; - let b = HeaderName::from_static("customheader"); - assert_eq!(a, b); - } - - #[test] - #[should_panic] - fn test_from_static_custom_short_uppercase() { - HeaderName::from_static("custom header"); - } - - #[test] - #[should_panic] - fn test_from_static_custom_short_symbol() { - HeaderName::from_static("CustomHeader"); - } - - // MaybeLower { lower: false } - #[test] - fn test_from_static_custom_long() { - let a = HeaderName { - inner: Repr::Custom(Custom(ByteStr::from_static( - "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent", - ))), - }; - let b = HeaderName::from_static( - "longer-than-63--thisheaderislongerthansixtythreecharactersandthushandleddifferent", - ); - assert_eq!(a, b); - } - - #[test] - #[should_panic] - fn test_from_static_custom_long_uppercase() { - HeaderName::from_static( - "Longer-Than-63--ThisHeaderIsLongerThanSixtyThreeCharactersAndThusHandledDifferent", - ); - } - - #[test] - #[should_panic] - fn test_from_static_custom_long_symbol() { - HeaderName::from_static( - "longer-than-63--thisheader{}{}{}{}islongerthansixtythreecharactersandthushandleddifferent" - ); - } - - #[test] - fn test_from_static_custom_single_char() { - let a = HeaderName { - inner: Repr::Custom(Custom(ByteStr::from_static("a"))), - }; - let b = HeaderName::from_static("a"); - assert_eq!(a, b); - } - - #[test] - #[should_panic] - fn test_from_static_empty() { - HeaderName::from_static(""); - } - - #[test] - fn test_all_tokens() { - HeaderName::from_static("!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyz"); - } - - #[test] - fn test_from_lowercase() { - HeaderName::from_lowercase(&[0; 10]).unwrap_err(); - HeaderName::from_lowercase(&[b'A'; 10]).unwrap_err(); - HeaderName::from_lowercase(&[0x1; 10]).unwrap_err(); - HeaderName::from_lowercase(&[0xFF; 10]).unwrap_err(); - //HeaderName::from_lowercase(&[0; 100]).unwrap_err(); - HeaderName::from_lowercase(&[b'A'; 100]).unwrap_err(); - HeaderName::from_lowercase(&[0x1; 100]).unwrap_err(); - HeaderName::from_lowercase(&[0xFF; 100]).unwrap_err(); - } -} diff --git a/vendor/http/src/header/value.rs b/vendor/http/src/header/value.rs deleted file mode 100644 index 99d1e155..00000000 --- a/vendor/http/src/header/value.rs +++ /dev/null @@ -1,798 +0,0 @@ -use bytes::{Bytes, BytesMut}; - -use std::convert::TryFrom; -use std::error::Error; -use std::fmt::Write; -use std::hash::{Hash, Hasher}; -use std::str::FromStr; -use std::{cmp, fmt, str}; - -use crate::header::name::HeaderName; - -/// Represents an HTTP header field value. -/// -/// In practice, HTTP header field values are usually valid ASCII. However, the -/// HTTP spec allows for a header value to contain opaque bytes as well. In this -/// case, the header field value is not able to be represented as a string. -/// -/// To handle this, the `HeaderValue` is useable as a type and can be compared -/// with strings and implements `Debug`. A `to_str` fn is provided that returns -/// an `Err` if the header value contains non visible ascii characters. -#[derive(Clone)] -pub struct HeaderValue { - inner: Bytes, - is_sensitive: bool, -} - -/// A possible error when converting a `HeaderValue` from a string or byte -/// slice. -pub struct InvalidHeaderValue { - _priv: (), -} - -/// A possible error when converting a `HeaderValue` to a string representation. -/// -/// Header field values may contain opaque bytes, in which case it is not -/// possible to represent the value as a string. -#[derive(Debug)] -pub struct ToStrError { - _priv: (), -} - -impl HeaderValue { - /// Convert a static string to a `HeaderValue`. - /// - /// This function will not perform any copying, however the string is - /// checked to ensure that no invalid characters are present. Only visible - /// ASCII characters (32-127) are permitted. - /// - /// # Panics - /// - /// This function panics if the argument contains invalid header value - /// characters. - /// - /// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345) - /// makes its way into stable, the panic message at compile-time is - /// going to look cryptic, but should at least point at your header value: - /// - /// ```text - /// error: any use of this value will cause an error - /// --> http/src/header/value.rs:67:17 - /// | - /// 67 | ([] as [u8; 0])[0]; // Invalid header value - /// | ^^^^^^^^^^^^^^^^^^ - /// | | - /// | index out of bounds: the length is 0 but the index is 0 - /// | inside `HeaderValue::from_static` at http/src/header/value.rs:67:17 - /// | inside `INVALID_HEADER` at src/main.rs:73:33 - /// | - /// ::: src/main.rs:73:1 - /// | - /// 73 | const INVALID_HEADER: HeaderValue = HeaderValue::from_static("жsome value"); - /// | ---------------------------------------------------------------------------- - /// ``` - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_static("hello"); - /// assert_eq!(val, "hello"); - /// ``` - #[inline] - #[allow(unconditional_panic)] // required for the panic circumvention - pub const fn from_static(src: &'static str) -> HeaderValue { - let bytes = src.as_bytes(); - let mut i = 0; - while i < bytes.len() { - if !is_visible_ascii(bytes[i]) { - // TODO: When msrv is bumped to larger than 1.57, this should be - // replaced with `panic!` macro. - // https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html#panic-in-const-contexts - // - // See the panics section of this method's document for details. - #[allow(clippy::no_effect, clippy::out_of_bounds_indexing)] - ([] as [u8; 0])[0]; // Invalid header value - } - i += 1; - } - - HeaderValue { - inner: Bytes::from_static(bytes), - is_sensitive: false, - } - } - - /// Attempt to convert a string to a `HeaderValue`. - /// - /// If the argument contains invalid header value characters, an error is - /// returned. Only visible ASCII characters (32-127) are permitted. Use - /// `from_bytes` to create a `HeaderValue` that includes opaque octets - /// (128-255). - /// - /// This function is intended to be replaced in the future by a `TryFrom` - /// implementation once the trait is stabilized in std. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_str("hello").unwrap(); - /// assert_eq!(val, "hello"); - /// ``` - /// - /// An invalid value - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_str("\n"); - /// assert!(val.is_err()); - /// ``` - #[inline] - #[allow(clippy::should_implement_trait)] - pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> { - HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes())) - } - - /// Converts a HeaderName into a HeaderValue - /// - /// Since every valid HeaderName is a valid HeaderValue this is done infallibly. - /// - /// # Examples - /// - /// ``` - /// # use http::header::{HeaderValue, HeaderName}; - /// # use http::header::ACCEPT; - /// let val = HeaderValue::from_name(ACCEPT); - /// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap()); - /// ``` - #[inline] - pub fn from_name(name: HeaderName) -> HeaderValue { - name.into() - } - - /// Attempt to convert a byte slice to a `HeaderValue`. - /// - /// If the argument contains invalid header value bytes, an error is - /// returned. Only byte values between 32 and 255 (inclusive) are permitted, - /// excluding byte 127 (DEL). - /// - /// This function is intended to be replaced in the future by a `TryFrom` - /// implementation once the trait is stabilized in std. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap(); - /// assert_eq!(val, &b"hello\xfa"[..]); - /// ``` - /// - /// An invalid value - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_bytes(b"\n"); - /// assert!(val.is_err()); - /// ``` - #[inline] - pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> { - HeaderValue::try_from_generic(src, Bytes::copy_from_slice) - } - - /// Attempt to convert a `Bytes` buffer to a `HeaderValue`. - /// - /// This will try to prevent a copy if the type passed is the type used - /// internally, and will copy the data if it is not. - pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue> - where - T: AsRef<[u8]> + 'static, - { - if_downcast_into!(T, Bytes, src, { - return HeaderValue::from_shared(src); - }); - - HeaderValue::from_bytes(src.as_ref()) - } - - /// Convert a `Bytes` directly into a `HeaderValue` without validating. - /// - /// This function does NOT validate that illegal bytes are not contained - /// within the buffer. - /// - /// ## Panics - /// In a debug build this will panic if `src` is not valid UTF-8. - /// - /// ## Safety - /// `src` must contain valid UTF-8. In a release build it is undefined - /// behaviour to call this with `src` that is not valid UTF-8. - pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue - where - T: AsRef<[u8]> + 'static, - { - if cfg!(debug_assertions) { - match HeaderValue::from_maybe_shared(src) { - Ok(val) => val, - Err(_err) => { - panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes"); - } - } - } else { - if_downcast_into!(T, Bytes, src, { - return HeaderValue { - inner: src, - is_sensitive: false, - }; - }); - - let src = Bytes::copy_from_slice(src.as_ref()); - HeaderValue { - inner: src, - is_sensitive: false, - } - } - } - - fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> { - HeaderValue::try_from_generic(src, std::convert::identity) - } - - fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>( - src: T, - into: F, - ) -> Result<HeaderValue, InvalidHeaderValue> { - for &b in src.as_ref() { - if !is_valid(b) { - return Err(InvalidHeaderValue { _priv: () }); - } - } - Ok(HeaderValue { - inner: into(src), - is_sensitive: false, - }) - } - - /// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII - /// chars. - /// - /// This function will perform a scan of the header value, checking all the - /// characters. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_static("hello"); - /// assert_eq!(val.to_str().unwrap(), "hello"); - /// ``` - pub fn to_str(&self) -> Result<&str, ToStrError> { - let bytes = self.as_ref(); - - for &b in bytes { - if !is_visible_ascii(b) { - return Err(ToStrError { _priv: () }); - } - } - - unsafe { Ok(str::from_utf8_unchecked(bytes)) } - } - - /// Returns the length of `self`. - /// - /// This length is in bytes. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_static("hello"); - /// assert_eq!(val.len(), 5); - /// ``` - #[inline] - pub fn len(&self) -> usize { - self.as_ref().len() - } - - /// Returns true if the `HeaderValue` has a length of zero bytes. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_static(""); - /// assert!(val.is_empty()); - /// - /// let val = HeaderValue::from_static("hello"); - /// assert!(!val.is_empty()); - /// ``` - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Converts a `HeaderValue` to a byte slice. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let val = HeaderValue::from_static("hello"); - /// assert_eq!(val.as_bytes(), b"hello"); - /// ``` - #[inline] - pub fn as_bytes(&self) -> &[u8] { - self.as_ref() - } - - /// Mark that the header value represents sensitive information. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let mut val = HeaderValue::from_static("my secret"); - /// - /// val.set_sensitive(true); - /// assert!(val.is_sensitive()); - /// - /// val.set_sensitive(false); - /// assert!(!val.is_sensitive()); - /// ``` - #[inline] - pub fn set_sensitive(&mut self, val: bool) { - self.is_sensitive = val; - } - - /// Returns `true` if the value represents sensitive data. - /// - /// Sensitive data could represent passwords or other data that should not - /// be stored on disk or in memory. By marking header values as sensitive, - /// components using this crate can be instructed to treat them with special - /// care for security reasons. For example, caches can avoid storing - /// sensitive values, and HPACK encoders used by HTTP/2.0 implementations - /// can choose not to compress them. - /// - /// Additionally, sensitive values will be masked by the `Debug` - /// implementation of `HeaderValue`. - /// - /// Note that sensitivity is not factored into equality or ordering. - /// - /// # Examples - /// - /// ``` - /// # use http::header::HeaderValue; - /// let mut val = HeaderValue::from_static("my secret"); - /// - /// val.set_sensitive(true); - /// assert!(val.is_sensitive()); - /// - /// val.set_sensitive(false); - /// assert!(!val.is_sensitive()); - /// ``` - #[inline] - pub fn is_sensitive(&self) -> bool { - self.is_sensitive - } -} - -impl AsRef<[u8]> for HeaderValue { - #[inline] - fn as_ref(&self) -> &[u8] { - self.inner.as_ref() - } -} - -impl fmt::Debug for HeaderValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_sensitive { - f.write_str("Sensitive") - } else { - f.write_str("\"")?; - let mut from = 0; - let bytes = self.as_bytes(); - for (i, &b) in bytes.iter().enumerate() { - if !is_visible_ascii(b) || b == b'"' { - if from != i { - f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?; - } - if b == b'"' { - f.write_str("\\\"")?; - } else { - write!(f, "\\x{:x}", b)?; - } - from = i + 1; - } - } - - f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?; - f.write_str("\"") - } - } -} - -impl From<HeaderName> for HeaderValue { - #[inline] - fn from(h: HeaderName) -> HeaderValue { - HeaderValue { - inner: h.into_bytes(), - is_sensitive: false, - } - } -} - -macro_rules! from_integers { - ($($name:ident: $t:ident => $max_len:expr),*) => {$( - impl From<$t> for HeaderValue { - fn from(num: $t) -> HeaderValue { - let mut buf = BytesMut::with_capacity($max_len); - let _ = buf.write_str(::itoa::Buffer::new().format(num)); - HeaderValue { - inner: buf.freeze(), - is_sensitive: false, - } - } - } - - #[test] - fn $name() { - let n: $t = 55; - let val = HeaderValue::from(n); - assert_eq!(val, &n.to_string()); - - let n = ::std::$t::MAX; - let val = HeaderValue::from(n); - assert_eq!(val, &n.to_string()); - } - )*}; -} - -from_integers! { - // integer type => maximum decimal length - - // u8 purposely left off... HeaderValue::from(b'3') could be confusing - from_u16: u16 => 5, - from_i16: i16 => 6, - from_u32: u32 => 10, - from_i32: i32 => 11, - from_u64: u64 => 20, - from_i64: i64 => 20 -} - -#[cfg(target_pointer_width = "16")] -from_integers! { - from_usize: usize => 5, - from_isize: isize => 6 -} - -#[cfg(target_pointer_width = "32")] -from_integers! { - from_usize: usize => 10, - from_isize: isize => 11 -} - -#[cfg(target_pointer_width = "64")] -from_integers! { - from_usize: usize => 20, - from_isize: isize => 20 -} - -#[cfg(test)] -mod from_header_name_tests { - use super::*; - use crate::header::map::HeaderMap; - use crate::header::name; - - #[test] - fn it_can_insert_header_name_as_header_value() { - let mut map = HeaderMap::new(); - map.insert(name::UPGRADE, name::SEC_WEBSOCKET_PROTOCOL.into()); - map.insert( - name::ACCEPT, - name::HeaderName::from_bytes(b"hello-world").unwrap().into(), - ); - - assert_eq!( - map.get(name::UPGRADE).unwrap(), - HeaderValue::from_bytes(b"sec-websocket-protocol").unwrap() - ); - - assert_eq!( - map.get(name::ACCEPT).unwrap(), - HeaderValue::from_bytes(b"hello-world").unwrap() - ); - } -} - -impl FromStr for HeaderValue { - type Err = InvalidHeaderValue; - - #[inline] - fn from_str(s: &str) -> Result<HeaderValue, Self::Err> { - HeaderValue::from_str(s) - } -} - -impl<'a> From<&'a HeaderValue> for HeaderValue { - #[inline] - fn from(t: &'a HeaderValue) -> Self { - t.clone() - } -} - -impl<'a> TryFrom<&'a str> for HeaderValue { - type Error = InvalidHeaderValue; - - #[inline] - fn try_from(t: &'a str) -> Result<Self, Self::Error> { - t.parse() - } -} - -impl<'a> TryFrom<&'a String> for HeaderValue { - type Error = InvalidHeaderValue; - #[inline] - fn try_from(s: &'a String) -> Result<Self, Self::Error> { - Self::from_bytes(s.as_bytes()) - } -} - -impl<'a> TryFrom<&'a [u8]> for HeaderValue { - type Error = InvalidHeaderValue; - - #[inline] - fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> { - HeaderValue::from_bytes(t) - } -} - -impl TryFrom<String> for HeaderValue { - type Error = InvalidHeaderValue; - - #[inline] - fn try_from(t: String) -> Result<Self, Self::Error> { - HeaderValue::from_shared(t.into()) - } -} - -impl TryFrom<Vec<u8>> for HeaderValue { - type Error = InvalidHeaderValue; - - #[inline] - fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> { - HeaderValue::from_shared(vec.into()) - } -} - -#[cfg(test)] -mod try_from_header_name_tests { - use super::*; - use crate::header::name; - - #[test] - fn it_converts_using_try_from() { - assert_eq!( - HeaderValue::try_from(name::UPGRADE).unwrap(), - HeaderValue::from_bytes(b"upgrade").unwrap() - ); - } -} - -const fn is_visible_ascii(b: u8) -> bool { - b >= 32 && b < 127 || b == b'\t' -} - -#[inline] -fn is_valid(b: u8) -> bool { - b >= 32 && b != 127 || b == b'\t' -} - -impl fmt::Debug for InvalidHeaderValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("InvalidHeaderValue") - // skip _priv noise - .finish() - } -} - -impl fmt::Display for InvalidHeaderValue { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("failed to parse header value") - } -} - -impl Error for InvalidHeaderValue {} - -impl fmt::Display for ToStrError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("failed to convert header to a str") - } -} - -impl Error for ToStrError {} - -// ===== PartialEq / PartialOrd ===== - -impl Hash for HeaderValue { - fn hash<H: Hasher>(&self, state: &mut H) { - self.inner.hash(state); - } -} - -impl PartialEq for HeaderValue { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - self.inner == other.inner - } -} - -impl Eq for HeaderValue {} - -impl PartialOrd for HeaderValue { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for HeaderValue { - #[inline] - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.inner.cmp(&other.inner) - } -} - -impl PartialEq<str> for HeaderValue { - #[inline] - fn eq(&self, other: &str) -> bool { - self.inner == other.as_bytes() - } -} - -impl PartialEq<[u8]> for HeaderValue { - #[inline] - fn eq(&self, other: &[u8]) -> bool { - self.inner == other - } -} - -impl PartialOrd<str> for HeaderValue { - #[inline] - fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> { - (*self.inner).partial_cmp(other.as_bytes()) - } -} - -impl PartialOrd<[u8]> for HeaderValue { - #[inline] - fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> { - (*self.inner).partial_cmp(other) - } -} - -impl PartialEq<HeaderValue> for str { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - *other == *self - } -} - -impl PartialEq<HeaderValue> for [u8] { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - *other == *self - } -} - -impl PartialOrd<HeaderValue> for str { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - self.as_bytes().partial_cmp(other.as_bytes()) - } -} - -impl PartialOrd<HeaderValue> for [u8] { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - self.partial_cmp(other.as_bytes()) - } -} - -impl PartialEq<String> for HeaderValue { - #[inline] - fn eq(&self, other: &String) -> bool { - *self == other[..] - } -} - -impl PartialOrd<String> for HeaderValue { - #[inline] - fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> { - self.inner.partial_cmp(other.as_bytes()) - } -} - -impl PartialEq<HeaderValue> for String { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - *other == *self - } -} - -impl PartialOrd<HeaderValue> for String { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - self.as_bytes().partial_cmp(other.as_bytes()) - } -} - -impl<'a> PartialEq<HeaderValue> for &'a HeaderValue { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - **self == *other - } -} - -impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - (**self).partial_cmp(other) - } -} - -impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue -where - HeaderValue: PartialEq<T>, -{ - #[inline] - fn eq(&self, other: &&'a T) -> bool { - *self == **other - } -} - -impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue -where - HeaderValue: PartialOrd<T>, -{ - #[inline] - fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> { - self.partial_cmp(*other) - } -} - -impl<'a> PartialEq<HeaderValue> for &'a str { - #[inline] - fn eq(&self, other: &HeaderValue) -> bool { - *other == *self - } -} - -impl<'a> PartialOrd<HeaderValue> for &'a str { - #[inline] - fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> { - self.as_bytes().partial_cmp(other.as_bytes()) - } -} - -#[test] -fn test_try_from() { - HeaderValue::try_from(vec![127]).unwrap_err(); -} - -#[test] -fn test_debug() { - let cases = &[ - ("hello", "\"hello\""), - ("hello \"world\"", "\"hello \\\"world\\\"\""), - ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""), - ]; - - for &(value, expected) in cases { - let val = HeaderValue::from_bytes(value.as_bytes()).unwrap(); - let actual = format!("{:?}", val); - assert_eq!(expected, actual); - } - - let mut sensitive = HeaderValue::from_static("password"); - sensitive.set_sensitive(true); - assert_eq!("Sensitive", format!("{:?}", sensitive)); -} |
