1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! Type aliases for standard connection types.

#[cfg(feature = "rustls")]
/// A `hyper` connector that uses the `rustls` crate for TLS. To use this in a smithy client,
/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter).
pub type Https = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>;

#[cfg(feature = "native-tls")]
/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client,
/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter).
pub type NativeTls = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;

#[cfg(feature = "rustls")]
/// A smithy connector that uses the `rustls` crate for TLS.
pub type Rustls = crate::hyper_ext::Adapter<Https>;

#[cfg(feature = "rustls")]
use hyper_rustls::ConfigBuilderExt;

// Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we
// don't need to repeatedly incur that cost.
#[cfg(feature = "rustls")]
lazy_static::lazy_static! {
    static ref HTTPS_NATIVE_ROOTS: Https = {
        hyper_rustls::HttpsConnectorBuilder::new()
            .with_tls_config(
                rustls::ClientConfig::builder()
                    .with_cipher_suites(&[
                        // TLS1.3 suites
                        rustls::cipher_suite::TLS13_AES_256_GCM_SHA384,
                        rustls::cipher_suite::TLS13_AES_128_GCM_SHA256,
                        // TLS1.2 suites
                        rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                        rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                        rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
                    ])
                    .with_safe_default_kx_groups()
                    .with_safe_default_protocol_versions()
                    .expect("Error with the TLS configuration. Please file a bug report under https://github.com/awslabs/smithy-rs/issues.")
                    .with_native_roots()
                    .with_no_client_auth()
            )
            .https_or_http()
            .enable_http1()
            .enable_http2()
            .build()
    };
}

#[cfg(feature = "rustls")]
/// Return a default HTTPS connector backed by the `rustls` crate.
///
/// It requires a minimum TLS version of 1.2.
/// It allows you to connect to both `http` and `https` URLs.
pub fn https() -> Https {
    HTTPS_NATIVE_ROOTS.clone()
}

#[cfg(feature = "native-tls")]
/// Return a default HTTPS connector backed by the `hyper_tls` crate.
///
/// It requires a minimum TLS version of 1.2.
/// It allows you to connect to both `http` and `https` URLs.
pub fn native_tls() -> NativeTls {
    // `TlsConnector` actually comes for here: https://docs.rs/native-tls/latest/native_tls/
    // hyper_tls just re-exports the crate for convenience.
    let mut tls = hyper_tls::native_tls::TlsConnector::builder();
    let tls = tls
        .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12))
        .build()
        .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e));
    let mut http = hyper::client::HttpConnector::new();
    http.enforce_http(false);
    hyper_tls::HttpsConnector::from((http, tls.into()))
}

#[cfg(all(test, any(feature = "native-tls", feature = "rustls")))]
mod tests {
    use crate::erase::DynConnector;
    use crate::hyper_ext::Adapter;
    use aws_smithy_http::body::SdkBody;
    use http::{Method, Request, Uri};
    use tower::{Service, ServiceBuilder};

    async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) {
        let mut svc = ServiceBuilder::new().service(conn);
        let req = Request::builder()
            .uri(uri)
            .method(Method::GET)
            .body(SdkBody::empty())
            .unwrap();
        let res = svc.call(req).await.unwrap();
        assert!(res.status().is_success());
    }

    #[cfg(feature = "native-tls")]
    mod native_tls_tests {
        use super::super::native_tls;
        use super::*;

        #[tokio::test]
        async fn test_native_tls_connector_can_make_http_requests() {
            let conn = Adapter::builder().build(native_tls());
            let conn = DynConnector::new(conn);
            let http_uri: Uri = "http://example.com/".parse().unwrap();

            send_request_and_assert_success(conn, &http_uri).await;
        }

        #[tokio::test]
        async fn test_native_tls_connector_can_make_https_requests() {
            let conn = Adapter::builder().build(native_tls());
            let conn = DynConnector::new(conn);
            let https_uri: Uri = "https://example.com/".parse().unwrap();

            send_request_and_assert_success(conn, &https_uri).await;
        }
    }

    #[cfg(feature = "rustls")]
    mod rustls_tests {
        use super::super::https;
        use super::*;

        #[tokio::test]
        async fn test_rustls_connector_can_make_http_requests() {
            let conn = Adapter::builder().build(https());
            let conn = DynConnector::new(conn);
            let http_uri: Uri = "http://example.com/".parse().unwrap();

            send_request_and_assert_success(conn, &http_uri).await;
        }

        #[tokio::test]
        async fn test_rustls_connector_can_make_https_requests() {
            let conn = Adapter::builder().build(https());
            let conn = DynConnector::new(conn);
            let https_uri: Uri = "https://example.com/".parse().unwrap();

            send_request_and_assert_success(conn, &https_uri).await;
        }
    }
}