From 2c9de1cc6cc703cf9b09b70fc9090eb1b5d293ca Mon Sep 17 00:00:00 2001
From: Gleb Pomykalov <gleb.pomykalov@workato.com>
Date: Fri, 1 Mar 2024 15:22:48 -0500
Subject: [PATCH] feat: add `HeaderMap::try_` methods to handle capacity
 overflow

---
 src/error.rs       |  11 +
 src/header/map.rs  | 572 ++++++++++++++++++++++++++++++++++++---------
 src/header/mod.rs  |   3 +-
 src/header/name.rs |   2 +-
 src/request.rs     |   2 +-
 src/response.rs    |   2 +-
 6 files changed, 483 insertions(+), 109 deletions(-)

diff --git a/src/error.rs b/src/error.rs
index ba690841..762ee1c2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -3,6 +3,7 @@ use std::fmt;
 use std::result;
 
 use crate::header;
+use crate::header::MaxSizeReached;
 use crate::method;
 use crate::status;
 use crate::uri;
@@ -27,6 +28,7 @@ enum ErrorKind {
     UriParts(uri::InvalidUriParts),
     HeaderName(header::InvalidHeaderName),
     HeaderValue(header::InvalidHeaderValue),
+    MaxSizeReached(MaxSizeReached),
 }
 
 impl fmt::Debug for Error {
@@ -61,6 +63,7 @@ impl Error {
             UriParts(ref e) => e,
             HeaderName(ref e) => e,
             HeaderValue(ref e) => e,
+            MaxSizeReached(ref e) => e,
         }
     }
 }
@@ -73,6 +76,14 @@ impl error::Error for Error {
     }
 }
 
