AWS SDK

AWS SDK

rev. c4f9295a7b4566dca79c361e3a2aa9e63cdf82e7

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/dns.rs

@@ -0,1 +0,40 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
use aws_smithy_runtime_api::client::dns::ResolveDns;
           6  +
use hyper_util::client::legacy::connect::dns::Name;
           7  +
use std::error::Error;
           8  +
use std::future::Future;
           9  +
use std::net::SocketAddr;
          10  +
use std::pin::Pin;
          11  +
use std::task::{Context, Poll};
          12  +
use std::vec;
          13  +
          14  +
/// A bridge that allows our `ResolveDns` trait to work with Hyper's `Resolver` interface (based on tower)
          15  +
#[derive(Clone)]
          16  +
pub(crate) struct HyperUtilResolver<R> {
          17  +
    pub(crate) resolver: R,
          18  +
}
          19  +
          20  +
impl<R: ResolveDns + Clone + 'static> tower::Service<Name> for HyperUtilResolver<R> {
          21  +
    type Response = vec::IntoIter<SocketAddr>;
          22  +
    type Error = Box<dyn Error + Send + Sync>;
          23  +
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
          24  +
          25  +
    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
          26  +
        Poll::Ready(Ok(()))
          27  +
    }
          28  +
          29  +
    fn call(&mut self, req: Name) -> Self::Future {
          30  +
        let resolver = self.resolver.clone();
          31  +
        Box::pin(async move {
          32  +
            let dns_entries = resolver.resolve_dns(req.as_str()).await?;
          33  +
            Ok(dns_entries
          34  +
                .into_iter()
          35  +
                .map(|ip_addr| SocketAddr::new(ip_addr, 0))
          36  +
                .collect::<Vec<_>>()
          37  +
                .into_iter())
          38  +
        })
          39  +
    }
          40  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/timeout.rs

@@ -0,1 +0,316 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
use std::error::Error;
           7  +
use std::fmt::Formatter;
           8  +
use std::future::Future;
           9  +
use std::pin::Pin;
          10  +
use std::task::{Context, Poll};
          11  +
use std::time::Duration;
          12  +
          13  +
use http_1x::Uri;
          14  +
use pin_project_lite::pin_project;
          15  +
          16  +
use aws_smithy_async::future::timeout::{TimedOutError, Timeout};
          17  +
use aws_smithy_async::rt::sleep::Sleep;
          18  +
use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep};
          19  +
use aws_smithy_runtime_api::box_error::BoxError;
          20  +
          21  +
#[derive(Debug)]
          22  +
pub(crate) struct HttpTimeoutError {
          23  +
    kind: &'static str,
          24  +
    duration: Duration,
          25  +
}
          26  +
          27  +
impl std::fmt::Display for HttpTimeoutError {
          28  +
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
          29  +
        write!(
          30  +
            f,
          31  +
            "{} timeout occurred after {:?}",
          32  +
            self.kind, self.duration
          33  +
        )
          34  +
    }
          35  +
}
          36  +
          37  +
impl Error for HttpTimeoutError {
          38  +
    // We implement the `source` function as returning a `TimedOutError` because when `downcast_error`
          39  +
    // or `find_source` is called with an `HttpTimeoutError` (or another error wrapping an `HttpTimeoutError`)
          40  +
    // this method will be checked to determine if it's a timeout-related error.
          41  +
    fn source(&self) -> Option<&(dyn Error + 'static)> {
          42  +
        Some(&TimedOutError)
          43  +
    }
          44  +
}
          45  +
          46  +
/// Timeout wrapper that will timeout on the initial TCP connection
          47  +
///
          48  +
/// # Stability
          49  +
/// This interface is unstable.
          50  +
#[derive(Clone, Debug)]
          51  +
pub(crate) struct ConnectTimeout<I> {
          52  +
    inner: I,
          53  +
    timeout: Option<(SharedAsyncSleep, Duration)>,
          54  +
}
          55  +
          56  +
impl<I> ConnectTimeout<I> {
          57  +
    /// Create a new `ConnectTimeout` around `inner`.
          58  +
    ///
          59  +
    /// Typically, `I` will implement [`hyper_util::client::legacy::connect::Connect`].
          60  +
    pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self {
          61  +
        Self {
          62  +
            inner,
          63  +
            timeout: Some((sleep, timeout)),
          64  +
        }
          65  +
    }
          66  +
          67  +
    pub(crate) fn no_timeout(inner: I) -> Self {
          68  +
        Self {
          69  +
            inner,
          70  +
            timeout: None,
          71  +
        }
          72  +
    }
          73  +
}
          74  +
          75  +
#[derive(Clone, Debug)]
          76  +
pub(crate) struct HttpReadTimeout<I> {
          77  +
    inner: I,
          78  +
    timeout: Option<(SharedAsyncSleep, Duration)>,
          79  +
}
          80  +
          81  +
impl<I> HttpReadTimeout<I> {
          82  +
    /// Create a new `HttpReadTimeout` around `inner`.
          83  +
    ///
          84  +
    /// Typically, `I` will implement [`tower::Service<http::Request<SdkBody>>`].
          85  +
    pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self {
          86  +
        Self {
          87  +
            inner,
          88  +
            timeout: Some((sleep, timeout)),
          89  +
        }
          90  +
    }
          91  +
          92  +
    pub(crate) fn no_timeout(inner: I) -> Self {
          93  +
        Self {
          94  +
            inner,
          95  +
            timeout: None,
          96  +
        }
          97  +
    }
          98  +
}
          99  +
         100  +
pin_project! {
         101  +
    /// Timeout future for Tower services
         102  +
    ///
         103  +
    /// Timeout future to handle timing out, mapping errors, and the possibility of not timing out
         104  +
    /// without incurring an additional allocation for each timeout layer.
         105  +
    #[project = MaybeTimeoutFutureProj]
         106  +
    pub enum MaybeTimeoutFuture<F> {
         107  +
        Timeout {
         108  +
            #[pin]
         109  +
            timeout: Timeout<F, Sleep>,
         110  +
            error_type: &'static str,
         111  +
            duration: Duration,
         112  +
        },
         113  +
        NoTimeout {
         114  +
            #[pin]
         115  +
            future: F
         116  +
        }
         117  +
    }
         118  +
}
         119  +
         120  +
impl<F, T, E> Future for MaybeTimeoutFuture<F>
         121  +
where
         122  +
    F: Future<Output = Result<T, E>>,
         123  +
    E: Into<BoxError>,
         124  +
{
         125  +
    type Output = Result<T, BoxError>;
         126  +
         127  +
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
         128  +
        let (timeout_future, kind, &mut duration) = match self.project() {
         129  +
            MaybeTimeoutFutureProj::NoTimeout { future } => {
         130  +
                return future.poll(cx).map_err(|err| err.into());
         131  +
            }
         132  +
            MaybeTimeoutFutureProj::Timeout {
         133  +
                timeout,
         134  +
                error_type,
         135  +
                duration,
         136  +
            } => (timeout, error_type, duration),
         137  +
        };
         138  +
        match timeout_future.poll(cx) {
         139  +
            Poll::Ready(Ok(response)) => Poll::Ready(response.map_err(|err| err.into())),
         140  +
            Poll::Ready(Err(_timeout)) => {
         141  +
                Poll::Ready(Err(HttpTimeoutError { kind, duration }.into()))
         142  +
            }
         143  +
            Poll::Pending => Poll::Pending,
         144  +
        }
         145  +
    }
         146  +
}
         147  +
         148  +
impl<I> tower::Service<Uri> for ConnectTimeout<I>
         149  +
