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