AWS SDK

AWS SDK

rev. 174400987dccd7e137fefa96b1143d21c7ddfb78 (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/tests/proxy_tests.rs

@@ -764,764 +911,914 @@
  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
@@ -1096,1099 +1155,1159 @@
 1116   1119   
        let result = make_http_request_through_proxy(proxy_config, &direct_url).await;
 1117   1120   
 1118   1121   
        assert!(result.is_ok(), "Direct request should succeed");
 1119   1122   
        let requests = direct_server.requests();
 1120   1123   
        assert_eq!(requests.len(), 1);
 1121   1124   
    }
 1122   1125   
}
 1123   1126   
 1124   1127   
/// Generic test function for CONNECT URI form validation
 1125   1128   
/// Tests that CONNECT requests use the correct host:port format
        1129  +
#[cfg(any(feature = "rustls-ring", feature = "s2n-tls"))]
 1126   1130   
async fn run_connect_uri_form_test(tls_provider: tls::Provider, provider_name: &str) {
 1127   1131   
    let target_host = "secure.example.com";
 1128   1132   
    let target_port = 443;
 1129   1133   
    let expected_connect_uri = format!("{}:{}", target_host, target_port);
 1130   1134   
 1131   1135   
    // Clone for use in closure
 1132   1136   
    let expected_uri_clone = expected_connect_uri.clone();
 1133   1137   
 1134   1138   
    let mock_proxy = MockProxyServer::new(move |req| {
 1135   1139   
        if req.method == "CONNECT" {
@@ -1165,1169 +0,1623 @@
 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  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http-client/tests/smoke_test_clients.rs

@@ -98,98 +0,241 @@
  118    118   
    let runtime_components = RuntimeComponentsBuilder::for_tests()
  119    119   
        .with_time_source(Some(SystemTimeSource::new()))
  120    120   
        .build()
  121    121   
        .unwrap();
  122    122   
    let connector = client.http_connector(&connector_settings, &runtime_components);
  123    123   
    let _response = connector
  124    124   
        .call(HttpRequest::get("https://amazon.com").unwrap())
  125    125   
        .await?;
  126    126   
    Ok(())
  127    127   
}
         128  +
         129  +
// ---------------------------------------------------------------------------
         130  +
// v2 smoke tests
         131  +
// ---------------------------------------------------------------------------
         132  +
         133  +
use aws_smithy_http_client::pool::{Client as PoolClient, SharedPool};
         134  +
         135  +
#[cfg(feature = "rustls-aws-lc")]
         136  +
#[tokio::test]
         137  +
async fn v2_aws_lc_client() {
         138  +
    let pool = SharedPool::builder()
         139  +
        .tls_provider(tls::Provider::Rustls(
         140  +
            tls::rustls_provider::CryptoMode::AwsLc,
         141  +
        ))
         142  +
        .build_https();
         143  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         144  +
}
         145  +
         146  +
#[cfg(feature = "rustls-aws-lc-fips")]
         147  +
#[tokio::test]
         148  +
async fn v2_aws_lc_fips_client() {
         149  +
    let pool = SharedPool::builder()
         150  +
        .tls_provider(tls::Provider::Rustls(
         151  +
            tls::rustls_provider::CryptoMode::AwsLcFips,
         152  +
        ))
         153  +
        .build_https();
         154  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         155  +
}
         156  +
         157  +
#[cfg(feature = "rustls-ring")]
         158  +
#[tokio::test]
         159  +
async fn v2_ring_client() {
         160  +
    let pool = SharedPool::builder()
         161  +
        .tls_provider(tls::Provider::Rustls(
         162  +
            tls::rustls_provider::CryptoMode::Ring,
         163  +
        ))
         164  +
        .build_https();
         165  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         166  +
}
         167  +
         168  +
#[cfg(feature = "s2n-tls")]
         169  +
#[tokio::test]
         170  +
async fn v2_s2n_tls_client() {
         171  +
    let pool = SharedPool::builder()
         172  +
        .tls_provider(tls::Provider::S2nTls)
         173  +
        .build_https();
         174  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         175  +
}
         176  +
         177  +
#[cfg(feature = "s2n-tls")]
         178  +
#[tokio::test]
         179  +
async fn v2_s2n_tls_timing_populated() {
         180  +
    use aws_smithy_http_client::pool::{ConnectionCreatedEvent, ConnectionEventListener};
         181  +
    use std::sync::Mutex;
         182  +
    use std::time::Duration;
         183  +
         184  +
    struct TimingListener(Mutex<Option<Duration>>);
         185  +
    impl ConnectionEventListener for TimingListener {
         186  +
        fn on_created(&self, event: &ConnectionCreatedEvent) {
         187  +
            *self.0.lock().unwrap() = Some(event.timing().connect_duration());
         188  +
        }
         189  +
    }
         190  +
         191  +
    let listener = Arc::new(TimingListener(Mutex::new(None)));
         192  +
    let pool = SharedPool::builder()
         193  +
        .tls_provider(tls::Provider::S2nTls)
         194  +
        .connection_event_listener(listener.clone() as Arc<dyn ConnectionEventListener>)
         195  +
        .build_https();
         196  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         197  +
         198  +
    let duration = listener
         199  +
        .0
         200  +
        .lock()
         201  +
        .unwrap()
         202  +
        .expect("timing should be populated");
         203  +
    assert!(
         204  +
        duration > Duration::ZERO,
         205  +
        "connect_duration should be > 0 (was {duration:?})"
         206  +
    );
         207  +
}
         208  +
         209  +
#[cfg(feature = "rustls-aws-lc")]
         210  +
#[tokio::test]
         211  +
async fn v2_rustls_timing_populated() {
         212  +
    use aws_smithy_http_client::pool::{ConnectionCreatedEvent, ConnectionEventListener};
         213  +
    use std::sync::Mutex;
         214  +
    use std::time::Duration;
         215  +
         216  +
    struct TimingListener(Mutex<Option<Duration>>);
         217  +
    impl ConnectionEventListener for TimingListener {
         218  +
        fn on_created(&self, event: &ConnectionCreatedEvent) {
         219  +
            *self.0.lock().unwrap() = Some(event.timing().connect_duration());
         220  +
        }
         221  +
    }
         222  +
         223  +
    let listener = Arc::new(TimingListener(Mutex::new(None)));
         224  +
    let pool = SharedPool::builder()
         225  +
        .tls_provider(tls::Provider::Rustls(
         226  +
            tls::rustls_provider::CryptoMode::AwsLc,
         227  +
        ))
         228  +
        .connection_event_listener(listener.clone() as Arc<dyn ConnectionEventListener>)
         229  +
        .build_https();
         230  +
    smoke_test_client(&PoolClient::new(&pool)).await.unwrap();
         231  +
         232  +
    let duration = listener
         233  +
        .0
         234  +
        .lock()
         235  +
        .unwrap()
         236  +
        .expect("timing should be populated");
         237  +
    assert!(
         238  +
        duration > Duration::ZERO,
         239  +
        "connect_duration should be > 0 (was {duration:?})"
         240  +
    );
         241  +
}

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

@@ -12,12 +72,72 @@
   32     32   
   33     33   
struct TestServer {
   34     34   
    _handle: JoinHandle<()>,
   35     35   
    listen_addr: SocketAddr,
   36     36   
    conn_count: Arc<()>,
   37     37   
}
   38     38   
   39     39   
impl TestServer {
   40     40   
    /// Return the number of active connections to this server
   41     41   
    fn conn_count(&self) -> usize {
   42         -
        // 1 reference for the struct MockProxyServer, 1 reference for the
          42  +
        // 1 reference for the struct TestServer, 1 reference for the
   43     43   
        // socket task.
   44     44   
        Arc::strong_count(&self.conn_count)
   45     45   
            .checked_sub(2)
   46     46   
            .expect("de-count 2 refs")
   47     47   
    }
   48     48   
}
   49     49   
   50     50   
async fn server() -> Result<TestServer, BoxError> {
   51     51   
    // Set process wide crypto provider
   52     52   
    let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
@@ -280,280 +339,437 @@
  300    300   
#[cfg(feature = "s2n-tls")]
  301    301   
#[tokio::test]
  302    302   
async fn test_s2n_tls_custom_ca() {
  303    303   
    let client = aws_smithy_http_client::Builder::new()
  304    304   
        .tls_provider(tls::Provider::S2nTls)
  305    305   
        .tls_context(tls_context_from_pem("tests/server.pem"))
  306    306   
        .build_https();
  307    307   
    run_tls_test(&client).await.unwrap()
  308    308   
}
  309    309   
         310  +
// ---------------------------------------------------------------------------
         311  +
// v2 TLS tests
         312  +
// ---------------------------------------------------------------------------
         313  +
         314  +
use aws_smithy_http_client::pool::{Client as PoolClient, SharedPool};
         315  +
         316  +
#[cfg(feature = "rustls-aws-lc")]
         317  +
#[should_panic(expected = "InvalidCertificate(UnknownIssuer)")]
         318  +
#[tokio::test]
         319  +
async fn test_v2_rustls_aws_lc_native_ca() {
         320  +
    let pool = SharedPool::builder()
         321  +
        .tls_provider(tls::Provider::Rustls(
         322  +
            tls::rustls_provider::CryptoMode::AwsLc,
         323  +
        ))
         324  +
        .build_https();
         325  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         326  +
}
         327  +
         328  +
#[cfg(feature = "rustls-aws-lc")]
         329  +
#[tokio::test]
         330  +
async fn test_v2_rustls_aws_lc_custom_ca() {
         331  +
    let pool = SharedPool::builder()
         332  +
        .tls_provider(tls::Provider::Rustls(
         333  +
            tls::rustls_provider::CryptoMode::AwsLc,
         334  +
        ))
         335  +
        .tls_context(tls_context_from_pem("tests/server.pem"))
         336  +
        .build_https();
         337  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         338  +
}
         339  +
         340  +
#[cfg(feature = "rustls-aws-lc-fips")]
         341  +
#[should_panic(expected = "InvalidCertificate(UnknownIssuer)")]
         342  +
#[tokio::test]
         343  +
async fn test_v2_rustls_aws_lc_fips_native_ca() {
         344  +
    let pool = SharedPool::builder()
         345  +
        .tls_provider(tls::Provider::Rustls(
         346  +
            tls::rustls_provider::CryptoMode::AwsLcFips,
         347  +
        ))
         348  +
        .build_https();
         349  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         350  +
}
         351  +
         352  +
#[cfg(feature = "rustls-aws-lc-fips")]
         353  +
#[tokio::test]
         354  +
async fn test_v2_rustls_aws_lc_fips_custom_ca() {
         355  +
    let pool = SharedPool::builder()
         356  +
        .tls_provider(tls::Provider::Rustls(
         357  +
            tls::rustls_provider::CryptoMode::AwsLcFips,
         358  +
        ))
         359  +
        .tls_context(tls_context_from_pem("tests/server.pem"))
         360  +
        .build_https();
         361  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         362  +
}
         363  +
         364  +
#[cfg(feature = "rustls-ring")]
         365  +
#[should_panic(expected = "InvalidCertificate(UnknownIssuer)")]
         366  +
#[tokio::test]
         367  +
async fn test_v2_rustls_ring_native_ca() {
         368  +
    let pool = SharedPool::builder()
         369  +
        .tls_provider(tls::Provider::Rustls(
         370  +
            tls::rustls_provider::CryptoMode::Ring,
         371  +
        ))
         372  +
        .build_https();
         373  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         374  +
}
         375  +
         376  +
#[cfg(feature = "rustls-ring")]
         377  +
#[tokio::test]
         378  +
async fn test_v2_rustls_ring_custom_ca() {
         379  +
    let pool = SharedPool::builder()
         380  +
        .tls_provider(tls::Provider::Rustls(
         381  +
            tls::rustls_provider::CryptoMode::Ring,
         382  +
        ))
         383  +
        .tls_context(tls_context_from_pem("tests/server.pem"))
         384  +
        .build_https();
         385  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         386  +
}
         387  +
         388  +
#[cfg(feature = "s2n-tls")]
         389  +
#[should_panic(expected = "Certificate is untrusted")]
         390  +
#[tokio::test]
         391  +
async fn test_v2_s2n_native_ca() {
         392  +
    let pool = SharedPool::builder()
         393  +
        .tls_provider(tls::Provider::S2nTls)
         394  +
        .build_https();
         395  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         396  +
}
         397  +
         398  +
#[cfg(feature = "s2n-tls")]
         399  +
#[tokio::test]
         400  +
async fn test_v2_s2n_tls_custom_ca() {
         401  +
    let pool = SharedPool::builder()
         402  +
        .tls_provider(tls::Provider::S2nTls)
         403  +
        .tls_context(tls_context_from_pem("tests/server.pem"))
         404  +
        .build_https();
         405  +
    run_tls_test(&PoolClient::new(&pool)).await.unwrap()
         406  +
}
         407  +
  310    408   
async fn run_tls_test(client: &dyn HttpClient) -> Result<(), BoxError> {
  311    409   
    run_tls_test_with_idle_timeout(client, None).await
  312    410   
}
  313    411   
  314    412   
async fn run_tls_test_with_idle_timeout(
  315    413   
    client: &dyn HttpClient,
  316    414   
    pool_timeout: Option<Duration>,
  317    415   
) -> Result<(), BoxError> {
  318    416   
    let server = server().await?;
  319    417   
    let start = tokio::time::Instant::now();

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-mocks/Cargo.toml

@@ -2,2 +45,45 @@
   22     22   
version = "1.6.0"
   23     23   
   24     24   
[dependencies.aws-smithy-runtime-api]
   25     25   
path = "../aws-smithy-runtime-api"
   26     26   
features = ["client", "http-1x", "test-util"]
   27     27   
version = "1.13.0"
   28     28   
   29     29   
[dependencies.aws-smithy-http-client]
   30     30   
path = "../aws-smithy-http-client"
   31     31   
features = ["test-util"]
   32         -
version = "1.2.0"
          32  +
version = "1.3.0"
   33     33   
[dev-dependencies.tokio]
   34     34   
version = "1"
   35     35   
features = ["full"]
   36     36   
   37     37   
[dev-dependencies.aws-smithy-async]
   38     38   
path = "../aws-smithy-async"
   39     39   
features = ["rt-tokio"]
   40     40   
version = "1.3.0"
   41     41   
   42     42   
[dev-dependencies.aws-smithy-runtime]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime-api/src/client/connection.rs

@@ -1,1 +113,150 @@
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
//! Types related to connection monitoring and management.
    7      7   
    8      8   
use aws_smithy_types::config_bag::{Storable, StoreReplace};
    9      9   
use std::fmt;
   10     10   
use std::net::SocketAddr;
   11     11   
use std::sync::{Arc, Mutex};
   12     12   
          13  +
/// Opaque identifier for a physical connection within a pool.
          14  +
///
          15  +
/// Distinct within a pool's lifetime under normal operation (backed by a
          16  +
/// monotonic 64-bit counter). All requests dispatched on the same
          17  +
/// connection (including H2 multiplexed requests) share the same id.
          18  +
/// Useful for correlating requests with connection lifecycle tracing events.
          19  +
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
          20  +
pub struct ConnectionId(u64);
          21  +
          22  +
impl ConnectionId {
          23  +
    /// Create a connection id from a raw numeric value.
          24  +
    ///
          25  +
    /// For HTTP client/pool implementations that assign connection
          26  +
    /// identifiers; the value should be distinct per live connection.
          27  +
    pub fn new(id: u64) -> Self {
          28  +
        Self(id)
          29  +
    }
          30  +
}
          31  +
          32  +
impl fmt::Display for ConnectionId {
          33  +
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
          34  +
        self.0.fmt(f)
          35  +
    }
          36  +
}
          37  +
   13     38   
/// Metadata that tracks the state of an active connection.
   14     39   
#[derive(Clone)]
   15     40   
pub struct ConnectionMetadata {
   16     41   
    is_proxied: bool,
   17     42   
    remote_addr: Option<SocketAddr>,
   18     43   
    local_addr: Option<SocketAddr>,
   19     44   
    poison_fn: Arc<dyn Fn() + Send + Sync>,
          45  +
    connection_id: Option<ConnectionId>,
   20     46   
}
   21     47   
   22     48   
impl ConnectionMetadata {
   23     49   
    /// Poison this connection, ensuring that it won't be reused.
   24     50   
    pub fn poison(&self) {
   25     51   
        tracing::debug!(
   26     52   
            see_for_more_info = "https://smithy-lang.github.io/smithy-rs/design/client/detailed_error_explanations.html",
   27     53   
            "Connection encountered an issue and should not be re-used. Marking it for closure"
   28     54   
        );
   29     55   
        (self.poison_fn)()
   30     56   
    }
   31     57   
   32     58   
    /// Create a new [`ConnectionMetadata`].
   33     59   
    #[deprecated(
   34     60   
        since = "1.1.0",
   35     61   
        note = "`ConnectionMetadata::new` is deprecated in favour of `ConnectionMetadata::builder`."
   36     62   
    )]
   37     63   
    pub fn new(
   38     64   
        is_proxied: bool,
   39     65   
        remote_addr: Option<SocketAddr>,
   40     66   
        poison: impl Fn() + Send + Sync + 'static,
   41     67   
    ) -> Self {
   42     68   
        Self {
   43     69   
            is_proxied,
   44     70   
            remote_addr,
   45     71   
            // need to use builder to set this field
   46     72   
            local_addr: None,
   47     73   
            poison_fn: Arc::new(poison),
          74  +
            connection_id: None,
   48     75   
        }
   49     76   
    }
   50     77   
   51     78   
    /// Builder for this connection metadata
   52     79   
    pub fn builder() -> ConnectionMetadataBuilder {
   53     80   
        ConnectionMetadataBuilder::new()
   54     81   
    }
   55     82   
   56     83   
    /// Get the remote address for this connection, if one is set.
   57     84   
    pub fn remote_addr(&self) -> Option<SocketAddr> {
   58     85   
        self.remote_addr
   59     86   
    }
   60     87   
   61     88   
    /// Get the local address for this connection, if one is set.
   62     89   
    pub fn local_addr(&self) -> Option<SocketAddr> {
   63     90   
        self.local_addr
   64     91   
    }
          92  +
          93  +
    /// Get the connection id, if one was assigned by the HTTP client.
          94  +
    ///
          95  +
    /// `Some` when the HTTP client assigns pool-level connection identifiers;
          96  +
    /// `None` for clients that do not track connection identity.
          97  +
    pub fn connection_id(&self) -> Option<ConnectionId> {
          98  +
        self.connection_id
          99  +
    }
   65    100   
}
   66    101   
   67    102   
impl fmt::Debug for ConnectionMetadata {
   68    103   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
   69    104   
        f.debug_struct("SmithyConnection")
   70    105   
            .field("is_proxied", &self.is_proxied)
   71    106   
            .field("remote_addr", &self.remote_addr)
   72    107   
            .field("local_addr", &self.local_addr)
         108  +
            .field("connection_id", &self.connection_id)
   73    109   
            .finish()
   74    110   
    }
   75    111   
}
   76    112   
   77    113   
/// Builder type that is used to construct a [`ConnectionMetadata`] value.
   78    114   
#[derive(Default)]
   79    115   
pub struct ConnectionMetadataBuilder {
   80    116   
    is_proxied: Option<bool>,
   81    117   
    remote_addr: Option<SocketAddr>,
   82    118   
    local_addr: Option<SocketAddr>,
   83    119   
    poison_fn: Option<Arc<dyn Fn() + Send + Sync>>,
         120  +
    connection_id: Option<ConnectionId>,
   84    121   
}
   85    122   
   86    123   
impl fmt::Debug for ConnectionMetadataBuilder {
   87    124   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
   88    125   
        f.debug_struct("ConnectionMetadataBuilder")
   89    126   
            .field("is_proxied", &self.is_proxied)
   90    127   
            .field("remote_addr", &self.remote_addr)
   91    128   
            .field("local_addr", &self.local_addr)
   92    129   
            .finish()
   93    130   
    }
@@ -128,165 +202,252 @@
  148    185   
    /// A poisoned connection will not be reused for subsequent requests by the pool
  149    186   
    pub fn set_poison_fn(
  150    187   
        &mut self,
  151    188   
        poison_fn: Option<impl Fn() + Send + Sync + 'static>,
  152    189   
    ) -> &mut Self {
  153    190   
        self.poison_fn =
  154    191   
            poison_fn.map(|poison_fn| Arc::new(poison_fn) as Arc<dyn Fn() + Send + Sync>);
  155    192   
        self
  156    193   
    }
  157    194   
         195  +
    /// Set the [`ConnectionId`] the HTTP client assigned to this connection.
         196  +
    pub fn connection_id(mut self, id: ConnectionId) -> Self {
         197  +
        self.connection_id = Some(id);
         198  +
        self
         199  +
    }
         200  +
         201  +
    /// Set the [`ConnectionId`] the HTTP client assigned to this connection.
         202  +
    pub fn set_connection_id(&mut self, id: Option<ConnectionId>) -> &mut Self {
         203  +
        self.connection_id = id;
         204  +
        self
         205  +
    }
         206  +
  158    207   
    /// Build a [`ConnectionMetadata`] value.
  159    208   
    ///
  160    209   
    /// # Panics
  161    210   
    ///
  162    211   
    /// If either the `is_proxied` or `poison_fn` has not been set, then this method will panic
  163    212   
    pub fn build(self) -> ConnectionMetadata {
  164    213   
        ConnectionMetadata {
  165    214   
            is_proxied: self
  166    215   
                .is_proxied
  167    216   
                .expect("is_proxied should be set for ConnectionMetadata"),
  168    217   
            remote_addr: self.remote_addr,
  169    218   
            local_addr: self.local_addr,
  170    219   
            poison_fn: self
  171    220   
                .poison_fn
  172    221   
                .expect("poison_fn should be set for ConnectionMetadata"),
         222  +
            connection_id: self.connection_id,
  173    223   
        }
  174    224   
    }
  175    225   
}
  176    226   
  177    227   