where
         150  +
    I: tower::Service<Uri>,
         151  +
    I::Error: Into<BoxError>,
         152  +
{
         153  +
    type Response = I::Response;
         154  +
    type Error = BoxError;
         155  +
    type Future = MaybeTimeoutFuture<I::Future>;
         156  +
         157  +
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
         158  +
        self.inner.poll_ready(cx).map_err(|err| err.into())
         159  +
    }
         160  +
         161  +
    fn call(&mut self, req: Uri) -> Self::Future {
         162  +
        match &self.timeout {
         163  +
            Some((sleep, duration)) => {
         164  +
                let sleep = sleep.sleep(*duration);
         165  +
                MaybeTimeoutFuture::Timeout {
         166  +
                    timeout: Timeout::new(self.inner.call(req), sleep),
         167  +
                    error_type: "HTTP connect",
         168  +
                    duration: *duration,
         169  +
                }
         170  +
            }
         171  +
            None => MaybeTimeoutFuture::NoTimeout {
         172  +
                future: self.inner.call(req),
         173  +
            },
         174  +
        }
         175  +
    }
         176  +
}
         177  +
         178  +
impl<I, B> tower::Service<http_1x::Request<B>> for HttpReadTimeout<I>
         179  +
where
         180  +
    I: tower::Service<http_1x::Request<B>>,
         181  +
    I::Error: Send + Sync + Error + 'static,
         182  +
{
         183  +
    type Response = I::Response;
         184  +
    type Error = BoxError;
         185  +
    type Future = MaybeTimeoutFuture<I::Future>;
         186  +
         187  +
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
         188  +
        self.inner.poll_ready(cx).map_err(|err| err.into())
         189  +
    }
         190  +
         191  +
    fn call(&mut self, req: http_1x::Request<B>) -> Self::Future {
         192  +
        match &self.timeout {
         193  +
            Some((sleep, duration)) => {
         194  +
                let sleep = sleep.sleep(*duration);
         195  +
                MaybeTimeoutFuture::Timeout {
         196  +
                    timeout: Timeout::new(self.inner.call(req), sleep),
         197  +
                    error_type: "HTTP read",
         198  +
                    duration: *duration,
         199  +
                }
         200  +
            }
         201  +
            None => MaybeTimeoutFuture::NoTimeout {
         202  +
                future: self.inner.call(req),
         203  +
            },
         204  +
        }
         205  +
    }
         206  +
}
         207  +
         208  +
#[cfg(test)]
         209  +
pub(crate) mod test {
         210  +
    use hyper::rt::ReadBufCursor;
         211  +
    use hyper_util::client::legacy::connect::{Connected, Connection};
         212  +
    use hyper_util::rt::TokioIo;
         213  +
    use tokio::net::TcpStream;
         214  +
         215  +
    use aws_smithy_async::future::never::Never;
         216  +
         217  +
    use aws_smithy_runtime_api::box_error::BoxError;
         218  +
    use aws_smithy_runtime_api::client::result::ConnectorError;
         219  +
    use http::Uri;
         220  +
    use hyper::http;
         221  +
    use hyper::rt::{Read, Write};
         222  +
    use std::future::Future;
         223  +
    use std::pin::Pin;
         224  +
    use std::task::{Context, Poll};
         225  +
         226  +
    #[allow(unused)]
         227  +
    fn connect_timeout_is_correct<T: Send + Sync + Clone + 'static>() {
         228  +
        is_send_sync::<super::ConnectTimeout<T>>();
         229  +
    }
         230  +
         231  +
    #[allow(unused)]
         232  +
    fn is_send_sync<T: Send + Sync>() {}
         233  +
         234  +
    /// A service that will never return whatever it is you want
         235  +
    ///
         236  +
    /// Returned futures will return Pending forever
         237  +
    #[non_exhaustive]
         238  +
    #[derive(Clone, Default, Debug)]
         239  +
    pub(crate) struct NeverConnects;
         240  +
    impl tower::Service<Uri> for NeverConnects {
         241  +
        type Response = TokioIo<TcpStream>;
         242  +
        type Error = ConnectorError;
         243  +
        type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
         244  +
         245  +
        fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
         246  +
            Poll::Ready(Ok(()))
         247  +
        }
         248  +
         249  +
        fn call(&mut self, _uri: Uri) -> Self::Future {
         250  +
            Box::pin(async move {
         251  +
                Never::new().await;
         252  +
                unreachable!()
         253  +
            })
         254  +
        }
         255  +
    }
         256  +
         257  +
    /// A service that will connect but never send any data
         258  +
    #[derive(Clone, Debug, Default)]
         259  +
    pub(crate) struct NeverReplies;
         260  +
    impl tower::Service<Uri> for NeverReplies {
         261  +
        type Response = EmptyStream;
         262  +
        type Error = BoxError;
         263  +
        type Future = std::future::Ready<Result<Self::Response, Self::Error>>;
         264  +
         265  +
        fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
         266  +
            Poll::Ready(Ok(()))
         267  +
        }
         268  +
         269  +
        fn call(&mut self, _req: Uri) -> Self::Future {
         270  +
            std::future::ready(Ok(EmptyStream))
         271  +
        }
         272  +
    }
         273  +
         274  +
    /// A stream that will never return or accept any data
         275  +
    #[non_exhaustive]
         276  +
    #[derive(Debug, Default)]
         277  +
    pub(crate) struct EmptyStream;
         278  +
    impl Read for EmptyStream {
         279  +
        fn poll_read(
         280  +
            self: Pin<&mut Self>,
         281  +
            _cx: &mut Context<'_>,
         282  +
            _buf: ReadBufCursor<'_>,
         283  +
        ) -> Poll<Result<(), std::io::Error>> {
         284  +
            Poll::Pending
         285  +
        }
         286  +
    }
         287  +
    impl Write for EmptyStream {
         288  +
        fn poll_write(
         289  +
            self: Pin<&mut Self>,
         290  +
            _cx: &mut Context<'_>,
         291  +
            _buf: &[u8],
         292  +
        ) -> Poll<Result<usize, std::io::Error>> {
         293  +
            Poll::Pending
         294  +
        }
         295  +
         296  +
        fn poll_flush(
         297  +
            self: Pin<&mut Self>,
         298  +
            _cx: &mut Context<'_>,
         299  +
        ) -> Poll<Result<(), std::io::Error>> {
         300  +
            Poll::Pending
         301  +
        }
         302  +
         303  +
        fn poll_shutdown(
         304  +
            self: Pin<&mut Self>,
         305  +
            _cx: &mut Context<'_>,
         306  +
        ) -> Poll<Result<(), std::io::Error>> {
         307  +
            Poll::Pending
         308  +
        }
         309  +
    }
         310  +
         311  +
    impl Connection for EmptyStream {
         312  +
        fn connected(&self) -> Connected {
         313  +
            Connected::new()
         314  +
        }
         315  +
    }
         316  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/client/tls.rs

@@ -0,1 +0,354 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
use crate::cfg::{cfg_rustls, cfg_s2n_tls};
           6  +
use crate::HttpClientError;
           7  +
           8  +
/// Choice of underlying cryptography library
           9  +
#[derive(Debug, Eq, PartialEq, Clone)]
          10  +
#[non_exhaustive]
          11  +
pub enum Provider {
          12  +
    #[cfg(any(
          13  +
        feature = "rustls-aws-lc",
          14  +
        feature = "rustls-aws-lc-fips",
          15  +
        feature = "rustls-ring"
          16  +
    ))]
          17  +
    /// TLS provider based on [rustls](https://github.com/rustls/rustls)
          18  +
    Rustls(rustls_provider::CryptoMode),
          19  +
    /// TLS provider based on [s2n-tls](https://github.com/aws/s2n-tls)
          20  +
    #[cfg(feature = "s2n-tls")]
          21  +
    S2nTls,
          22  +
}
          23  +
          24  +
/// TLS related configuration object
          25  +
#[derive(Debug, Clone)]
          26  +
pub struct TlsContext {
          27  +
    #[allow(unused)]
          28  +
    trust_store: TrustStore,
          29  +
}
          30  +
          31  +
impl TlsContext {
          32  +
    /// Create a new [TlsContext] builder
          33  +
    pub fn builder() -> TlsContextBuilder {
          34  +
        TlsContextBuilder::new()
          35  +
    }
          36  +
}
          37  +
          38  +
impl Default for TlsContext {
          39  +
    fn default() -> Self {
          40  +
        TlsContext::builder().build().expect("valid default config")
          41  +
    }
          42  +
}
          43  +
          44  +
