Skip to content

Commit c86f230

Browse files
committed
fix: correct HeaderMap iterators' len and size_hint for multi-value
headers fixes #3969
1 parent 9188286 commit c86f230

File tree

2 files changed

+22
-16
lines changed

2 files changed

+22
-16
lines changed

actix-http/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Encode the HTTP/1 `Connection: Upgrade` header in Camel-Case when camel-case header formatting is enabled.[#3953]
6+
- Fix `HeaderMap` iterators' `len()` and `size_hint()` implementations for multi-value headers.
67

78
[#3953]: https://github.com/actix/actix-web/pull/3953
89

actix-http/src/header/map.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ impl HeaderMap {
537537
/// assert!(pairs.contains(&(&header::SET_COOKIE, &HeaderValue::from_static("two=2"))));
538538
/// ```
539539
pub fn iter(&self) -> Iter<'_> {
540-
Iter::new(self.inner.iter())
540+
Iter::new(self.inner.iter(), self.len())
541541
}
542542

543543
/// An iterator over all contained header names.
@@ -626,7 +626,8 @@ impl HeaderMap {
626626
/// assert!(map.is_empty());
627627
/// ```
628628
pub fn drain(&mut self) -> Drain<'_> {
629-
Drain::new(self.inner.drain())
629+
let len = self.len();
630+
Drain::new(self.inner.drain(), len)
630631
}
631632
}
632633

@@ -638,7 +639,8 @@ impl IntoIterator for HeaderMap {
638639

639640
#[inline]
640641
fn into_iter(self) -> Self::IntoIter {
641-
IntoIter::new(self.inner.into_iter())
642+
let len = self.len();
643+
IntoIter::new(self.inner.into_iter(), len)
642644
}
643645
}
644646

@@ -648,7 +650,7 @@ impl<'a> IntoIterator for &'a HeaderMap {
648650

649651
#[inline]
650652
fn into_iter(self) -> Self::IntoIter {
651-
Iter::new(self.inner.iter())
653+
Iter::new(self.inner.iter(), self.len())
652654
}
653655
}
654656

@@ -760,14 +762,16 @@ pub struct Iter<'a> {
760762
inner: hash_map::Iter<'a, HeaderName, Value>,
761763
multi_inner: Option<(&'a HeaderName, &'a SmallVec<[HeaderValue; 4]>)>,
762764
multi_idx: usize,
765+
remaining: usize,
763766
}
764767

765768
impl<'a> Iter<'a> {
766-
fn new(iter: hash_map::Iter<'a, HeaderName, Value>) -> Self {
769+
fn new(iter: hash_map::Iter<'a, HeaderName, Value>, remaining: usize) -> Self {
767770
Self {
768771
inner: iter,
769772
multi_idx: 0,
770773
multi_inner: None,
774+
remaining,
771775
}
772776
}
773777
}
@@ -781,6 +785,7 @@ impl<'a> Iterator for Iter<'a> {
781785
match vals.get(self.multi_idx) {
782786
Some(val) => {
783787
self.multi_idx += 1;
788+
self.remaining -= 1;
784789
return Some((name, val));
785790
}
786791
None => {
@@ -800,9 +805,7 @@ impl<'a> Iterator for Iter<'a> {
800805

801806
#[inline]
802807
fn size_hint(&self) -> (usize, Option<usize>) {
803-
// take inner lower bound
804-
// make no attempt at an upper bound
805-
(self.inner.size_hint().0, None)
808+
(self.remaining, Some(self.remaining))
806809
}
807810
}
808811

@@ -818,14 +821,16 @@ pub struct Drain<'a> {
818821
inner: hash_map::Drain<'a, HeaderName, Value>,
819822
multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>,
820823
multi_idx: usize,
824+
remaining: usize,
821825
}
822826

823827
impl<'a> Drain<'a> {
824-
fn new(iter: hash_map::Drain<'a, HeaderName, Value>) -> Self {
828+
fn new(iter: hash_map::Drain<'a, HeaderName, Value>, remaining: usize) -> Self {
825829
Self {
826830
inner: iter,
827831
multi_inner: None,
828832
multi_idx: 0,
833+
remaining,
829834
}
830835
}
831836
}
@@ -838,6 +843,7 @@ impl Iterator for Drain<'_> {
838843
if let Some((ref mut name, ref mut vals)) = self.multi_inner {
839844
if !vals.is_empty() {
840845
// OPTIMIZE: array removals
846+
self.remaining -= 1;
841847
return Some((name.take(), vals.remove(0)));
842848
} else {
843849
// no more items in value iterator; reset state
@@ -855,9 +861,7 @@ impl Iterator for Drain<'_> {
855861

856862
#[inline]
857863
fn size_hint(&self) -> (usize, Option<usize>) {
858-
// take inner lower bound
859-
// make no attempt at an upper bound
860-
(self.inner.size_hint().0, None)
864+
(self.remaining, Some(self.remaining))
861865
}
862866
}
863867

@@ -872,13 +876,15 @@ impl iter::FusedIterator for Drain<'_> {}
872876
pub struct IntoIter {
873877
inner: hash_map::IntoIter<HeaderName, Value>,
874878
multi_inner: Option<(HeaderName, smallvec::IntoIter<[HeaderValue; 4]>)>,
879+
remaining: usize,
875880
}
876881

877882
impl IntoIter {
878-
fn new(inner: hash_map::IntoIter<HeaderName, Value>) -> Self {
883+
fn new(inner: hash_map::IntoIter<HeaderName, Value>, remaining: usize) -> Self {
879884
Self {
880885
inner,
881886
multi_inner: None,
887+
remaining,
882888
}
883889
}
884890
}
@@ -891,6 +897,7 @@ impl Iterator for IntoIter {
891897
if let Some((ref name, ref mut vals)) = self.multi_inner {
892898
match vals.next() {
893899
Some(val) => {
900+
self.remaining -= 1;
894901
return Some((name.clone(), val));
895902
}
896903
None => {
@@ -909,9 +916,7 @@ impl Iterator for IntoIter {
909916

910917
#[inline]
911918
fn size_hint(&self) -> (usize, Option<usize>) {
912-
// take inner lower bound
913-
// make no attempt at an upper bound
914-
(self.inner.size_hint().0, None)
919+
(self.remaining, Some(self.remaining))
915920
}
916921
}
917922

0 commit comments

Comments
 (0)