type LoaderFn = dyn Fn() -> Option<ConnectionMetadata> + Send + Sync;
  178    228   
  179    229   
/// State for a middleware that will monitor and manage connections.
  180    230   
#[derive(Clone, Default)]
  181    231   
pub struct CaptureSmithyConnection {
  182    232   
    loader: Arc<Mutex<Option<Box<LoaderFn>>>>,

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/Cargo.toml

@@ -39,39 +99,99 @@
   59     59   
version = "0.1.1"
   60     60   
   61     61   
[dependencies.aws-smithy-types]
   62     62   
path = "../aws-smithy-types"
   63     63   
features = ["http-body-0-4-x"]
   64     64   
version = "1.6.0"
   65     65   
   66     66   
[dependencies.aws-smithy-http-client]
   67     67   
path = "../aws-smithy-http-client"
   68     68   
optional = true
   69         -
version = "1.2.0"
          69  +
version = "1.3.0"
   70     70   
   71     71   
[dependencies.http-02x]
   72     72   
package = "http"
   73     73   
version = "0.2.12"
   74     74   
   75     75   
[dependencies.http-1x]
   76     76   
package = "http"
   77     77   
version = "1.3.1"
   78     78   
   79     79   
[dependencies.http-body-04x]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/tests/reconnect_on_transient_error.rs

@@ -1,1 +309,366 @@
    9      9   
    HttpStatusCodeClassifier, TransientErrorClassifier,
   10     10   
};
   11     11   
