AWS SDK

AWS SDK

rev. 33341052640bdff4ac4f4158b00a75df08ff159b (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-legacy-http/src/event_stream/sender.rs

@@ -0,1 +0,377 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
use aws_smithy_eventstream::frame::{write_message_to, MarshallMessage, SignMessage};
           7  +
use aws_smithy_eventstream::message_size_hint::MessageSizeHint;
           8  +
use aws_smithy_runtime_api::client::result::SdkError;
           9  +
use aws_smithy_types::error::ErrorMetadata;
          10  +
use bytes::Bytes;
          11  +
use futures_core::Stream;
          12  +
use std::error::Error as StdError;
          13  +
use std::fmt;
          14  +
use std::fmt::Debug;
          15  +
use std::marker::PhantomData;
          16  +
use std::pin::Pin;
          17  +
use std::task::{Context, Poll};
          18  +
use tracing::trace;
          19  +
          20  +
/// Input type for Event Streams.
          21  +
pub struct EventStreamSender<T, E> {
          22  +
    input_stream: Pin<Box<dyn Stream<Item = Result<T, E>> + Send + Sync>>,
          23  +
}
          24  +
          25  +
impl<T, E> Debug for EventStreamSender<T, E> {
          26  +
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
          27  +
        let name_t = std::any::type_name::<T>();
          28  +
        let name_e = std::any::type_name::<E>();
          29  +
        write!(f, "EventStreamSender<{name_t}, {name_e}>")
          30  +
    }
          31  +
}
          32  +
          33  +
impl<T: Send + Sync + 'static, E: StdError + Send + Sync + 'static> EventStreamSender<T, E> {
          34  +
    /// Creates an `EventStreamSender` from a single item.
          35  +
    pub fn once(item: Result<T, E>) -> Self {
          36  +
        Self::from(futures_util::stream::once(async move { item }))
          37  +
    }
          38  +
}
          39  +
          40  +
impl<T, E: StdError + Send + Sync + 'static> EventStreamSender<T, E> {
          41  +
    #[doc(hidden)]
          42  +
    pub fn into_body_stream(
          43  +
        self,
          44  +
        marshaller: impl MarshallMessage<Input = T> + Send + Sync + 'static,
          45  +
        error_marshaller: impl MarshallMessage<Input = E> + Send + Sync + 'static,
          46  +
        signer: impl SignMessage + Send + Sync + 'static,
          47  +
    ) -> MessageStreamAdapter<T, E> {
          48  +
        MessageStreamAdapter::new(marshaller, error_marshaller, signer, self.input_stream)
          49  +
    }
          50  +
}
          51  +
          52  +
impl<T, E, S> From<S> for EventStreamSender<T, E>
          53  +
where
          54  +
    S: Stream<Item = Result<T, E>> + Send + Sync + 'static,
          55  +
{
          56  +
    fn from(stream: S) -> Self {
          57  +
        EventStreamSender {
          58  +
            input_stream: Box::pin(stream),
          59  +
        }
          60  +
    }
          61  +
}
          62  +
          63  +
/// An error that occurs within a message stream.
          64  +
#[derive(Debug)]
          65  +
pub struct MessageStreamError {
          66  +
    kind: MessageStreamErrorKind,
          67  +
    pub(crate) meta: ErrorMetadata,
          68  +
}
          69  +
          70  +
#[derive(Debug)]
          71  +
enum MessageStreamErrorKind {
          72  +
    Unhandled(Box<dyn std::error::Error + Send + Sync + 'static>),
          73  +
}
          74  +
          75  +
impl MessageStreamError {
          76  +
    /// Creates the `MessageStreamError::Unhandled` variant from any error type.
          77  +
    pub fn unhandled(err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
          78  +
        Self {
          79  +
            meta: Default::default(),
          80  +
            kind: MessageStreamErrorKind::Unhandled(err.into()),
          81  +
        }
          82  +
    }
          83  +
          84  +
    /// Creates the `MessageStreamError::Unhandled` variant from an [`ErrorMetadata`].
          85  +
    pub fn generic(err: ErrorMetadata) -> Self {
          86  +
        Self {
          87  +
            meta: err.clone(),
          88  +
            kind: MessageStreamErrorKind::Unhandled(err.into()),
          89  +
        }
          90  +
    }
          91  +
          92  +
    /// Returns error metadata, which includes the error code, message,
          93  +
    /// request ID, and potentially additional information.
          94  +
    pub fn meta(&self) -> &ErrorMetadata {
          95  +
        &self.meta
          96  +
    }
          97  +
}
          98  +
          99  +
impl StdError for MessageStreamError {
         100  +
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
         101  +
        match &self.kind {
         102  +
            MessageStreamErrorKind::Unhandled(source) => Some(source.as_ref() as _),
         103  +
        }
         104  +
    }
         105  +
}
         106  +
         107  +
impl fmt::Display for MessageStreamError {
         108  +
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         109  +
        match &self.kind {
         110  +
            MessageStreamErrorKind::Unhandled(_) => write!(f, "message stream error"),
         111  +
        }
         112  +
    }
         113  +
}
         114  +
         115  +
/// Adapts a `Stream<SmithyMessageType>` to a signed `Stream<Bytes>` by using the provided
         116  +
/// message marshaller and signer implementations.
         117  +
///
         118  +
/// This will yield an `Err(SdkError::ConstructionFailure)` if a message can't be
         119  +
/// marshalled into an Event Stream frame, (e.g., if the message payload was too large).
         120  +
#[allow(missing_debug_implementations)]
         121  +