/// Builder for TLS related configuration
          45  +
#[derive(Debug)]
          46  +
pub struct TlsContextBuilder {
          47  +
    trust_store: TrustStore,
          48  +
}
          49  +
          50  +
impl TlsContextBuilder {
          51  +
    fn new() -> Self {
          52  +
        TlsContextBuilder {
          53  +
            trust_store: TrustStore::default(),
          54  +
        }
          55  +
    }
          56  +
          57  +
    /// Configure the trust store to use for the TLS context
          58  +
    pub fn with_trust_store(mut self, trust_store: TrustStore) -> Self {
          59  +
        self.trust_store = trust_store;
          60  +
        self
          61  +
    }
          62  +
          63  +
    /// Build a new [TlsContext]
          64  +
    pub fn build(self) -> Result<TlsContext, HttpClientError> {
          65  +
        Ok(TlsContext {
          66  +
            trust_store: self.trust_store,
          67  +
        })
          68  +
    }
          69  +
}
          70  +
          71  +
/// PEM encoded certificate
          72  +
#[allow(unused)]
          73  +
#[derive(Debug, Clone)]
          74  +
struct CertificatePEM(Vec<u8>);
          75  +
          76  +
impl From<&[u8]> for CertificatePEM {
          77  +
    fn from(value: &[u8]) -> Self {
          78  +
        CertificatePEM(value.to_vec())
          79  +
    }
          80  +
}
          81  +
          82  +
/// Container for root certificates able to provide a root-of-trust for connection authentication
          83  +
///
          84  +
/// Platform native root certificates are enabled by default. To start with a clean trust
          85  +
/// store use [TrustStore::empty]
          86  +
#[derive(Debug, Clone)]
          87  +
pub struct TrustStore {
          88  +
    enable_native_roots: bool,
          89  +
    custom_certs: Vec<CertificatePEM>,
          90  +
}
          91  +
          92  +
impl TrustStore {
          93  +
    /// Create a new empty trust store
          94  +
    pub fn empty() -> Self {
          95  +
        Self {
          96  +
            enable_native_roots: false,
          97  +
            custom_certs: Vec::new(),
          98  +
        }
          99  +
    }
         100  +
         101  +
    /// Enable or disable using the platform's native trusted root certificate store
         102  +
    ///
         103  +
    /// Default: true
         104  +
    pub fn with_native_roots(mut self, enable_native_roots: bool) -> Self {
         105  +
        self.enable_native_roots = enable_native_roots;
         106  +
        self
         107  +
    }
         108  +
         109  +
    /// Add the PEM encoded certificate to the trust store
         110  +
    ///
         111  +
    /// This may be called more than once to add multiple certificates.
         112  +
    /// NOTE: PEM certificate contents are not validated until passed to the configured
         113  +
    /// TLS provider.
         114  +
    pub fn with_pem_certificate(mut self, pem_bytes: impl Into<Vec<u8>>) -> Self {
         115  +
        // ideally we'd validate here but rustls-pki-types converts to DER when loading and S2N
         116  +
        // still expects PEM encoding. Store the raw bytes and let the TLS implementation validate
         117  +
        self.custom_certs.push(CertificatePEM(pem_bytes.into()));
         118  +
        self
         119  +
    }
         120  +
         121  +
    /// Add the PEM encoded certificate to the trust store
         122  +
    ///
         123  +
    /// This may be called more than once to add multiple certificates.
         124  +
    /// NOTE: PEM certificate contents are not validated until passed to the configured
         125  +
    /// TLS provider.
         126  +
    pub fn add_pem_certificate(&mut self, pem_bytes: impl Into<Vec<u8>>) -> &mut Self {
         127  +
        self.custom_certs.push(CertificatePEM(pem_bytes.into()));
         128  +
        self
         129  +
    }
         130  +
}
         131  +
         132  +
impl Default for TrustStore {
         133  +
    fn default() -> Self {
         134  +
        Self {
         135  +
            enable_native_roots: true,
         136  +
            custom_certs: Vec::new(),
         137  +
        }
         138  +
    }
         139  +
}
         140  +
         141  +
cfg_rustls! {
         142  +
    /// rustls based support and adapters
         143  +
    pub mod rustls_provider {
         144  +
        use crate::client::tls::Provider;
         145  +
        use rustls::crypto::CryptoProvider;
         146  +
         147  +
        /// Choice of underlying cryptography library (this only applies to rustls)
         148  +
        #[derive(Debug, Eq, PartialEq, Clone)]
         149  +
        #[non_exhaustive]
         150  +
        pub enum CryptoMode {
         151  +
            /// Crypto based on [ring](https://github.com/briansmith/ring)
         152  +
            #[cfg(feature = "rustls-ring")]
         153  +
            Ring,
         154  +
            /// Crypto based on [aws-lc](https://github.com/aws/aws-lc-rs)
         155  +
            #[cfg(feature = "rustls-aws-lc")]
         156  +
            AwsLc,
         157  +
            /// FIPS compliant variant of [aws-lc](https://github.com/aws/aws-lc-rs)
         158  +
            #[cfg(feature = "rustls-aws-lc-fips")]
         159  +
            AwsLcFips,
         160  +
        }
         161  +
         162  +
        impl CryptoMode {
         163  +
            fn provider(self) -> CryptoProvider {
         164  +
                match self {
         165  +
                    #[cfg(feature = "rustls-aws-lc")]
         166  +
                    CryptoMode::AwsLc => rustls::crypto::aws_lc_rs::default_provider(),
         167  +
         168  +
                    #[cfg(feature = "rustls-ring")]
         169  +
                    CryptoMode::Ring => rustls::crypto::ring::default_provider(),
         170  +
         171  +
                    #[cfg(feature = "rustls-aws-lc-fips")]
         172  +
                    CryptoMode::AwsLcFips => {
         173  +
                        let provider = rustls::crypto::default_fips_provider();
         174  +
                        assert!(
         175  +
                            provider.fips(),
         176  +
                            "FIPS was requested but the provider did not support FIPS"
         177  +
                        );
         178  +
                        provider
         179  +
                    }
         180  +
                }
         181  +
            }
         182  +
        }
         183  +
         184  +
        impl Provider {
         185  +
            /// Create a TLS provider based on [rustls](https://github.com/rustls/rustls)
         186  +
            /// and the given [`CryptoMode`]
         187  +
            pub fn rustls(mode: CryptoMode) -> Provider {
         188  +
                Provider::Rustls(mode)
         189  +
            }
         190  +
        }
         191  +
         192  +
        pub(crate) mod build_connector {
         193  +
            use crate::client::tls::rustls_provider::CryptoMode;
         194  +
            use crate::tls::TlsContext;
         195  +
            use hyper_util::client::legacy as client;
         196  +
            use client::connect::HttpConnector;
         197  +
            use rustls::crypto::CryptoProvider;
         198  +
            use std::sync::Arc;
         199  +
            use rustls_pki_types::CertificateDer;
         200  +
            use rustls_pki_types::pem::PemObject;
         201  +
            use rustls_native_certs::CertificateResult;
         202  +
            use std::sync::LazyLock;
         203  +
         204  +
            /// Cached native certificates
         205  +
            ///
         206  +
            /// Creating a `with_native_roots()` hyper_rustls client re-loads system certs
         207  +
            /// each invocation (which can take 300ms on OSx). Cache the loaded certs
         208  +
            /// to avoid repeatedly incurring that cost.
         209  +
            pub(crate) static NATIVE_ROOTS: LazyLock<Vec<CertificateDer<'static>>> = LazyLock::new(|| {
         210  +
                let CertificateResult { certs, errors, .. } = rustls_native_certs::load_native_certs();
         211  +
                if !errors.is_empty() {
         212  +
                    tracing::warn!("native root CA certificate loading errors: {errors:?}")
         213  +
                }
         214  +
         215  +
                if certs.is_empty() {
         216  +
                    tracing::warn!("no native root CA certificates found!");
         217  +
                }
         218  +
         219  +
                // NOTE: unlike hyper-rustls::with_native_roots we don't validate here, we'll do that later
         220  +
                // for now we have a collection of certs that may or may not be valid.
         221  +
                certs
         222  +
            });
         223  +
         224  +
            fn restrict_ciphers(base: CryptoProvider) -> CryptoProvider {
         225  +
                let suites = &[
         226  +
                    rustls::CipherSuite::TLS13_AES_256_GCM_SHA384,
         227  +
                    rustls::CipherSuite::TLS13_AES_128_GCM_SHA256,
         228  +
                    // TLS1.2 suites
         229  +
                    rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
         230  +
                    rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
         231  +
                    rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
         232  +
                    rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
         233  +
                    rustls::CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
         234  +
                ];
         235  +
                let supported_suites = suites
         236  +
                    .iter()
         237  +
                    .flat_map(|suite| {
         238  +
                        base.cipher_suites
         239  +
                            .iter()
         240  +
                            .find(|s| &s.suite() == suite)
         241  +
                            .cloned()
         242  +
                    })
         243  +
                    .collect::<Vec<_>>();
         244  +
                CryptoProvider {
         245  +
                    cipher_suites: supported_suites,
         246  +
                    ..base
         247  +
                }
         248  +
            }
         249  +
         250  +
            impl TlsContext {
         251  +
                fn rustls_root_certs(&self) -> rustls::RootCertStore {
         252  +
                    let mut roots = rustls::RootCertStore::empty();
         253  +
                    if self.trust_store.enable_native_roots {
         254  +
                        let (valid, _invalid) = roots.add_parsable_certificates(
         255  +
                           NATIVE_ROOTS.clone()
         256  +
                        );
         257  +
                        debug_assert!(valid > 0, "TrustStore configured to enable native roots but no valid root certificates parsed!");
         258  +
                    }
         259  +
         260  +
                    for pem_cert in &self.trust_store.custom_certs {
         261  +
                        let ders = CertificateDer::pem_slice_iter(&pem_cert.0).collect::<Result<Vec<_>, _> >().expect("valid PEM certificate");
         262  +
                        for cert in ders {
         263  +
                            roots.add(cert).expect("cert parsable")
         264  +
                        }
         265  +
                    }
         266  +
         267  +
                    roots
         268  +
                }
         269  +
            }
         270  +
         271  +
            pub(crate) fn wrap_connector<R>(
         272  +
                mut conn: HttpConnector<R>,
         273  +
                crypto_mode: CryptoMode,
         274  +
                tls_context: &TlsContext,
         275  +
            ) -> hyper_rustls::HttpsConnector<HttpConnector<R>> {
         276  +
                let root_certs = tls_context.rustls_root_certs();
         277  +
                conn.enforce_http(false);
         278  +
                hyper_rustls::HttpsConnectorBuilder::new()
         279  +
                    .with_tls_config(
         280  +
                        rustls::ClientConfig::builder_with_provider(Arc::new(restrict_ciphers(crypto_mode.provider())))
         281  +
                            .with_safe_default_protocol_versions()
         282  +
                            .expect("Error with the TLS configuration. Please file a bug report under https://github.com/smithy-lang/smithy-rs/issues.")
         283  +
                            .with_root_certificates(root_certs)
         284  +
                            .with_no_client_auth()
         285  +
                    )
         286  +
                    .https_or_http()
         287  +
                    .enable_http1()
         288  +
                    .enable_http2()
         289  +
                    .wrap_connector(conn)
         290  +
            }
         291  +
        }
         292  +
    }
         293  +
}
         294  +
         295  +
