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
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_1x::request::Builder,
215 key: HeaderName,
216 value: V,
217) -> http_1x::request::Builder
218where
219 HeaderValue: TryFrom<V>,
220 <HeaderValue as TryFrom<V>>::Error: Into<http_1x::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_1x::response::Builder,
236 key: HeaderName,
237 value: V,
238) -> http_1x::response::Builder
239where
240 HeaderValue: TryFrom<V>,
241 <HeaderValue as TryFrom<V>>::Error: Into<http_1x::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
392pub fn append_merge_header_maps_http_1x(
395 mut lhs: http_1x::HeaderMap<http_1x::HeaderValue>,
396 rhs: http_1x::HeaderMap<http_1x::HeaderValue>,
397) -> http_1x::HeaderMap<http_1x::HeaderValue> {
398 let mut last_header_name_seen = None;
399 for (header_name, header_value) in rhs.into_iter() {
400 match (&mut last_header_name_seen, header_name) {
405 (_, Some(header_name)) => {
406 lhs.append(header_name.clone(), header_value);
407 last_header_name_seen = Some(header_name);
408 }
409 (Some(header_name), None) => {
410 lhs.append(header_name.clone(), header_value);
411 }
412 (None, None) => unreachable!(),
413 };
414 }
415
416 lhs
417}
418
419#[cfg(test)]
420mod test {
421 use super::quote_header_value;
422 use crate::header::{
423 append_merge_header_maps, headers_for_prefix, many_dates, many_dates_bytes, one_or_none,
424 one_or_none_bytes, read_many_from_str, read_many_from_str_bytes, read_many_primitive,
425 read_many_primitive_bytes, set_request_header_if_absent, set_response_header_if_absent,
426 ParseError,
427 };
428 use aws_smithy_runtime_api::http::Request;
429 use aws_smithy_types::error::display::DisplayErrorContext;
430 use aws_smithy_types::{date_time::Format, DateTime};
431 use http_1x::header::{HeaderMap, HeaderName, HeaderValue};
432 use std::collections::HashMap;
433
434 #[test]
435 fn put_on_request_if_absent() {
436 let builder = http_1x::Request::builder().header("foo", "bar");
437 let builder = set_request_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
438 let builder =
439 set_request_header_if_absent(builder, HeaderName::from_static("other"), "value");
440 let req = builder.body(()).expect("valid request");
441 assert_eq!(
442 req.headers().get_all("foo").iter().collect::<Vec<_>>(),
443 vec!["bar"]
444 );
445 assert_eq!(
446 req.headers().get_all("other").iter().collect::<Vec<_>>(),
447 vec!["value"]
448 );
449 }
450
451 #[test]
452 fn put_on_response_if_absent() {
453 let builder = http_1x::Response::builder().header("foo", "bar");
454 let builder = set_response_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
455 let builder =
456 set_response_header_if_absent(builder, HeaderName::from_static("other"), "value");
457 let response = builder.body(()).expect("valid response");
458 assert_eq!(
459 response.headers().get_all("foo").iter().collect::<Vec<_>>(),
460 vec!["bar"]
461 );
462 assert_eq!(
463 response
464 .headers()
465 .get_all("other")
466 .iter()
467 .collect::<Vec<_>>(),
468 vec!["value"]
469 );
470 }
471
472 #[test]
473 fn parse_floats() {
474 let test_request = http_1x::Request::builder()
475 .header("X-Float-Multi", "0.0,Infinity,-Infinity,5555.5")
476 .header("X-Float-Error", "notafloat")
477 .body(())
478 .unwrap();
479 assert_eq!(
480 read_many_primitive::<f32>(
481 test_request
482 .headers()
483 .get_all("X-Float-Multi")
484 .iter()
485 .map(|v| v.to_str().unwrap())
486 )
487 .expect("valid"),
488 vec![0.0, f32::INFINITY, f32::NEG_INFINITY, 5555.5]
489 );
490 let message = format!(
491 "{}",
492 DisplayErrorContext(
493 read_many_primitive::<f32>(
494 test_request
495 .headers()
496 .get_all("X-Float-Error")
497 .iter()
498 .map(|v| v.to_str().unwrap())
499 )
500 .expect_err("invalid")
501 )
502 );
503 let expected = "output failed to parse in headers: failed reading a list of primitives: failed to parse input as f32";
504 assert!(
505 message.starts_with(expected),
506 "expected '{message}' to start with '{expected}'"
507 );
508 }
509
510 #[test]
511 fn test_many_dates() {
512 let test_request = http_1x::Request::builder()
513 .header("Empty", "")
514 .header("SingleHttpDate", "Wed, 21 Oct 2015 07:28:00 GMT")
515 .header(
516 "MultipleHttpDates",
517 "Wed, 21 Oct 2015 07:28:00 GMT,Thu, 22 Oct 2015 07:28:00 GMT",
518 )
519 .header("SingleEpochSeconds", "1234.5678")
520 .header("MultipleEpochSeconds", "1234.5678,9012.3456")
521 .body(())
522 .unwrap();
523 let read = |name: &str, format: Format| {
524 many_dates(
525 test_request
526 .headers()
527 .get_all(name)
528 .iter()
529 .map(|v| v.to_str().unwrap()),
530 format,
531 )
532 };
533 let read_valid = |name: &str, format: Format| read(name, format).expect("valid");
534 assert_eq!(
535 read_valid("Empty", Format::DateTime),
536 Vec::<DateTime>::new()
537 );
538 assert_eq!(
539 read_valid("SingleHttpDate", Format::HttpDate),
540 vec![DateTime::from_secs_and_nanos(1445412480, 0)]
541 );
542 assert_eq!(
543 read_valid("MultipleHttpDates", Format::HttpDate),
544 vec![
545 DateTime::from_secs_and_nanos(1445412480, 0),
546 DateTime::from_secs_and_nanos(1445498880, 0)
547 ]
548 );
549 assert_eq!(
550 read_valid("SingleEpochSeconds", Format::EpochSeconds),
551 vec![DateTime::from_secs_and_nanos(1234, 567_800_000)]
552 );
553 assert_eq!(
554 read_valid("MultipleEpochSeconds", Format::EpochSeconds),
555 vec![
556 DateTime::from_secs_and_nanos(1234, 567_800_000),
557 DateTime::from_secs_and_nanos(9012, 345_600_000)
558 ]
559 );
560 }
561
562 const NON_UTF8_VALUE: &[u8] = b"value-\xe9";
564
565 #[test]
566 fn bytes_helpers_agree_with_str_helpers_on_valid_utf8() {
567 assert_eq!(
568 one_or_none::<String>([" foo "].into_iter()).unwrap(),
569 one_or_none_bytes::<String>([b" foo ".as_slice()].into_iter()).unwrap(),
570 );
571 assert_eq!(
572 read_many_from_str::<String>(["\"foo,bar\",baz"].into_iter()).unwrap(),
573 read_many_from_str_bytes::<String>([b"\"foo,bar\",baz".as_slice()].into_iter())
574 .unwrap(),
575 );
576 assert_eq!(
577 read_many_primitive::<i16>(["1,2", "3"].into_iter()).unwrap(),
578 read_many_primitive_bytes::<i16>([b"1,2".as_slice(), b"3".as_slice()].into_iter())
579 .unwrap(),
580 );
581 assert_eq!(
582 many_dates(
583 ["Mon, 16 Dec 2019 23:48:18 GMT"].into_iter(),
584 Format::HttpDate
585 )
586 .unwrap(),
587 many_dates_bytes(
588 [b"Mon, 16 Dec 2019 23:48:18 GMT".as_slice()].into_iter(),
589 Format::HttpDate
590 )
591 .unwrap(),
592 );
593 }
594
595 #[test]
596 fn bytes_helpers_reject_non_utf8() {
597 let expected = "header was not valid utf-8";
598
599 let err = one_or_none_bytes::<String>([NON_UTF8_VALUE].into_iter()).expect_err("non-utf8");
600 assert!(err.to_string().contains(expected), "{err}");
601
602 let err =
603 read_many_from_str_bytes::<String>([NON_UTF8_VALUE].into_iter()).expect_err("non-utf8");
604 assert!(err.to_string().contains(expected), "{err}");
605
606 let err =
607 read_many_primitive_bytes::<i16>([NON_UTF8_VALUE].into_iter()).expect_err("non-utf8");
608 assert!(err.to_string().contains(expected), "{err}");
609
610 let err =
611 many_dates_bytes([NON_UTF8_VALUE].into_iter(), Format::HttpDate).expect_err("non-utf8");
612 assert!(err.to_string().contains(expected), "{err}");
613 }
614
615 #[test]
616 fn one_or_none_bytes_reports_multiple_before_non_utf8() {
617 let err = one_or_none_bytes::<String>([NON_UTF8_VALUE, b"second".as_slice()].into_iter())
620 .expect_err("multiple values");
621 assert!(
622 err.to_string()
623 .contains("expected a single value but found multiple"),
624 "{err}"
625 );
626 }
627
628 #[test]
629 fn read_many_strings() {
630 let test_request = http_1x::Request::builder()
631 .header("Empty", "")
632 .header("Foo", " foo")
633 .header("FooTrailing", "foo ")
634 .header("FooInQuotes", "\" foo \"")
635 .header("CommaInQuotes", "\"foo,bar\",baz")
636 .header("CommaInQuotesTrailing", "\"foo,bar\",baz ")
637 .header("QuoteInQuotes", "\"foo\\\",bar\",\"\\\"asdf\\\"\",baz")
638 .header(
639 "QuoteInQuotesWithSpaces",
640 "\"foo\\\",bar\", \"\\\"asdf\\\"\", baz",
641 )
642 .header("JunkFollowingQuotes", "\"\\\"asdf\\\"\"baz")
643 .header("EmptyQuotes", "\"\",baz")
644 .header("EscapedSlashesInQuotes", "foo, \"(foo\\\\bar)\"")
645 .body(())
646 .unwrap();
647 let read = |name: &str| {
648 read_many_from_str::<String>(
649 test_request
650 .headers()
651 .get_all(name)
652 .iter()
653 .map(|v| v.to_str().unwrap()),
654 )
655 };
656 let read_valid = |name: &str| read(name).expect("valid");
657 assert_eq!(read_valid("Empty"), Vec::<String>::new());
658 assert_eq!(read_valid("Foo"), vec!["foo"]);
659 assert_eq!(read_valid("FooTrailing"), vec!["foo"]);
660 assert_eq!(read_valid("FooInQuotes"), vec![" foo "]);
661 assert_eq!(read_valid("CommaInQuotes"), vec!["foo,bar", "baz"]);
662 assert_eq!(read_valid("CommaInQuotesTrailing"), vec!["foo,bar", "baz"]);
663 assert_eq!(
664 read_valid("QuoteInQuotes"),
665 vec!["foo\",bar", "\"asdf\"", "baz"]
666 );
667 assert_eq!(
668 read_valid("QuoteInQuotesWithSpaces"),
669 vec!["foo\",bar", "\"asdf\"", "baz"]
670 );
671 assert!(read("JunkFollowingQuotes").is_err());
672 assert_eq!(read_valid("EmptyQuotes"), vec!["", "baz"]);
673 assert_eq!(
674 read_valid("EscapedSlashesInQuotes"),
675 vec!["foo", "(foo\\bar)"]
676 );
677 }
678
679 #[test]
680 fn read_many_bools() {
681 let test_request = http_1x::Request::builder()
682 .header("X-Bool-Multi", "true,false")
683 .header("X-Bool-Multi", "true")
684 .header("X-Bool", "true")
685 .header("X-Bool-Invalid", "truth,falsy")
686 .header("X-Bool-Single", "true,false,true,true")
687 .header("X-Bool-Quoted", "true,\"false\",true,true")
688 .body(())
689 .unwrap();
690 assert_eq!(
691 read_many_primitive::<bool>(
692 test_request
693 .headers()
694 .get_all("X-Bool-Multi")
695 .iter()
696 .map(|v| v.to_str().unwrap())
697 )
698 .expect("valid"),
699 vec![true, false, true]
700 );
701
702 assert_eq!(
703 read_many_primitive::<bool>(
704 test_request
705 .headers()
706 .get_all("X-Bool")
707 .iter()
708 .map(|v| v.to_str().unwrap())
709 )
710 .unwrap(),
711 vec![true]
712 );
713 assert_eq!(
714 read_many_primitive::<bool>(
715 test_request
716 .headers()
717 .get_all("X-Bool-Single")
718 .iter()
719 .map(|v| v.to_str().unwrap())
720 )
721 .unwrap(),
722 vec![true, false, true, true]
723 );
724 assert_eq!(
725 read_many_primitive::<bool>(
726 test_request
727 .headers()
728 .get_all("X-Bool-Quoted")
729 .iter()
730 .map(|v| v.to_str().unwrap())
731 )
732 .unwrap(),
733 vec![true, false, true, true]
734 );
735 read_many_primitive::<bool>(
736 test_request
737 .headers()
738 .get_all("X-Bool-Invalid")
739 .iter()
740 .map(|v| v.to_str().unwrap()),
741 )
742 .expect_err("invalid");
743 }
744
745 #[test]
746 fn check_read_many_i16() {
747 let test_request = http_1x::Request::builder()
748 .header("X-Multi", "123,456")
749 .header("X-Multi", "789")
750 .header("X-Num", "777")
751 .header("X-Num-Invalid", "12ef3")
752 .header("X-Num-Single", "1,2,3,-4,5")
753 .header("X-Num-Quoted", "1, \"2\",3,\"-4\",5")
754 .body(())
755 .unwrap();
756 assert_eq!(
757 read_many_primitive::<i16>(
758 test_request
759 .headers()
760 .get_all("X-Multi")
761 .iter()
762 .map(|v| v.to_str().unwrap())
763 )
764 .expect("valid"),
765 vec![123, 456, 789]
766 );
767
768 assert_eq!(
769 read_many_primitive::<i16>(
770 test_request
771 .headers()
772 .get_all("X-Num")
773 .iter()
774 .map(|v| v.to_str().unwrap())
775 )
776 .unwrap(),
777 vec![777]
778 );
779 assert_eq!(
780 read_many_primitive::<i16>(
781 test_request
782 .headers()
783 .get_all("X-Num-Single")
784 .iter()
785 .map(|v| v.to_str().unwrap())
786 )
787 .unwrap(),
788 vec![1, 2, 3, -4, 5]
789 );
790 assert_eq!(
791 read_many_primitive::<i16>(
792 test_request
793 .headers()
794 .get_all("X-Num-Quoted")
795 .iter()
796 .map(|v| v.to_str().unwrap())
797 )
798 .unwrap(),
799 vec![1, 2, 3, -4, 5]
800 );
801 read_many_primitive::<i16>(
802 test_request
803 .headers()
804 .get_all("X-Num-Invalid")
805 .iter()
806 .map(|v| v.to_str().unwrap()),
807 )
808 .expect_err("invalid");
809 }
810
811 #[test]
812 fn test_prefix_headers() {
813 let test_request = Request::try_from(
814 http_1x::Request::builder()
815 .header("X-Prefix-A", "123,456")
816 .header("X-Prefix-B", "789")
817 .header("X-Prefix-C", "777")
818 .header("X-Prefix-C", "777")
819 .body(())
820 .unwrap(),
821 )
822 .unwrap();
823 let resp: Result<HashMap<String, Vec<i16>>, ParseError> =
824 headers_for_prefix(test_request.headers().iter().map(|h| h.0), "X-Prefix-")
825 .map(|(key, header_name)| {
826 let values = test_request.headers().get_all(header_name);
827 read_many_primitive(values).map(|v| (key.to_string(), v))
828 })
829 .collect();
830 let resp = resp.expect("valid");
831 assert_eq!(resp.get("a"), Some(&vec![123_i16, 456_i16]));
832 }
833
834 #[test]
835 fn test_quote_header_value() {
836 assert_eq!("", "e_header_value(""));
837 assert_eq!("foo", "e_header_value("foo"));
838 assert_eq!("\" foo\"", "e_header_value(" foo"));
839 assert_eq!("foo bar", "e_header_value("foo bar"));
840 assert_eq!("\"foo,bar\"", "e_header_value("foo,bar"));
841 assert_eq!("\",\"", "e_header_value(","));
842 assert_eq!("\"\\\"foo\\\"\"", "e_header_value("\"foo\""));
843 assert_eq!("\"\\\"f\\\\oo\\\"\"", "e_header_value("\"f\\oo\""));
844 assert_eq!("\"(\"", "e_header_value("("));
845 assert_eq!("\")\"", "e_header_value(")"));
846 }
847
848 #[test]
849 fn test_append_merge_header_maps_with_shared_key() {
850 let header_name = HeaderName::from_static("some_key");
851 let left_header_value = HeaderValue::from_static("lhs value");
852 let right_header_value = HeaderValue::from_static("rhs value");
853
854 let mut left_hand_side_headers = HeaderMap::new();
855 left_hand_side_headers.insert(header_name.clone(), left_header_value.clone());
856
857 let mut right_hand_side_headers = HeaderMap::new();
858 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
859
860 let merged_header_map =
861 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
862 let actual_merged_values: Vec<_> =
863 merged_header_map.get_all(header_name).into_iter().collect();
864
865 let expected_merged_values = vec![left_header_value, right_header_value];
866
867 assert_eq!(actual_merged_values, expected_merged_values);
868 }
869
870 #[test]
871 fn test_append_merge_header_maps_with_multiple_values_in_left_hand_map() {
872 let header_name = HeaderName::from_static("some_key");
873 let left_header_value_1 = HeaderValue::from_static("lhs value 1");
874 let left_header_value_2 = HeaderValue::from_static("lhs_value 2");
875 let right_header_value = HeaderValue::from_static("rhs value");
876
877 let mut left_hand_side_headers = HeaderMap::new();
878 left_hand_side_headers.insert(header_name.clone(), left_header_value_1.clone());
879 left_hand_side_headers.append(header_name.clone(), left_header_value_2.clone());
880
881 let mut right_hand_side_headers = HeaderMap::new();
882 right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
883
884 let merged_header_map =
885 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
886 let actual_merged_values: Vec<_> =
887 merged_header_map.get_all(header_name).into_iter().collect();
888
889 let expected_merged_values =
890 vec![left_header_value_1, left_header_value_2, right_header_value];
891
892 assert_eq!(actual_merged_values, expected_merged_values);
893 }
894
895 #[test]
896 fn test_append_merge_header_maps_with_empty_left_hand_map() {
897 let header_name = HeaderName::from_static("some_key");
898 let right_header_value_1 = HeaderValue::from_static("rhs value 1");
899 let right_header_value_2 = HeaderValue::from_static("rhs_value 2");
900
901 let left_hand_side_headers = HeaderMap::new();
902
903 let mut right_hand_side_headers = HeaderMap::new();
904 right_hand_side_headers.insert(header_name.clone(), right_header_value_1.clone());
905 right_hand_side_headers.append(header_name.clone(), right_header_value_2.clone());
906
907 let merged_header_map =
908 append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
909 let actual_merged_values: Vec<_> =
910 merged_header_map.get_all(header_name).into_iter().collect();
911
912 let expected_merged_values = vec![right_header_value_1, right_header_value_2];
913
914 assert_eq!(actual_merged_values, expected_merged_values);
915 }
916}