pub struct MessageStreamAdapter<T, E: StdError + Send + Sync + 'static> {
         122  +
    marshaller: Box<dyn MarshallMessage<Input = T> + Send + Sync>,
         123  +
    error_marshaller: Box<dyn MarshallMessage<Input = E> + Send + Sync>,
         124  +
    signer: Box<dyn SignMessage + Send + Sync>,
         125  +
    stream: Pin<Box<dyn Stream<Item = Result<T, E>> + Send>>,
         126  +
    end_signal_sent: bool,
         127  +
    _phantom: PhantomData<E>,
         128  +
}
         129  +
         130  +
impl<T, E: StdError + Send + Sync + 'static> Unpin for MessageStreamAdapter<T, E> {}
         131  +
         132  +
impl<T, E: StdError + Send + Sync + 'static> MessageStreamAdapter<T, E> {
         133  +
    /// Create a new `MessageStreamAdapter`.
         134  +
    pub fn new(
         135  +
        marshaller: impl MarshallMessage<Input = T> + Send + Sync + 'static,
         136  +
        error_marshaller: impl MarshallMessage<Input = E> + Send + Sync + 'static,
         137  +
        signer: impl SignMessage + Send + Sync + 'static,
         138  +
        stream: Pin<Box<dyn Stream<Item = Result<T, E>> + Send>>,
         139  +
    ) -> Self {
         140  +
        MessageStreamAdapter {
         141  +
            marshaller: Box::new(marshaller),
         142  +
            error_marshaller: Box::new(error_marshaller),
         143  +
            signer: Box::new(signer),
         144  +
            stream,
         145  +
            end_signal_sent: false,
         146  +
            _phantom: Default::default(),
         147  +
        }
         148  +
    }
         149  +
}
         150  +
         151  +
impl<T, E: StdError + Send + Sync + 'static> Stream for MessageStreamAdapter<T, E> {
         152  +
    type Item =
         153  +
        Result<Bytes, SdkError<E, aws_smithy_runtime_api::client::orchestrator::HttpResponse>>;
         154  +
         155  +
    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
         156  +
        match self.stream.as_mut().poll_next(cx) {
         157  +
            Poll::Ready(message_option) => {
         158  +
                if let Some(message_result) = message_option {
         159  +
                    let message = match message_result {
         160  +
                        Ok(message) => self
         161  +
                            .marshaller
         162  +
                            .marshall(message)
         163  +
                            .map_err(SdkError::construction_failure)?,
         164  +
                        Err(message) => self
         165  +
                            .error_marshaller
         166  +
                            .marshall(message)
         167  +
                            .map_err(SdkError::construction_failure)?,
         168  +
                    };
         169  +
         170  +
                    trace!(unsigned_message = ?message, "signing event stream message");
         171  +
                    let message = self
         172  +
                        .signer
         173  +
                        .sign(message)
         174  +
                        .map_err(SdkError::construction_failure)?;
         175  +
         176  +
                    let mut buffer = Vec::with_capacity(message.size_hint());
         177  +
                    write_message_to(&message, &mut buffer)
         178  +
                        .map_err(SdkError::construction_failure)?;
         179  +
                    trace!(signed_message = ?buffer, "sending signed event stream message");
         180  +
                    Poll::Ready(Some(Ok(Bytes::from(buffer))))
         181  +
                } else if !self.end_signal_sent {
         182  +
                    self.end_signal_sent = true;
         183  +
                    match self.signer.sign_empty() {
         184  +
                        Some(sign) => {
         185  +
                            let message = sign.map_err(SdkError::construction_failure)?;
         186  +
                            let mut buffer = Vec::with_capacity(message.size_hint());
         187  +
                            write_message_to(&message, &mut buffer)
         188  +
                                .map_err(SdkError::construction_failure)?;
         189  +
                            trace!(signed_message = ?buffer, "sending signed empty message to terminate the event stream");
         190  +
                            Poll::Ready(Some(Ok(Bytes::from(buffer))))
         191  +
                        }
         192  +
                        None => Poll::Ready(None),
         193  +
                    }
         194  +
                } else {
         195  +
                    Poll::Ready(None)
         196  +
                }
         197  +
            }
         198  +
            Poll::Pending => Poll::Pending,
         199  +
        }
         200  +
    }
         201  +
}
         202  +
         203  +
#[cfg(test)]
         204  +
