AWS SDK

AWS SDK

rev. 756b1cca7fb9fc6aa0861954bbb4bb8a606ee759

Files changed:

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

@@ -39,39 +99,99 @@
   59     59   
path = "../aws-smithy-http"
   60     60   
version = "0.62.3"
   61     61   
   62     62   
[dependencies.aws-smithy-json]
   63     63   
path = "../aws-smithy-json"
   64     64   
version = "0.61.5"
   65     65   
   66     66   
[dependencies.aws-smithy-runtime]
   67     67   
path = "../aws-smithy-runtime"
   68     68   
features = ["client"]
   69         -
version = "1.9.0"
          69  +
version = "1.9.1"
   70     70   
   71     71   
[dependencies.aws-smithy-runtime-api]
   72     72   
path = "../aws-smithy-runtime-api"
   73     73   
features = ["client"]
   74     74   
version = "1.9.0"
   75     75   
   76     76   
[dependencies.aws-smithy-types]
   77     77   
path = "../aws-smithy-types"
   78     78   
version = "1.3.2"
   79     79   
@@ -106,106 +157,157 @@
  126    126   
version = "1.2.5"
  127    127   
  128    128   
[dev-dependencies.aws-smithy-http-client]
  129    129   
path = "../aws-smithy-http-client"
  130    130   
features = ["default-client", "test-util"]
  131    131   
version = "1.1.0"
  132    132   
  133    133   
[dev-dependencies.aws-smithy-runtime]
  134    134   
path = "../aws-smithy-runtime"
  135    135   
features = ["client", "test-util"]
  136         -
version = "1.9.0"
         136  +
version = "1.9.1"
  137    137   
  138    138   
[dev-dependencies.aws-smithy-runtime-api]
  139    139   
path = "../aws-smithy-runtime-api"
  140    140   
features = ["test-util"]
  141    141   
version = "1.9.0"
  142    142   
  143    143   
[dev-dependencies.futures-util]
  144    144   
version = "0.3.29"
  145    145   
default-features = false
  146    146   

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

@@ -28,28 +88,88 @@
   48     48   
optional = true
   49     49   
version = "0.60.11"
   50     50   
   51     51   
[dependencies.aws-smithy-http]
   52     52   
path = "../aws-smithy-http"
   53     53   
version = "0.62.3"
   54     54   
   55     55   
[dependencies.aws-smithy-runtime]
   56     56   
path = "../aws-smithy-runtime"
   57     57   
features = ["client"]
   58         -
version = "1.9.0"
          58  +
version = "1.9.1"
   59     59   
   60     60   
[dependencies.aws-smithy-runtime-api]
   61     61   
path = "../aws-smithy-runtime-api"
   62     62   
features = ["client"]
   63     63   
version = "1.9.0"
   64     64   
   65     65   
[dependencies.aws-smithy-types]
   66     66   
path = "../aws-smithy-types"
   67     67   
version = "1.3.2"
   68     68   

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

@@ -14,14 +0,44 @@
   34     34   
features = ["full"]
   35     35   
   36     36   
[dev-dependencies.aws-smithy-async]
   37     37   
path = "../aws-smithy-async"
   38     38   
features = ["rt-tokio"]
   39     39   
version = "1.2.5"
   40     40   
   41     41   
[dev-dependencies.aws-smithy-runtime]
   42     42   
path = "../aws-smithy-runtime"
   43     43   
features = ["client"]
   44         -
version = "1.9.0"
          44  +
version = "1.9.1"

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

@@ -1,1 +94,98 @@
    1      1   
# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
    2      2   
[package]
    3      3   
name = "aws-smithy-runtime"
    4         -
version = "1.9.0"
           4  +
version = "1.9.1"
    5      5   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Zelda Hessler <zhessler@amazon.com>"]
    6      6   
description = "The new smithy runtime crate"
    7      7   
edition = "2021"
    8      8   
license = "Apache-2.0"
    9      9   
repository = "https://github.com/smithy-lang/smithy-rs"
   10     10   
[package.metadata.docs.rs]
   11     11   
all-features = true
   12     12   
targets = ["x86_64-unknown-linux-gnu"]
   13     13   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   14     14   
rustdoc-args = ["--cfg", "docsrs"]
   15     15   
   16     16   
[package.metadata.smithy-rs-release-tooling]
   17     17   
stable = true
   18     18   
[package.metadata.cargo-udeps.ignore]
   19     19   
normal = ["aws-smithy-http"]
   20     20   
   21     21   
[features]
   22     22   
client = ["aws-smithy-runtime-api/client", "aws-smithy-types/http-body-1-x"]
   23     23   
http-auth = ["aws-smithy-runtime-api/http-auth"]
   24     24   
connector-hyper-0-14-x = ["dep:aws-smithy-http-client", "aws-smithy-http-client?/hyper-014"]
   25     25   
tls-rustls = ["dep:aws-smithy-http-client", "aws-smithy-http-client?/legacy-rustls-ring", "connector-hyper-0-14-x"]
   26     26   
default-https-client = ["dep:aws-smithy-http-client", "aws-smithy-http-client?/rustls-aws-lc"]
   27         -
rt-tokio = ["tokio/rt"]
          27  +
rt-tokio = ["tokio/rt", "hickory-resolver"]
   28     28   
test-util = ["aws-smithy-runtime-api/test-util", "dep:tracing-subscriber", "aws-smithy-http-client/test-util", "legacy-test-util"]
   29     29   
legacy-test-util = ["aws-smithy-runtime-api/test-util", "dep:tracing-subscriber", "aws-smithy-http-client/test-util", "connector-hyper-0-14-x", "aws-smithy-http-client/legacy-test-util"]
   30     30   
wire-mock = ["legacy-test-util", "aws-smithy-http-client/wire-mock"]
   31     31   
   32     32   
[dependencies]
   33     33   
bytes = "1.10.0"
   34     34   
