Skip to content

Commit c819dbe

Browse files
Zelda Hesslerjjant
authored andcommitted
Fix: native-tls client creation to support HTTPS again (#2360)
* update: use `enforce_http = false` when creating native-tls hyper connector refactor: move smithy conns module to its own file add: sanity tests ensuring we can make HTTP and HTTPS requests with the rustls and native-tls connectors remove: `use crate::*` imports in favor of explicit imports * update: CHANGELOG.next.toml * add: feature gate to conns tests
1 parent 164907d commit c819dbe

File tree

5 files changed

+168
-79
lines changed

5 files changed

+168
-79
lines changed

CHANGELOG.next.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,15 @@ message = "Fix inconsistent casing in services re-export."
3434
references = ["smithy-rs#2349"]
3535
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" }
3636
author = "hlbarber"
37+
38+
[[aws-sdk-rust]]
39+
message = "Fix issue where clients using native-tls connector were prevented from making HTTPS requests."
40+
references = ["aws-sdk-rust#736"]
41+
meta = { "breaking" = false, "tada" = false, "bug" = true }
42+
author = "Velfi"
43+
44+
[[smithy-rs]]
45+
message = "Fix issue where clients using native-tls connector were prevented from making HTTPS requests."
46+
references = ["aws-sdk-rust#736"]
47+
meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" }
48+
author = "Velfi"

rust-runtime/aws-smithy-client/src/bounds.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! required for `call` and friends.
88
//!
99
//! The short-hands will one day be true [trait aliases], but for now they are traits with blanket
10-
//! implementations. Also, due to [compiler limitations], the bounds repeat a nubmer of associated
10+
//! implementations. Also, due to [compiler limitations], the bounds repeat a number of associated
1111
//! types with bounds so that those bounds [do not need to be repeated] at the call site. It's a
1212
//! bit of a mess to define, but _should_ be invisible to callers.
1313
//!
@@ -17,8 +17,12 @@
1717
1818
use crate::erase::DynConnector;
1919
use crate::http_connector::HttpConnector;
20-
use crate::*;
21-
use aws_smithy_http::result::ConnectorError;
20+
use aws_smithy_http::body::SdkBody;
21+
use aws_smithy_http::operation::{self, Operation};
22+
use aws_smithy_http::response::ParseHttpResponse;
23+
use aws_smithy_http::result::{ConnectorError, SdkError, SdkSuccess};
24+
use aws_smithy_http::retry::ClassifyRetry;
25+
use tower::{Layer, Service};
2226

2327
/// A service that has parsed a raw Smithy response.
2428
pub type Parsed<S, O, Retry> =
@@ -75,14 +79,14 @@ where
7579
}
7680
}
7781

78-
/// A Smithy middleware service that adjusts [`aws_smithy_http::operation::Request`]s.
82+
/// A Smithy middleware service that adjusts [`aws_smithy_http::operation::Request`](operation::Request)s.
7983
///
8084
/// This trait has a blanket implementation for all compatible types, and should never be
8185
/// implemented.
8286
pub trait SmithyMiddlewareService:
8387
Service<
84-
aws_smithy_http::operation::Request,
85-
Response = aws_smithy_http::operation::Response,
88+
operation::Request,
89+
Response = operation::Response,
8690
Error = aws_smithy_http_tower::SendOperationError,
8791
Future = <Self as SmithyMiddlewareService>::Future,
8892
>
@@ -96,8 +100,8 @@ pub trait SmithyMiddlewareService:
96100
impl<T> SmithyMiddlewareService for T
97101
where
98102
T: Service<
99-
aws_smithy_http::operation::Request,
100-
Response = aws_smithy_http::operation::Response,
103+
operation::Request,
104+
Response = operation::Response,
101105
Error = aws_smithy_http_tower::SendOperationError,
102106
>,
103107
T::Future: Send + 'static,
@@ -143,7 +147,7 @@ pub trait SmithyRetryPolicy<O, T, E, Retry>:
143147
/// Forwarding type to `E` for bound inference.
144148
///
145149
/// See module-level docs for details.
146-
type E: Error;
150+
type E: std::error::Error;
147151