mod tests {
         205  +
    use super::MarshallMessage;
         206  +
    use crate::event_stream::{EventStreamSender, MessageStreamAdapter};
         207  +
    use async_stream::stream;
         208  +
    use aws_smithy_eventstream::error::Error as EventStreamError;
         209  +
    use aws_smithy_eventstream::frame::{
         210  +
        read_message_from, write_message_to, NoOpSigner, SignMessage, SignMessageError,
         211  +
    };
         212  +
    use aws_smithy_runtime_api::client::result::SdkError;
         213  +
    use aws_smithy_types::event_stream::{Header, HeaderValue, Message};
         214  +
    use bytes::Bytes;
         215  +
    use futures_core::Stream;
         216  +
    use futures_util::stream::StreamExt;
         217  +
    use std::error::Error as StdError;
         218  +
         219  +
    #[derive(Debug, Eq, PartialEq)]
         220  +
    struct TestMessage(String);
         221  +
         222  +
    #[derive(Debug)]
         223  +
    struct Marshaller;
         224  +
    impl MarshallMessage for Marshaller {
         225  +
        type Input = TestMessage;
         226  +
         227  +
        fn marshall(&self, input: Self::Input) -> Result<Message, EventStreamError> {
         228  +
            Ok(Message::new(input.0.as_bytes().to_vec()))
         229  +
        }
         230  +
    }
         231  +
    #[derive(Debug)]
         232  +
    struct ErrorMarshaller;
         233  +
    impl MarshallMessage for ErrorMarshaller {
         234  +
        type Input = TestServiceError;
         235  +
         236  +
        fn marshall(&self, _input: Self::Input) -> Result<Message, EventStreamError> {
         237  +
            Err(read_message_from(&b""[..]).expect_err("this should always fail"))
         238  +
        }
         239  +
    }
         240  +
         241  +
    #[derive(Debug)]
         242  +
    struct TestServiceError;
         243  +
    impl std::fmt::Display for TestServiceError {
         244  +
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         245  +
            write!(f, "TestServiceError")
         246  +
        }
         247  +
    }
         248  +
    impl StdError for TestServiceError {}
         249  +
         250  +
    #[derive(Debug)]
         251  +
    struct TestSigner;
         252  +
    impl SignMessage for TestSigner {
         253  +
        fn sign(&mut self, message: Message) -> Result<Message, SignMessageError> {
         254  +
            let mut buffer = Vec::new();
         255  +
            write_message_to(&message, &mut buffer).unwrap();
         256  +
            Ok(Message::new(buffer).add_header(Header::new("signed", HeaderValue::Bool(true))))
         257  +
        }
         258  +
         259  +
        fn sign_empty(&mut self) -> Option<Result<Message, SignMessageError>> {
         260  +
            Some(Ok(
         261  +
                Message::new(&b""[..]).add_header(Header::new("signed", HeaderValue::Bool(true)))
         262  +
            ))
         263  +
        }
         264  +
    }
         265  +
         266  +
    fn check_send_sync<T: Send + Sync>(value: T) -> T {
         267  +
        value
         268  +
    }
         269  +
         270  +
    #[test]
         271  +
    fn event_stream_sender_send_sync() {
         272  +
        check_send_sync(EventStreamSender::from(stream! {
         273  +
            yield Result::<_, SignMessageError>::Ok(TestMessage("test".into()));
         274  +
        }));
         275  +
    }
         276  +
         277  +
    fn check_compatible_with_hyper_wrap_stream<S, O, E>(stream: S) -> S
         278  +
    where
         279  +
        S: Stream<Item = Result<O, E>> + Send + 'static,
         280  +
        O: Into<Bytes> + 'static,
         281  +
        E: Into<Box<dyn StdError + Send + Sync + 'static>> + 'static,
         282  +
    {
         283  +
        stream
         284  +
    }
         285  +
         286  +
    #[tokio::test]
         287  +
    async fn message_stream_adapter_success() {
         288  +
        let stream = stream! {
         289  +
            yield Ok(TestMessage("test".into()));
         290  +
        };
         291  +
        let mut adapter = check_compatible_with_hyper_wrap_stream(MessageStreamAdapter::<
         292  +
            TestMessage,
         293  +
            TestServiceError,
         294  +
        >::new(
         295  +
            Marshaller,
         296  +
            ErrorMarshaller,
         297  +
            TestSigner,
         298  +
            Box::pin(stream),
         299  +
        ));
         300  +
         301  +
        let mut sent_bytes = adapter.next().await.unwrap().unwrap();
         302  +
        let sent = read_message_from(&mut sent_bytes).unwrap();
         303  +
        assert_eq!("signed", sent.headers()[0].name().as_str());
         304  +
        assert_eq!(&HeaderValue::Bool(true), sent.headers()[0].value());
         305  +
        let inner = read_message_from(&mut (&sent.payload()[..])).unwrap();
         306  +
        assert_eq!(&b"test"[..], &inner.payload()[..]);
         307  +
         308  +
        let mut end_signal_bytes = adapter.next().await.unwrap().unwrap();
         309  +
        let end_signal = read_message_from(&mut end_signal_bytes).unwrap();
         310  +
        assert_eq!("signed", end_signal.headers()[0].name().as_str());
         311  +
        assert_eq!(&HeaderValue::Bool(true), end_signal.headers()[0].value());
         312  +
        assert_eq!(0, end_signal.payload().len());
         313  +
    }
         314  +
         315  +
    #[tokio::test]
         316  +
    async fn message_stream_adapter_construction_failure() {
         317  +
        let stream = stream! {
         318  +
            yield Err(TestServiceError);
         319  +
        };
         320  +
        let mut adapter = check_compatible_with_hyper_wrap_stream(MessageStreamAdapter::<
         321  +
            TestMessage,
         322  +
            TestServiceError,
         323  +
        >::new(
         324  +
            Marshaller,
         325  +
            ErrorMarshaller,
         326  +
            NoOpSigner {},
         327  +
            Box::pin(stream),
         328  +
        ));
         329  +
         330  +
        let result = adapter.next().await.unwrap();
         331  +
        assert!(result.is_err());
         332  +
        assert!(matches!(
         333  +
            result.err().unwrap(),
         334  +
            SdkError::ConstructionFailure(_)
         335  +
        ));
         336  +
    }
         337  +
         338  +
    #[tokio::test]
         339  +
    async fn event_stream_sender_once() {
         340  +
        let sender = EventStreamSender::once(Ok(TestMessage("test".into())));
         341  +
        let mut adapter = MessageStreamAdapter::<TestMessage, TestServiceError>::new(
         342  +
            Marshaller,
         343  +
            ErrorMarshaller,
         344  +
            TestSigner,
         345  +
            sender.input_stream,
         346  +
        );
         347  +
         348  +
        let mut sent_bytes = adapter.next().await.unwrap().unwrap();
         349  +
        let sent = read_message_from(&mut sent_bytes).unwrap();
         350  +
        assert_eq!("signed", sent.headers()[0].name().as_str());
         351  +
        let inner = read_message_from(&mut (&sent.payload()[..])).unwrap();
         352  +
        assert_eq!(&b"test"[..], &inner.payload()[..]);
         353  +
         354  +
        // Should get end signal next
         355  +
        let mut end_signal_bytes = adapter.next().await.unwrap().unwrap();
         356  +
        let end_signal = read_message_from(&mut end_signal_bytes).unwrap();
         357  +
        assert_eq!("signed", end_signal.headers()[0].name().as_str());
         358  +
        assert_eq!(0, end_signal.payload().len());
         359  +
         360  +
        // Stream should be exhausted
         361  +
        assert!(adapter.next().await.is_none());
         362  +
    }
         363  +
         364  +
    // Verify the developer experience for this compiles
         365  +
    #[allow(unused)]
         366  +
    fn event_stream_input_ergonomics() {
         367  +
        fn check(input: impl Into<EventStreamSender<TestMessage, TestServiceError>>) {
         368  +
            let _: EventStreamSender<TestMessage, TestServiceError> = input.into();
         369  +
        }
         370  +
        check(stream! {
         371  +
            yield Ok(TestMessage("test".into()));
         372  +
        });
         373  +
        check(stream! {
         374  +
            yield Err(TestServiceError);
         375  +
        });
         376  +
    }
         377  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-legacy-http/src/futures_stream_adapter.rs