use aws_smithy_async::rt::sleep::TokioSleep;
   12     12   
use aws_smithy_runtime::client::http::hyper_014::HyperClientBuilder;
   13     13   
use aws_smithy_runtime::client::http::test_util::wire::{
   14     14   
    RecordedEvent, ReplayedEvent, WireMockServer,
   15     15   
};
   16     16   
use aws_smithy_runtime::client::orchestrator::operation::Operation;
   17     17   
use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs;
   18     18   
use aws_smithy_runtime::{ev, match_events};
          19  +
use aws_smithy_runtime_api::client::http::SharedHttpClient;
   19     20   
use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext;
   20     21   
use aws_smithy_runtime_api::client::orchestrator::OrchestratorError;
   21     22   
use aws_smithy_runtime_api::client::retries::classifiers::{ClassifyRetry, RetryAction};
          23  +
use aws_smithy_runtime_api::shared::IntoShared;
   22     24   
use aws_smithy_types::body::SdkBody;
   23     25   
use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind, ReconnectMode, RetryConfig};
   24     26   
use aws_smithy_types::timeout::TimeoutConfig;
   25         -
use hyper_0_14::client::Builder as HyperBuilder;
   26     27   
use std::fmt;
   27     28   
use std::time::Duration;
   28     29   
   29     30   