cfg_s2n_tls! {
         296  +
    /// s2n-tls based support and adapters
         297  +
    pub(crate) mod s2n_tls_provider {
         298  +
        pub(crate) mod build_connector {
         299  +
            use hyper_util::client::legacy as client;
         300  +
            use client::connect::HttpConnector;
         301  +
            use s2n_tls::security::Policy;
         302  +
            use crate::tls::TlsContext;
         303  +
            use std::sync::LazyLock;
         304  +
         305  +
            // Default S2N security policy which sets protocol versions and cipher suites
         306  +
            //  See https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html
         307  +
            const S2N_POLICY_VERSION: &str = "20230317";
         308  +
         309  +
            fn base_config() -> s2n_tls::config::Builder {
         310  +
                let mut builder = s2n_tls::config::Config::builder();
         311  +
                let policy = Policy::from_version(S2N_POLICY_VERSION).unwrap();
         312  +
                builder.set_security_policy(&policy).expect("valid s2n security policy");
         313  +
                // default is true
         314  +
                builder.with_system_certs(false).unwrap();
         315  +
                builder
         316  +
            }
         317  +
         318  +
            static CACHED_CONFIG: LazyLock<s2n_tls::config::Config> = LazyLock::new(|| {
         319  +
                let mut config = base_config();
         320  +
                config.with_system_certs(true).unwrap();
         321  +
                // actually loads the system certs
         322  +
                config.build().expect("valid s2n config")
         323  +
            });
         324  +
         325  +
            impl TlsContext {
         326  +
                fn s2n_config(&self) -> s2n_tls::config::Config {
         327  +
                    // TODO(s2n-tls): s2n does not support turning a config back into a builder or a way to load a trust store and re-use it
         328  +
                    // instead if we are only using the defaults then use a cached config, otherwise pay the cost to build a new one
         329  +
                    if self.trust_store.enable_native_roots && self.trust_store.custom_certs.is_empty() {
         330  +
                        CACHED_CONFIG.clone()
         331  +
                    } else {
         332  +
                        let mut config = base_config();
         333  +
                        config.with_system_certs(self.trust_store.enable_native_roots).unwrap();
         334  +
                        for pem_cert in &self.trust_store.custom_certs {
         335  +
                            config.trust_pem(pem_cert.0.as_slice()).expect("valid certificate");
         336  +
                        }
         337  +
                        config.build().expect("valid s2n config")
         338  +
                    }
         339  +
                }
         340  +
            }
         341  +
         342  +
            pub(crate) fn wrap_connector<R>(
         343  +
                mut http_connector: HttpConnector<R>,
         344  +
                tls_context: &TlsContext,
         345  +
            ) -> s2n_tls_hyper::connector::HttpsConnector<HttpConnector<R>> {
         346  +
                let config = tls_context.s2n_config();
         347  +
                http_connector.enforce_http(false);
         348  +
                let mut builder = s2n_tls_hyper::connector::HttpsConnector::builder_with_http(http_connector, config);
         349  +
                builder.with_plaintext_http(true);
         350  +
                builder.build()
         351  +
            }
         352  +
        }
         353  +
    }
         354  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/error.rs

@@ -0,1 +0,26 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
use aws_smithy_runtime_api::box_error::BoxError;
           6  +
use std::fmt;
           7  +
           8  +
/// HTTP client errors
           9  +
///
          10  +
/// This is normally due to configuration issues, internal SDK bugs, or other user error.
          11  +
#[derive(Debug)]
          12  +
pub struct HttpClientError {
          13  +
    source: Option<BoxError>,
          14  +
}
          15  +
          16  +
impl fmt::Display for HttpClientError {
          17  +
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
          18  +
        write!(f, "unknown HTTP client error")
          19  +
    }
          20  +
}
          21  +
          22  +
impl std::error::Error for HttpClientError {
          23  +
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
          24  +
        self.source.as_ref().map(|err| err.as_ref() as _)
          25  +
    }
          26  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/hyper_legacy.rs

Renamed from tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/src/client/http/hyper_014.rs

@@ -1,1 +151,154 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6         -
use crate::client::http::connection_poisoning::CaptureSmithyConnection;
    7         -
use crate::client::http::hyper_014::timeout_middleware::HttpTimeoutError;
           6  +
use crate::hyper_legacy::timeout_middleware::HttpTimeoutError;
    8      7   
use aws_smithy_async::future::timeout::TimedOutError;
    9      8   
use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep};
   10      9   
use aws_smithy_runtime_api::box_error::BoxError;
          10  +
