|
69 | 69 | // dst.scheme() would need to derive Eq to be matchable;
|
70 | 70 | // use an if cascade instead
|
71 | 71 | match dst.scheme() {
|
72 |
| - Some(scheme) if scheme == &http::uri::Scheme::HTTP => { |
| 72 | + Some(scheme) if scheme == &http::uri::Scheme::HTTP && !self.force_https => { |
73 | 73 | let future = self.http.call(dst);
|
74 | 74 | return Box::pin(async move {
|
75 | 75 | Ok(MaybeHttpsStream::Http(future.await.map_err(Into::into)?))
|
@@ -199,3 +199,104 @@ pub trait ResolveServerName {
|
199 | 199 | uri: &Uri,
|
200 | 200 | ) -> Result<ServerName<'static>, Box<dyn std::error::Error + Sync + Send>>;
|
201 | 201 | }
|
| 202 | + |
| 203 | +#[cfg(all( |
| 204 | + test, |
| 205 | + any(feature = "ring", feature = "aws-lc-rs"), |
| 206 | + any( |
| 207 | + feature = "rustls-native-certs", |
| 208 | + feature = "webpki-roots", |
| 209 | + feature = "rustls-platform-verifier", |
| 210 | + ) |
| 211 | +))] |
| 212 | +mod tests { |
| 213 | + use std::future::poll_fn; |
| 214 | + |
| 215 | + use http::Uri; |
| 216 | + use hyper_util::client::legacy::connect::HttpConnector; |
| 217 | + use tower_service::Service; |
| 218 | + |
| 219 | + use super::HttpsConnector; |
| 220 | + use crate::{ConfigBuilderExt, HttpsConnectorBuilder}; |
| 221 | + |
| 222 | + fn tls_config() -> rustls::ClientConfig { |
| 223 | + #[cfg(feature = "rustls-native-certs")] |
| 224 | + return rustls::ClientConfig::builder() |
| 225 | + .with_native_roots() |
| 226 | + .unwrap() |
| 227 | + .with_no_client_auth(); |
| 228 | + |
| 229 | + #[cfg(feature = "webpki-roots")] |
| 230 | + return rustls::ClientConfig::builder() |
| 231 | + .with_webpki_roots() |
| 232 | + .with_no_client_auth(); |
| 233 | + |
| 234 | + #[cfg(feature = "rustls-platform-verifier")] |
| 235 | + return rustls::ClientConfig::builder() |
| 236 | + .with_platform_verifier() |
| 237 | + .with_no_client_auth(); |
| 238 | + } |
| 239 | + |
| 240 | + fn https_or_http_connector() -> HttpsConnector<HttpConnector> { |
| 241 | + HttpsConnectorBuilder::new() |
| 242 | + .with_tls_config(tls_config()) |
| 243 | + .https_or_http() |
| 244 | + .enable_http1() |
| 245 | + .build() |
| 246 | + } |
| 247 | + |
| 248 | + fn https_only_connector() -> HttpsConnector<HttpConnector> { |
| 249 | + HttpsConnectorBuilder::new() |
| 250 | + .with_tls_config(tls_config()) |
| 251 | + .https_only() |
| 252 | + .enable_http1() |
| 253 | + .build() |
| 254 | + } |
| 255 | + |
| 256 | + async fn oneshot<S, Req>(mut service: S, req: Req) -> Result<S::Response, S::Error> |
| 257 | + where |
| 258 | + S: Service<Req>, |
| 259 | + { |
| 260 | + poll_fn(|cx| service.poll_ready(cx)).await?; |
| 261 | + service.call(req).await |
| 262 | + } |
| 263 | + |
| 264 | + fn https_uri() -> Uri { |
| 265 | + Uri::from_static("https://google.com") |
| 266 | + } |
| 267 | + |
| 268 | + fn http_uri() -> Uri { |
| 269 | + Uri::from_static("http://google.com") |
| 270 | + } |
| 271 | + |
| 272 | + #[tokio::test] |
| 273 | + async fn connects_https() { |
| 274 | + oneshot(https_or_http_connector(), https_uri()) |
| 275 | + .await |
| 276 | + .unwrap(); |
| 277 | + } |
| 278 | + |
| 279 | + #[tokio::test] |
| 280 | + async fn connects_http() { |
| 281 | + oneshot(https_or_http_connector(), http_uri()) |
| 282 | + .await |
| 283 | + .unwrap(); |
| 284 | + } |
| 285 | + |
| 286 | + #[tokio::test] |
| 287 | + async fn connects_https_only() { |
| 288 | + oneshot(https_only_connector(), https_uri()) |
| 289 | + .await |
| 290 | + .unwrap(); |
| 291 | + } |
| 292 | + |
| 293 | + #[tokio::test] |
| 294 | + async fn enforces_https_only() { |
| 295 | + let message = oneshot(https_only_connector(), http_uri()) |
| 296 | + .await |
| 297 | + .unwrap_err() |
| 298 | + .to_string(); |
| 299 | + |
| 300 | + assert_eq!(message, "unsupported scheme http"); |
| 301 | + } |
| 302 | +} |
0 commit comments