const END_OF_TEST: &str = "end_of_test";
   30     31   
   31     32   
#[derive(Debug)]
   32     33   
struct OperationError(ErrorKind);
   33     34   
   34     35   
impl fmt::Display for OperationError {
   35     36   
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
   36     37   
        write!(f, "{:?}", self)
   37     38   
    }
   38     39   
}
   39     40   
   40     41   
impl ProvideErrorKind for OperationError {
   41     42   
    fn retryable_error_kind(&self) -> Option<ErrorKind> {
   42     43   
        Some(self.0)
   43     44   
    }
   44     45   
   45     46   
    fn code(&self) -> Option<&str> {
   46     47   
        None
   47     48   
    }
   48     49   
}
   49     50   
   50     51   
impl std::error::Error for OperationError {}
   51     52   
   52     53   
#[derive(Debug)]
   53     54   
struct TestRetryClassifier;
   54     55   
   55     56   
impl ClassifyRetry for TestRetryClassifier {
   56     57   
    fn classify_retry(&self, ctx: &InterceptorContext) -> RetryAction {
   57     58   
        tracing::info!("classifying retry for {ctx:?}");
   58         -
        // Check for a result
   59     59   
        let output_or_error = ctx.output_or_error();
   60         -
        // Check for an error
   61     60   
        let error = match output_or_error {
   62     61   
            Some(Ok(_)) | None => return RetryAction::NoActionIndicated,
   63     62   
            Some(Err(err)) => err,
   64     63   
        };
   65     64   
   66     65   
        let action = if let Some(err) = error.as_operation_error() {
   67     66   
            tracing::info!("its an operation error: {err:?}");
   68     67   
            let err = err.downcast_ref::<OperationError>().unwrap();
   69     68   
            RetryAction::retryable_error(err.0)
   70     69   
        } else {
   71     70   
            tracing::info!("its something else... using other classifiers");
   72     71   
            let action = TransientErrorClassifier::<OperationError>::new().classify_retry(ctx);
   73     72   
            if action == RetryAction::NoActionIndicated {
   74     73   
                HttpStatusCodeClassifier::default().classify_retry(ctx)
   75     74   
            } else {
   76     75   
                action
   77     76   
            }
   78     77   
        };
   79     78   
   80     79   
        tracing::info!("classified as {action:?}");
   81     80   
        action
   82     81   
    }
   83     82   
   84     83   
    fn name(&self) -> &'static str {
   85     84   
        "test"
   86     85   
    }
   87     86   
}
   88     87   
   89         -