fastrand = "2.3.0"
   35     35   
pin-project-lite = "0.2.14"
   36     36   
pin-utils = "0.1.0"
   37     37   
tracing = "0.1.40"
   38     38   
   39     39   
[dependencies.aws-smithy-async]
   40     40   
path = "../aws-smithy-async"
   41     41   
version = "1.2.5"
   42     42   
   43     43   
[dependencies.aws-smithy-http]
   44     44   
path = "../aws-smithy-http"
   45     45   
version = "0.62.3"
   46     46   
   47     47   
[dependencies.aws-smithy-observability]
   48     48   
path = "../aws-smithy-observability"
   49     49   
version = "0.1.3"
   50     50   
   51     51   
[dependencies.aws-smithy-runtime-api]
   52     52   
path = "../aws-smithy-runtime-api"
   53     53   
version = "1.9.0"
   54     54   
   55     55   
[dependencies.aws-smithy-types]
   56     56   
path = "../aws-smithy-types"
   57     57   
features = ["http-body-0-4-x"]
   58     58   
version = "1.3.2"
   59     59   
   60     60   
[dependencies.aws-smithy-http-client]
   61     61   
path = "../aws-smithy-http-client"
   62     62   
optional = true
   63     63   
version = "1.1.0"
   64     64   
          65  +
[dependencies.hickory-resolver]
          66  +
version = "0.25.2"
          67  +
optional = true
          68  +
   65     69   
[dependencies.http-02x]
   66     70   
package = "http"
   67     71   
version = "0.2.9"
   68     72   
   69     73   
[dependencies.http-1x]
   70     74   
package = "http"
   71     75   
version = "1"
   72     76   
   73     77   
[dependencies.http-body-04x]
   74     78   
package = "http-body"

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

@@ -1,1 +0,18 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
//! Built-in DNS resolver implementations.
    7      7   
    8      8   
#[cfg(all(feature = "rt-tokio", not(target_family = "wasm")))]
    9         -
mod tokio {
   10         -
    use aws_smithy_runtime_api::client::dns::{DnsFuture, ResolveDns, ResolveDnsError};
   11         -
    use std::io::{Error as IoError, ErrorKind as IoErrorKind};
   12         -
    use std::net::ToSocketAddrs;
           9  +
mod tokio;
   13     10   
   14         -
    /// DNS resolver that uses `tokio::spawn_blocking` to resolve DNS using the standard library.
   15         -
    ///
   16         -
    /// This implementation isn't available for WASM targets.
   17         -
    #[non_exhaustive]
   18         -
    #[derive(Debug, Default)]
   19         -
    pub struct TokioDnsResolver;
   20         -
   21         -
    impl TokioDnsResolver {
   22         -
        /// Creates a new Tokio DNS resolver
   23         -
        pub fn new() -> Self {
   24         -
            Self
   25         -
        }
   26         -
    }
          11  +
#[cfg(all(feature = "rt-tokio", not(target_family = "wasm")))]
          12  +
pub use self::tokio::TokioDnsResolver;
   27     13   
   28         -
    impl ResolveDns for TokioDnsResolver {
   29         -
        fn resolve_dns<'a>(&'a self, name: &'a str) -> DnsFuture<'a> {
   30         -
            let name = name.to_string();
   31         -
            DnsFuture::new(async move {
   32         -
                let result = tokio::task::spawn_blocking(move || (name, 0).to_socket_addrs()).await;
   33         -
                match result {
   34         -
                    Err(join_failure) => Err(ResolveDnsError::new(IoError::new(
   35         -
                        IoErrorKind::Other,
   36         -
                        join_failure,
   37         -
                    ))),
   38         -
                    Ok(Ok(dns_result)) => {
   39         -
                        Ok(dns_result.into_iter().map(|addr| addr.ip()).collect())
   40         -
                    }
   41         -
                    Ok(Err(dns_failure)) => Err(ResolveDnsError::new(dns_failure)),
   42         -
                }
   43         -
            })
   44         -
        }
   45         -
    }
   46         -
}
          14  +
#[cfg(all(feature = "rt-tokio", not(target_family = "wasm")))]
          15  +
mod caching;
   47     16   
   48     17   
#[cfg(all(feature = "rt-tokio", not(target_family = "wasm")))]
   49         -
pub use self::tokio::TokioDnsResolver;
          18  +
pub use self::caching::CachingDnsResolver;

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/src/client/dns/caching.rs

@@ -0,1 +0,157 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
use std::{
           7  +
    io::{Error as IoError, ErrorKind as IoErrorKind},
           8  +
    net::IpAddr,
           9  +
    time::Duration,
          10  +
};
          11  +
          12  +
use aws_smithy_runtime_api::client::dns::{DnsFuture, ResolveDns, ResolveDnsError};
          13  +
use hickory_resolver::{
          14  +
    config::{NameServerConfigGroup, ResolverConfig},
          15  +
    name_server::TokioConnectionProvider,
          16  +
    Resolver,
          17  +
};
          18  +
          19  +
/// DNS resolver that uses [hickory_resolver] and caches DNS entries in memory.
          20  +
///
          21  +
/// This resolver requires a [tokio] runtime to function and isn't available for WASM targets.
          22  +
#[non_exhaustive]
          23  +
#[derive(Debug)]
          24  +
pub struct CachingDnsResolver {
          25  +
    resolver: Resolver<TokioConnectionProvider>,
          26  +
}
          27  +
          28  +
impl Default for CachingDnsResolver {
          29  +
    /// Constructs a new Tokio based [ResolveDns] with the system configuration.
          30  +
    /// This uses `/etc/resolv.conf` on Unix OSes and registry settings on Windows.
          31  +
    fn default() -> Self {
          32  +
        Self {
          33  +
            resolver: Resolver::builder_tokio().expect("In tokio runtime").build(),
          34  +
        }
          35  +
    }
          36  +
}
          37  +
          38  +