use aws_smithy_runtime_api::client::connection::CaptureSmithyConnection;
   11     11   
use aws_smithy_runtime_api::client::connection::ConnectionMetadata;
   12     12   
use aws_smithy_runtime_api::client::connector_metadata::ConnectorMetadata;
   13     13   
use aws_smithy_runtime_api::client::http::{
   14     14   
    HttpClient, HttpConnector, HttpConnectorFuture, HttpConnectorSettings, SharedHttpClient,
   15     15   
    SharedHttpConnector,
   16     16   
};
   17     17   
use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse};
   18     18   
use aws_smithy_runtime_api::client::result::ConnectorError;
   19     19   
use aws_smithy_runtime_api::client::runtime_components::{
   20     20   
    RuntimeComponents, RuntimeComponentsBuilder,
   21     21   
};
   22     22   
use aws_smithy_runtime_api::shared::IntoShared;
   23     23   
use aws_smithy_types::body::SdkBody;
   24     24   
use aws_smithy_types::config_bag::ConfigBag;
   25     25   
use aws_smithy_types::error::display::DisplayErrorContext;
   26     26   
use aws_smithy_types::retry::ErrorKind;
   27     27   
use h2::Reason;
   28     28   
use hyper_0_14::client::connect::{capture_connection, CaptureConnection, Connection, HttpInfo};
   29     29   
use std::borrow::Cow;
   30     30   
use std::collections::HashMap;
   31     31   
use std::error::Error;
   32     32   
use std::fmt;
   33     33   
use std::sync::RwLock;
   34     34   
use std::time::Duration;
   35     35   
use tokio::io::{AsyncRead, AsyncWrite};
   36     36   
   37         -
#[cfg(feature = "tls-rustls")]
          37  +
#[cfg(feature = "legacy-rustls-ring")]
   38     38   
mod default_connector {
   39     39   
    use aws_smithy_async::rt::sleep::SharedAsyncSleep;
   40     40   
    use aws_smithy_runtime_api::client::http::HttpConnectorSettings;
          41  +
    use legacy_hyper_rustls as hyper_rustls;
          42  +
    use legacy_rustls as rustls;
          43  +
    use std::sync::LazyLock;
   41     44   
   42     45   
    // Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we
   43     46   
    // don't need to repeatedly incur that cost.
   44         -
    pub(crate) static HTTPS_NATIVE_ROOTS: once_cell::sync::Lazy<
          47  +
    pub(crate) static HTTPS_NATIVE_ROOTS: LazyLock<
   45     48   
        hyper_rustls::HttpsConnector<hyper_0_14::client::HttpConnector>,
   46         -
    > = once_cell::sync::Lazy::new(default_tls);
          49  +
    > = LazyLock::new(default_tls);
   47     50   
   48     51   
    fn default_tls() -> hyper_rustls::HttpsConnector<hyper_0_14::client::HttpConnector> {
   49     52   
        use hyper_rustls::ConfigBuilderExt;
   50     53   
        hyper_rustls::HttpsConnectorBuilder::new()
   51     54   
               .with_tls_config(
   52     55   
                rustls::ClientConfig::builder()
   53     56   
                    .with_cipher_suites(&[
   54     57   
                        // TLS1.3 suites
   55     58   
                        rustls::cipher_suite::TLS13_AES_256_GCM_SHA384,
   56     59   
                        rustls::cipher_suite::TLS13_AES_128_GCM_SHA256,
   57     60   
                        // TLS1.2 suites
   58     61   
                        rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   59     62   
                        rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   60     63   
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   61     64   
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   62     65   
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
   63     66   
                    ])
   64     67   
                    .with_safe_default_kx_groups()
   65     68   
                    .with_safe_default_protocol_versions()
   66     69   
                    .expect("Error with the TLS configuration. Please file a bug report under https://github.com/smithy-lang/smithy-rs/issues.")
   67     70   
                    .with_native_roots()
   68     71   
                    .with_no_client_auth()
   69     72   
            )
   70     73   
            .https_or_http()
   71     74   
            .enable_http1()
   72     75   
            .enable_http2()
   73     76   
            .build()
   74     77   
    }
   75     78   
   76     79   
    pub(super) fn base(
   77     80   
        settings: &HttpConnectorSettings,
   78     81   
        sleep: Option<SharedAsyncSleep>,
   79     82   
    ) -> super::HyperConnectorBuilder {
   80     83   
        let mut hyper = super::HyperConnector::builder().connector_settings(settings.clone());
   81     84   
        if let Some(sleep) = sleep {
   82     85   
            hyper = hyper.sleep_impl(sleep);
   83     86   
        }
   84     87   
        hyper
   85     88   
    }
   86     89   
   87     90   
    /// Return a default HTTPS connector backed by the `rustls` crate.
   88     91   
    ///
   89     92   
    /// It requires a minimum TLS version of 1.2.
   90     93   
    /// It allows you to connect to both `http` and `https` URLs.
   91     94   
    pub(super) fn https() -> hyper_rustls::HttpsConnector<hyper_0_14::client::HttpConnector> {
   92     95   
        HTTPS_NATIVE_ROOTS.clone()
   93     96   
    }
   94     97   
}
   95     98   
   96     99   
/// Given `HttpConnectorSettings` and an `SharedAsyncSleep`, create a `SharedHttpConnector` from defaults depending on what cargo features are activated.
   97    100   
pub fn default_connector(
   98    101   
    settings: &HttpConnectorSettings,
   99    102   
    sleep: Option<SharedAsyncSleep>,
  100    103   
) -> Option<SharedHttpConnector> {
  101         -
    #[cfg(feature = "tls-rustls")]
         104  +
    #[cfg(feature = "legacy-rustls-ring")]
  102    105   
    {
  103    106   
        tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new default connector");
  104    107   
        let hyper = default_connector::base(settings, sleep).build_https();
  105    108   
        Some(SharedHttpConnector::new(hyper))
  106    109   
    }
  107         -
    #[cfg(not(feature = "tls-rustls"))]
         110  +
    #[cfg(not(feature = "legacy-rustls-ring"))]
  108    111   
    {
  109    112   
        tracing::trace!(settings = ?settings, sleep = ?sleep, "no default connector available");
  110    113   
        None
  111    114   
    }
  112    115   
}
  113    116   
  114    117   
/// Creates a hyper-backed HTTPS client from defaults depending on what cargo features are activated.
  115    118   
pub fn default_client() -> Option<SharedHttpClient> {
  116         -
    #[cfg(feature = "tls-rustls")]
         119  +
    #[cfg(feature = "legacy-rustls-ring")]
  117    120   
    {
  118    121   
        tracing::trace!("creating a new default hyper 0.14.x client");
  119    122   
        Some(HyperClientBuilder::new().build_https())
  120    123   
    }
  121         -
    #[cfg(not(feature = "tls-rustls"))]
         124  +
    #[cfg(not(feature = "legacy-rustls-ring"))]
  122    125   
    {
  123    126   
        tracing::trace!("no default connector available");
  124    127   
        None
  125    128   
    }
  126    129   
}
  127    130   
  128    131   
/// [`HttpConnector`] that uses [`hyper_0_14`] to make HTTP requests.
  129    132   
///
  130    133   
/// This connector also implements socket connect and read timeouts.
  131    134   
