1use aws_smithy_types::date_time::Format;
9use aws_smithy_types::primitive::Parse;
10use aws_smithy_types::DateTime;
11use http_1x::header::{HeaderMap, HeaderName, HeaderValue};
12use std::borrow::Cow;
13use std::error::Error;
14use std::fmt;
15use std::str::FromStr;
16
17#[derive(Debug)]
19pub struct ParseError {
20 message: Cow<'static, str>,
21 source: Option<Box<dyn Error + Send + Sync + 'static>>,
22}
23
24impl ParseError {
25 pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
27 Self {
28 message: message.into(),
29 source: None,
30 }
31 }
32
33 pub fn with_source(self, source: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
35 Self {
36 source: Some(source.into()),
37 ..self
38 }
39 }
40}
41
42impl fmt::Display for ParseError {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "output failed to parse in headers: {}", self.message)
45 }
46}
47
48impl Error for ParseError {
49 fn source(&self) -> Option<&(dyn Error + 'static)> {
50 self.source.as_ref().map(|err| err.as_ref() as _)
51 }
52}
53
54pub fn many_dates<'a>(
59 values: impl Iterator<Item = &'a str>,
60 format: Format,
61) -> Result<Vec<DateTime>, ParseError> {
62 let mut out = vec![];
63 for header in values {
64 let mut header = header;
65 while !header.is_empty() {
66 let (v, next) = DateTime::read(header, format, ',').map_err(|err| {
67 ParseError::new(format!("header could not be parsed as date: {}", err))
68 })?;
69 out.push(v);
70 header = next;
71 }
72 }
73 Ok(out)
74}
75
76pub fn headers_for_prefix<'a>(
79 header_names: impl Iterator<Item = &'a str>,
80 key: &'a str,
81) -> impl Iterator<Item = (&'a str, &'a str)> {
82 let lower_key = key.to_ascii_lowercase();
83 header_names
84 .filter(move |k| k.starts_with(&lower_key))
85 .map(move |k| (&k[key.len()..], k))
86}
87
88pub fn read_many_from_str<'a, T: FromStr>(
90 values: impl Iterator<Item = &'a str>,
91) -> Result<Vec<T>, ParseError>
92where
93 T::Err: Error + Send + Sync + 'static,
94{
95 read_many(values, |v: &str| {
96 v.parse().map_err(|err| {
97 ParseError::new("failed during `FromString` conversion").with_source(err)
98 })
99 })
100}
101
102pub fn read_many_primitive<'a, T: Parse>(
104 values: impl Iterator<Item = &'a str>,
105) -> Result<Vec<T>, ParseError> {
106 read_many(values, |v: &str| {
107 T::parse_smithy_primitive(v)
108 .map_err(|err| ParseError::new("failed reading a list of primitives").with_source(err))
109 })
110}
111
112fn read_many<'a, T>(
114 values: impl Iterator<Item = &'a str>,
115 f: impl Fn(&str) -> Result<T, ParseError>,
116) -> Result<Vec<T>, ParseError> {
117 let mut out = vec![];
118 for header in values {
119 let mut header = header.as_bytes();
120 while !header.is_empty() {
121 let (v, next) = read_one(header, &f)?;
122 out.push(v);
123 header = next;
124 }
125 }
126 Ok(out)
127}
128
129pub fn one_or_none<'a, T: FromStr>(
133 mut values: impl Iterator<Item = &'a str>,
134) -> Result<Option<T>, ParseError>
135where
136 T::Err: Error + Send + Sync + 'static,
137{
138 let first = match values.next() {
139 Some(v) => v,
140 None => return Ok(None),
141 };
142 match values.next() {
143 None => T::from_str(first.trim())
144 .map_err(|err| ParseError::new("failed to parse string").with_source(err))
145 .map(Some),
146 Some(_) => Err(ParseError::new(
147 "expected a single value but found multiple",
148 )),
149 }
150}
151
152pub fn set_request_header_if_absent<V>(
154 request: http_1x::request::Builder,
155 key: HeaderName,
156 value: V,
157) -> http_1x::request::Builder
158where
159 HeaderValue: TryFrom<V>,
160 <HeaderValue as TryFrom<V>>::Error: Into<http_1x::Error>,
161{
162 if !request
163 .headers_ref()
164 .map(|map| map.contains_key(&key))
165 .unwrap_or(false)
166 {
167 request.header(key, value)
168 } else {
169 request
170 }
171}
172
173pub fn set_response_header_if_absent<V>(
175 response: http_1x::response::Builder,
176 key: HeaderName,
177 value: V,
178) -> http_1x::response::Builder
179where
180 HeaderValue: TryFrom<V>,
181 <HeaderValue as TryFrom<V>>::Error: Into<http_1x::Error>,
182{
183 if !response
184 .headers_ref()
185 .map(|map| map.contains_key(&key))
186 .unwrap_or(false)
187 {
188 response.header(key, value)
189 } else {
190 response
191 }
192}
193
194mod parse_multi_header {
198 use super::ParseError;
199 use std::borrow::Cow;
200
201 fn trim(s: Cow<'_, str>) -> Cow<'_, str> {
202 match s {
203 Cow::Owned(s) => Cow::Owned(s.trim().into()),
204 Cow::Borrowed(s) => Cow::Borrowed(s.trim()),
205 }
206 }
207
208 fn replace<'a>(value: Cow<'a, str>, pattern: &str, replacement: &str) -> Cow<'a, str> {
209 if value.contains(pattern) {
210 Cow::Owned(value.replace(pattern, replacement))
211 } else {
212 value
213 }
214 }
215
216 pub(crate) fn read_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
220 for (index, &byte) in input.iter().enumerate() {
221 let current_slice = &input[index..];
222 match byte {
223 b' ' | b'\t' => { }
224 b'"' => return read_quoted_value(¤t_slice[1..]),
225 _ => {
226 let (value, rest) = read_unquoted_value(current_slice)?;
227 return Ok((trim(value), rest));
228 }
229 }
230 }
231
232 Ok((Cow::Borrowed(""), &[]))
234 }
235
236 fn read_unquoted_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
237 let next_delim = input.iter().position(|&b| b == b',').unwrap_or(input.len());
238 let (first, next) = input.split_at(next_delim);
239 let first = std::str::from_utf8(first)
240 .map_err(|_| ParseError::new("header was not valid utf-8"))?;
241 Ok((Cow::Borrowed(first), then_comma(next).unwrap()))
242 }
243
244 fn read_quoted_value(input: &[u8]) -> Result<(Cow<'_, str>, &[u8]), ParseError> {
247 for index in 0..input.len() {
248 match input[index] {
249 b'"' if index == 0 || input[index - 1] != b'\\' => {
250 let mut inner = Cow::Borrowed(
251 std::str::from_utf8(&input[0..index])
252 .map_err(|_| ParseError::new("header was not valid utf-8"))?,
253 );
254 inner = replace(inner, "\\\"", "\"");
255 inner = replace(inner, "\\\\", "\\");
256 let rest = then_comma(&input[(index + 1)..])?;
257 return Ok((inner, rest));
258 }
259 _ => {}
260 }
261 }
262 Err(ParseError::new(
263 "header value had quoted value without end quote",
264 ))
265 }
266
267 fn then_comma(s: &[u8]) -> Result<&[u8], ParseError> {
268 if s.is_empty() {
269 Ok(s)
270 } else if s.starts_with(b",") {
271 Ok(&s[1..])
272 } else {
273 Err(ParseError::new("expected delimiter `,`"))
274 }
275 }
276}
277
278fn read_one<'a, T>(
280 s: &'a [u8],
281 f: &impl Fn(&str) -> Result<T, ParseError>,
282) -> Result<(T, &'a [u8]), ParseError> {
283 let (value, rest) = parse_multi_header::read_value(s)?;
284 Ok((f(&value)?, rest))
285}
286
287pub fn quote_header_value<'a>(value: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
289 let value = value.into();
290 if value.trim().len() != value.len()
291 || value.contains('"')
292 || value.contains(',')
293 || value.contains('(')
294 || value.contains(')')
295 {
296 Cow::Owned(format!(
297 "\"{}\"",
298 value.replace('\\', "\\\\").replace('"', "\\\"")
299 ))
300 } else {
301 value
302 }
303}
304
305pub fn append_merge_header_maps(
308 mut lhs: HeaderMap<HeaderValue>,
309 rhs: HeaderMap<HeaderValue>,
310) -> HeaderMap<HeaderValue> {
311 let mut last_header_name_seen = None;
312 for (header_name, header_value) in rhs.into_iter() {
313 match (&mut last_header_name_seen, header_name) {
318 (_, Some(header_name)) => {
319 lhs.append(header_name.clone(), header_value);
320 last_header_name_seen = Some(header_name);
321 }
322 (Some(header_name), None) => {
323 lhs.append(header_name.clone(), header_value);
324 }
325 (None, None) => unreachable!(),
326 };
327 }
328
329 lhs
330}
331
332pub fn append_merge_header_maps_http_1x(
335 mut lhs: http_1x::HeaderMap<http_1x::HeaderValue>,
336 rhs: http_1x::HeaderMap<http_1x::HeaderValue>,
337) -> http_1x::HeaderMap<http_1x::HeaderValue> {
338 let mut last_header_name_seen = None;
339 for (header_name, header_value) in rhs.into_iter() {
340 match (&mut last_header_name_seen, header_name) {
345 (_, Some(header_name)) => {
346 lhs.append(header_name.clone(), header_value);
347 last_header_name_seen = Some(header_name);
348 }
349 (Some(header_name), None) => {
350 lhs.append(header_name.clone(), header_value);
351 }
352 (None, None) => unreachable!(),
353 };
354 }
355
356 lhs
357}
358
359#[cfg(test)]
360mod test {
361 use super::quote_header_value;
362 use crate::header::{
363 append_merge_header_maps, headers_for_prefix, many_dates, read_many_from_str,
364 read_many_primitive, set_request_header_if_absent, set_response_header_if_absent,
365 ParseError,
366 };
367 use aws_smithy_runtime_api::http::Request;
368 use aws_smithy_types::error::display::DisplayErrorContext;
369 use aws_smithy_types::{date_time::Format, DateTime};
370 use http_1x::header::{HeaderMap, HeaderName, HeaderValue};
371 use std::collections::HashMap;
372
373 #[test]
374 fn put_on_request_if_absent() {
375 let builder = http_1x::Request::builder().header("foo", "bar");
376 let builder = set_request_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
377 let builder =
378 set_request_header_if_absent(builder, HeaderName::from_static("other"), "value");
379 let req = builder.body(()).expect("valid request");
380 assert_eq!(
381 req.headers().get_all("foo").iter().collect::<Vec<_>>(),
382 vec!["bar"]
383 );
384 assert_eq!(
385 req.headers().get_all("other").iter().collect::<Vec<_>>(),
386 vec!["value"]
387 );
388 }
389
390 #[test]
391 fn put_on_response_if_absent() {
392 let builder = http_1x::Response::builder().header("foo", "bar");
393 let builder = set_response_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
394 let builder =
395 set_response_header_if_absent(builder, HeaderName::from_static("other"), "value");
396 let response = builder.body(()).expect("valid response");
397 assert_eq!(
398 response.headers().get_all("foo").iter().collect::<Vec<_>>(),
399 vec!["bar"]
400 );
401 assert_eq!(
402 response
403 .headers()
404 .get_all("other")
405 .iter()
406 .collect::<Vec<_>>(),
407 vec!["value"]
408 );
409 }
410
411 #[test]
412 fn parse_floats() {
413 let test_request = http_1x::Request::builder()
414 .header("X-Float-Multi", "0.0,Infinity,-Infinity,5555.5")
415 .header("X-Float-Error", "notafloat")
416 .body(())
417 .unwrap();
418 assert_eq!(
419 read_many_primitive::<f32>(
420 test_request
421 .headers()
422 .get_all("X-Float-Multi")
423 .iter()
424 .map(|v| v.to_str().unwrap())
425 )
426 .expect("valid"),
427 vec![0.0, f32::INFINITY, f32::NEG_INFINITY, 5555.5]
428 );
429 let message = format!(
430 "{}",
431 DisplayErrorContext(
432 read_many_primitive::<f32>(
433 test_request
434 .headers()
435 .get_all("X-Float-Error")
436 .iter()
437 .map(|v| v.to_str().unwrap())
438 )
439 .expect_err("invalid")
440 )
441 );
442 let expected = "output failed to parse in headers: failed reading a list of primitives: failed to parse input as f32";
443 assert!(
444 message.starts_with(expected),
445 "expected '{message}' to start with '{expected}'"
446 );
447 }
448
449 #[test]
450 fn test_many_dates() {
451 let test_request = http_1x::Request::builder()
452 .header("Empty", "")
453 .header("SingleHttpDate", "Wed, 21 Oct 2015 07:28:00 GMT")
454 .header(
455 "MultipleHttpDates",
456 "Wed, 21 Oct 2015 07:28:00 GMT,Thu, 22 Oct 2015 07:28:00 GMT",
457 )
458 .header("SingleEpochSeconds", "1234.5678")
459 .header("MultipleEpochSeconds", "1234.5678,9012.3456")
460 .body(())
461 .unwrap();
462 let read = |name: &str, format: Format| {
463 many_dates(
464 test_request
465 .headers()
466 .get_all(name)
467 .iter()
468 .map(|v| v.to_str().unwrap()),
469 format,
470 )
471 };
472 let read_valid = |name: &str, format: Format| read(name, format).expect("valid");
473 assert_eq!(
474 read_valid("Empty", Format::DateTime),
475 Vec::<DateTime>::new()
476 );
477 assert_eq!(
478 read_valid("SingleHttpDate", Format::HttpDate),
479 vec![DateTime::from_secs_and_nanos(1445412480, 0)]
480 );
481 assert_eq!(
482 read_valid("MultipleHttpDates", Format::HttpDate),
483 vec![
484 DateTime::from_secs_and_nanos(1445412480, 0),
485 DateTime::from_secs_and_nanos(1445498880, 0)
486 ]
487 );
488 assert_eq!(
489 read_valid("SingleEpochSeconds", Format::EpochSeconds),
490 vec![DateTime::from_secs_and_nanos(1234, 567_800_000)]
491 );
492 assert_eq!(
493 read_valid("MultipleEpochSeconds", Format::EpochSeconds),
494 vec![
495 DateTime::from_secs_and_nanos(1234, 567_800_000),
496 DateTime::from_secs_and_nanos(9012, 345_600_000)
497 ]
498 );
499 }
500
501 #[test]
502 fn read_many_strings() {
503 let test_request = http_1x::Request::builder()
504 .header("Empty", "")
505 .header("Foo", " foo")
506 .header("FooTrailing", "foo ")
507 .header("FooInQuotes", "\" foo \"")
508 .header("CommaInQuotes", "\"foo,bar\",baz")
509 .header("CommaInQuotesTrailing", "\"foo,bar\",baz ")
510 .header("QuoteInQuotes", "\"foo\\\",bar\",\"\\\"asdf\\\"\",baz")
511 .header(
512 "QuoteInQuotesWithSpaces",
513 "\"foo\\\",bar\", \"\\\"asdf\\\"\", baz",
514 )
515 .header("JunkFollowingQuotes", "\"\\\"asdf\\\"\"baz")
516 .header("EmptyQuotes", "\"\",baz")
517 .header("EscapedSlashesInQuotes", "foo, \"(foo\\\\bar)\"")
518 .body(())
519 .unwrap();
520 let read = |name: &str| {
521 read_many_from_str::<String>(
522 test_request
523 .headers()
524 .get_all(name)
525 .iter()
526 .map(|v| v.to_str().unwrap()),
527 )
528 };
529 let read_valid = |name: &str| read(name).expect("valid");
530 assert_eq!(read_valid("Empty"), Vec::<String>::new());
531 assert_eq!(read_valid("Foo"), vec!["foo"]);
532 assert_eq!(read_valid("FooTrailing"), vec!["foo"]);
533 assert_eq!(read_valid("FooInQuotes"), vec![" foo "]);
534 assert_eq!(read_valid("CommaInQuotes"), vec!["foo,bar", "baz"]);
535 assert_eq!(read_valid("CommaInQuotesTrailing"), vec!["foo,bar", "baz"]);
536 assert_eq!(
537 read_valid("QuoteInQuotes"),
538 vec!["foo\",bar", "\"asdf\"", "baz"]
539 );
540 assert_eq!(
541 read_valid("QuoteInQuotesWithSpaces"),
542 vec!["foo\",bar", "\"asdf\"", "baz"]
543 );
544 assert!(read("JunkFollowingQuotes").is_err());
545 assert_eq!(read_valid("EmptyQuotes"), vec!["", "baz"]);
546 assert_eq!(
547 read_valid("EscapedSlashesInQuotes"),
548 vec!["foo", "(foo\\bar)"]
549 );
550 }
551
552 #[test]
553 fn read_many_bools() {
554 let test_request = http_1x::Request::builder()
555 .header("X-Bool-Multi", "true,false")
556 .header("X-Bool-Multi", "true")
557 .header("X-Bool", "true")
558 .header("X-Bool-Invalid", "truth,falsy")
559 .header("X-Bool-Single", "true,false,true,true")
560 .header("X-Bool-Quoted", "true,\"false\",true,true")
561 .body(())
562 .unwrap();
563 assert_eq!(
564 read_many_primitive::<bool>(
565 test_request
566 .headers()
567 .get_all("X-Bool-Multi")
568 .iter()
569 .map(|v| v.to_str().unwrap())
570 )
571 .expect("valid"),
572 vec![true, false, true]
573 );
574
575 assert_eq!(
576 read_many_primitive::<bool>(
577 test_request
578 .headers()
579 .get_all("X-Bool")
580 .iter()
581 .map(|v| v.to_str().unwrap())
582 )
583 .unwrap(),
584 vec![true]
585 );
586 assert_eq!(
587 read_many_primitive::<bool>(
588 test_request
589 .headers()
590 .get_all("X-Bool-Single")
591 .iter()
592 .map(|v| v.to_str().unwrap())
593 )
594 .unwrap(),
595 vec![true, false, true, true]
596 );
597 assert_eq!(
598 read_many_primitive::<bool>(
599 test_request
600 .headers()
601 .get_all("X-Bool-Quoted")
602 .iter()
603 .map(|v| v.to_str().unwrap())
604 )
605 .unwrap(),
606 vec![true, false, true, true]
607 );
608 read_many_primitive::<bool>(
609 test_request
610 .headers()
611 .get_all("X-Bool-Invalid")
612 .iter()
613 .map(|v| v.to_str().unwrap()),
614 )
615 .expect_err("invalid");
616 }
617
618 #[test]
619 fn check_read_many_i16() {
620 let test_request = http_1x::Request::builder()
621 .header("X-Multi", "123,456")
622 .header("X-Multi", "789")
623 .header("X-Num", "777")
624 .header("X-Num-Invalid", "12ef3")
625 .header("X-Num-Single", "1,2,3,-4,5")
626 .header("X-Num-Quoted", "1, \"2\",3,\"-4\",5")
627 .body(())
628 .unwrap();
629 assert_eq!(
630 read_many_primitive::<i16>(
631 test_request
632 .headers()
633 .get_all("X-Multi")
634 .iter()
635 .map(|v| v.to_str().unwrap())
636 )
637 .expect("valid"),
638 vec![123, 456, 789]
639 );
640
641 assert_eq!(
642 read_many_primitive::<i16>(
643 test_request
644 .headers()
645 .get_all("X-Num")
646 .iter()
647 .map(|v| v.to_str().unwrap())
648 )
649 .unwrap(),
650 vec![777]
651 );
652 assert_eq!(
653 read_many_primitive::<i16>(
654 test_request
655 .headers()
656 .get_all("X-Num-Single")
657 .iter()
658 .map(|v| v.to_str().unwrap())
659 )
660 .unwrap(),
661 vec![1, 2, 3, -4, 5]
662 );
663 assert_eq!(
664 read_many_primitive::<i16>(
665 test_request
666 .headers()
667 .get_all("X-Num-Quoted")
668 .iter()
669 .map(|v| v.to_str().unwrap())
670 )
671 .unwrap(),
672 vec![1, 2, 3, -4, 5]
673 );
674 read_many_primitive::<i16>(
675 test_request
676 .headers()
677 .get_all("X-Num-Invalid")
678 .iter()
679 .map(|v| v.to_str().unwrap()),
680 )
681 .expect_err("invalid");
682 }
683
684 #[test]
685 fn test_prefix_headers() {
686 let test_request = Request::try_from(
687 http_1x::Request::builder()
688 .header("X-Prefix-A", "123,456")
689 .header("X-Prefix-B", "789")
690 .header("X-Prefix-C", "777")
691 .header("X-Prefix-C", "777")
692 .body(())
693 .unwrap(),
694 )
695 .unwrap();
696 let resp: Result<HashMap<String, Vec<i16>>, ParseError> =
697 headers_for_prefix(test_request.headers().iter().map(|h| h.0), "X-Prefix-")
698 .map(|(key, header_name)| {
699 let values = test_request.headers().get_all(header_name);
700 read_many_primitive(values).map(|v| (key.to_string(), v))
701 })
702 .collect();
703 let resp = resp.expect("valid");
704 assert_eq!(resp.get("a"), Some(&vec![123_i16, 456_i16]));
705 }
706
707 #[test]
708 fn test_quote_header_value() {
709 assert_eq!("", "e_header_value(""));
710 assert_eq!("foo", "e_header_value("foo"));
711 assert_eq!("\" foo\"", "e_header_value(" foo"));
712 assert_eq!("foo bar", "e_header_value("foo bar"));
713 assert_eq!("\"foo,bar\"", "e_header_value("foo,bar"));
714 assert_eq!("\",\"", "e_header_value(","));
715 assert_eq!("\"\\\"foo\\\"\"", "e_header_value("\"foo\""));
716 assert_eq!("\"\\\"f\\\\oo\\\"\"", "e_header_value("\"f\\oo\""));
717 assert_eq!("\"(\"", "e_header_value("("));
718 assert_eq!("\")\"", "e_header_value(")"));
719 }
720
721 #[test]
722 fn test_append_merge_header_maps_with_shared_key() {
723 let header_name = HeaderName::from_static("some_key");
724 let left_header_value = HeaderValue::from_static("lhs value");
725 let right_header_value = HeaderValue::from_static("rhs value");
726
727 let mut left_hand_side_headers = HeaderMap::new();
728 left_hand_side_headers.insert(header_name.clone(), left_header_value.clone());
729
730 let mut right_hand_side_headers = HeaderMap::new();
731 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
732
733 let merged_header_map =
734 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
735 let actual_merged_values: Vec<_> =
736 merged_header_map.get_all(header_name).into_iter().collect();
737
738 let expected_merged_values = vec![left_header_value, right_header_value];
739
740 assert_eq!(actual_merged_values, expected_merged_values);
741 }
742
743 #[test]
744 fn test_append_merge_header_maps_with_multiple_values_in_left_hand_map() {
745 let header_name = HeaderName::from_static("some_key");
746 let left_header_value_1 = HeaderValue::from_static("lhs value 1");
747 let left_header_value_2 = HeaderValue::from_static("lhs_value 2");
748 let right_header_value = HeaderValue::from_static("rhs value");
749
750 let mut left_hand_side_headers = HeaderMap::new();
751 left_hand_side_headers.insert(header_name.clone(), left_header_value_1.clone());
752 left_hand_side_headers.append(header_name.clone(), left_header_value_2.clone());
753
754 let mut right_hand_side_headers = HeaderMap::new();
755 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
756
757 let merged_header_map =
758 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
759 let actual_merged_values: Vec<_> =
760 merged_header_map.get_all(header_name).into_iter().collect();
761
762 let expected_merged_values =
763 vec![left_header_value_1, left_header_value_2, right_header_value];
764
765 assert_eq!(actual_merged_values, expected_merged_values);
766 }
767
768 #[test]
769 fn test_append_merge_header_maps_with_empty_left_hand_map() {
770 let header_name = HeaderName::from_static("some_key");
771 let right_header_value_1 = HeaderValue::from_static("rhs value 1");
772 let right_header_value_2 = HeaderValue::from_static("rhs_value 2");
773
774 let left_hand_side_headers = HeaderMap::new();
775
776 let mut right_hand_side_headers = HeaderMap::new();
777 right_hand_side_headers.insert(header_name.clone(), right_header_value_1.clone());
778 right_hand_side_headers.append(header_name.clone(), right_header_value_2.clone());
779
780 let merged_header_map =
781 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
782 let actual_merged_values: Vec<_> =
783 merged_header_map.get_all(header_name).into_iter().collect();
784
785 let expected_merged_values = vec![right_header_value_1, right_header_value_2];
786
787 assert_eq!(actual_merged_values, expected_merged_values);
788 }
789}