impl CachingDnsResolver {
          39  +
    /// Creates a new DNS resolver that caches IP addresses in memory.
          40  +
    pub fn builder() -> CachingDnsResolverBuilder {
          41  +
        CachingDnsResolverBuilder {
          42  +
            nameservers: None,
          43  +
            timeout: None,
          44  +
            attempts: None,
          45  +
            cache_size: None,
          46  +
            num_concurrent_reqs: None,
          47  +
        }
          48  +
    }
          49  +
          50  +
    /// Flush the cache
          51  +
    pub fn clear_cache(&self) {
          52  +
        self.resolver.clear_cache();
          53  +
    }
          54  +
}
          55  +
          56  +
impl ResolveDns for CachingDnsResolver {
          57  +
    fn resolve_dns<'a>(&'a self, name: &'a str) -> DnsFuture<'a> {
          58  +
        DnsFuture::new(async move {
          59  +
            let result = self.resolver.lookup_ip(name).await;
          60  +
          61  +
            match result {
          62  +
                Ok(ips) => Ok(ips.into_iter().collect()),
          63  +
                Err(failure) => Err(ResolveDnsError::new(IoError::new(
          64  +
                    IoErrorKind::Other,
          65  +
                    failure,
          66  +
                ))),
          67  +
            }
          68  +
        })
          69  +
    }
          70  +
}
          71  +
          72  +
pub struct CachingDnsResolverBuilder {
          73  +
    nameservers: Option<Nameservers>,
          74  +
    timeout: Option<Duration>,
          75  +
    attempts: Option<usize>,
          76  +
    cache_size: Option<usize>,
          77  +
    num_concurrent_reqs: Option<usize>,
          78  +
}
          79  +
          80  +
struct Nameservers {
          81  +
    ips: Vec<IpAddr>,
          82  +
    port: u16,
          83  +
}
          84  +
          85  +
impl CachingDnsResolverBuilder {
          86  +
    /// Configure upstream nameservers and the port to use for resolution. Defaults to the system
          87  +
    /// configuration.
          88  +
    pub fn nameservers(mut self, ips: &[IpAddr], port: u16) -> Self {
          89  +
        self.nameservers = Some(Nameservers {
          90  +
            ips: ips.to_vec(),
          91  +
            port,
          92  +
        });
          93  +
        self
          94  +
    }
          95  +
          96  +
    /// Specify the timeout for a request. Defaults to 5 seconds.
          97  +
    pub fn timeout(mut self, timeout: Duration) -> Self {
          98  +
        self.timeout = Some(timeout);
          99  +
        self
         100  +
    }
         101  +
         102  +
    /// Number of retries after lookup failure before giving up. Defaults to 2.
         103  +
    pub fn attempts(mut self, attempts: usize) -> Self {
         104  +
        self.attempts = Some(attempts);
         105  +
        self
         106  +
    }
         107  +
         108  +
    /// Cache size is in number of records (some records can be large). Defaults to 32.
         109  +
    pub fn cache_size(mut self, cache_size: usize) -> Self {
         110  +
        self.cache_size = Some(cache_size);
         111  +
        self
         112  +
    }
         113  +
         114  +
    /// Number of concurrent requests per query.
         115  +
    ///
         116  +
    /// Where more than one nameserver is configured, this configures the resolver
         117  +
    /// to send queries to a number of servers in parallel. Defaults to 2. Setting
         118  +
    /// to 0 or 1 will execute requests serially.
         119  +
    pub fn num_concurrent_reqs(mut self, num_concurrent_reqs: usize) -> Self {
         120  +
        self.num_concurrent_reqs = Some(num_concurrent_reqs);
         121  +
        self
         122  +
    }
         123  +
         124  +
    pub fn build(self) -> CachingDnsResolver {
         125  +
        let mut builder = if let Some(nameservers) = self.nameservers {
         126  +
            let nameserver_config =
         127  +
                NameServerConfigGroup::from_ips_clear(&nameservers.ips, nameservers.port, true);
         128  +
            let resolver_config = ResolverConfig::from_parts(None, vec![], nameserver_config);
         129  +
         130  +
            Resolver::builder_with_config(resolver_config, TokioConnectionProvider::default())
         131  +
        } else {
         132  +
            Resolver::builder_tokio().expect("Successfully read system config")
         133  +
        };
         134  +
         135  +
        let opts = builder.options_mut();
         136  +
         137  +
        if let Some(timeout) = self.timeout {
         138  +
            opts.timeout = timeout;
         139  +
        }
         140  +
         141  +
        if let Some(attempts) = self.attempts {
         142  +
            opts.attempts = attempts;
         143  +
        }
         144  +
         145  +
        if let Some(cache_size) = self.cache_size {
         146  +
            opts.cache_size = cache_size;
         147  +
        }
         148  +
         149  +
        if let Some(num_concurrent_reqs) = self.num_concurrent_reqs {
         150  +
            opts.num_concurrent_reqs = num_concurrent_reqs;
         151  +
        }
         152  +
         153  +
        CachingDnsResolver {
         154  +
            resolver: builder.build(),
         155  +
        }
         156  +
    }
         157  +
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/src/client/dns/tokio.rs

@@ -0,1 +0,39 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
use aws_smithy_runtime_api::client::dns::{DnsFuture, ResolveDns, ResolveDnsError};
           7  +
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
           8  +
use std::net::ToSocketAddrs;
           9  +
          10  +
/// DNS resolver that uses `tokio::spawn_blocking` to resolve DNS using the standard library.
          11  +
///
          12  +
/// This implementation isn't available for WASM targets.
          13  +
#[non_exhaustive]
          14  +
#[derive(Debug, Default)]
          15  +
pub struct TokioDnsResolver;
          16  +
          17  +
impl TokioDnsResolver {
          18  +
    /// Creates a new Tokio DNS resolver
          19  +
    pub fn new() -> Self {
          20  +
        Self
          21  +
    }
          22  +
}
          23  +
          24  +
impl ResolveDns for TokioDnsResolver {
          25  +
    fn resolve_dns<'a>(&'a self, name: &'a str) -> DnsFuture<'a> {
          26  +
        let name = name.to_string();
          27  +
        DnsFuture::new(async move {
          28  +
            let result = tokio::task::spawn_blocking(move || (name, 0).to_socket_addrs()).await;
          29  +
            match result {
          30  +
                Err(join_failure) => Err(ResolveDnsError::new(IoError::new(
          31  +
                    IoErrorKind::Other,
          32  +
                    join_failure,
          33  +
                ))),
          34  +
                Ok(Ok(dns_result)) => Ok(dns_result.into_iter().map(|addr| addr.ip()).collect()),
          35  +
                Ok(Err(dns_failure)) => Err(ResolveDnsError::new(dns_failure)),
          36  +
            }
          37  +
        })
          38  +
    }
          39  +
}

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

@@ -0,1 +0,333 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
#![cfg(all(feature = "client", feature = "rt-tokio", not(target_family = "wasm")))]
           7  +
           8  +
use aws_smithy_runtime::client::dns::CachingDnsResolver;
           9  +
use aws_smithy_runtime_api::client::dns::ResolveDns;
          10  +
use std::{
          11  +
    net::{IpAddr, Ipv4Addr},
          12  +
    time::{Duration, Instant},
          13  +
};
          14  +
use tokio::test;
          15  +
          16  +
#[test]
          17  +
async fn test_dns_caching_performance() {
          18  +
    let dns_server = test_dns_server::setup_dns_server().await;
          19  +
    let (dns_ip, dns_port) = dns_server.addr();
          20  +
          21  +
    let resolver = CachingDnsResolver::builder()
          22  +
        .nameservers(&[dns_ip], dns_port)
          23  +
        .cache_size(1)
          24  +
        .build();
          25  +
          26  +
    let hostname = "example.com";
          27  +
          28  +
    // First resolution should hit the network
          29  +
    let start = Instant::now();
          30  +
    let first_result = resolver.resolve_dns(hostname).await;
          31  +
    let first_duration = start.elapsed();
          32  +
          33  +
    let first_ips = first_result.unwrap();
          34  +
    assert!(!first_ips.is_empty());
          35  +
          36  +
    // Second resolution should hit the cache
          37  +
    let start = Instant::now();
          38  +
    let second_result = resolver.resolve_dns(hostname).await;
          39  +
    let second_duration = start.elapsed();
          40  +
          41  +
    let second_ips = second_result.unwrap();
          42  +
          43  +
    // Verify same IPs returned
          44  +
    assert_eq!(first_ips, second_ips);
          45  +
          46  +
    // Verify correct IP returned
          47  +
    assert_eq!(vec![IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))], first_ips);
          48  +
          49  +
    // Cache hit should be faster
          50  +
    assert!(second_duration < first_duration);
          51  +
}
          52  +
          53  +