148152
/// Forwarding type to `Retry` for bound inference.
149153
///
@@ -155,7 +159,7 @@ impl<R, O, T, E, Retry> SmithyRetryPolicy<O, T, E, Retry> for R
155159
where
156160
R: tower::retry::Policy<Operation<O, Retry>, SdkSuccess<T>, SdkError<E>> + Clone,
157161
O: ParseHttpResponse<Output = Result<T, E>> + Send + Sync + Clone + 'static,
158-
E: Error,
162+
E: std::error::Error,
159163
Retry: ClassifyRetry<SdkSuccess<T>, SdkError<E>>,
160164
{
161165
type O = O;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
//! Type aliases for standard connection types.
7+
8+
#[cfg(feature = "rustls")]
9+
/// A `hyper` connector that uses the `rustls` crate for TLS. To use this in a smithy client,
10+
/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter).
11+
pub type Https = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>;
12+
13+
#[cfg(feature = "native-tls")]
14+
/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client,
15+
/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter).
16+
pub type NativeTls = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
17+
18+
#[cfg(feature = "rustls")]
19+
/// A smithy connector that uses the `rustls` crate for TLS.
20+
pub type Rustls = crate::hyper_ext::Adapter<Https>;
21+
22+
// Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we
23+
// don't need to repeatedly incur that cost.
24+
#[cfg(feature = "rustls")]
25+
lazy_static::lazy_static! {
26+
static ref HTTPS_NATIVE_ROOTS: Https = {
27+
hyper_rustls::HttpsConnectorBuilder::new()
28+
.with_native_roots()
29+
.https_or_http()
30+
.enable_http1()
31+
.enable_http2()
32+
.build()
33+
};
34+
}
35+
36+
#[cfg(feature = "rustls")]
37+
/// Return a default HTTPS connector backed by the `rustls` crate.
38+
///
39+
/// It requires a minimum TLS version of 1.2.
40+
/// It allows you to connect to both `http` and `https` URLs.
41+
pub fn https() -> Https {
42+
HTTPS_NATIVE_ROOTS.clone()
43+
}
44+
45+
#[cfg(feature = "native-tls")]
46+
/// Return a default HTTPS connector backed by the `hyper_tls` crate.
47+
///
48+
/// It requires a minimum TLS version of 1.2.
49+
/// It allows you to connect to both `http` and `https` URLs.
50+
pub fn native_tls() -> NativeTls {
51+
// `TlsConnector` actually comes for here: https://docs.rs/native-tls/latest/native_tls/
52+
// hyper_tls just re-exports the crate for convenience.
53+
let mut tls = hyper_tls::native_tls::TlsConnector::builder();
54+
let tls = tls
55+
.min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12))
56+
.build()
57+
.unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e));
58+
let mut http = hyper::client::HttpConnector::new();
59+
http.enforce_http(false);
60+
hyper_tls::HttpsConnector::from((http, tls.into()))
61+
}
62+
63+
#[cfg(all(test, any(feature = "native-tls", feature = "rustls")))]
64+
mod tests {
65+
use crate::erase::DynConnector;
66+
use crate::hyper_ext::Adapter;
67+
use aws_smithy_http::body::SdkBody;
68+
use http::{Method, Request, Uri};
69+
use tower::{Service, ServiceBuilder};
70+
71+
async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) {
72+
let mut svc = ServiceBuilder::new().service(conn);
73+
let req = Request::builder()
74+
.uri(uri)
75+
.method(Method::GET)
76+
.body(SdkBody::empty())
77+
.unwrap();
78+
let res = svc.call(req).await.unwrap();
79+
assert!(res.status().is_success());
80+
}
81+
82+
#[cfg(feature = "native-tls")]
83+
mod native_tls_tests {
84+
use super::super::native_tls;
85+
use super::*;
86+
87+
#[tokio::test]
88+
async fn test_native_tls_connector_can_make_http_requests() {
89+
let conn = Adapter::builder().build(native_tls());
90+
let conn = DynConnector::new(conn);
91+
let http_uri: Uri = "http://example.com/".parse().unwrap();
92+
93+
send_request_and_assert_success(conn, &http_uri).await;
94+
}
95+
96+
#[tokio::test]
97+
async fn test_native_tls_connector_can_make_https_requests() {
98+
let conn = Adapter::builder().build(native_tls());
99+
let conn = DynConnector::new(conn);
100+
let https_uri: Uri = "https://example.com/".parse().unwrap();
101+
102+
send_request_and_assert_success(conn, &https_uri).await;
103+
}
104+
}
105+
106+
#[cfg(feature = "rustls")]
107+
mod rustls_tests {
108+
use super::super::https;
109+
use super::*;
110+
111+
#[tokio::test]
112+
async fn test_rustls_connector_can_make_http_requests() {
113+
let conn = Adapter::builder().build(https());
114+
let conn = DynConnector::new(conn);
115+
let http_uri: Uri = "http://example.com/".parse().unwrap();
116+
117+
send_request_and_assert_success(conn, &http_uri).await;
118+
}
119+
120+
#[tokio::test]
121+
async fn test_rustls_connector_can_make_https_requests() {
122+
let conn = Adapter::builder().build(https());
123+
let conn = DynConnector::new(conn);
124+
let https_uri: Uri = "https://example.com/".parse().unwrap();
125+
126+
send_request_and_assert_success(conn, &https_uri).await;
127+
}
128+
}
129+
}