@@ -0,1 +0,62 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
use aws_smithy_types::body::SdkBody;
           7  +
use aws_smithy_types::byte_stream::error::Error as ByteStreamError;
           8  +
use aws_smithy_types::byte_stream::ByteStream;
           9  +
use bytes::Bytes;
          10  +
use futures_core::stream::Stream;
          11  +
use std::pin::Pin;
          12  +
use std::task::{Context, Poll};
          13  +
          14  +
/// A new-type wrapper to enable the impl of the `futures_core::stream::Stream` trait
          15  +
///
          16  +
/// [`ByteStream`] no longer implements `futures_core::stream::Stream` so we wrap it in the
          17  +
/// new-type to enable the trait when it is required.
          18  +
///
          19  +
/// This is meant to be used by codegen code, and users should not need to use it directly.
          20  +
#[derive(Debug)]
          21  +
pub struct FuturesStreamCompatByteStream(ByteStream);
          22  +
          23  +
impl FuturesStreamCompatByteStream {
          24  +
    /// Creates a new `FuturesStreamCompatByteStream` by wrapping `stream`.
          25  +
    pub fn new(stream: ByteStream) -> Self {
          26  +
        Self(stream)
          27  +
    }
          28  +
          29  +
    /// Returns [`SdkBody`] of the wrapped [`ByteStream`].
          30  +
    pub fn into_inner(self) -> SdkBody {
          31  +
        self.0.into_inner()
          32  +
    }
          33  +
}
          34  +
          35  +
impl Stream for FuturesStreamCompatByteStream {
          36  +
    type Item = Result<Bytes, ByteStreamError>;
          37  +
          38  +
    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
          39  +
        Pin::new(&mut self.0).poll_next(cx)
          40  +
    }
          41  +
}
          42  +
          43  +
#[cfg(test)]
          44  +
mod tests {
          45  +
    use super::*;
          46  +
    use futures_core::stream::Stream;
          47  +
          48  +
    fn check_compatible_with_hyper_wrap_stream<S, O, E>(stream: S) -> S
          49  +
    where
          50  +
        S: Stream<Item = Result<O, E>> + Send + 'static,
          51  +
        O: Into<Bytes> + 'static,
          52  +
        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>> + 'static,
          53  +
    {
          54  +
        stream
          55  +
    }
          56  +
          57  +
    #[test]
          58  +
    fn test_byte_stream_stream_can_be_made_compatible_with_hyper_wrap_stream() {
          59  +
        let stream = ByteStream::from_static(b"Hello world");
          60  +
        check_compatible_with_hyper_wrap_stream(FuturesStreamCompatByteStream::new(stream));
          61  +
    }
          62  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-legacy-http/src/header.rs

@@ -0,1 +0,762 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! Utilities for parsing information from headers
           7  +
           8  +
use aws_smithy_types::date_time::Format;
           9  +
use aws_smithy_types::primitive::Parse;
          10  +
use aws_smithy_types::DateTime;
          11  +
use http_02x::header::{HeaderMap, HeaderName, HeaderValue};
          12  +
use std::borrow::Cow;
          13  +
use std::error::Error;
          14  +
use std::fmt;
          15  +
use std::str::FromStr;
          16  +
          17  +
/// An error was encountered while parsing a header
          18  +
#[derive(Debug)]
          19  +
pub struct ParseError {
          20  +
    message: Cow<'static, str>,
          21  +
    source: Option<Box<dyn Error + Send + Sync + 'static>>,
          22  +
}
          23  +
          24  +
impl ParseError {
          25  +
    /// Create a new parse error with the given `message`
          26  +
    pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
          27  +
        Self {
          28  +
            message: message.into(),
          29  +
            source: None,
          30  +
        }
          31  +
    }
          32  +
          33  +
    /// Attach a source to this error.
          34  +
    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  +
          42  +
impl 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  +
          48  +
impl 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  +
          54  +
/// Read all the dates from the header map at `key` according the `format`
          55  +
///
          56  +
/// This is separate from `read_many` below because we need to invoke `DateTime::read` to take advantage
          57  +
/// of comma-aware parsing
          58  +
pub 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  +
          76  +
/// Returns an iterator over pairs where the first element is the unprefixed header name that
          77  +
/// starts with the input `key` prefix, and the second element is the full header name.
          78  +
pub 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  +
          88  +
/// Convert a `HeaderValue` into a `Vec<T>` where `T: FromStr`
          89  +
pub fn read_many_from_str<'a, T: FromStr>(
          90  +
    values: impl Iterator<Item = &'a str>,
          91  +
) -> Result<Vec<T>, ParseError>
          92  +
where
          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  +
         102  +
/// Convert a `HeaderValue` into a `Vec<T>` where `T: Parse`
         103  +
pub 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  +
         112  +
/// Read many comma / header delimited values from HTTP headers for `FromStr` types
         113  +
fn 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  +
         129  +
/// Read exactly one or none from a headers iterator
         130  +
///
         131  +