// Ignored since the cache is only eventually consistent w.r.t. size. So hard to get
          54  +
// an exact measure. But the logs here are useful to manually check the performance,
          55  +
// so not deleting this.
          56  +
#[test]
          57  +
#[ignore = "Cache is eventually consistent w.r.t. size."]
          58  +
async fn test_dns_cache_size_limit() {
          59  +
    let dns_server = test_dns_server::setup_dns_server().await;
          60  +
    let (dns_ip, dns_port) = dns_server.addr();
          61  +
          62  +
    let resolver = CachingDnsResolver::builder()
          63  +
        .nameservers(&[dns_ip], dns_port)
          64  +
        .cache_size(1)
          65  +
        .build();
          66  +
          67  +
    // Resolve first hostname
          68  +
    let start = Instant::now();
          69  +
    let result1 = resolver.resolve_dns("example.com").await.unwrap();
          70  +
    let result1_duration = start.elapsed();
          71  +
    assert_eq!(vec![IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))], result1);
          72  +
          73  +
    // Resolve second hostname (should replace first response in cache)
          74  +
    let start = Instant::now();
          75  +
    let result2 = resolver.resolve_dns("aws.com").await.unwrap();
          76  +
    let result2_duration = start.elapsed();
          77  +
    assert_eq!(vec![IpAddr::V4(Ipv4Addr::new(5, 6, 7, 8))], result2);
          78  +
          79  +
    println!("RESULT1 DURATION: {result1_duration:#?}");
          80  +
    println!("RESULT2 DURATION: {result2_duration:#?}");
          81  +
          82  +
    let start = Instant::now();
          83  +
    let _result1_again = resolver.resolve_dns("example.com").await.unwrap();
          84  +
    let result1_again_duration = start.elapsed();
          85  +
          86  +
    let start = Instant::now();
          87  +
    let _result2_again = resolver.resolve_dns("aws.com").await.unwrap();
          88  +
    let result2_again_duration = start.elapsed();
          89  +
          90  +
    println!("RESULT1 AGAIN DURATION: {result1_again_duration:#?}");
          91  +
    println!("RESULT2 AGAIN DURATION: {result2_again_duration:#?}");
          92  +
    assert!(result1_again_duration < result2_again_duration);
          93  +
}
          94  +
          95  +
#[test]
          96  +
async fn test_dns_error_handling() {
          97  +
    let dns_server = test_dns_server::setup_dns_server().await;
          98  +
    let (dns_ip, dns_port) = dns_server.addr();
          99  +
         100  +
    let resolver = CachingDnsResolver::builder()
         101  +
        .nameservers(&[dns_ip], dns_port)
         102  +
        .timeout(Duration::from_millis(100))
         103  +
        .attempts(1)
         104  +
        .build();
         105  +
         106  +
    // Try to resolve an invalid hostname
         107  +
    let result = resolver
         108  +
        .resolve_dns("invalid.nonexistent.domain.test")
         109  +
        .await;
         110  +
    assert!(result.is_err());
         111  +
}
         112  +
         113  +