///
@@ -175,178 +235,238 @@
  195    198   
            None => timeout_middleware::HttpReadTimeout::no_timeout(base),
  196    199   
        };
  197    200   
        HyperConnector {
  198    201   
            adapter: Box::new(Adapter {
  199    202   
                client: read_timeout,
  200    203   
            }),
  201    204   
        }
  202    205   
    }
  203    206   
  204    207   
    /// Create a [`HyperConnector`] with the default rustls HTTPS implementation.
  205         -
    #[cfg(feature = "tls-rustls")]
         208  +
    #[cfg(feature = "legacy-rustls-ring")]
  206    209   
    pub fn build_https(self) -> HyperConnector {
  207    210   
        self.build(default_connector::https())
  208    211   
    }
  209    212   
  210    213   
    /// Set the async sleep implementation used for timeouts
  211    214   
    ///
  212    215   
    /// Calling this is only necessary for testing or to use something other than
  213    216   
    /// [`default_async_sleep`].
  214    217   
    pub fn sleep_impl(mut self, sleep_impl: impl AsyncSleep + 'static) -> Self {
  215    218   
        self.sleep_impl = Some(sleep_impl.into_shared());
@@ -478,481 +621,624 @@
  498    501   
/// This builder can be used to customize the underlying TCP connector used, as well as
  499    502   
/// hyper client configuration.
  500    503   
///
  501    504   
/// # Examples
  502    505   
///
  503    506   
/// Construct a Hyper client with the default TLS implementation (rustls).
  504    507   
/// This can be useful when you want to share a Hyper connector between multiple
  505    508   
/// generated Smithy clients.
  506    509   
///
  507    510   
/// ```no_run,ignore
  508         -
/// use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
         511  +
/// use aws_smithy_http_client::hyper_014::HyperClientBuilder;
  509    512   
///
  510    513   
/// let http_client = HyperClientBuilder::new().build_https();
  511    514   
///
  512    515   
/// // This connector can then be given to a generated service Config
  513    516   
/// let config = my_service_client::Config::builder()
  514    517   
///     .endpoint_url("http://localhost:1234")
  515    518   
///     .http_client(http_client)
  516    519   
///     .build();
  517    520   
/// let client = my_service_client::Client::from_conf(config);
  518    521   
/// ```
  519    522   
///
  520    523   
/// ## Use a Hyper client with WebPKI roots
  521    524   
///
  522    525   
/// A use case for where you may want to use the [`HyperClientBuilder`] is when
  523    526   
/// setting Hyper client settings that aren't otherwise exposed by the `Config`
  524    527   
/// builder interface. Some examples include changing:
  525    528   
///
  526    529   
/// - Hyper client settings
  527    530   
/// - Allowed TLS cipher suites
  528    531   
/// - Using an alternative TLS connector library (not the default, rustls)
  529    532   
/// - CA trust root certificates (illustrated using WebPKI below)
  530    533   
///
  531    534   
/// ```no_run,ignore
  532         -
/// use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
         535  +
/// use aws_smithy_http_client::hyper_014::HyperClientBuilder;
  533    536   
///
  534    537   
/// let https_connector = hyper_rustls::HttpsConnectorBuilder::new()
  535    538   
///     .with_webpki_roots()
  536    539   
///     .https_only()
  537    540   
///     .enable_http1()
  538    541   
///     .enable_http2()
  539    542   
///     .build();
  540    543   
/// let http_client = HyperClientBuilder::new().build(https_connector);
  541    544   
///
  542    545   
/// // This connector can then be given to a generated service Config
  543    546   
/// let config = my_service_client::Config::builder()
  544    547   
///     .endpoint_url("https://example.com")
  545    548   
///     .http_client(http_client)
  546    549   
///     .build();
  547    550   
/// let client = my_service_client::Client::from_conf(config);
  548    551   
/// ```
  549    552   
#[derive(Clone, Default, Debug)]
  550    553   
pub struct HyperClientBuilder {
  551    554   
    client_builder: Option<hyper_0_14::client::Builder>,
  552    555   
}
  553    556   
  554    557   
impl HyperClientBuilder {
  555    558   
    /// Creates a new builder.
  556    559   
    pub fn new() -> Self {
  557    560   
        Self::default()
  558    561   
    }
  559    562   
  560    563   
    /// Override the Hyper client [`Builder`](hyper_0_14::client::Builder) used to construct this client.
  561    564   
    ///
  562    565   
    /// This enables changing settings like forcing HTTP2 and modifying other default client behavior.
  563    566   
    pub fn hyper_builder(mut self, hyper_builder: hyper_0_14::client::Builder) -> Self {
  564    567   
        self.client_builder = Some(hyper_builder);
  565    568   
        self
  566    569   
    }
  567    570   
  568    571   
    /// Override the Hyper client [`Builder`](hyper_0_14::client::Builder) used to construct this client.
  569    572   
    ///
  570    573   
    /// This enables changing settings like forcing HTTP2 and modifying other default client behavior.
  571    574   
    pub fn set_hyper_builder(
  572    575   
        &mut self,
  573    576   
        hyper_builder: Option<hyper_0_14::client::Builder>,
  574    577   
    ) -> &mut Self {
  575    578   
        self.client_builder = hyper_builder;
  576    579   
        self
  577    580   
    }
  578    581   
  579    582   
    /// Create a hyper client with the default rustls HTTPS implementation.
  580    583   
    ///
  581    584   
    /// The trusted certificates will be loaded later when this becomes the selected
  582    585   
    /// HTTP client for a Smithy client.
  583         -
    #[cfg(feature = "tls-rustls")]
         586  +
    #[cfg(feature = "legacy-rustls-ring")]
  584    587   
    pub fn build_https(self) -> SharedHttpClient {
  585    588   
        self.build_with_fn(default_connector::https)
  586    589   
    }
  587    590   
  588    591   
    /// Create a [`SharedHttpClient`] from this builder and a given connector.
  589    592   
    ///
  590    593   
    #[cfg_attr(
  591         -
        feature = "tls-rustls",
         594  +
        feature = "legacy-rustls-ring",
  592    595   
        doc = "Use [`build_https`](HyperClientBuilder::build_https) if you don't want to provide a custom TCP connector."
  593    596   
    )]
  594    597   
    pub fn build<C>(self, tcp_connector: C) -> SharedHttpClient
  595    598   
    where
  596    599   
        C: Clone + Send + Sync + 'static,
  597    600   
        C: hyper_0_14::service::Service<http_02x::Uri>,
  598    601   
        C::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
  599    602   
        C::Future: Unpin + Send + 'static,
  600    603   
        C::Error: Into<BoxError>,
  601    604   
    {
@@ -792,795 +884,887 @@
  812    815   
                    }
  813    816   
                }
  814    817   
                None => MaybeTimeoutFuture::NoTimeout {
  815    818   
                    future: self.inner.call(req),
  816    819   
                },
  817    820   
            }
  818    821   
        }
  819    822   
    }
  820    823   
  821    824   
    #[cfg(test)]
  822         -
    mod test {
  823         -
        use crate::client::http::hyper_014::HyperConnector;
         825  +
    pub(crate) mod test {
         826  +
        use crate::hyper_014::HyperConnector;
  824    827   
        use aws_smithy_async::assert_elapsed;
  825    828   
        use aws_smithy_async::future::never::Never;
  826    829   
        use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep};
  827    830   
        use aws_smithy_runtime_api::box_error::BoxError;
  828    831   
        use aws_smithy_runtime_api::client::http::HttpConnectorSettings;
  829    832   
        use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
  830    833   
        use aws_smithy_runtime_api::client::result::ConnectorError;
  831    834   
        use aws_smithy_types::error::display::DisplayErrorContext;
  832    835   
        use hyper_0_14::client::connect::{Connected, Connection};
  833    836   
        use std::future::Future;
  834    837   
        use std::pin::Pin;
  835    838   
        use std::task::{Context, Poll};
  836    839   
        use std::time::Duration;
  837    840   
        use tokio::io::ReadBuf;
  838    841   
        use tokio::io::{AsyncRead, AsyncWrite};
  839    842   
        use tokio::net::TcpStream;
  840    843   
  841    844   
        #[allow(unused)]
  842    845   
        fn connect_timeout_is_correct<T: Send + Sync + Clone + 'static>() {
  843    846   
            is_send_sync::<super::ConnectTimeout<T>>();
  844    847   
        }
  845    848   
  846    849   
        #[allow(unused)]
  847    850   
        fn is_send_sync<T: Send + Sync>() {}
  848    851   
  849    852   
        /// A service that will never return whatever it is you want
  850    853   
        ///
  851    854   
        /// Returned futures will return Pending forever
  852    855   
        #[non_exhaustive]
  853    856   
        #[derive(Clone, Default, Debug)]
  854         -
        struct NeverConnects;
         857  +
        pub(crate) struct NeverConnects;
  855    858   
        impl hyper_0_14::service::Service<http_02x::Uri> for NeverConnects {
  856    859   
            type Response = TcpStream;
  857    860   
            type Error = ConnectorError;
  858    861   
            type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
  859    862   
  860    863   
            fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
  861    864   
                Poll::Ready(Ok(()))
  862    865   
            }
  863    866   
  864    867   
            fn call(&mut self, _uri: http_02x::Uri) -> Self::Future {
@@ -966,969 +1052,1055 @@
  986    989   
            let expected = "timeout: HTTP read timeout occurred after 2s";
  987    990   
            assert!(
  988    991   
                message.contains(expected),
  989    992   
                "expected '{message}' to contain '{expected}'"
  990    993   
            );
  991    994   
            assert_elapsed!(now, Duration::from_secs(2));
  992    995   
        }
  993    996   
    }
  994    997   
}
  995    998   
  996         -