rust-runtime/aws-smithy-client/src/lib.rs

Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424

2525
pub mod bounds;
2626
pub mod erase;
27+
pub mod http_connector;
28+
pub mod never;
2729
pub mod retry;
30+
pub mod timeout;
2831

2932
// https://github.com/rust-lang/rust/issues/72081
3033
#[allow(rustdoc::private_doc_tests)]
@@ -36,8 +39,8 @@ pub mod dvr;
3639
#[cfg(feature = "test-util")]
3740
pub mod test_connection;
3841

39-
pub mod http_connector;
40-
42+
#[cfg(feature = "client-hyper")]
43+
pub mod conns;
4144
#[cfg(feature = "client-hyper")]
4245
pub mod hyper_ext;
4346

@@ -47,78 +50,19 @@ pub mod hyper_ext;
4750
#[doc(hidden)]
4851
pub mod static_tests;
4952

50-
pub mod never;
51-
pub mod timeout;
52-
pub use timeout::TimeoutLayer;
53-
54-
/// Type aliases for standard connection types.
55-
#[cfg(feature = "client-hyper")]
56-
#[allow(missing_docs)]
57-
pub mod conns {
58-
#[cfg(feature = "rustls")]
59-
pub type Https = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>;
60-
61-
// Creating a `with_native_roots` HTTP client takes 300ms on OS X. Cache this so that we
62-
// don't need to repeatedly incur that cost.
63-
#[cfg(feature = "rustls")]
64-
lazy_static::lazy_static! {
65-
static ref HTTPS_NATIVE_ROOTS: Https = {
66-
hyper_rustls::HttpsConnectorBuilder::new()
67-
.with_native_roots()
68-
.https_or_http()
69-
.enable_http1()
70-
.enable_http2()
71-
.build()
72-
};
73-
}
74-
75-
#[cfg(feature = "rustls")]
76-
/// Return a default HTTPS connector backed by the `rustls` crate.
77-
///
78-
/// It requires a minimum TLS version of 1.2.
79-
/// It allows you to connect to both `http` and `https` URLs.
80-
pub fn https() -> Https {
81-
HTTPS_NATIVE_ROOTS.clone()
82-
}
83-
84-
#[cfg(feature = "native-tls")]
85-
/// Return a default HTTPS connector backed by the `hyper_tls` crate.
86-
///
87-
/// It requires a minimum TLS version of 1.2.
88-
/// It allows you to connect to both `http` and `https` URLs.
89-
pub fn native_tls() -> NativeTls {
90-
let mut tls = hyper_tls::native_tls::TlsConnector::builder();
91-
let tls = tls
92-
.min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12))
93-
.build()
94-
.unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e));
95-
let http = hyper::client::HttpConnector::new();
96-
hyper_tls::HttpsConnector::from((http, tls.into()))
97-
}
98-
99-
#[cfg(feature = "native-tls")]
100-
pub type NativeTls = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
101-
102-
#[cfg(feature = "rustls")]
103-
pub type Rustls =
104-
crate::hyper_ext::Adapter<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>;
105-
}
106-
10753
use aws_smithy_async::rt::sleep::AsyncSleep;
108-
use aws_smithy_http::body::SdkBody;
10954
use aws_smithy_http::operation::Operation;
11055
use aws_smithy_http::response::ParseHttpResponse;
11156
pub use aws_smithy_http::result::{SdkError, SdkSuccess};
112-
use aws_smithy_http::retry::ClassifyRetry;
11357
use aws_smithy_http_tower::dispatch::DispatchLayer;
11458
use aws_smithy_http_tower::parse_response::ParseResponseLayer;
11559
use aws_smithy_types::error::display::DisplayErrorContext;
11660
use aws_smithy_types::retry::ProvideErrorKind;
11761
use aws_smithy_types::timeout::OperationTimeoutConfig;
118-
use std::error::Error;
11962
use std::sync::Arc;
12063
use timeout::ClientTimeoutParams;
121-
use tower::{Layer, Service, ServiceBuilder, ServiceExt};
64+
pub use timeout::TimeoutLayer;
65+
use tower::{Service, ServiceBuilder, ServiceExt};
12266
use tracing::{debug_span, field, field::display, Instrument};
12367