// Kind of janky minimal test utility for creating a local DNS server
         114  +
#[cfg(test)]
         115  +
mod test_dns_server {
         116  +
    use std::{
         117  +
        collections::HashMap,
         118  +
        net::{IpAddr, Ipv4Addr, SocketAddr},
         119  +
        sync::Arc,
         120  +
        time::Duration,
         121  +
    };
         122  +
    use tokio::{net::UdpSocket, sync::Notify, task::JoinHandle};
         123  +
         124  +
    pub async fn setup_dns_server() -> TestDnsServer {
         125  +
        let mut records = HashMap::new();
         126  +
        records.insert(
         127  +
            "example.com".to_string(),
         128  +
            IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)),
         129  +
        );
         130  +
        records.insert("aws.com".to_string(), IpAddr::V4(Ipv4Addr::new(5, 6, 7, 8)));
         131  +
        records.insert(
         132  +
            "foo.com".to_string(),
         133  +
            IpAddr::V4(Ipv4Addr::new(9, 10, 11, 12)),
         134  +
        );
         135  +
         136  +
        TestDnsServer::start(records).await.unwrap()
         137  +
    }
         138  +
         139  +
    pub struct TestDnsServer {
         140  +
        handle: JoinHandle<()>,
         141  +
        addr: SocketAddr,
         142  +
        shutdown: Arc<Notify>,
         143  +
    }
         144  +
         145  +
    impl TestDnsServer {
         146  +
        pub async fn start(
         147  +
            records: HashMap<String, IpAddr>,
         148  +
        ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
         149  +
            // localhost, random port
         150  +
            let socket = UdpSocket::bind("127.0.0.1:0").await?;
         151  +
            let addr = socket.local_addr()?;
         152  +
         153  +
            let shutdown = Arc::new(Notify::new());
         154  +
            let shutdown_clone = shutdown.clone();
         155  +
         156  +
            let handle = tokio::spawn(async move {
         157  +
                let mut buf = [0; 512];
         158  +
                loop {
         159  +
                    tokio::select! {
         160  +
                        _ = shutdown_clone.notified() => break,
         161  +
                        result = socket.recv_from(&mut buf) => {
         162  +
                            if let Ok((len, src)) = result {
         163  +
                                // Short sleep before returning DNS response to simulate network latency
         164  +
                                tokio::time::sleep(Duration::from_millis(1000)).await;
         165  +
                                let response = create_dns_response(&buf[..len], &records);
         166  +
                                let _ = socket.send_to(&response, src).await;
         167  +
                            }
         168  +
                        }
         169  +
                    }
         170  +
                }
         171  +
            });
         172  +
         173  +
            Ok(TestDnsServer {
         174  +
                handle,
         175  +
                addr,
         176  +
                shutdown,
         177  +
            })
         178  +
        }
         179  +
         180  +
        pub fn addr(&self) -> (IpAddr, u16) {
         181  +
            (self.addr.ip(), self.addr.port())
         182  +
        }
         183  +
    }
         184  +
         185  +
    impl Drop for TestDnsServer {
         186  +
        fn drop(&mut self) {
         187  +
            self.shutdown.notify_one();
         188  +
            self.handle.abort();
         189  +
        }
         190  +
    }
         191  +
         192  +
    fn create_dns_response(query: &[u8], records: &HashMap<String, IpAddr>) -> Vec<u8> {
         193  +
        let parsed = DnsQuery::parse(query).unwrap_or_default();
         194  +
        let ip = records.get(&parsed.domain).copied().unwrap();
         195  +
         196  +
        let response = DnsResponse {
         197  +
            id: parsed.id,
         198  +
            flags: 0x8180, // Standard response flags
         199  +
            question: parsed.domain,
         200  +
            answer_ip: ip,
         201  +
            ttl: 300,
         202  +
        };
         203  +
         204  +
        response.to_bytes()
         205  +
    }
         206  +
         207  +
    #[derive(Debug, Default)]
         208  +
    #[allow(dead_code)]
         209  +
    struct DnsQuery {
         210  +
        id: u16,
         211  +
        flags: u16,
         212  +
        question_count: u16,
         213  +
        domain: String,
         214  +
        query_type: u16,
         215  +
        query_class: u16,
         216  +
    }
         217  +
         218  +
    impl DnsQuery {
         219  +
        fn parse(data: &[u8]) -> Option<Self> {
         220  +
            if data.len() < 12 {
         221  +
                return None;
         222  +
            }
         223  +
         224  +
            let id = u16::from_be_bytes([data[0], data[1]]);
         225  +
            let flags = u16::from_be_bytes([data[2], data[3]]);
         226  +
            let question_count = u16::from_be_bytes([data[4], data[5]]);
         227  +
         228  +
            if question_count == 0 {
         229  +
                return None;
         230  +
            }
         231  +
         232  +
            // Parse domain name starting at byte 12
         233  +
            let mut pos = 12;
         234  +
            let mut domain = String::new();
         235  +
         236  +
            while pos < data.len() {
         237  +
                let len = data[pos] as usize;
         238  +
                if len == 0 {
         239  +
                    pos += 1;
         240  +
                    break;
         241  +
                }
         242  +
         243  +
                if !domain.is_empty() {
         244  +
                    domain.push('.');
         245  +
                }
         246  +
         247  +
                pos += 1;
         248  +
                if pos + len > data.len() {
         249  +
                    return None;
         250  +
                }
         251  +
         252  +
                if let Ok(label) = std::str::from_utf8(&data[pos..pos + len]) {
         253  +
                    domain.push_str(label);
         254  +
                }
         255  +
                pos += len;
         256  +
            }
         257  +
         258  +
            if pos + 4 > data.len() {
         259  +
                return None;
         260  +
            }
         261  +
         262  +
            let query_type = u16::from_be_bytes([data[pos], data[pos + 1]]);
         263  +
            let query_class = u16::from_be_bytes([data[pos + 2], data[pos + 3]]);
         264  +
         265  +
            Some(DnsQuery {
         266  +
                id,
         267  +
                flags,
         268  +
                question_count,
         269  +
                domain,
         270  +
                query_type,
         271  +
                query_class,
         272  +
            })
         273  +
        }
         274  +
    }
         275  +
         276  +
    #[derive(Debug)]
         277  +
    #[allow(dead_code)]
         278  +
    struct DnsResponse {
         279  +
        id: u16,
         280  +
        flags: u16,
         281  +
        question: String,
         282  +
        answer_ip: IpAddr,
         283  +
        ttl: u32,
         284  +
    }
         285  +
         286  +
    impl DnsResponse {
         287  +
        fn to_bytes(&self) -> Vec<u8> {
         288  +
            // 30ish required bytes, 11 more added for the question section
         289  +
            // since the longest domain we currently use is 11 bytes long
         290  +
            let mut response = Vec::with_capacity(41);
         291  +
         292  +
            // Header (12 bytes) all values besides id/flags hardcoded
         293  +
            response.extend_from_slice(&self.id.to_be_bytes());
         294  +
            response.extend_from_slice(&self.flags.to_be_bytes());
         295  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Questions: 1
         296  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Answers: 1
         297  +
            response.extend_from_slice(&0u16.to_be_bytes()); // Authority: 0
         298  +
            response.extend_from_slice(&0u16.to_be_bytes()); // Additional: 0
         299  +
         300  +
            // Question section
         301  +
            // In a more ideal world the DnsResponse would contain a ref to the
         302  +
            // DnsQuery that triggered this response and recreate the question section
         303  +
            // from that
         304  +
            for label in self.question.split('.') {
         305  +
                response.push(label.len() as u8);
         306  +
                response.extend_from_slice(label.as_bytes());
         307  +
            }
         308  +
            response.push(0); // End of name
         309  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Type A
         310  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Class IN
         311  +
         312  +
            // Answer section
         313  +
            response.extend_from_slice(&[0xc0, 0x0c]); // Name pointer
         314  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Type A
         315  +
            response.extend_from_slice(&1u16.to_be_bytes()); // Class IN
         316  +
            response.extend_from_slice(&self.ttl.to_be_bytes()); // TTL
         317  +
         318  +
            match self.answer_ip {
         319  +
                IpAddr::V4(ipv4) => {
         320  +
                    response.extend_from_slice(&4u16.to_be_bytes()); // Data length
         321  +
                    response.extend_from_slice(&ipv4.octets());
         322  +
                }
         323  +
                IpAddr::V6(_) => {
         324  +
                    // Unsupported, fallback to IPv4
         325  +
                    response.extend_from_slice(&4u16.to_be_bytes());
         326  +
                    response.extend_from_slice(&[127, 0, 0, 1]);
         327  +
                }
         328  +
            }
         329  +
         330  +
            response
         331  +
        }
         332  +
    }
         333  +
}

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

