AWS SDK

AWS SDK

rev. 216923451858ade2005582a17756783995eedc29

Files changed:

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

@@ -1,0 +377,0 @@
    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

@@ -1,0 +62,0 @@
    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

@@ -1,0 +762,0 @@
    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         -
}