+impl From<MaxSizeReached> for Error {
+    fn from(err: MaxSizeReached) -> Error {
+        Error {
+            inner: ErrorKind::MaxSizeReached(err),
+        }
+    }
+}
+
 impl From<status::InvalidStatusCode> for Error {
     fn from(err: status::InvalidStatusCode) -> Error {
         Error {
diff --git a/src/header/map.rs b/src/header/map.rs
index fa56bedc..399a5e81 100644
--- a/src/header/map.rs
+++ b/src/header/map.rs
@@ -213,6 +213,11 @@ pub struct ValueDrain<'a, 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 {
@@ -441,7 +446,7 @@ impl HeaderMap {
     /// assert_eq!(0, map.capacity());
     /// ```
     pub fn new() -> Self {
-        HeaderMap::with_capacity(0)
+        HeaderMap::try_with_capacity(0).unwrap()
     }
 }
 
@@ -457,7 +462,7 @@ impl<T> HeaderMap<T> {
     ///
     /// # Panics
     ///
-    /// Requested capacity too large: would overflow `usize`.
+    /// This method panics if capacity exceeds max `HeaderMap` capacity.
     ///
     /// # Examples
     ///
@@ -469,32 +474,57 @@ impl<T> HeaderMap<T> {
     /// 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 {
-            HeaderMap {
+            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 => panic!(
-                    "requested capacity {} too large: next power of two would overflow `usize`",
-                    capacity
-                ),
+                None => return Err(MaxSizeReached { _priv: () }),
             };
-            assert!(raw_cap <= MAX_SIZE, "requested capacity too large");
+            if raw_cap > MAX_SIZE {
+                return Err(MaxSizeReached { _priv: () });
+            }
             debug_assert!(raw_cap > 0);
 
-            HeaderMap {
+            Ok(HeaderMap {
                 mask: (raw_cap - 1) as Size,
                 indices: vec![Pos::none(); raw_cap].into_boxed_slice(),
                 entries: Vec::with_capacity(raw_cap),
                 extra_values: Vec::new(),
                 danger: Danger::Green,
-            }
+            })
         }
     }
 
@@ -629,7 +659,7 @@ impl<T> HeaderMap<T> {
     ///
     /// # Panics
     ///
-    /// Panics if the new allocation size overflows `usize`.
+    /// Panics if the new allocation size overflows `HeaderMap` `MAX_SIZE`.
     ///
     /// # Examples
     ///
@@ -641,27 +671,60 @@ impl<T> HeaderMap<T> {
     /// # 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)
-            .expect("reserve overflow");
+            .ok_or_else(|| MaxSizeReached::new())?;
 
         if cap > self.indices.len() {
-            let cap = cap.next_power_of_two();
-            assert!(cap <= MAX_SIZE, "header map reserve over max capacity");
-            assert!(cap != 0, "header map reserve overflowed");
+            let cap = cap
+                .checked_next_power_of_two()
+                .ok_or_else(|| MaxSizeReached::new())?;
+            if cap > MAX_SIZE {
+                return Err(MaxSizeReached::new());
+            }
 
             if self.entries.is_empty() {
                 self.mask = cap as Size - 1;
                 self.indices = vec![Pos::none(); cap].into_boxed_slice();
                 self.entries = Vec::with_capacity(usable_capacity(cap));
             } else {
-                self.grow(cap);
+                self.try_grow(cap)?;
             }
         }
+
+        Ok(())
     }
 
     /// Returns a reference to the value associated with the key.
@@ -1031,6 +1094,10 @@ impl<T> HeaderMap<T> {
     /// 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
     ///
     /// ```
@@ -1056,7 +1123,7 @@ impl<T> HeaderMap<T> {
     where
         K: IntoHeaderName,
     {
-        key.entry(self)
+        key.try_entry(self).expect("size overflows MAX_SIZE")
     }
 
     /// Gets the given key's corresponding entry in the map for in-place
@@ -1068,22 +1135,33 @@ impl<T> HeaderMap<T> {
     /// 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.
+    ///
+    /// It may return `MaxSizeReached` error if size exceeds the maximum
+    /// `HeaderMap` capacity
     pub fn try_entry<K>(&mut self, key: K) -> Result<Entry<'_, T>, InvalidHeaderName>
     where
         K: AsHeaderName,
     {
-        key.try_entry(self)
+        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 entry2<K>(&mut self, key: K) -> Entry<'_, T>
+    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.reserve_one();
+        self.try_reserve_one()?;
 
-        insert_phase_one!(
+        Ok(insert_phase_one!(
             self,
             key,
             probe,
@@ -1109,7 +1187,7 @@ impl<T> HeaderMap<T> {
                 probe,
                 danger,
             })
-        )
+        ))
     }
 
     /// Inserts a key-value pair into the map.
@@ -1127,6 +1205,10 @@ impl<T> HeaderMap<T> {
     /// 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
     ///
     /// ```
@@ -1143,18 +1225,56 @@ impl<T> HeaderMap<T> {
     where
         K: IntoHeaderName,
     {
-        key.insert(self, val)
+        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 insert2<K>(&mut self, key: K, value: T) -> Option<T>
+    fn try_insert2<K>(&mut self, key: K, value: T) -> Result<Option<T>, MaxSizeReached>
     where
         K: Hash + Into<HeaderName>,
         HeaderName: PartialEq<K>,
     {
-        self.reserve_one();
+        self.try_reserve_one()?;
 
-        insert_phase_one!(
+        Ok(insert_phase_one!(
             self,
             key,
             probe,
@@ -1165,7 +1285,7 @@ impl<T> HeaderMap<T> {
             {
                 let _ = danger; // Make lint happy
                 let index = self.entries.len();
-                self.insert_entry(hash, key.into(), value);
+                self.try_insert_entry(hash, key.into(), value)?;
                 self.indices[probe] = Pos::new(index, hash);
                 None
             },
@@ -1173,10 +1293,10 @@ impl<T> HeaderMap<T> {
             Some(self.insert_occupied(pos, value)),
             // Robinhood
             {
-                self.insert_phase_two(key.into(), value, hash, probe, danger);
+                self.try_insert_phase_two(key.into(), value, hash, probe, danger)?;
                 None
             }
-        )
+        ))
     }
 
     /// Set an occupied bucket to the given value
@@ -1224,6 +1344,10 @@ impl<T> HeaderMap<T> {
     /// updated, though; this matters for types that can be `==` without being
     /// identical.
     ///
+    /// # Panics
+    ///
+    /// This method panics if capacity exceeds max `HeaderMap` capacity
+    ///
     /// # Examples
     ///
     /// ```
@@ -1244,18 +1368,56 @@ impl<T> HeaderMap<T> {
     where
         K: IntoHeaderName,
     {
-        key.append(self, value)
+        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 append2<K>(&mut self, key: K, value: T) -> bool
+    fn try_append2<K>(&mut self, key: K, value: T) -> Result<bool, MaxSizeReached>
     where
         K: Hash + Into<HeaderName>,
         HeaderName: PartialEq<K>,
     {
-        self.reserve_one();
+        self.try_reserve_one()?;
 
-        insert_phase_one!(
+        Ok(insert_phase_one!(
             self,
             key,
             probe,
@@ -1266,7 +1428,7 @@ impl<T> HeaderMap<T> {
             {
                 let _ = danger;
                 let index = self.entries.len();
-                self.insert_entry(hash, key.into(), value);
+                self.try_insert_entry(hash, key.into(), value)?;
                 self.indices[probe] = Pos::new(index, hash);
                 false
             },
@@ -1277,11 +1439,11 @@ impl<T> HeaderMap<T> {
             },
             // Robinhood
             {
-                self.insert_phase_two(key.into(), value, hash, probe, danger);
+                self.try_insert_phase_two(key.into(), value, hash, probe, danger)?;
 
                 false
             }
-        )
+        ))
     }
 
     #[inline]
@@ -1317,17 +1479,17 @@ impl<T> HeaderMap<T> {
 
     /// phase 2 is post-insert where we forward-shift `Pos` in the indices.
     #[inline]
-    fn insert_phase_two(
+    fn try_insert_phase_two(
         &mut self,
         key: HeaderName,
         value: T,
         hash: HashValue,
         probe: usize,
         danger: bool,
-    ) -> usize {
+    ) -> Result<usize, MaxSizeReached> {
         // Push the value and get the index
         let index = self.entries.len();
-        self.insert_entry(hash, key, value);
+        self.try_insert_entry(hash, key, value)?;
 
         let num_displaced = do_insert_phase_two(&mut self.indices, probe, Pos::new(index, hash));
 
@@ -1336,7 +1498,7 @@ impl<T> HeaderMap<T> {
             self.danger.set_yellow();
         }
 
-        index
+        Ok(index)
     }
 
     /// Removes a key from the map, returning the value associated with the key.
@@ -1458,8 +1620,15 @@ impl<T> HeaderMap<T> {
     }
 
     #[inline]
-    fn insert_entry(&mut self, hash: HashValue, key: HeaderName, value: T) {
-        assert!(self.entries.len() < MAX_SIZE, "header map at capacity");
+    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,
@@ -1467,6 +1636,8 @@ impl<T> HeaderMap<T> {
             value,
             links: None,
         });
+
+        Ok(())
     }
 
     fn rebuild(&mut self) {
@@ -1516,7 +1687,7 @@ impl<T> HeaderMap<T> {
         }
     }
 
-    fn reserve_one(&mut self) {
+    fn try_reserve_one(&mut self) -> Result<(), MaxSizeReached> {
         let len = self.entries.len();
 
         if self.danger.is_yellow() {
@@ -1530,7 +1701,7 @@ impl<T> HeaderMap<T> {
                 let new_cap = self.indices.len() * 2;
 
                 // Grow the capacity
-                self.grow(new_cap);
+                self.try_grow(new_cap)?;
             } else {
                 self.danger.set_red();
 
@@ -1549,16 +1720,18 @@ impl<T> HeaderMap<T> {
                 self.entries = Vec::with_capacity(usable_capacity(new_raw_cap));
             } else {
                 let raw_cap = self.indices.len();
-                self.grow(raw_cap << 1);
+                self.try_grow(raw_cap << 1)?;
             }
         }
+
+        Ok(())
     }
 
     #[inline]
-    fn grow(&mut self, new_raw_cap: usize) {
-        assert!(new_raw_cap <= MAX_SIZE, "requested capacity too large");
-        // This path can never be reached when handling the first allocation in
-        // the map.
+    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;
@@ -1591,6 +1764,7 @@ impl<T> HeaderMap<T> {
         // Reserve additional entry slots
         let more = self.capacity() - self.entries.len();
         self.entries.reserve_exact(more);
+        Ok(())
     }
 
     #[inline]
@@ -1914,7 +2088,7 @@ impl<T> Extend<(Option<HeaderName>, T)> for HeaderMap<T> {
         };
 
         'outer: loop {
-            let mut entry = match self.entry2(key) {
+            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.
@@ -1988,7 +2162,7 @@ impl<T: fmt::Debug> fmt::Debug for HeaderMap<T> {
 
 impl<T> Default for HeaderMap<T> {
     fn default() -> Self {
-        HeaderMap::with_capacity(0)
+        HeaderMap::try_with_capacity(0).expect("zero capacity should never fail")
     }
 }
 
@@ -2315,6 +2489,10 @@ impl<'a, T> Entry<'a, T> {
     ///
     /// Returns a mutable reference to the **first** value in the entry.
     ///
+    /// # Panics
+    ///
+    /// This method panics if capacity exceeds max `HeaderMap` capacity
+    ///
     /// # Examples
     ///
     /// ```
@@ -2338,11 +2516,47 @@ impl<'a, T> Entry<'a, T> {
     /// 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) => e.into_mut(),
-            Vacant(e) => e.insert(default),
+            Occupied(e) => Ok(e.into_mut()),
+            Vacant(e) => e.try_insert(default),
         }
     }
 
@@ -2372,20 +2586,66 @@ impl<'a, T> Entry<'a, T> {
     /// # use http::HeaderMap;
     /// # use http::header::HOST;
     /// let mut map = HeaderMap::new();
-    /// map.insert(HOST, "world".parse().unwrap());
+    /// map.try_insert(HOST, "world".parse().unwrap()).unwrap();
     ///
-    /// let res = map.entry("host")
-    ///     .or_insert_with(|| unreachable!());
+    /// 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) => e.into_mut(),
-            Vacant(e) => e.insert(default()),
+            Occupied(e) => Ok(e.into_mut()),
+            Vacant(e) => e.try_insert(default()),
         }
     }
 
@@ -2460,12 +2720,33 @@ impl<'a, T> VacantEntry<'a, T> {
     /// 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
-            .insert_phase_two(self.key, value, self.hash, self.probe, self.danger);
+        let index =
+            self.map
+                .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?;
 
-        &mut self.map.entries[index].value
+        Ok(&mut self.map.entries[index].value)
     }
 
     /// Insert the value into the entry.
@@ -2479,24 +2760,47 @@ impl<'a, T> VacantEntry<'a, T> {
     /// # use http::header::*;
     /// let mut map = HeaderMap::new();
     ///
-    /// if let Entry::Vacant(v) = map.entry("x-hello") {
-    ///     let mut e = v.insert_entry("world".parse().unwrap());
+    /// 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
-            .insert_phase_two(self.key, value, self.hash, self.probe, self.danger);
+        let index =
+            self.map
+                .try_insert_phase_two(self.key, value, self.hash, self.probe, self.danger)?;
 
-        OccupiedEntry {
+        Ok(OccupiedEntry {
             map: self.map,
             index,
             probe: self.probe,
-        }
+        })
     }
 }
 
@@ -3244,6 +3548,30 @@ impl Danger {
     }
 }
 
+// ===== 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]
@@ -3306,7 +3634,7 @@ where
  */
 
 mod into_header_name {
-    use super::{Entry, HdrName, HeaderMap, HeaderName};
+    use super::{Entry, HdrName, HeaderMap, HeaderName, MaxSizeReached};
 
     /// A marker trait used to identify values that can be used as insert keys
     /// to a `HeaderMap`.
@@ -3322,31 +3650,36 @@ mod into_header_name {
     // without breaking any external crate.
     pub trait Sealed {
         #[doc(hidden)]
-        fn insert<T>(self, map: &mut HeaderMap<T>, val: T) -> Option<T>;
+        fn try_insert<T>(self, map: &mut HeaderMap<T>, val: T)
+            -> Result<Option<T>, MaxSizeReached>;
 
         #[doc(hidden)]
-        fn append<T>(self, map: &mut HeaderMap<T>, val: T) -> bool;
+        fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached>;
 
         #[doc(hidden)]
-        fn entry<T>(self, map: &mut HeaderMap<T>) -> Entry<'_, T>;
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached>;
     }
 
     // ==== impls ====
 
     impl Sealed for HeaderName {
         #[inline]
-        fn insert<T>(self, map: &mut HeaderMap<T>, val: T) -> Option<T> {
-            map.insert2(self, val)
+        fn try_insert<T>(
+            self,
+            map: &mut HeaderMap<T>,
+            val: T,
+        ) -> Result<Option<T>, MaxSizeReached> {
+            map.try_insert2(self, val)
         }
 
         #[inline]
-        fn append<T>(self, map: &mut HeaderMap<T>, val: T) -> bool {
-            map.append2(self, val)
+        fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached> {
+            map.try_append2(self, val)
         }
 
         #[inline]
-        fn entry<T>(self, map: &mut HeaderMap<T>) -> Entry<'_, T> {
-            map.entry2(self)
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> {
+            map.try_entry2(self)
         }
     }
 
@@ -3354,17 +3687,21 @@ mod into_header_name {
 
     impl<'a> Sealed for &'a HeaderName {
         #[inline]
-        fn insert<T>(self, map: &mut HeaderMap<T>, val: T) -> Option<T> {
-            map.insert2(self, val)
+        fn try_insert<T>(
+            self,
+            map: &mut HeaderMap<T>,
+            val: T,
+        ) -> Result<Option<T>, MaxSizeReached> {
+            map.try_insert2(self, val)
         }
         #[inline]
-        fn append<T>(self, map: &mut HeaderMap<T>, val: T) -> bool {
-            map.append2(self, val)
+        fn try_append<T>(self, map: &mut HeaderMap<T>, val: T) -> Result<bool, MaxSizeReached> {
+            map.try_append2(self, val)
         }
 
         #[inline]
-        fn entry<T>(self, map: &mut HeaderMap<T>) -> Entry<'_, T> {
-            map.entry2(self)
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> {
+            map.try_entry2(self)
         }
     }
 
@@ -3372,17 +3709,21 @@ mod into_header_name {
 
     impl Sealed for &'static str {
         #[inline]
-        fn insert<T>(self, map: &mut HeaderMap<T>, val: T) -> Option<T> {
-            HdrName::from_static(self, move |hdr| map.insert2(hdr, val))
+        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 append<T>(self, map: &mut HeaderMap<T>, val: T) -> bool {
-            HdrName::from_static(self, move |hdr| map.append2(hdr, val))
+        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 entry<T>(self, map: &mut HeaderMap<T>) -> Entry<'_, T> {
-            HdrName::from_static(self, move |hdr| map.entry2(hdr))
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, MaxSizeReached> {
+            HdrName::from_static(self, move |hdr| map.try_entry2(hdr))
         }
     }
 
@@ -3390,12 +3731,31 @@ mod into_header_name {
 }
 
 mod as_header_name {
-    use super::{Entry, HdrName, HeaderMap, HeaderName, InvalidHeaderName};
+    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.
     //
@@ -3406,7 +3766,7 @@ mod as_header_name {
     // without breaking any external crate.
     pub trait Sealed {
         #[doc(hidden)]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName>;
+        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)>;
@@ -3419,8 +3779,8 @@ mod as_header_name {
 
     impl Sealed for HeaderName {
         #[inline]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName> {
-            Ok(map.entry2(self))
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> {
+            Ok(map.try_entry2(self)?)
         }
 
         #[inline]
@@ -3437,8 +3797,8 @@ mod as_header_name {
 
     impl<'a> Sealed for &'a HeaderName {
         #[inline]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName> {
-            Ok(map.entry2(self))
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> {
+            Ok(map.try_entry2(self)?)
         }
 
         #[inline]
@@ -3455,8 +3815,10 @@ mod as_header_name {
 
     impl<'a> Sealed for &'a str {
         #[inline]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName> {
-            HdrName::from_bytes(self.as_bytes(), move |hdr| map.entry2(hdr))
+        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]
@@ -3473,8 +3835,8 @@ mod as_header_name {
 
     impl Sealed for String {
         #[inline]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName> {
-            self.as_str().try_entry(map)
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> {
+            Ok(self.as_str().try_entry(map)?)
         }
 
         #[inline]
@@ -3491,7 +3853,7 @@ mod as_header_name {
 
     impl<'a> Sealed for &'a String {
         #[inline]
-        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, InvalidHeaderName> {
+        fn try_entry<T>(self, map: &mut HeaderMap<T>) -> Result<Entry<'_, T>, TryEntryError> {
             self.as_str().try_entry(map)
         }
 
@@ -3531,7 +3893,7 @@ fn test_bounds() {
 #[test]
 fn skip_duplicates_during_key_iteration() {
     let mut map = HeaderMap::new();
-    map.append("a", HeaderValue::from_static("a"));
-    map.append("a", HeaderValue::from_static("b"));
+    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/src/header/mod.rs b/src/header/mod.rs
index 0c55bffe..5d405767 100644
--- a/src/header/mod.rs
+++ b/src/header/mod.rs
@@ -76,7 +76,8 @@ mod value;
 
 pub use self::map::{
     AsHeaderName, Drain, Entry, GetAll, HeaderMap, IntoHeaderName, IntoIter, Iter, IterMut, Keys,
-    OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values, ValuesMut,
+    MaxSizeReached, OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values,
+    ValuesMut,
 };
 pub use self::name::{HeaderName, InvalidHeaderName};
 pub use self::value::{HeaderValue, InvalidHeaderValue, ToStrError};
diff --git a/src/header/name.rs b/src/header/name.rs
index e00e229d..6b7e0298 100644
--- a/src/header/name.rs
+++ b/src/header/name.rs
@@ -1338,7 +1338,7 @@ impl fmt::Display for HeaderName {
 }
 
 impl InvalidHeaderName {
-    fn new() -> InvalidHeaderName {
+    pub(super) fn new() -> InvalidHeaderName {
         InvalidHeaderName { _priv: () }
     }
 }
diff --git a/src/request.rs b/src/request.rs
index 293c598c..d4c5bf54 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -908,7 +908,7 @@ impl Builder {
         self.and_then(move |mut head| {
             let name = <HeaderName as TryFrom<K>>::try_from(key).map_err(Into::into)?;
             let value = <HeaderValue as TryFrom<V>>::try_from(value).map_err(Into::into)?;
-            head.headers.append(name, value);
+            head.headers.try_append(name, value)?;
             Ok(head)
         })
     }
diff --git a/src/response.rs b/src/response.rs
index 0cc925ce..312cc5f8 100644
--- a/src/response.rs
+++ b/src/response.rs
@@ -618,7 +618,7 @@ impl Builder {
         self.and_then(move |mut head| {
             let name = <HeaderName as TryFrom<K>>::try_from(key).map_err(Into::into)?;
             let value = <HeaderValue as TryFrom<V>>::try_from(value).map_err(Into::into)?;
-            head.headers.append(name, value);
+            head.headers.try_append(name, value)?;
             Ok(head)
         })
     }