784 784 | }
|
785 785 |
|
786 786 | // ================================================================================================
|
787 787 | // HTTPS/CONNECT Tunneling Tests
|
788 788 | // ================================================================================================
|
789 789 | //
|
790 790 | // These tests are for HTTPS tunneling through HTTP proxies using the CONNECT method.
|
791 791 |
|
792 792 | /// Helper function to make HTTPS requests through proxy using TLS providers
|
793 793 | /// This is similar to make_http_request_through_proxy but uses TLS-enabled connectors
|
794 + | #[cfg(any(feature = "rustls-ring", feature = "s2n-tls"))]
|
794 795 | async fn make_https_request_through_proxy(
|
795 796 | proxy_config: ProxyConfig,
|
796 797 | target_url: &str,
|
797 798 | tls_provider: tls::Provider,
|
798 799 | ) -> Result<(StatusCode, String), Box<dyn std::error::Error + Send + Sync>> {
|
799 800 | let http_client = http_client_fn(move |settings, _components| {
|
800 801 | let connector = Connector::builder()
|
801 802 | .proxy_config(proxy_config.clone())
|
802 803 | .connector_settings(settings.clone())
|
803 804 | .tls_provider(tls_provider.clone())
|
804 805 | .build();
|
805 806 |
|
806 807 | aws_smithy_runtime_api::client::http::SharedHttpConnector::new(connector)
|
807 808 | });
|
808 809 |
|
809 810 | let connector_settings = HttpConnectorSettings::builder().build();
|
810 811 | let runtime_components = RuntimeComponentsBuilder::for_tests()
|
811 812 | .with_time_source(Some(SystemTimeSource::new()))
|
812 813 | .build()
|
813 814 | .unwrap();
|
814 815 |
|
815 816 | let http_connector = http_client.http_connector(&connector_settings, &runtime_components);
|
816 817 |
|
817 818 | let request = HttpRequest::get(target_url)
|
818 819 | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
|
819 820 |
|
820 821 | let response = http_connector.call(request).await?;
|
821 822 |
|
822 823 | let status = response.status();
|
823 824 | let body_bytes = response.into_body().collect().await?.to_bytes();
|
824 825 | let body_string = String::from_utf8(body_bytes.to_vec())?;
|
825 826 |
|
826 827 | Ok((status.into(), body_string))
|
827 828 | }
|
828 829 |
|
829 830 | /// Generic test function for HTTPS CONNECT with authentication
|
830 831 | /// Tests that HTTPS requests through HTTP proxy use CONNECT method with proper auth headers
|
832 + | #[cfg(any(feature = "rustls-ring", feature = "s2n-tls"))]
|
831 833 | async fn run_https_connect_with_auth_test(tls_provider: tls::Provider, provider_name: &str) {
|
832 834 | let mock_proxy = MockProxyServer::new(|req| {
|
833 835 | // For HTTPS through HTTP proxy, we should see a CONNECT request
|
834 836 | assert_eq!(req.method, "CONNECT");
|
835 837 | assert_eq!(req.uri, "secure.aws.amazon.com:443");
|
836 838 |
|
837 839 | // Verify authentication header is present
|
838 840 | let expected_auth = format!(
|
839 841 | "Basic {}",
|
840 842 | base64::prelude::BASE64_STANDARD.encode("connectuser:connectpass")
|
841 843 | );
|
842 844 | assert_eq!(req.headers.get("proxy-authorization"), Some(&expected_auth));
|
843 845 |
|
844 846 | // Return 400 to avoid dealing with actual TLS tunneling
|
845 847 | // The important part is that we got the CONNECT request with correct auth
|
846 848 | Response::builder()
|
847 849 | .status(StatusCode::BAD_REQUEST)
|
848 850 | .body("CONNECT tunnel setup failed".to_string())
|
849 851 | .unwrap()
|
850 852 | })
|
851 853 | .await;
|
852 854 |
|
853 855 | // Configure proxy with authentication
|
854 856 | let proxy_config = ProxyConfig::all(format!("http://{}", mock_proxy.addr()))
|
855 857 | .unwrap()
|
856 858 | .with_basic_auth("connectuser", "connectpass");
|
857 859 |
|
858 860 | // Make HTTPS request - should trigger CONNECT method
|
859 861 | let target_url = "https://secure.aws.amazon.com/api/secure";
|
860 862 | let result = make_https_request_through_proxy(proxy_config, target_url, tls_provider).await;
|
861 863 |
|
862 864 | // We expect this to fail with a connection error since we returned 400
|
863 865 | // The important thing is that the CONNECT request was made correctly
|
864 866 | assert!(
|
865 867 | result.is_err(),
|
866 868 | "CONNECT tunnel should fail with 400 response for {}",
|
867 869 | provider_name
|
868 870 | );
|
869 871 |
|
870 872 | // Verify the proxy received the CONNECT request
|
871 873 | let requests = mock_proxy.requests();
|
872 874 | assert_eq!(
|
873 875 | requests.len(),
|
874 876 | 1,
|
875 877 | "Proxy should have received exactly one CONNECT request for {}",
|
876 878 | provider_name
|
877 879 | );
|
878 880 | }
|
879 881 |
|
880 882 | /// Generic test function for CONNECT without authentication (should get 407)
|
881 883 | /// Tests that HTTPS requests without auth get proper 407 response
|
884 + | #[cfg(any(feature = "rustls-ring", feature = "s2n-tls"))]
|
882 885 | async fn run_https_connect_auth_required_test(tls_provider: tls::Provider, provider_name: &str) {
|
883 886 | let mock_proxy = MockProxyServer::new(|req| {
|
884 887 | // For HTTPS through HTTP proxy, we should see a CONNECT request
|
885 888 | assert_eq!(req.method, "CONNECT");
|
886 889 | assert_eq!(req.uri, "secure.aws.amazon.com:443");
|
887 890 |
|
888 891 | // No auth header should be present
|
889 892 | assert!(!req.headers.contains_key("proxy-authorization"));
|
890 893 |
|
891 894 | // Return 407 Proxy Authentication Required
|
1185 1189 | .await;
|
1186 1190 | }
|
1187 1191 |
|
1188 1192 | /// Tests CONNECT method URI form for HTTPS tunneling - s2n-tls provider
|
1189 1193 | /// Verifies that CONNECT requests use the correct host:port format
|
1190 1194 | #[cfg(feature = "s2n-tls")]
|
1191 1195 | #[tokio::test]
|
1192 1196 | async fn test_connect_uri_form_s2n_tls() {
|
1193 1197 | run_connect_uri_form_test(tls::Provider::S2nTls, "s2n-tls").await;
|
1194 1198 | }
|
1199 + |
|
1200 + | // ================================================================================================
|
1201 + | // V2 client proxy parity tests
|
1202 + | // ================================================================================================
|
1203 + |
|
1204 + | async fn make_v2_http_request_through_proxy(
|
1205 + | proxy_config: ProxyConfig,
|
1206 + | target_url: &str,
|
1207 + | ) -> Result<(StatusCode, String), Box<dyn std::error::Error + Send + Sync>> {
|
1208 + | use aws_smithy_http_client::pool::{Client, SharedPool};
|
1209 + |
|
1210 + | let pool = SharedPool::builder()
|
1211 + | .proxy_config(proxy_config)
|
1212 + | .build_http();
|
1213 + | let http_client = Client::new(&pool);
|
1214 + | let connector_settings = HttpConnectorSettings::builder().build();
|
1215 + | let runtime_components = RuntimeComponentsBuilder::for_tests()
|
1216 + | .with_time_source(Some(SystemTimeSource::new()))
|
1217 + | .build()
|
1218 + | .unwrap();
|
1219 + | let http_connector = http_client.http_connector(&connector_settings, &runtime_components);
|
1220 + |
|
1221 + | let request = HttpRequest::get(target_url)
|
1222 + | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
|
1223 + | let response = http_connector.call(request).await?;
|
1224 + | let status = response.status();
|
1225 + | let body_bytes = response.into_body().collect().await?.to_bytes();
|
1226 + | let body_string = String::from_utf8(body_bytes.to_vec())?;
|
1227 + | Ok((status.into(), body_string))
|
1228 + | }
|
1229 + |
|
1230 + | #[cfg(any(
|
1231 + | feature = "rustls-aws-lc",
|
1232 + | feature = "rustls-aws-lc-fips",
|
1233 + | feature = "rustls-ring"
|
1234 + | ))]
|
1235 + | async fn make_v2_https_request_through_proxy(
|
1236 + | proxy_config: ProxyConfig,
|
1237 + | target_url: &str,
|
1238 + | tls_provider: tls::Provider,
|
1239 + | ) -> Result<(StatusCode, String), Box<dyn std::error::Error + Send + Sync>> {
|
1240 + | use aws_smithy_http_client::pool::{Client, SharedPool};
|
1241 + |
|
1242 + | let pool = SharedPool::builder()
|
1243 + | .tls_provider(tls_provider)
|
1244 + | .proxy_config(proxy_config)
|
1245 + | .build_https();
|
1246 + | let http_client = Client::new(&pool);
|
1247 + | let connector_settings = HttpConnectorSettings::builder().build();
|
1248 + | let runtime_components = RuntimeComponentsBuilder::for_tests()
|
1249 + | .with_time_source(Some(SystemTimeSource::new()))
|
1250 + | .build()
|
1251 + | .unwrap();
|
1252 + | let http_connector = http_client.http_connector(&connector_settings, &runtime_components);
|
1253 + |
|
1254 + | let request = HttpRequest::get(target_url)
|
1255 + | .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
|
1256 + | let response = http_connector.call(request).await?;
|
1257 + | let status = response.status();
|
1258 + | let body_bytes = response.into_body().collect().await?.to_bytes();
|
1259 + | let body_string = String::from_utf8(body_bytes.to_vec())?;
|
1260 + | Ok((status.into(), body_string))
|
1261 + | }
|
1262 + |
|
1263 + | #[tokio::test]
|
1264 + | async fn test_v2_http_proxy_basic_request() {
|
1265 + | let mock_proxy = MockProxyServer::new(|req| {
|
1266 + | assert_eq!(req.method, "GET");
|
1267 + | assert_eq!(req.uri, "http://aws.amazon.com/v2/api");
|
1268 + | Response::builder()
|
1269 + | .status(StatusCode::OK)
|
1270 + | .body("v2 proxied response".to_string())
|
1271 + | .unwrap()
|
1272 + | })
|
1273 + | .await;
|
1274 + |
|
1275 + | let proxy_config = ProxyConfig::http(format!("http://{}", mock_proxy.addr())).unwrap();
|
1276 + | let (status, body) =
|
1277 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/v2/api")
|
1278 + | .await
|
1279 + | .expect("v2 HTTP request through proxy should succeed");
|
1280 + |
|
1281 + | assert_eq!(status, StatusCode::OK);
|
1282 + | assert_eq!(body, "v2 proxied response");
|
1283 + | assert_eq!(mock_proxy.requests()[0].uri, "http://aws.amazon.com/v2/api");
|
1284 + | }
|
1285 + |
|
1286 + | #[tokio::test]
|
1287 + | async fn test_v2_proxy_authentication() {
|
1288 + | let mock_proxy = MockProxyServer::with_auth_validation("v2user", "v2pass").await;
|
1289 + |
|
1290 + | let proxy_config = ProxyConfig::http(format!("http://{}", mock_proxy.addr()))
|
1291 + | .unwrap()
|
1292 + | .with_basic_auth("v2user", "v2pass");
|
1293 + |
|
1294 + | let (status, _) =
|
1295 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/auth/test")
|
1296 + | .await
|
1297 + | .expect("authenticated v2 proxy request should succeed");
|
1298 + | assert_eq!(status, StatusCode::OK);
|
1299 + |
|
1300 + | let requests = mock_proxy.requests();
|
1301 + | let auth_header = requests[0]
|
1302 + | .headers
|
1303 + | .get("proxy-authorization")
|
1304 + | .expect("Proxy-Authorization header should be set");
|
1305 + | let expected = format!(
|
1306 + | "Basic {}",
|
1307 + | base64::engine::general_purpose::STANDARD.encode("v2user:v2pass")
|
1308 + | );
|
1309 + | assert_eq!(auth_header, &expected);
|
1310 + | }
|
1311 + |
|
1312 + | #[tokio::test]
|
1313 + | async fn test_v2_proxy_disabled_uses_direct_connection() {
|
1314 + | let mock_proxy = MockProxyServer::new(|_| {
|
1315 + | Response::builder()
|
1316 + | .status(StatusCode::OK)
|
1317 + | .body("should never reach proxy".to_string())
|
1318 + | .unwrap()
|
1319 + | })
|
1320 + | .await;
|
1321 + | let direct_mock = MockProxyServer::new(|_| {
|
1322 + | Response::builder()
|
1323 + | .status(StatusCode::OK)
|
1324 + | .body("direct response".to_string())
|
1325 + | .unwrap()
|
1326 + | })
|
1327 + | .await;
|
1328 + |
|
1329 + | let target_url = format!("http://{}/direct", direct_mock.addr());
|
1330 + | let (status, body) = make_v2_http_request_through_proxy(ProxyConfig::disabled(), &target_url)
|
1331 + | .await
|
1332 + | .expect("direct v2 request should succeed");
|
1333 + | assert_eq!(status, StatusCode::OK);
|
1334 + | assert_eq!(body, "direct response");
|
1335 + | assert_eq!(mock_proxy.requests().len(), 0);
|
1336 + | assert_eq!(direct_mock.requests().len(), 1);
|
1337 + | }
|
1338 + |
|
1339 + | #[tokio::test]
|
1340 + | async fn test_v2_proxy_from_environment_variables() {
|
1341 + | let mock_proxy = MockProxyServer::new(|req| {
|
1342 + | assert_eq!(req.uri, "http://aws.amazon.com/env/api");
|
1343 + | Response::builder()
|
1344 + | .status(StatusCode::OK)
|
1345 + | .body("env-proxy response".to_string())
|
1346 + | .unwrap()
|
1347 + | })
|
1348 + | .await;
|
1349 + | let proxy_url = format!("http://{}", mock_proxy.addr());
|
1350 + |
|
1351 + | let result = with_env_vars(&[("HTTP_PROXY", &proxy_url)], || async {
|
1352 + | let proxy_config = ProxyConfig::from_env();
|
1353 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/env/api").await
|
1354 + | })
|
1355 + | .await;
|
1356 + |
|
1357 + | let (status, body) = result.expect("env-var v2 proxy request should succeed");
|
1358 + | assert_eq!(status, StatusCode::OK);
|
1359 + | assert_eq!(body, "env-proxy response");
|
1360 + | assert_eq!(mock_proxy.requests().len(), 1);
|
1361 + | }
|
1362 + |
|
1363 + | #[tokio::test]
|
1364 + | async fn test_v2_no_proxy_bypass_rules() {
|
1365 + | let mock_proxy = MockProxyServer::new(|_| {
|
1366 + | Response::builder()
|
1367 + | .status(StatusCode::OK)
|
1368 + | .body("proxied".to_string())
|
1369 + | .unwrap()
|
1370 + | })
|
1371 + | .await;
|
1372 + | let direct_mock = MockProxyServer::new(|_| {
|
1373 + | Response::builder()
|
1374 + | .status(StatusCode::OK)
|
1375 + | .body("direct".to_string())
|
1376 + | .unwrap()
|
1377 + | })
|
1378 + | .await;
|
1379 + |
|
1380 + | let proxy_url = format!("http://{}", mock_proxy.addr());
|
1381 + | let result = with_env_vars(
|
1382 + | &[("HTTP_PROXY", &proxy_url), ("NO_PROXY", "127.0.0.1")],
|
1383 + | || async {
|
1384 + | let proxy_config = ProxyConfig::from_env();
|
1385 + | let target_url = format!("http://{}/bypassed", direct_mock.addr());
|
1386 + | make_v2_http_request_through_proxy(proxy_config, &target_url).await
|
1387 + | },
|
1388 + | )
|
1389 + | .await;
|
1390 + |
|
1391 + | let (status, body) = result.expect("bypassed request should succeed");
|
1392 + | assert_eq!(status, StatusCode::OK);
|
1393 + | assert_eq!(body, "direct");
|
1394 + | assert_eq!(mock_proxy.requests().len(), 0, "proxy should be bypassed");
|
1395 + | assert_eq!(direct_mock.requests().len(), 1);
|
1396 + | }
|
1397 + |
|
1398 + | #[tokio::test]
|
1399 + | async fn test_v2_proxy_connection_failure() {
|
1400 + | let proxy_config = ProxyConfig::http("http://127.0.0.1:1").unwrap();
|
1401 + | let result =
|
1402 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/fail").await;
|
1403 + | assert!(
|
1404 + | result.is_err(),
|
1405 + | "connection to non-existent proxy should fail"
|
1406 + | );
|
1407 + | }
|
1408 + |
|
1409 + | #[tokio::test]
|
1410 + | async fn test_v2_proxy_authentication_failure() {
|
1411 + | let mock_proxy = MockProxyServer::with_auth_validation("correct", "password").await;
|
1412 + |
|
1413 + | let proxy_config = ProxyConfig::http(format!("http://{}", mock_proxy.addr()))
|
1414 + | .unwrap()
|
1415 + | .with_basic_auth("wrong", "creds");
|
1416 + |
|
1417 + | let (status, _) =
|
1418 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/auth/fail")
|
1419 + | .await
|
1420 + | .expect("request should complete (proxy returns 407)");
|
1421 + | assert_eq!(status, StatusCode::PROXY_AUTHENTICATION_REQUIRED);
|
1422 + | }
|
1423 + |
|
1424 + | #[tokio::test]
|
1425 + | async fn test_v2_explicit_proxy_disable_overrides_environment() {
|
1426 + | let mock_proxy = MockProxyServer::new(|_| {
|
1427 + | Response::builder()
|
1428 + | .status(StatusCode::OK)
|
1429 + | .body("via proxy".to_string())
|
1430 + | .unwrap()
|
1431 + | })
|
1432 + | .await;
|
1433 + | let direct_mock = MockProxyServer::new(|_| {
|
1434 + | Response::builder()
|
1435 + | .status(StatusCode::OK)
|
1436 + | .body("direct".to_string())
|
1437 + | .unwrap()
|
1438 + | })
|
1439 + | .await;
|
1440 + |
|
1441 + | let proxy_url = format!("http://{}", mock_proxy.addr());
|
1442 + | let result = with_env_vars(&[("HTTP_PROXY", &proxy_url)], || async {
|
1443 + | let proxy_config = ProxyConfig::disabled();
|
1444 + | let target_url = format!("http://{}/path", direct_mock.addr());
|
1445 + | make_v2_http_request_through_proxy(proxy_config, &target_url).await
|
1446 + | })
|
1447 + | .await;
|
1448 + |
|
1449 + | let (status, body) = result.expect("direct request should succeed");
|
1450 + | assert_eq!(status, StatusCode::OK);
|
1451 + | assert_eq!(body, "direct");
|
1452 + | assert_eq!(mock_proxy.requests().len(), 0);
|
1453 + | assert_eq!(direct_mock.requests().len(), 1);
|
1454 + | }
|
1455 + |
|
1456 + | #[tokio::test]
|
1457 + | async fn test_v2_http_proxy_absolute_uri_form() {
|
1458 + | let mock_proxy = MockProxyServer::new(|req| {
|
1459 + | assert_eq!(
|
1460 + | req.uri, "http://aws.amazon.com/path?query=1",
|
1461 + | "proxy should receive absolute-form URI"
|
1462 + | );
|
1463 + | Response::builder()
|
1464 + | .status(StatusCode::OK)
|
1465 + | .body("ok".to_string())
|
1466 + | .unwrap()
|
1467 + | })
|
1468 + | .await;
|
1469 + |
|
1470 + | let proxy_config = ProxyConfig::http(format!("http://{}", mock_proxy.addr())).unwrap();
|
1471 + | let (status, _) =
|
1472 + | make_v2_http_request_through_proxy(proxy_config, "http://aws.amazon.com/path?query=1")
|
1473 + | .await
|
1474 + | .expect("request should succeed");
|
1475 + | assert_eq!(status, StatusCode::OK);
|
1476 + | }
|
1477 + |
|
1478 + | #[tokio::test]
|
1479 + | async fn test_v2_direct_http_origin_uri_form() {
|
1480 + | let direct_mock = MockProxyServer::new(|req| {
|
1481 + | assert_eq!(
|
1482 + | req.uri, "/path?query=1",
|
1483 + | "direct server should receive origin-form URI"
|
1484 + | );
|
1485 + | Response::builder()
|
1486 + | .status(StatusCode::OK)
|
1487 + | .body("ok".to_string())
|
1488 + | .unwrap()
|
1489 + | })
|
1490 + | .await;
|
1491 + |
|
1492 + | let target_url = format!("http://{}/path?query=1", direct_mock.addr());
|
1493 + | let (status, _) = make_v2_http_request_through_proxy(ProxyConfig::disabled(), &target_url)
|
1494 + | .await
|
1495 + | .expect("request should succeed");
|
1496 + | assert_eq!(status, StatusCode::OK);
|
1497 + | }
|
1498 + |
|
1499 + | #[tokio::test]
|
1500 + | async fn test_v2_set_proxy_config_none_clears_proxy() {
|
1501 + | use aws_smithy_http_client::pool::{Client, SharedPool};
|
1502 + |
|
1503 + | let mock_proxy = MockProxyServer::new(|_| {
|
1504 + | Response::builder()
|
1505 + | .status(StatusCode::OK)
|
1506 + | .body("via proxy".to_string())
|
1507 + | .unwrap()
|
1508 + | })
|
1509 + | .await;
|
1510 + | let direct_mock = MockProxyServer::new(|_| {
|
1511 + | Response::builder()
|
1512 + | .status(StatusCode::OK)
|
1513 + | .body("direct".to_string())
|
1514 + | .unwrap()
|
1515 + | })
|
1516 + | .await;
|
1517 + |
|
1518 + | let mut builder = SharedPool::builder();
|
1519 + | builder.set_proxy_config(Some(
|
1520 + | ProxyConfig::http(format!("http://{}", mock_proxy.addr())).unwrap(),
|
1521 + | ));
|
1522 + | builder.set_proxy_config(None);
|
1523 + | let pool = builder.build_http();
|
1524 + | let http_client = Client::new(&pool);
|
1525 + |
|
1526 + | let connector_settings = HttpConnectorSettings::builder().build();
|
1527 + | let runtime_components = RuntimeComponentsBuilder::for_tests()
|
1528 + | .with_time_source(Some(SystemTimeSource::new()))
|
1529 + | .build()
|
1530 + | .unwrap();
|
1531 + | let http_connector = http_client.http_connector(&connector_settings, &runtime_components);
|
1532 + |
|
1533 + | let target_url = format!("http://{}/path", direct_mock.addr());
|
1534 + | let request = HttpRequest::get(&target_url).unwrap();
|
1535 + | let response = http_connector.call(request).await.unwrap();
|
1536 + | assert_eq!(response.status(), StatusCode::OK.into());
|
1537 + | assert_eq!(mock_proxy.requests().len(), 0);
|
1538 + | assert_eq!(direct_mock.requests().len(), 1);
|
1539 + | }
|
1540 + |
|
1541 + | #[cfg(any(
|
1542 + | feature = "rustls-aws-lc",
|
1543 + | feature = "rustls-aws-lc-fips",
|
1544 + | feature = "rustls-ring"
|
1545 + | ))]
|
1546 + | #[tokio::test]
|
1547 + | async fn test_v2_https_connect_with_auth() {
|
1548 + | let mock_proxy = MockProxyServer::new(|req| {
|
1549 + | assert_eq!(req.method, "CONNECT");
|
1550 + | assert_eq!(req.uri, "secure.aws.amazon.com:443");
|
1551 + |
|
1552 + | let expected_auth = format!(
|
1553 + | "Basic {}",
|
1554 + | base64::prelude::BASE64_STANDARD.encode("connectuser:connectpass")
|
1555 + | );
|
1556 + | assert_eq!(req.headers.get("proxy-authorization"), Some(&expected_auth));
|
1557 + |
|
1558 + | Response::builder()
|
1559 + | .status(StatusCode::BAD_REQUEST)
|
1560 + | .body("CONNECT tunnel setup failed".to_string())
|
1561 + | .unwrap()
|
1562 + | })
|
1563 + | .await;
|
1564 + |
|
1565 + | let proxy_config = ProxyConfig::all(format!("http://{}", mock_proxy.addr()))
|
1566 + | .unwrap()
|
1567 + | .with_basic_auth("connectuser", "connectpass");
|
1568 + |
|
1569 + | let tls_provider = tls::Provider::Rustls(tls::rustls_provider::CryptoMode::AwsLc);
|
1570 + | let result = make_v2_https_request_through_proxy(
|
1571 + | proxy_config,
|
1572 + | "https://secure.aws.amazon.com/api/secure",
|
1573 + | tls_provider,
|
1574 + | )
|
1575 + | .await;
|
1576 + |
|
1577 + | assert!(
|
1578 + | result.is_err(),
|
1579 + | "CONNECT tunnel should fail with 400 response"
|
1580 + | );
|
1581 + | let requests = mock_proxy.requests();
|
1582 + | assert_eq!(requests.len(), 1);
|
1583 + | assert_eq!(requests[0].method, "CONNECT");
|
1584 + | assert_eq!(requests[0].uri, "secure.aws.amazon.com:443");
|
1585 + | }
|
1586 + |
|
1587 + | #[cfg(any(
|
1588 + | feature = "rustls-aws-lc",
|
1589 + | feature = "rustls-aws-lc-fips",
|
1590 + | feature = "rustls-ring"
|
1591 + | ))]
|
1592 + | #[tokio::test]
|
1593 + | async fn test_v2_https_connect_auth_required() {
|
1594 + | let mock_proxy = MockProxyServer::new(|req| {
|
1595 + | assert_eq!(req.method, "CONNECT");
|
1596 + | if req.headers.get("proxy-authorization").is_none() {
|
1597 + | return Response::builder()
|
1598 + | .status(StatusCode::PROXY_AUTHENTICATION_REQUIRED)
|
1599 + | .body("auth required".to_string())
|
1600 + | .unwrap();
|
1601 + | }
|
1602 + | Response::builder()
|
1603 + | .status(StatusCode::OK)
|
1604 + | .body(String::new())
|
1605 + | .unwrap()
|
1606 + | })
|
1607 + | .await;
|
1608 + |
|
1609 + | let proxy_config = ProxyConfig::all(format!("http://{}", mock_proxy.addr())).unwrap();
|
1610 + | let tls_provider = tls::Provider::Rustls(tls::rustls_provider::CryptoMode::AwsLc);
|
1611 + | let result = make_v2_https_request_through_proxy(
|
1612 + | proxy_config,
|
1613 + | "https://secure.aws.amazon.com/api",
|
1614 + | tls_provider,
|
1615 + | )
|
1616 + | .await;
|
1617 + |
|
1618 + | assert!(result.is_err(), "should fail without proxy auth");
|
1619 + | let requests = mock_proxy.requests();
|
1620 + | assert_eq!(requests.len(), 1);
|
1621 + | assert_eq!(requests[0].method, "CONNECT");
|
1622 + | assert!(requests[0].headers.get("proxy-authorization").is_none());
|
1623 + | }
|