/// This function does not perform comma splitting like `read_many`
         132  +
pub fn one_or_none<'a, T: FromStr>(
         133  +
    mut values: impl Iterator<Item = &'a str>,
         134  +
) -> Result<Option<T>, ParseError>
         135  +
where
         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  +
         152  +
/// Given an HTTP request, set a request header if that header was not already set.
         153  +
pub fn set_request_header_if_absent<V>(
         154  +
    request: http_02x::request::Builder,
         155  +
    key: HeaderName,
         156  +
    value: V,
         157  +
) -> http_02x::request::Builder
         158  +
where
         159  +
    HeaderValue: TryFrom<V>,
         160  +
    <HeaderValue as TryFrom<V>>::Error: Into<http_02x::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  +
         173  +
/// Given an HTTP response, set a response header if that header was not already set.
         174  +
pub fn set_response_header_if_absent<V>(
         175  +
    response: http_02x::response::Builder,
         176  +
    key: HeaderName,
         177  +
    value: V,
         178  +
) -> http_02x::response::Builder
         179  +
where
         180  +
    HeaderValue: TryFrom<V>,
         181  +
    <HeaderValue as TryFrom<V>>::Error: Into<http_02x::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  +
         194  +
/// Functions for parsing multiple comma-delimited header values out of a
         195  +
/// single header. This parsing adheres to
         196  +
/// [RFC-7230's specification of header values](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6).
         197  +
mod 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  +
    /// Reads a single value out of the given input, and returns a tuple containing
         217  +
    /// the parsed value and the remainder of the slice that can be used to parse
         218  +
    /// more values.
         219  +
    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' => { /* skip whitespace */ }
         224  +
                b'"' => return read_quoted_value(&current_slice[1..]),
         225  +
                _ => {
         226  +
                    let (value, rest) = read_unquoted_value(current_slice)?;
         227  +
                    return Ok((trim(value), rest));
         228  +
                }
         229  +
            }
         230  +
        }
         231  +
         232  +
        // We only end up here if the entire header value was whitespace or empty
         233  +
        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  +
    /// Reads a header value that is surrounded by quotation marks and may have escaped
         245  +
    /// quotes inside of it.
         246  +
    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  +
         278  +
/// Read one comma delimited value for `FromStr` types
         279  +
fn 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  +
         287  +
/// Conditionally quotes and escapes a header value if the header value contains a comma or quote.
         288  +
pub 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  +
         305  +
/// Given two [`HeaderMap`]s, merge them together and return the merged `HeaderMap`. If the
         306  +
/// two `HeaderMap`s share any keys, values from the right `HeaderMap` be appended to the left `HeaderMap`.
         307  +
pub 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  +
        // For each yielded item that has None provided for the `HeaderName`,
         314  +
        // then the associated header name is the same as that of the previously
         315  +
        // yielded item. The first yielded item will have `HeaderName` set.
         316  +
        // https://docs.rs/http/latest/http/header/struct.HeaderMap.html#method.into_iter-2
         317  +
        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  +
         332  +
#[cfg(test)]
         333  +