async fn h1_and_h2(events: Vec<ReplayedEvent>, match_clause: impl Fn(&[RecordedEvent])) {
   90         -
    wire_level_test(
   91         -
        events.clone(),
   92         -
        |_b| {},
   93         -
        ReconnectMode::ReconnectOnTransientError,
   94         -
        &match_clause,
   95         -
    )
   96         -
    .await;
   97         -
    wire_level_test(
   98         -
        events,
   99         -
        |b| {
  100         -
            b.http2_only(true);
  101         -
        },
  102         -
        ReconnectMode::ReconnectOnTransientError,
  103         -
        match_clause,
  104         -
    )
  105         -
    .await;
  106         -
    tracing::info!("h2 ok!");
          88  +
/// MakeClient — parameterizes tests over HTTP stacks
          89  +
trait MakeClient: Send + Sync {
          90  +
    fn make(&self, mock: &WireMockServer) -> SharedHttpClient;
          91  +
}
          92  +
          93  +
/// hyper 0.14 legacy stack
          94  +
struct Hyper014Client;
          95  +
          96  +
impl MakeClient for Hyper014Client {
          97  +
    fn make(&self, mock: &WireMockServer) -> SharedHttpClient {
          98  +
        HyperClientBuilder::new()
          99  +
            .build(hyper_0_14::client::HttpConnector::new_with_resolver(
         100  +
                mock.dns_resolver(),
         101  +
            ))
         102  +
            .into_shared()
         103  +
    }
         104  +
}
         105  +
         106  +
/// hyper 0.14 with HTTP/2 only
         107  +
struct Hyper014H2Client;
         108  +
         109  +
impl MakeClient for Hyper014H2Client {
         110  +
    fn make(&self, mock: &WireMockServer) -> SharedHttpClient {
         111  +
        let mut hyper_builder = hyper_0_14::Client::builder();
         112  +
        hyper_builder.http2_only(true);
         113  +
        HyperClientBuilder::new()
         114  +
            .hyper_builder(hyper_builder)
         115  +
            .build(hyper_0_14::client::HttpConnector::new_with_resolver(
         116  +
                mock.dns_resolver(),
         117  +
            ))
         118  +
            .into_shared()
         119  +
    }
         120  +
}
         121  +
         122  +
/// hyper 1.x stack via public Builder API
         123  +
struct Hyper1xClient;
         124  +
         125  +
impl MakeClient for Hyper1xClient {
         126  +
    fn make(&self, mock: &WireMockServer) -> SharedHttpClient {
         127  +
        aws_smithy_http_client::Builder::new().build_with_resolver(mock.dns_resolver())
         128  +
    }
         129  +
}
         130  +
         131  +
/// HTTP client backed by the composable connection pool.
         132  +
struct Hyper1xV2Client;
         133  +
         134  +
impl MakeClient for Hyper1xV2Client {
         135  +
    fn make(&self, mock: &WireMockServer) -> SharedHttpClient {
         136  +
        let pool = aws_smithy_http_client::pool::SharedPool::builder()
         137  +
            .dns_resolver(mock.dns_resolver())
         138  +
            .build_http();
         139  +
        aws_smithy_http_client::pool::Client::new(&pool).into_shared()
         140  +
    }
  107    141   
}
  108    142   
  109         -
/// Repeatedly send test operation until `end_of_test` is received
  110         -
///
  111         -
/// When the test is over, match_clause is evaluated
  112         -
async fn wire_level_test(
         143  +
/// Repeatedly send test operation until `end_of_test` is received, then run match_clause.
         144  +
async fn run_test(
         145  +
    make_client: &dyn MakeClient,
  113    146   
    events: Vec<ReplayedEvent>,
  114         -
    hyper_builder_settings: impl Fn(&mut HyperBuilder),
  115    147   
    reconnect_mode: ReconnectMode,
  116    148   
    match_clause: impl Fn(&[RecordedEvent]),
  117    149   
) {
  118         -
    let mut hyper_builder = hyper_0_14::Client::builder();
  119         -
    hyper_builder_settings(&mut hyper_builder);
  120         -
  121    150   
    let mock = WireMockServer::start(events).await;
  122         -
    let http_client = HyperClientBuilder::new()
  123         -
        .hyper_builder(hyper_builder)
  124         -
        .build(hyper_0_14::client::HttpConnector::new_with_resolver(
  125         -
            mock.dns_resolver(),
  126         -
        ));
         151  +
    let http_client = make_client.make(&mock);
  127    152   
  128    153   
    let operation = Operation::builder()
  129    154   
        .service_name("test")
  130    155   
        .operation_name("test")
  131    156   
        .no_auth()
  132    157   
        .endpoint_url(&mock.endpoint_url())
  133    158   
        .http_client(http_client)
  134    159   
        .timeout_config(
  135    160   
            TimeoutConfig::builder()
  136    161   
                .operation_attempt_timeout(Duration::from_millis(100))
  137    162   
                .build(),
  138    163   
        )
  139    164   
        .standard_retry(&RetryConfig::standard().with_reconnect_mode(reconnect_mode))
  140    165   
        .retry_classifier(TestRetryClassifier)
  141    166   
        .sleep_impl(TokioSleep::new())
  142    167   
        .with_connection_poisoning()
  143    168   
        .serializer({
  144    169   
            let endpoint_url = mock.endpoint_url();
  145    170   
            move |_| {
  146         -
                let request = http_02x::Request::builder()
         171  +
                let request = http_1x::Request::builder()
  147    172   
                    .uri(endpoint_url.clone())
  148         -
                    // Make the body non-replayable since we don't actually want to retry
  149         -
                    .body(SdkBody::from_body_0_4(SdkBody::from("body")))
         173  +
                    .body(SdkBody::from("body"))
  150    174   
                    .unwrap()
  151    175   
                    .try_into()
  152    176   
                    .unwrap();
  153    177   
                tracing::info!("serializing request: {request:?}");
  154    178   
                Ok(request)
  155    179   
            }
  156    180   
        })
  157    181   
        .deserializer(|response| {
  158    182   
            tracing::info!("deserializing response: {:?}", response);
  159    183   
            match response.status() {
  160    184   
                s if s.is_success() => {
  161    185   
                    Ok(String::from_utf8(response.body().bytes().unwrap().into()).unwrap())
  162    186   
                }
  163    187   
                s if s.is_client_error() => Err(OrchestratorError::operation(OperationError(
  164    188   
                    ErrorKind::ServerError,
  165    189   
                ))),
  166    190   
                s if s.is_server_error() => Err(OrchestratorError::operation(OperationError(
  167    191   
                    ErrorKind::TransientError,
  168    192   
                ))),
  169    193   
                _ => panic!("unexpected status: {}", response.status()),
  170    194   
            }
  171    195   
        })
  172    196   
        .build();
  173    197   
  174    198   
    let mut iteration = 0;
  175    199   
    loop {
  176    200   
        tracing::info!("iteration {iteration}...");
  177    201   
        match operation.invoke(()).await {
  178    202   
            Ok(resp) => {
  179    203   
                tracing::info!("response: {:?}", resp);
  180    204   
                if resp == END_OF_TEST {
  181    205   
                    break;
  182    206   
                }
  183    207   
            }
  184    208   
            Err(e) => tracing::info!("error: {:?}", e),
  185    209   
        }
  186    210   
        iteration += 1;
  187    211   
        if iteration > 50 {
  188    212   
            panic!("probably an infinite loop; no satisfying 'end_of_test' response received");
  189    213   
        }
  190    214   
    }
  191    215   
    let events = mock.events();
  192    216   
    match_clause(&events);
  193    217   
    mock.shutdown();
  194    218   
}
  195    219   
         220  +
/// Run a test against all HTTP stacks
         221  +
async fn all_stacks(
         222  +
    events: Vec<ReplayedEvent>,
         223  +
    reconnect_mode: ReconnectMode,
         224  +
    match_clause: impl Fn(&[RecordedEvent]),
         225  +
) {
         226  +
    run_test(
         227  +
        &Hyper014Client,
         228  +
        events.clone(),
         229  +
        reconnect_mode,
         230  +
        &match_clause,
         231  +
    )
         232  +
    .await;
         233  +
    run_test(
         234  +
        &Hyper014H2Client,
         235  +
        events.clone(),
         236  +
        reconnect_mode,
         237  +
        &match_clause,
         238  +
    )
         239  +
    .await;
         240  +
    run_test(
         241  +
        &Hyper1xClient,
         242  +
        events.clone(),
         243  +
        reconnect_mode,
         244  +
        &match_clause,
         245  +
    )
         246  +
    .await;
         247  +
    run_test(&Hyper1xV2Client, events, reconnect_mode, &match_clause).await;
         248  +
}
         249  +
  196    250   
#[tokio::test]
  197    251   
async fn non_transient_errors_no_reconnect() {
  198    252   
    let _logs = capture_test_logs();
  199         -
    h1_and_h2(
         253  +
    all_stacks(
  200    254   
        vec![
  201    255   
            ReplayedEvent::status(400),
  202    256   
            ReplayedEvent::with_body(END_OF_TEST),
  203    257   
        ],
         258  +
        ReconnectMode::ReconnectOnTransientError,
  204    259   
        match_events!(ev!(dns), ev!(connect), ev!(http(400)), ev!(http(200))),
  205    260   
    )
  206         -
    .await
         261  +
    .await;
  207    262   
}
  208    263   
  209    264   
#[tokio::test]
  210    265   
async fn reestablish_dns_on_503() {
  211    266   
    let _logs = capture_test_logs();
  212         -
    h1_and_h2(
         267  +
    all_stacks(
  213    268   
        vec![
  214    269   
            ReplayedEvent::status(503),
  215    270   
            ReplayedEvent::status(503),
  216    271   
            ReplayedEvent::status(503),
  217    272   
            ReplayedEvent::with_body(END_OF_TEST),
  218    273   
        ],
         274  +
        ReconnectMode::ReconnectOnTransientError,
  219    275   
        match_events!(
  220    276   
            // first request
  221    277   
            ev!(dns),
  222    278   
            ev!(connect),
  223    279   
            ev!(http(503)),
  224    280   
            // second request
  225    281   
            ev!(dns),
  226    282   
            ev!(connect),
  227    283   
            ev!(http(503)),
  228    284   
            // third request
  229    285   
            ev!(dns),
  230    286   
            ev!(connect),
  231    287   
            ev!(http(503)),
  232    288   
            // all good
  233    289   
            ev!(dns),
  234    290   
            ev!(connect),
  235    291   
            ev!(http(200))
  236    292   
        ),
  237    293   
    )
  238    294   
    .await;
  239    295   
}
  240    296   
  241    297   
#[tokio::test]
  242    298   
async fn connection_shared_on_success() {
  243    299   
    let _logs = capture_test_logs();
  244         -
    h1_and_h2(
         300  +
    all_stacks(
  245    301   
        vec![
  246    302   
            ReplayedEvent::ok(),
  247    303   
            ReplayedEvent::ok(),
  248    304   
            ReplayedEvent::status(503),
  249    305   
            ReplayedEvent::with_body(END_OF_TEST),
  250    306   
        ],
         307  +
        ReconnectMode::ReconnectOnTransientError,
  251    308   
        match_events!(
  252    309   
            ev!(dns),
  253    310   
            ev!(connect),
  254    311   
            ev!(http(200)),
  255    312   
            ev!(http(200)),
  256    313   
            ev!(http(503)),
  257    314   
            ev!(dns),
  258    315   
            ev!(connect),
  259    316   
            ev!(http(200))
  260    317   
        ),
  261    318   
    )
  262    319   
    .await;
  263    320   
}
  264    321   
  265    322   
#[tokio::test]
  266    323   
async fn no_reconnect_when_disabled() {
  267    324   
    let _logs = capture_test_logs();
  268         -
    wire_level_test(
         325  +
    all_stacks(
  269    326   
        vec![
  270    327   
            ReplayedEvent::status(503),
  271    328   
            ReplayedEvent::with_body(END_OF_TEST),
  272    329   
        ],
  273         -
        |_b| {},
  274    330   
        ReconnectMode::ReuseAllConnections,
  275    331   
        match_events!(ev!(dns), ev!(connect), ev!(http(503)), ev!(http(200))),
  276    332   
    )
  277    333   
    .await;
  278    334   
}
  279    335   
  280    336   
#[tokio::test]
  281    337   
async fn connection_reestablished_after_timeout() {
  282    338   
    let _logs = capture_test_logs();
  283         -
    h1_and_h2(
         339  +
    all_stacks(
  284    340   
        vec![
  285    341   
            ReplayedEvent::ok(),
  286    342   
            ReplayedEvent::Timeout,
  287    343   
            ReplayedEvent::ok(),
  288    344   
            ReplayedEvent::Timeout,
  289    345   
            ReplayedEvent::with_body(END_OF_TEST),
  290    346   
        ],
         347  +
        ReconnectMode::ReconnectOnTransientError,
  291    348   
        match_events!(
  292    349   
            // first connection
  293    350   
            ev!(dns),
  294    351   
            ev!(connect),
  295    352   
            ev!(http(200)),
  296    353   
            // reuse but got a timeout
  297    354   
            ev!(timeout),
  298    355   
            // so we reconnect
  299    356   
            ev!(dns),
  300    357   
            ev!(connect),

tmp-codegen-diff/aws-sdk/sdk/bedrockruntime/Cargo.toml

@@ -89,89 +149,149 @@
  109    109   
version = "1.3.0"
  110    110   
  111    111   
[dev-dependencies.aws-smithy-eventstream]
  112    112   
path = "../aws-smithy-eventstream"
  113    113   
features = ["test-util"]
  114    114   
version = "0.61.1"
  115    115   
  116    116   
[dev-dependencies.aws-smithy-http-client]
  117    117   
path = "../aws-smithy-http-client"
  118    118   
features = ["test-util", "wire-mock"]
  119         -
version = "1.2.0"
         119  +
version = "1.3.0"
  120    120   
  121    121   
[dev-dependencies.aws-smithy-protocol-test]
  122    122   
path = "../aws-smithy-protocol-test"
  123    123   
version = "0.64.0"
  124    124   
  125    125   
[dev-dependencies.aws-smithy-runtime]
  126    126   
path = "../aws-smithy-runtime"
  127    127   
features = ["test-util"]
  128    128   
version = "1.12.0"
  129    129   

tmp-codegen-diff/aws-sdk/sdk/cloudwatchlogs/Cargo.toml

@@ -82,82 +142,142 @@
  102    102   
version = "1.3.0"
  103    103   
  104    104   
[dev-dependencies.aws-smithy-eventstream]
  105    105   
path = "../aws-smithy-eventstream"
  106    106   
features = ["test-util"]
  107    107   
version = "0.61.1"
  108    108   
  109    109   
[dev-dependencies.aws-smithy-http-client]
  110    110   
path = "../aws-smithy-http-client"
  111    111   
features = ["test-util", "wire-mock"]
  112         -
version = "1.2.0"
         112  +
version = "1.3.0"
  113    113   
  114    114   
[dev-dependencies.aws-smithy-protocol-test]
  115    115   
path = "../aws-smithy-protocol-test"
  116    116   
version = "0.64.0"
  117    117   
  118    118   
[dev-dependencies.aws-smithy-runtime]
  119    119   
path = "../aws-smithy-runtime"
  120    120   
features = ["test-util"]
  121    121   
version = "1.12.0"
  122    122   

tmp-codegen-diff/aws-sdk/sdk/codecatalyst/Cargo.toml

@@ -71,71 +131,131 @@
   91     91   
version = "1.8.0"
   92     92   
   93     93   
[dev-dependencies.aws-smithy-async]
   94     94   
path = "../aws-smithy-async"
   95     95   
features = ["test-util"]
   96     96   
version = "1.3.0"
   97     97   
   98     98   
[dev-dependencies.aws-smithy-http-client]
   99     99   
path = "../aws-smithy-http-client"
  100    100   
features = ["test-util", "wire-mock"]
  101         -
version = "1.2.0"
         101  +
version = "1.3.0"
  102    102   
  103    103   
[dev-dependencies.aws-smithy-protocol-test]
  104    104   
path = "../aws-smithy-protocol-test"
  105    105   
version = "0.64.0"
  106    106   
  107    107   
[dev-dependencies.aws-smithy-runtime]
  108    108   
path = "../aws-smithy-runtime"
  109    109   
features = ["test-util"]
  110    110   
version = "1.12.0"
  111    111   

tmp-codegen-diff/aws-sdk/sdk/dynamodb/Cargo.toml

@@ -77,77 +137,137 @@
   97     97   
version = "1.8.0"
   98     98   
   99     99   
[dev-dependencies.aws-smithy-async]
  100    100   
path = "../aws-smithy-async"
  101    101   
features = ["test-util"]
  102    102   
version = "1.3.0"
  103    103   
  104    104   
[dev-dependencies.aws-smithy-http-client]
  105    105   
path = "../aws-smithy-http-client"
  106    106   
features = ["test-util", "wire-mock"]
  107         -
version = "1.2.0"
         107  +
version = "1.3.0"
  108    108   
  109    109   
[dev-dependencies.aws-smithy-protocol-test]
  110    110   
path = "../aws-smithy-protocol-test"
  111    111   
version = "0.64.0"
  112    112   
  113    113   
[dev-dependencies.aws-smithy-runtime]
  114    114   
path = "../aws-smithy-runtime"
  115    115   
features = ["test-util"]
  116    116   
version = "1.12.0"
  117    117   

tmp-codegen-diff/aws-sdk/sdk/ec2/Cargo.toml

@@ -76,76 +136,136 @@
   96     96   
version = "1.8.0"
   97     97   
   98     98   
[dev-dependencies.aws-smithy-async]
   99     99   
path = "../aws-smithy-async"
  100    100   
features = ["test-util"]
  101    101   
version = "1.3.0"
  102    102   
  103    103   
[dev-dependencies.aws-smithy-http-client]
  104    104   
path = "../aws-smithy-http-client"
  105    105   
features = ["test-util", "wire-mock"]
  106         -
version = "1.2.0"
         106  +
version = "1.3.0"
  107    107   
  108    108   
[dev-dependencies.aws-smithy-protocol-test]
  109    109   
path = "../aws-smithy-protocol-test"
  110    110   
version = "0.64.0"
  111    111   
  112    112   
[dev-dependencies.aws-smithy-runtime]
  113    113   
path = "../aws-smithy-runtime"
  114    114   
features = ["test-util"]
  115    115   
version = "1.12.0"
  116    116   

tmp-codegen-diff/aws-sdk/sdk/glacier/Cargo.toml

@@ -81,81 +141,141 @@
  101    101   
version = "1.8.0"
  102    102   
  103    103   
[dev-dependencies.aws-smithy-async]
  104    104   
path = "../aws-smithy-async"
  105    105   
features = ["test-util"]
  106    106   
version = "1.3.0"
  107    107   
  108    108   
[dev-dependencies.aws-smithy-http-client]
  109    109   
path = "../aws-smithy-http-client"
  110    110   
features = ["test-util", "wire-mock"]
  111         -
version = "1.2.0"
         111  +
version = "1.3.0"
  112    112   
  113    113   
[dev-dependencies.aws-smithy-protocol-test]
  114    114   
path = "../aws-smithy-protocol-test"
  115    115   
version = "0.64.0"
  116    116   
  117    117   
[dev-dependencies.aws-smithy-runtime]
  118    118   
path = "../aws-smithy-runtime"
  119    119   
features = ["test-util"]
  120    120   
version = "1.12.0"
  121    121   

tmp-codegen-diff/aws-sdk/sdk/iam/Cargo.toml

@@ -76,76 +136,136 @@
   96     96   
version = "1.8.0"
   97     97   
   98     98   
[dev-dependencies.aws-smithy-async]
   99     99   
path = "../aws-smithy-async"
  100    100   
features = ["test-util"]
  101    101   
version = "1.3.0"
  102    102   
  103    103   
[dev-dependencies.aws-smithy-http-client]
  104    104   
path = "../aws-smithy-http-client"
  105    105   
features = ["test-util", "wire-mock"]
  106         -
version = "1.2.0"
         106  +
version = "1.3.0"
  107    107   
  108    108   
[dev-dependencies.aws-smithy-protocol-test]
  109    109   
path = "../aws-smithy-protocol-test"
  110    110   
version = "0.64.0"
  111    111   
  112    112   
[dev-dependencies.aws-smithy-runtime]
  113    113   
path = "../aws-smithy-runtime"
  114    114   
features = ["test-util"]
  115    115   
version = "1.12.0"
  116    116   

tmp-codegen-diff/aws-sdk/sdk/kms/Cargo.toml

@@ -71,71 +131,131 @@
   91     91   
version = "1.8.0"
   92     92   
   93     93   
[dev-dependencies.aws-smithy-async]
   94     94   
path = "../aws-smithy-async"
   95     95   
features = ["test-util"]
   96     96   
version = "1.3.0"
   97     97   
   98     98   
[dev-dependencies.aws-smithy-http-client]
   99     99   
path = "../aws-smithy-http-client"
  100    100   
features = ["test-util", "wire-mock"]
  101         -
version = "1.2.0"
         101  +
version = "1.3.0"
  102    102   
  103    103   
[dev-dependencies.aws-smithy-protocol-test]
  104    104   
path = "../aws-smithy-protocol-test"
  105    105   
version = "0.64.0"
  106    106   
  107    107   
[dev-dependencies.aws-smithy-runtime]
  108    108   
path = "../aws-smithy-runtime"
  109    109   
features = ["test-util"]
  110    110   
version = "1.12.0"
  111    111   

tmp-codegen-diff/aws-sdk/sdk/lambda/Cargo.toml

@@ -82,82 +142,142 @@
  102    102   
version = "1.3.0"
  103    103   
  104    104   
[dev-dependencies.aws-smithy-eventstream]
  105    105   
path = "../aws-smithy-eventstream"
  106    106   
features = ["test-util"]
  107    107   
version = "0.61.1"
  108    108   
  109    109   
[dev-dependencies.aws-smithy-http-client]
  110    110   
path = "../aws-smithy-http-client"
  111    111   
features = ["test-util", "wire-mock"]
  112         -
version = "1.2.0"
         112  +
version = "1.3.0"
  113    113   
  114    114   
[dev-dependencies.aws-smithy-protocol-test]
  115    115   
path = "../aws-smithy-protocol-test"
  116    116   
version = "0.64.0"
  117    117   
  118    118   
[dev-dependencies.aws-smithy-runtime]
  119    119   
path = "../aws-smithy-runtime"
  120    120   
features = ["test-util"]
  121    121   
version = "1.12.0"
  122    122