#[cfg(all(test, feature = "test-util"))]
         999  +
#[cfg(test)]
  997   1000   
mod test {
  998         -
    use crate::client::http::hyper_014::{HyperClientBuilder, HyperConnector};
  999         -
    use crate::client::http::test_util::NeverTcpConnector;
        1001  +
    use crate::hyper_legacy::timeout_middleware::test::NeverConnects;
        1002  +
    use crate::hyper_legacy::{HyperClientBuilder, HyperConnector};
 1000   1003   
    use aws_smithy_async::time::SystemTimeSource;
 1001   1004   
    use aws_smithy_runtime_api::box_error::BoxError;
 1002   1005   
    use aws_smithy_runtime_api::client::http::{HttpClient, HttpConnectorSettings};
 1003   1006   
    use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
 1004   1007   
    use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder;
 1005   1008   
    use hyper_0_14::client::connect::{Connected, Connection};
 1006   1009   
    use std::io::{Error, ErrorKind};
 1007   1010   
    use std::pin::Pin;
 1008   1011   
    use std::sync::atomic::{AtomicU32, Ordering};
 1009   1012   
    use std::sync::Arc;
 1010   1013   
    use std::task::{Context, Poll};
 1011   1014   
    use std::time::Duration;
 1012   1015   
    use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
 1013   1016   
 1014   1017   
    #[tokio::test]
 1015   1018   
    async fn connector_selection() {
 1016   1019   
        // Create a client that increments a count every time it creates a new HyperConnector
 1017   1020   
        let creation_count = Arc::new(AtomicU32::new(0));
 1018   1021   
        let http_client = HyperClientBuilder::new().build_with_fn({
 1019   1022   
            let count = creation_count.clone();
 1020   1023   
            move || {
 1021   1024   
                count.fetch_add(1, Ordering::Relaxed);
 1022         -
                NeverTcpConnector::new()
        1025  +
                NeverConnects::default()
 1023   1026   
            }
 1024   1027   
        });
 1025   1028   
 1026   1029   
        // This configuration should result in 4 separate connectors with different timeout settings
 1027   1030   
        let settings = [
 1028   1031   
            HttpConnectorSettings::builder()
 1029   1032   
                .connect_timeout(Duration::from_secs(3))
 1030   1033   
                .build(),
 1031   1034   
            HttpConnectorSettings::builder()
 1032   1035   
                .read_timeout(Duration::from_secs(3))

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/lib.rs

@@ -0,1 +0,106 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
/* Automatically managed default lints */
           7  +
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
           8  +
/* End of automatically managed default lints */
           9  +
          10  +
//! HTTP client implementation for smithy-rs generated code.
          11  +
//!
          12  +
//! # Crate Features
          13  +
//!
          14  +
//! - `default-client`: Enable default HTTP client implementation (based on hyper 1.x).
          15  +
//! - `rustls-ring`: Enable TLS provider based on `rustls` using `ring` as the crypto provider
          16  +
//! - `rustls-aws-lc`: Enable TLS provider based on `rustls` using `aws-lc` as the crypto provider
          17  +
//! - `rustls-aws-lc-fips`: Same as `rustls-aws-lc` feature but using a FIPS compliant version of `aws-lc`
          18  +
//! - `s2n-tls`: Enable TLS provider based on `s2n-tls` using `aws-lc` as the crypto provider.
          19  +
//! - `hyper-014`: (Deprecated) HTTP client implementation based on hyper-0.14.x.
          20  +
//! - `test-util`: Enables utilities for unit tests. DO NOT ENABLE IN PRODUCTION.
          21  +
          22  +
#![warn(
          23  +
    missing_docs,
          24  +
    rustdoc::missing_crate_level_docs,
          25  +
    unreachable_pub,
          26  +
    rust_2018_idioms
          27  +
)]
          28  +
#![cfg_attr(docsrs, feature(doc_cfg))]
          29  +
          30  +
// ideally hyper_014 would just be exposed as is but due to
          31  +
// https://github.com/rust-lang/rust/issues/47238 we get clippy warnings we can't suppress
          32  +
#[cfg(feature = "hyper-014")]
          33  +
pub(crate) mod hyper_legacy;
          34  +
          35  +
/// Legacy HTTP and TLS connectors that use hyper 0.14.x and rustls.
          36  +
#[cfg(feature = "hyper-014")]
          37  +
#[deprecated = "hyper 0.14.x support is deprecated, please migrate to 1.x client"]
          38  +
pub mod hyper_014 {
          39  +
    pub use crate::hyper_legacy::*;
          40  +
}
          41  +
          42  +
/// Default HTTP and TLS connectors
          43  +
#[cfg(feature = "default-client")]
          44  +
pub(crate) mod client;
          45  +
#[cfg(feature = "default-client")]
          46  +
pub use client::{default_connector, tls, Builder, Connector, ConnectorBuilder};
          47  +
          48  +
#[cfg(feature = "test-util")]
          49  +
pub mod test_util;
          50  +
          51  +
mod error;
          52  +
pub use error::HttpClientError;
          53  +
          54  +
#[allow(unused_macros, unused_imports)]
          55  +
#[macro_use]
          56  +
pub(crate) mod cfg {
          57  +
    /// Any TLS provider enabled
          58  +
    macro_rules! cfg_tls {
          59  +
        ($($item:item)*) => {
          60  +
            $(
          61  +
                #[cfg(any(
          62  +
                    feature = "rustls-aws-lc",
          63  +
                    feature = "rustls-aws-lc-fips",
          64  +
                    feature = "rustls-ring",
          65  +
                    feature = "s2n-tls",
          66  +
                ))]
          67  +
                #[cfg_attr(docsrs, doc(cfg(any(
          68  +
                    feature = "rustls-aws-lc",
          69  +
                    feature = "rustls-aws-lc-fips",
          70  +
                    feature = "rustls-ring",
          71  +
                    feature = "s2n-tls",
          72  +
                ))))]
          73  +
                $item
          74  +
            )*
          75  +
        }
          76  +
    }
          77  +
          78  +
    /// Any rustls provider enabled
          79  +
    macro_rules! cfg_rustls {
          80  +
        ($($item:item)*) => {
          81  +
            $(
          82  +
                #[cfg(any(
          83  +
                    feature = "rustls-aws-lc",
          84  +
                    feature = "rustls-aws-lc-fips",
          85  +
                    feature = "rustls-ring"
          86  +
                ))]
          87  +
                #[cfg_attr(docsrs, doc(cfg(any(feature = "rustls-aws-lc", feature = "rustls-aws-lc-fips", feature = "rustls-ring"))))]
          88  +
                $item
          89  +
            )*
          90  +
        }
          91  +
    }
          92  +
          93  +
    macro_rules! cfg_s2n_tls {
          94  +
        ($($item:item)*) => {
          95  +
            $(
          96  +
                #[cfg(feature = "s2n-tls")]
          97  +
                #[cfg_attr(docsrs, doc(cfg(feature = "s2n-tls")))]
          98  +
                $item
          99  +
            )*
         100  +
        }
         101  +
    }
         102  +
         103  +
    pub(crate) use cfg_rustls;
         104  +
    pub(crate) use cfg_s2n_tls;
         105  +
    pub(crate) use cfg_tls;
         106  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/test_util.rs