@@ -12,12 +69,69 @@
   32     32   
path = "../aws-smithy-async"
   33     33   
version = "1.2.5"
   34     34   
   35     35   
[dependencies.aws-smithy-types]
   36     36   
path = "../aws-smithy-types"
   37     37   
version = "1.3.2"
   38     38   
   39     39   
[dependencies.aws-smithy-runtime]
   40     40   
path = "../aws-smithy-runtime"
   41     41   
optional = true
   42         -
version = "1.9.0"
          42  +
version = "1.9.1"
   43     43   
   44     44   
[dependencies.aws-smithy-runtime-api]
   45     45   
path = "../aws-smithy-runtime-api"
   46     46   
features = ["client"]
   47     47   
version = "1.9.0"
   48     48   
   49     49   
[dependencies.hyper-rustls]
   50     50   
version = "0.24"
   51     51   
optional = true
   52     52   
features = ["rustls-native-certs", "http2", "webpki-roots"]

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

@@ -20,20 +80,80 @@
   40     40   
features = ["event-stream"]
   41     41   
version = "0.62.3"
   42     42   
   43     43   
[dependencies.aws-smithy-json]
   44     44   
path = "../aws-smithy-json"
   45     45   
version = "0.61.5"
   46     46   
   47     47   
[dependencies.aws-smithy-runtime]
   48     48   
path = "../aws-smithy-runtime"
   49     49   
features = ["client", "http-auth"]
   50         -
version = "1.9.0"
          50  +
version = "1.9.1"
   51     51   
   52     52   
[dependencies.aws-smithy-runtime-api]
   53     53   
path = "../aws-smithy-runtime-api"
   54     54   
features = ["client", "http-02x", "http-auth"]
   55     55   
version = "1.9.0"
   56     56   
   57     57   
[dependencies.aws-smithy-types]
   58     58   
path = "../aws-smithy-types"
   59     59   
features = ["http-body-0-4-x"]
   60     60   
version = "1.3.2"
@@ -90,90 +150,150 @@
  110    110   
features = ["test-util", "wire-mock"]
  111    111   
version = "1.1.0"
  112    112   
  113    113   
[dev-dependencies.aws-smithy-protocol-test]
  114    114   
path = "../aws-smithy-protocol-test"
  115    115   
version = "0.63.4"
  116    116   
  117    117   
[dev-dependencies.aws-smithy-runtime]
  118    118   
path = "../aws-smithy-runtime"
  119    119   
features = ["test-util"]
  120         -
version = "1.9.0"
         120  +
version = "1.9.1"
  121    121   
  122    122   
[dev-dependencies.aws-smithy-runtime-api]
  123    123   
path = "../aws-smithy-runtime-api"
  124    124   
features = ["test-util"]
  125    125   
version = "1.9.0"
  126    126   
  127    127   
[dev-dependencies.aws-smithy-types]
  128    128   
path = "../aws-smithy-types"
  129    129   