mod test {
         334  +
    use super::quote_header_value;
         335  +
    use crate::header::{
         336  +
        append_merge_header_maps, headers_for_prefix, many_dates, read_many_from_str,
         337  +
        read_many_primitive, set_request_header_if_absent, set_response_header_if_absent,
         338  +
        ParseError,
         339  +
    };
         340  +
    use aws_smithy_runtime_api::http::Request;
         341  +
    use aws_smithy_types::error::display::DisplayErrorContext;
         342  +
    use aws_smithy_types::{date_time::Format, DateTime};
         343  +
    use http_02x::header::{HeaderMap, HeaderName, HeaderValue};
         344  +
    use std::collections::HashMap;
         345  +
         346  +
    #[test]
         347  +
    fn put_on_request_if_absent() {
         348  +
        let builder = http_02x::Request::builder().header("foo", "bar");
         349  +
        let builder = set_request_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
         350  +
        let builder =
         351  +
            set_request_header_if_absent(builder, HeaderName::from_static("other"), "value");
         352  +
        let req = builder.body(()).expect("valid request");
         353  +
        assert_eq!(
         354  +
            req.headers().get_all("foo").iter().collect::<Vec<_>>(),
         355  +
            vec!["bar"]
         356  +
        );
         357  +
        assert_eq!(
         358  +
            req.headers().get_all("other").iter().collect::<Vec<_>>(),
         359  +
            vec!["value"]
         360  +
        );
         361  +
    }
         362  +
         363  +
    #[test]
         364  +
    fn put_on_response_if_absent() {
         365  +
        let builder = http_02x::Response::builder().header("foo", "bar");
         366  +
        let builder = set_response_header_if_absent(builder, HeaderName::from_static("foo"), "baz");
         367  +
        let builder =
         368  +
            set_response_header_if_absent(builder, HeaderName::from_static("other"), "value");
         369  +
        let response = builder.body(()).expect("valid response");
         370  +
        assert_eq!(
         371  +
            response.headers().get_all("foo").iter().collect::<Vec<_>>(),
         372  +
            vec!["bar"]
         373  +
        );
         374  +
        assert_eq!(
         375  +
            response
         376  +
                .headers()
         377  +
                .get_all("other")
         378  +
                .iter()
         379  +
                .collect::<Vec<_>>(),
         380  +
            vec!["value"]
         381  +
        );
         382  +
    }
         383  +
         384  +
    #[test]
         385  +
    fn parse_floats() {
         386  +
        let test_request = http_02x::Request::builder()
         387  +
            .header("X-Float-Multi", "0.0,Infinity,-Infinity,5555.5")
         388  +
            .header("X-Float-Error", "notafloat")
         389  +
            .body(())
         390  +
            .unwrap();
         391  +
        assert_eq!(
         392  +
            read_many_primitive::<f32>(
         393  +
                test_request
         394  +
                    .headers()
         395  +
                    .get_all("X-Float-Multi")
         396  +
                    .iter()
         397  +
                    .map(|v| v.to_str().unwrap())
         398  +
            )
         399  +
            .expect("valid"),
         400  +
            vec![0.0, f32::INFINITY, f32::NEG_INFINITY, 5555.5]
         401  +
        );
         402  +
        let message = format!(
         403  +
            "{}",
         404  +
            DisplayErrorContext(
         405  +
                read_many_primitive::<f32>(
         406  +
                    test_request
         407  +
                        .headers()
         408  +
                        .get_all("X-Float-Error")
         409  +
                        .iter()
         410  +
                        .map(|v| v.to_str().unwrap())
         411  +
                )
         412  +
                .expect_err("invalid")
         413  +
            )
         414  +
        );
         415  +
        let expected = "output failed to parse in headers: failed reading a list of primitives: failed to parse input as f32";
         416  +
        assert!(
         417  +
            message.starts_with(expected),
         418  +
            "expected '{message}' to start with '{expected}'"
         419  +
        );
         420  +
    }
         421  +
         422  +
    #[test]
         423  +
    fn test_many_dates() {
         424  +
        let test_request = http_02x::Request::builder()
         425  +
            .header("Empty", "")
         426  +
            .header("SingleHttpDate", "Wed, 21 Oct 2015 07:28:00 GMT")
         427  +
            .header(
         428  +
                "MultipleHttpDates",
         429  +
                "Wed, 21 Oct 2015 07:28:00 GMT,Thu, 22 Oct 2015 07:28:00 GMT",
         430  +
            )
         431  +
            .header("SingleEpochSeconds", "1234.5678")
         432  +
            .header("MultipleEpochSeconds", "1234.5678,9012.3456")
         433  +
            .body(())
         434  +
            .unwrap();
         435  +
        let read = |name: &str, format: Format| {
         436  +
            many_dates(
         437  +
                test_request
         438  +
                    .headers()
         439  +
                    .get_all(name)
         440  +
                    .iter()
         441  +
                    .map(|v| v.to_str().unwrap()),
         442  +
                format,
         443  +
            )
         444  +
        };
         445  +
        let read_valid = |name: &str, format: Format| read(name, format).expect("valid");
         446  +
        assert_eq!(
         447  +
            read_valid("Empty", Format::DateTime),
         448  +
            Vec::<DateTime>::new()
         449  +
        );
         450  +
        assert_eq!(
         451  +
            read_valid("SingleHttpDate", Format::HttpDate),
         452  +
            vec![DateTime::from_secs_and_nanos(1445412480, 0)]
         453  +
        );
         454  +
        assert_eq!(
         455  +
            read_valid("MultipleHttpDates", Format::HttpDate),
         456  +
            vec![
         457  +
                DateTime::from_secs_and_nanos(1445412480, 0),
         458  +
                DateTime::from_secs_and_nanos(1445498880, 0)
         459  +
            ]
         460  +
        );
         461  +
        assert_eq!(
         462  +
            read_valid("SingleEpochSeconds", Format::EpochSeconds),
         463  +
            vec![DateTime::from_secs_and_nanos(1234, 567_800_000)]
         464  +
        );
         465  +
        assert_eq!(
         466  +
            read_valid("MultipleEpochSeconds", Format::EpochSeconds),
         467  +
            vec![
         468  +
                DateTime::from_secs_and_nanos(1234, 567_800_000),
         469  +
                DateTime::from_secs_and_nanos(9012, 345_600_000)
         470  +
            ]
         471  +
        );
         472  +
    }
         473  +
         474  +
    #[test]
         475  +
    fn read_many_strings() {
         476  +
        let test_request = http_02x::Request::builder()
         477  +
            .header("Empty", "")
         478  +
            .header("Foo", "  foo")
         479  +
            .header("FooTrailing", "foo   ")
         480  +
            .header("FooInQuotes", "\"  foo  \"")
         481  +
            .header("CommaInQuotes", "\"foo,bar\",baz")
         482  +
            .header("CommaInQuotesTrailing", "\"foo,bar\",baz  ")
         483  +
            .header("QuoteInQuotes", "\"foo\\\",bar\",\"\\\"asdf\\\"\",baz")
         484  +
            .header(
         485  +
                "QuoteInQuotesWithSpaces",
         486  +
                "\"foo\\\",bar\", \"\\\"asdf\\\"\", baz",
         487  +
            )
         488  +
            .header("JunkFollowingQuotes", "\"\\\"asdf\\\"\"baz")
         489  +
            .header("EmptyQuotes", "\"\",baz")
         490  +
            .header("EscapedSlashesInQuotes", "foo, \"(foo\\\\bar)\"")
         491  +
            .body(())
         492  +
            .unwrap();
         493  +
        let read = |name: &str| {
         494  +
            read_many_from_str::<String>(
         495  +
                test_request
         496  +
                    .headers()
         497  +
                    .get_all(name)
         498  +
                    .iter()
         499  +
                    .map(|v| v.to_str().unwrap()),
         500  +
            )
         501  +
        };
         502  +
        let read_valid = |name: &str| read(name).expect("valid");
         503  +
        assert_eq!(read_valid("Empty"), Vec::<String>::new());
         504  +
        assert_eq!(read_valid("Foo"), vec!["foo"]);
         505  +
        assert_eq!(read_valid("FooTrailing"), vec!["foo"]);
         506  +
        assert_eq!(read_valid("FooInQuotes"), vec!["  foo  "]);
         507  +
        assert_eq!(read_valid("CommaInQuotes"), vec!["foo,bar", "baz"]);
         508  +
        assert_eq!(read_valid("CommaInQuotesTrailing"), vec!["foo,bar", "baz"]);
         509  +
        assert_eq!(
         510  +
            read_valid("QuoteInQuotes"),
         511  +
            vec!["foo\",bar", "\"asdf\"", "baz"]
         512  +
        );
         513  +
        assert_eq!(
         514  +
            read_valid("QuoteInQuotesWithSpaces"),
         515  +
            vec!["foo\",bar", "\"asdf\"", "baz"]
         516  +
        );
         517  +
        assert!(read("JunkFollowingQuotes").is_err());
         518  +
        assert_eq!(read_valid("EmptyQuotes"), vec!["", "baz"]);
         519  +
        assert_eq!(
         520  +
            read_valid("EscapedSlashesInQuotes"),
         521  +
            vec!["foo", "(foo\\bar)"]
         522  +
        );
         523  +
    }
         524  +
         525  +
    #[test]
         526  +
    fn read_many_bools() {
         527  +
        let test_request = http_02x::Request::builder()
         528  +
            .header("X-Bool-Multi", "true,false")
         529  +
            .header("X-Bool-Multi", "true")
         530  +
            .header("X-Bool", "true")
         531  +
            .header("X-Bool-Invalid", "truth,falsy")
         532  +
            .header("X-Bool-Single", "true,false,true,true")
         533  +
            .header("X-Bool-Quoted", "true,\"false\",true,true")
         534  +
            .body(())
         535  +
            .unwrap();
         536  +
        assert_eq!(
         537  +
            read_many_primitive::<bool>(
         538  +
                test_request
         539  +
                    .headers()
         540  +
                    .get_all("X-Bool-Multi")
         541  +
                    .iter()
         542  +
                    .map(|v| v.to_str().unwrap())
         543  +
            )
         544  +
            .expect("valid"),
         545  +
            vec![true, false, true]
         546  +
        );
         547  +
         548  +
        assert_eq!(
         549  +
            read_many_primitive::<bool>(
         550  +
                test_request
         551  +
                    .headers()
         552  +
                    .get_all("X-Bool")
         553  +
                    .iter()
         554  +
                    .map(|v| v.to_str().unwrap())
         555  +
            )
         556  +
            .unwrap(),
         557  +
            vec![true]
         558  +
        );
         559  +
        assert_eq!(
         560  +
            read_many_primitive::<bool>(
         561  +
                test_request
         562  +
                    .headers()
         563  +
                    .get_all("X-Bool-Single")
         564  +
                    .iter()
         565  +
                    .map(|v| v.to_str().unwrap())
         566  +
            )
         567  +
            .unwrap(),
         568  +
            vec![true, false, true, true]
         569  +
        );
         570  +
        assert_eq!(
         571  +
            read_many_primitive::<bool>(
         572  +
                test_request
         573  +
                    .headers()
         574  +
                    .get_all("X-Bool-Quoted")
         575  +
                    .iter()
         576  +
                    .map(|v| v.to_str().unwrap())
         577  +
            )
         578  +
            .unwrap(),
         579  +
            vec![true, false, true, true]
         580  +
        );
         581  +
        read_many_primitive::<bool>(
         582  +
            test_request
         583  +
                .headers()
         584  +
                .get_all("X-Bool-Invalid")
         585  +
                .iter()
         586  +
                .map(|v| v.to_str().unwrap()),
         587  +
        )
         588  +
        .expect_err("invalid");
         589  +
    }
         590  +
         591  +
    #[test]
         592  +
    fn check_read_many_i16() {
         593  +
        let test_request = http_02x::Request::builder()
         594  +
            .header("X-Multi", "123,456")
         595  +
            .header("X-Multi", "789")
         596  +
            .header("X-Num", "777")
         597  +
            .header("X-Num-Invalid", "12ef3")
         598  +
            .header("X-Num-Single", "1,2,3,-4,5")
         599  +
            .header("X-Num-Quoted", "1, \"2\",3,\"-4\",5")
         600  +
            .body(())
         601  +
            .unwrap();
         602  +
        assert_eq!(
         603  +
            read_many_primitive::<i16>(
         604  +
                test_request
         605  +
                    .headers()
         606  +
                    .get_all("X-Multi")
         607  +
                    .iter()
         608  +
                    .map(|v| v.to_str().unwrap())
         609  +
            )
         610  +
            .expect("valid"),
         611  +
            vec![123, 456, 789]
         612  +
        );
         613  +
         614  +
        assert_eq!(
         615  +
            read_many_primitive::<i16>(
         616  +
                test_request
         617  +
                    .headers()
         618  +
                    .get_all("X-Num")
         619  +
                    .iter()
         620  +
                    .map(|v| v.to_str().unwrap())
         621  +
            )
         622  +
            .unwrap(),
         623  +
            vec![777]
         624  +
        );
         625  +
        assert_eq!(
         626  +
            read_many_primitive::<i16>(
         627  +
                test_request
         628  +
                    .headers()
         629  +
                    .get_all("X-Num-Single")
         630  +
                    .iter()
         631  +
                    .map(|v| v.to_str().unwrap())
         632  +
            )
         633  +
            .unwrap(),
         634  +
            vec![1, 2, 3, -4, 5]
         635  +
        );
         636  +
        assert_eq!(
         637  +
            read_many_primitive::<i16>(
         638  +
                test_request
         639  +
                    .headers()
         640  +
                    .get_all("X-Num-Quoted")
         641  +
                    .iter()
         642  +
                    .map(|v| v.to_str().unwrap())
         643  +
            )
         644  +
            .unwrap(),
         645  +
            vec![1, 2, 3, -4, 5]
         646  +
        );
         647  +
        read_many_primitive::<i16>(
         648  +
            test_request
         649  +
                .headers()
         650  +
                .get_all("X-Num-Invalid")
         651  +
                .iter()
         652  +
                .map(|v| v.to_str().unwrap()),
         653  +
        )
         654  +
        .expect_err("invalid");
         655  +
    }
         656  +
         657  +
    #[test]
         658  +
    fn test_prefix_headers() {
         659  +
        let test_request = Request::try_from(
         660  +
            http_02x::Request::builder()
         661  +
                .header("X-Prefix-A", "123,456")
         662  +
                .header("X-Prefix-B", "789")
         663  +
                .header("X-Prefix-C", "777")
         664  +
                .header("X-Prefix-C", "777")
         665  +
                .body(())
         666  +
                .unwrap(),
         667  +
        )
         668  +
        .unwrap();
         669  +
        let resp: Result<HashMap<String, Vec<i16>>, ParseError> =
         670  +
            headers_for_prefix(test_request.headers().iter().map(|h| h.0), "X-Prefix-")
         671  +
                .map(|(key, header_name)| {
         672  +
                    let values = test_request.headers().get_all(header_name);
         673  +
                    read_many_primitive(values).map(|v| (key.to_string(), v))
         674  +
                })
         675  +
                .collect();
         676  +
        let resp = resp.expect("valid");
         677  +
        assert_eq!(resp.get("a"), Some(&vec![123_i16, 456_i16]));
         678  +
    }
         679  +
         680  +
    #[test]
         681  +
    fn test_quote_header_value() {
         682  +
        assert_eq!("", &quote_header_value(""));
         683  +
        assert_eq!("foo", &quote_header_value("foo"));
         684  +
        assert_eq!("\"  foo\"", &quote_header_value("  foo"));
         685  +
        assert_eq!("foo bar", &quote_header_value("foo bar"));
         686  +
        assert_eq!("\"foo,bar\"", &quote_header_value("foo,bar"));
         687  +
        assert_eq!("\",\"", &quote_header_value(","));
         688  +
        assert_eq!("\"\\\"foo\\\"\"", &quote_header_value("\"foo\""));
         689  +
        assert_eq!("\"\\\"f\\\\oo\\\"\"", &quote_header_value("\"f\\oo\""));
         690  +
        assert_eq!("\"(\"", &quote_header_value("("));
         691  +
        assert_eq!("\")\"", &quote_header_value(")"));
         692  +
    }
         693  +
         694  +
    #[test]
         695  +
    fn test_append_merge_header_maps_with_shared_key() {
         696  +
        let header_name = HeaderName::from_static("some_key");
         697  +
        let left_header_value = HeaderValue::from_static("lhs value");
         698  +
        let right_header_value = HeaderValue::from_static("rhs value");
         699  +
         700  +
        let mut left_hand_side_headers = HeaderMap::new();
         701  +
        left_hand_side_headers.insert(header_name.clone(), left_header_value.clone());
         702  +
         703  +
        let mut right_hand_side_headers = HeaderMap::new();
         704  +
        right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
         705  +
         706  +
        let merged_header_map =
         707  +
            append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
         708  +
        let actual_merged_values: Vec<_> =
         709  +
            merged_header_map.get_all(header_name).into_iter().collect();
         710  +
         711  +
        let expected_merged_values = vec![left_header_value, right_header_value];
         712  +
         713  +
        assert_eq!(actual_merged_values, expected_merged_values);
         714  +
    }
         715  +
         716  +
    #[test]
         717  +
    fn test_append_merge_header_maps_with_multiple_values_in_left_hand_map() {
         718  +
        let header_name = HeaderName::from_static("some_key");
         719  +
        let left_header_value_1 = HeaderValue::from_static("lhs value 1");
         720  +
        let left_header_value_2 = HeaderValue::from_static("lhs_value 2");
         721  +
        let right_header_value = HeaderValue::from_static("rhs value");
         722  +
         723  +
        let mut left_hand_side_headers = HeaderMap::new();
         724  +
        left_hand_side_headers.insert(header_name.clone(), left_header_value_1.clone());
         725  +
        left_hand_side_headers.append(header_name.clone(), left_header_value_2.clone());
         726  +
         727  +
        let mut right_hand_side_headers = HeaderMap::new();
         728  +
        right_hand_side_headers.insert(header_name.clone(), right_header_value.clone());
         729  +
         730  +
        let merged_header_map =
         731  +
            append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
         732  +
        let actual_merged_values: Vec<_> =
         733  +
            merged_header_map.get_all(header_name).into_iter().collect();
         734  +
         735  +
        let expected_merged_values =
         736  +
            vec![left_header_value_1, left_header_value_2, right_header_value];
         737  +
         738  +
        assert_eq!(actual_merged_values, expected_merged_values);
         739  +
    }
         740  +
         741  +
    #[test]
         742  +
    fn test_append_merge_header_maps_with_empty_left_hand_map() {
         743  +
        let header_name = HeaderName::from_static("some_key");
         744  +
        let right_header_value_1 = HeaderValue::from_static("rhs value 1");
         745  +
        let right_header_value_2 = HeaderValue::from_static("rhs_value 2");
         746  +
         747  +
        let left_hand_side_headers = HeaderMap::new();
         748  +
         749  +
        let mut right_hand_side_headers = HeaderMap::new();
         750  +
        right_hand_side_headers.insert(header_name.clone(), right_header_value_1.clone());
         751  +
        right_hand_side_headers.append(header_name.clone(), right_header_value_2.clone());
         752  +
         753  +
        let merged_header_map =
         754  +
            append_merge_header_maps(left_hand_side_headers, right_hand_side_headers);
         755  +
        let actual_merged_values: Vec<_> =
         756  +
            merged_header_map.get_all(header_name).into_iter().collect();
         757  +
         758  +
        let expected_merged_values = vec![right_header_value_1, right_header_value_2];
         759  +
         760  +
        assert_eq!(actual_merged_values, expected_merged_values);
         761  +
    }
         762  +
}