Renamed from tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/src/client/http/test_util.rs

@@ -1,1 +58,67 @@
    5      5   
    6      6   
//! Various fake/mock clients for testing.
    7      7   
//!
    8      8   
//! Each test client is useful for different test use cases:
    9      9   
//! - [`capture_request()`]: If you don't care what the response is, but just want to
   10     10   
//! check that the serialized request is what you expect, then use `capture_request`.
   11     11   
//! Or, alternatively, if you don't care what the request is, but want to always
   12     12   
//! respond with a given response, then capture request can also be useful since
   13     13   
//! you can optionally give it a response to return.
   14     14   
#![cfg_attr(
   15         -
    feature = "connector-hyper-0-14-x",
          15  +
    feature = "default-client",
   16     16   
    doc = "- [`dvr`]: If you want to record real-world traffic and then replay it later, then DVR's"
   17     17   
)]
   18     18   
//! [`RecordingClient`](dvr::RecordingClient) and [`ReplayingClient`](dvr::ReplayingClient)
   19     19   
//! can accomplish this, and the recorded traffic can be saved to JSON and checked in. Note: if
   20     20   
//! the traffic recording has sensitive information in it, such as signatures or authorization,
   21     21   
//! you will need to manually scrub this out if you intend to store the recording alongside
   22     22   
//! your tests.
   23     23   
//! - [`StaticReplayClient`]: If you want to have a set list of requests and their responses in a test,
   24     24   
//! then the static replay client will be useful. On construction, it takes a list of request/response
   25     25   
//! pairs that represent each expected request and the response for that test. At the end of the test,
   26     26   
//! you can ask the client to verify that the requests matched the expectations.
   27     27   
//! - [`infallible_client_fn`]: Allows you to create a client from an infallible function
   28     28   
//! that takes a request and returns a response.
   29     29   
//! - [`NeverClient`]: Useful for testing timeouts, where you want the client to never respond.
   30     30   
//!
   31     31   
#![cfg_attr(
   32         -
    feature = "connector-hyper-0-14-x",
          32  +
    any(feature = "hyper-014", feature = "default-client"),
   33     33   
    doc = "
   34     34   
There is also the [`NeverTcpConnector`], which makes it easy to test connect/read timeouts.
   35     35   
   36     36   
Finally, for socket-level mocking, see the [`wire`] module.
   37     37   
"
   38     38   
)]
          39  +
   39     40   
mod capture_request;
   40     41   
pub use capture_request::{capture_request, CaptureRequestHandler, CaptureRequestReceiver};
   41     42   
   42         -
#[cfg(feature = "connector-hyper-0-14-x")]
          43  +
#[cfg(feature = "legacy-test-util")]
          44  +
pub use capture_request::legacy_capture_request;
          45  +
   43     46   
pub mod dvr;
   44     47   
   45     48   
mod replay;
   46     49   
pub use replay::{ReplayEvent, StaticReplayClient};
   47     50   
   48     51   
mod infallible;
   49     52   
pub use infallible::infallible_client_fn;
   50     53   
          54  +
// infallible based on http_02x stack had to be duplicated to avoid breaking API changes
          55  +
#[allow(missing_docs)]
          56  +
#[cfg(feature = "legacy-test-util")]
          57  +
pub mod legacy_infallible;
          58  +
   51     59   
mod never;
   52     60   
pub use never::NeverClient;
   53     61   
   54         -
#[cfg(feature = "connector-hyper-0-14-x")]
          62  +
#[cfg(any(feature = "hyper-014", feature = "default-client"))]
   55     63   
pub use never::NeverTcpConnector;
   56     64   
   57         -
#[cfg(all(feature = "connector-hyper-0-14-x", feature = "wire-mock"))]
          65  +
mod body;
          66  +
#[cfg(all(feature = "default-client", feature = "wire-mock"))]
   58     67   
pub mod wire;

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/src/test_util/body.rs

@@ -0,1 +0,95 @@
           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_runtime_api::box_error::BoxError;
           7  +
use aws_smithy_types::body::SdkBody;
           8  +
use bytes::Bytes;
           9  +
use http_body_1x::{Frame, SizeHint};
          10  +
use pin_project_lite::pin_project;
          11  +
use std::future::poll_fn;
          12  +
use std::pin::{pin, Pin};
          13  +
use std::task::{Context, Poll};
          14  +
use tokio::sync::mpsc;
          15  +
          16  +
/// Create a `SdkBody` with an associated sender half.
          17  +
///
          18  +
/// Useful for sending data from another thread/task and test scenarios.
          19  +
pub(crate) fn channel_body() -> (Sender, SdkBody) {
          20  +
    let (tx, rx) = mpsc::channel(1);
          21  +
    let sender = Sender { tx };
          22  +
    let ch_body = ChannelBody { rx };
          23  +
    (sender, SdkBody::from_body_1_x(ch_body))
          24  +
}
          25  +
          26  +
/// Sender half of channel based `SdkBody` implementation useful for testing.
          27  +
///
          28  +
/// Roughly a replacement for hyper 0.14.x `Sender` body.
          29  +
///
          30  +
/// ## Body Closing
          31  +
///
          32  +
/// The request body will always be closed normally when the sender is dropped. If you
          33  +
/// want to close the connection with an incomplete response, call [`Sender::abort()`] method to
          34  +
/// abort the body in an abnormal fashion.
          35  +
#[derive(Debug)]
          36  +
pub(crate) struct Sender {
          37  +
    tx: mpsc::Sender<Result<Frame<Bytes>, BoxError>>,
          38  +
}
          39  +
          40  +
impl Sender {
          41  +
    /// Send data on data channel when it's ready
          42  +
    pub(crate) async fn send_data(&mut self, chunk: Bytes) -> Result<(), BoxError> {
          43  +
        let frame = Frame::data(chunk);
          44  +
        self.tx.send(Ok(frame)).await.map_err(|e| e.into())
          45  +
    }
          46  +
          47  +
    // TODO(test-utils): we can add support for trailers if needed in the future
          48  +
          49  +
    /// Abort the body in an abnormal fashion
          50  +
    pub(crate) fn abort(self) {
          51  +
        let _ = self.tx.clone().try_send(Err("body write aborted".into()));
          52  +
    }
          53  +
}
          54  +
          55  +
pin_project! {
          56  +
    struct ChannelBody {
          57  +
        rx: mpsc::Receiver<Result<Frame<Bytes>, BoxError>>
          58  +
    }
          59  +
}
          60  +
          61  +
impl http_body_1x::Body for ChannelBody {
          62  +
    type Data = Bytes;
          63  +
    type Error = BoxError;
          64  +
          65  +
    fn poll_frame(
          66  +
        self: Pin<&mut Self>,
          67  +
        cx: &mut Context<'_>,
          68  +
    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
          69  +
        let this = self.project();
          70  +
        this.rx.poll_recv(cx)
          71  +
    }
          72  +
          73  +
    fn is_end_stream(&self) -> bool {
          74  +
        self.rx.is_closed()
          75  +
    }
          76  +
          77  +
    fn size_hint(&self) -> SizeHint {
          78  +
        SizeHint::default()
          79  +
    }
          80  +
}
          81  +
          82  +
pub(crate) async fn next_data_frame(body: &mut SdkBody) -> Option<Result<Bytes, BoxError>> {
          83  +
    use http_body_1x::Body;
          84  +
    let mut pinned = pin!(body);
          85  +
    match poll_fn(|cx| pinned.as_mut().poll_frame(cx)).await? {
          86  +
        Ok(frame) => {
          87  +
            if frame.is_data() {
          88  +
                Some(Ok(frame.into_data().unwrap()))
          89  +
            } else {
          90  +
                None
          91  +
            }
          92  +
        }
          93  +
        Err(err) => Some(Err(err)),
          94  +
    }
          95  +
}