features = ["test-util"]
  130    130   
version = "1.3.2"

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

@@ -16,16 +76,76 @@
   36     36   
features = ["event-stream"]
   37     37   
version = "0.62.3"
   38     38   
   39     39   
[dependencies.aws-smithy-json]
   40     40   
path = "../aws-smithy-json"
   41     41   
version = "0.61.5"
   42     42   
   43     43   
[dependencies.aws-smithy-runtime]
   44     44   
path = "../aws-smithy-runtime"
   45     45   
features = ["client"]
   46         -
version = "1.9.0"
          46  +
version = "1.9.1"
   47     47   
   48     48   
[dependencies.aws-smithy-runtime-api]
   49     49   
path = "../aws-smithy-runtime-api"
   50     50   
features = ["client", "http-02x"]
   51     51   
version = "1.9.0"
   52     52   
   53     53   
[dependencies.aws-smithy-types]
   54     54   
path = "../aws-smithy-types"
   55     55   
version = "1.3.2"
   56     56   
@@ -81,81 +141,141 @@
  101    101   
features = ["test-util", "wire-mock"]
  102    102   
version = "1.1.0"
  103    103   
  104    104   
[dev-dependencies.aws-smithy-protocol-test]
  105    105   
path = "../aws-smithy-protocol-test"
  106    106   
version = "0.63.4"
  107    107   
  108    108   
[dev-dependencies.aws-smithy-runtime]
  109    109   
path = "../aws-smithy-runtime"
  110    110   
features = ["test-util"]
  111         -
version = "1.9.0"
         111  +
version = "1.9.1"
  112    112   
  113    113   
[dev-dependencies.aws-smithy-runtime-api]
  114    114   
path = "../aws-smithy-runtime-api"
  115    115   
features = ["test-util"]
  116    116   
version = "1.9.0"
  117    117   
  118    118   
[dev-dependencies.aws-smithy-types]
  119    119   
path = "../aws-smithy-types"
  120    120   
features = ["test-util"]
  121    121   
version = "1.3.2"

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

@@ -10,10 +130,130 @@
   30     30   
path = "../aws-smithy-http"
   31     31   
version = "0.62.3"
   32     32   
   33     33   
[dependencies.aws-smithy-json]
   34     34   
path = "../aws-smithy-json"
   35     35   
version = "0.61.5"
   36     36   
   37     37   
[dependencies.aws-smithy-runtime]
   38     38   
path = "../aws-smithy-runtime"
   39     39   
features = ["client", "http-auth"]
   40         -
version = "1.9.0"
          40  +
version = "1.9.1"
   41     41   
   42     42   
[dependencies.aws-smithy-runtime-api]
   43     43   
path = "../aws-smithy-runtime-api"
   44     44   
features = ["client", "http-02x", "http-auth"]
   45     45   
version = "1.9.0"
   46     46   
   47     47   
[dependencies.aws-smithy-types]
   48     48   
path = "../aws-smithy-types"
   49     49   
version = "1.3.2"
   50     50   
   51     51   
[dependencies.aws-types]
   52     52   
path = "../aws-types"
   53     53   
version = "1.3.8"
   54     54   
   55     55   
[dependencies.bytes]
   56     56   
version = "1.4.0"
   57     57   
   58     58   
[dependencies.fastrand]
   59     59   
version = "2.0.0"
   60     60   
   61     61   
[dependencies.http]
   62     62   
version = "0.2.9"
   63     63   
   64     64   
[dependencies.regex-lite]
   65     65   
version = "0.1.5"
   66     66   
   67     67   
[dependencies.tracing]
   68     68   
version = "0.1"
   69     69   
[dev-dependencies.aws-config]
   70     70   
path = "../aws-config"
   71     71   
version = "1.8.6"
   72     72   
   73     73   
[dev-dependencies.aws-credential-types]
   74     74   
path = "../aws-credential-types"
   75     75   
features = ["test-util"]
   76     76   
version = "1.2.6"
   77     77   
   78     78   
[dev-dependencies.aws-runtime]
   79     79   
path = "../aws-runtime"
   80     80   
features = ["test-util"]
   81     81   
version = "1.5.10"
   82     82   
   83     83   
[dev-dependencies.aws-smithy-async]
   84     84   
path = "../aws-smithy-async"
   85     85   
features = ["test-util"]
   86     86   
version = "1.2.5"
   87     87   
   88     88   
[dev-dependencies.aws-smithy-http-client]
   89     89   
path = "../aws-smithy-http-client"
   90     90   
features = ["test-util", "wire-mock"]
   91     91   
version = "1.1.0"
   92     92   
   93     93   
[dev-dependencies.aws-smithy-protocol-test]
   94     94   
path = "../aws-smithy-protocol-test"
   95     95   
version = "0.63.4"
   96     96   
   97     97   
[dev-dependencies.aws-smithy-runtime]
   98     98   
path = "../aws-smithy-runtime"
   99     99   
features = ["test-util"]
  100         -
version = "1.9.0"
         100  +
version = "1.9.1"
  101    101   
  102    102   
[dev-dependencies.aws-smithy-runtime-api]
  103    103   
path = "../aws-smithy-runtime-api"
  104    104   
features = ["test-util"]
  105    105   
version = "1.9.0"
  106    106   
  107    107   
[dev-dependencies.aws-smithy-types]
  108    108   
path = "../aws-smithy-types"
  109    109   
features = ["test-util"]
  110    110   
version = "1.3.2"

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

@@ -10,10 +70,70 @@
   30     30   
path = "../aws-smithy-http"
   31     31   
version = "0.62.3"
   32     32   
   33     33   
[dependencies.aws-smithy-json]
   34     34   
path = "../aws-smithy-json"
   35     35   
version = "0.61.5"
   36     36   
   37     37   
[dependencies.aws-smithy-runtime]
   38     38   
path = "../aws-smithy-runtime"
   39     39   
features = ["client"]
   40         -