12468
/// Smithy service client.
@@ -131,7 +75,7 @@ use tracing::{debug_span, field, field::display, Instrument};
13175
/// such as those used for routing (like the URL), authentication, and authorization.
13276
///
13377
/// The middleware takes the form of a [`tower::Layer`] that wraps the actual connection for each
134-
/// request. The [`tower::Service`] that the middleware produces must accept requests of the type
78+
/// request. The [`tower::Service`](Service) that the middleware produces must accept requests of the type
13579
/// [`aws_smithy_http::operation::Request`] and return responses of the type
13680
/// [`http::Response<SdkBody>`], most likely by modifying the provided request in place, passing it
13781
/// to the inner service, and then ultimately returning the inner service's response.
@@ -165,9 +109,9 @@ impl<C, M> Client<C, M>
165109
where
166110
M: Default,
167111
{
168-
/// Create a Smithy client from the given `connector`, a middleware default, the [standard
169-
/// retry policy](crate::retry::Standard), and the [`default_async_sleep`](aws_smithy_async::rt::sleep::default_async_sleep)
170-
/// sleep implementation.
112+
/// Create a Smithy client from the given `connector`, a middleware default, the
113+
/// [standard retry policy](retry::Standard), and the
114+
/// [`default_async_sleep`](aws_smithy_async::rt::sleep::default_async_sleep) sleep implementation.
171115
pub fn new(connector: C) -> Self {
172116
Builder::new()
173117
.connector(connector)

rust-runtime/aws-smithy-client/src/static_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! This module provides types useful for static tests.
66
#![allow(missing_docs, missing_debug_implementations)]
77

8-
use crate::{Builder, Error, Operation, ParseHttpResponse, ProvideErrorKind};
8+
use crate::{Builder, Operation, ParseHttpResponse, ProvideErrorKind};
99
use aws_smithy_http::operation;
1010
use aws_smithy_http::retry::DefaultResponseRetryClassifier;
1111

@@ -17,7 +17,7 @@ impl std::fmt::Display for TestOperationError {
1717
unreachable!("only used for static tests")
1818
}
1919
}
20-
impl Error for TestOperationError {}
20+
impl std::error::Error for TestOperationError {}
2121
impl ProvideErrorKind for TestOperationError {
2222
fn retryable_error_kind(&self) -> Option<aws_smithy_types::retry::ErrorKind> {
2323
unreachable!("only used for static tests")

0 commit comments

Comments
 (0)