version = "1.9.0"
          40  +
version = "1.9.1"
   41     41   
   42     42   
[dependencies.aws-smithy-runtime-api]
   43     43   
path = "../aws-smithy-runtime-api"
   44     44   
features = ["client", "http-02x"]
   45     45   
version = "1.9.0"
   46     46   
   47     47   
[dependencies.aws-smithy-types]
   48     48   
path = "../aws-smithy-types"
   49     49   
version = "1.3.2"
   50     50   

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

@@ -10,10 +70,70 @@
   30     30   
path = "../aws-smithy-http"
   31     31   
version = "0.62.3"
   32     32   
   33     33   
[dependencies.aws-smithy-json]
   34     34   
path = "../aws-smithy-json"
   35     35   
version = "0.61.5"
   36     36   
   37     37   
[dependencies.aws-smithy-runtime]
   38     38   
path = "../aws-smithy-runtime"
   39     39   
features = ["client"]
   40         -
version = "1.9.0"
          40  +
version = "1.9.1"
   41     41   
   42     42   
[dependencies.aws-smithy-runtime-api]
   43     43   
path = "../aws-smithy-runtime-api"
   44     44   
features = ["client", "http-02x"]
   45     45   
version = "1.9.0"
   46     46   
   47     47   
[dependencies.aws-smithy-types]
   48     48   
path = "../aws-smithy-types"
   49     49   
version = "1.3.2"
   50     50   
@@ -73,73 +133,133 @@
   93     93   
features = ["test-util", "wire-mock"]
   94     94   
version = "1.1.0"
   95     95   
   96     96   
[dev-dependencies.aws-smithy-protocol-test]
   97     97   
path = "../aws-smithy-protocol-test"
   98     98   
version = "0.63.4"
   99     99   
  100    100   
[dev-dependencies.aws-smithy-runtime]
  101    101   
path = "../aws-smithy-runtime"
  102    102   
features = ["test-util"]
  103         -
version = "1.9.0"
         103  +
version = "1.9.1"
  104    104   
  105    105   
[dev-dependencies.aws-smithy-runtime-api]
  106    106   
path = "../aws-smithy-runtime-api"
  107    107   
features = ["test-util"]
  108    108   
version = "1.9.0"
  109    109   
  110    110   
[dev-dependencies.aws-smithy-types]
  111    111   
path = "../aws-smithy-types"
  112    112   
features = ["test-util"]
  113    113   
version = "1.3.2"

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

@@ -14,14 +135,135 @@
   34     34   
path = "../aws-smithy-json"
   35     35   
version = "0.61.5"
   36     36   
   37     37   
[dependencies.aws-smithy-query]
   38     38   
path = "../aws-smithy-query"
   39     39   
version = "0.60.7"
   40     40   
   41     41   
[dependencies.aws-smithy-runtime]
   42     42   
path = "../aws-smithy-runtime"
   43     43   
features = ["client"]
   44         -
version = "1.9.0"
          44  +
version = "1.9.1"
   45     45   
   46     46   
[dependencies.aws-smithy-runtime-api]
   47     47   
path = "../aws-smithy-runtime-api"
   48     48   
features = ["client", "http-02x"]
   49     49   
version = "1.9.0"
   50     50   
   51     51   
[dependencies.aws-smithy-types]
   52     52   
path = "../aws-smithy-types"
   53     53   
version = "1.3.2"
   54     54   
   55     55   
[dependencies.aws-smithy-xml]
   56     56   
path = "../aws-smithy-xml"
   57     57   
version = "0.60.10"
   58     58   
   59     59   
[dependencies.aws-types]
   60     60   
path = "../aws-types"
   61     61   
version = "1.3.8"
   62     62   
   63     63   
[dependencies.fastrand]
   64     64   
version = "2.0.0"
   65     65   
   66     66   
[dependencies.http]
   67     67   
version = "0.2.9"
   68     68   
   69     69   
[dependencies.regex-lite]
   70     70   
version = "0.1.5"
   71     71   
   72     72   
[dependencies.tracing]
   73     73   
version = "0.1"
   74     74   
[dev-dependencies.aws-config]
   75     75   
path = "../aws-config"
   76     76   
version = "1.8.6"
   77     77   
   78     78   
[dev-dependencies.aws-credential-types]
   79     79   
path = "../aws-credential-types"
   80     80   
features = ["test-util"]
   81     81   
version = "1.2.6"
   82     82   
   83     83   
[dev-dependencies.aws-runtime]
   84     84   
path = "../aws-runtime"
   85     85   
features = ["test-util"]
   86     86   
version = "1.5.10"
   87     87   
   88     88   
[dev-dependencies.aws-smithy-async]
   89     89   
path = "../aws-smithy-async"
   90     90   
features = ["test-util"]
   91     91   
version = "1.2.5"
   92     92   
   93     93   
[dev-dependencies.aws-smithy-http-client]
   94     94   
path = "../aws-smithy-http-client"
   95     95   
features = ["test-util", "wire-mock"]
   96     96   
version = "1.1.0"
   97     97   
   98     98   
[dev-dependencies.aws-smithy-protocol-test]
   99     99   
path = "../aws-smithy-protocol-test"
  100    100   
version = "0.63.4"
  101    101   
  102    102   
[dev-dependencies.aws-smithy-runtime]
  103    103   
path = "../aws-smithy-runtime"
  104    104   
features = ["test-util"]
  105         -
version = "1.9.0"
         105  +
version = "1.9.1"
  106    106   
  107    107   
[dev-dependencies.aws-smithy-runtime-api]
  108    108   
path = "../aws-smithy-runtime-api"
  109    109   
features = ["test-util"]
  110    110   
version = "1.9.0"
  111    111   
  112    112   
[dev-dependencies.aws-smithy-types]
  113    113   
path = "../aws-smithy-types"
  114    114   
features = ["test-util"]
  115    115   
version = "1.3.2"