diff --git a/.changelog/flexible-checksums.md b/.changelog/flexible-checksums.md new file mode 100644 index 0000000000..de76f7a8d3 --- /dev/null +++ b/.changelog/flexible-checksums.md @@ -0,0 +1,10 @@ +--- +applies_to: ["client", "aws-sdk-rust"] +authors: ["landonxjames"] +references: ["smithy-rs#3845"] +breaking: false +new_feature: true +bug_fix: true +--- + +Updating the implementation of flexible checksums to match the updated spec. diff --git a/aws/rust-runtime/Cargo.lock b/aws/rust-runtime/Cargo.lock index 1774e88d38..fc1de1db37 100644 --- a/aws/rust-runtime/Cargo.lock +++ b/aws/rust-runtime/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -149,7 +149,7 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.4.4" dependencies = [ "arbitrary", "aws-credential-types", @@ -195,7 +195,7 @@ version = "0.60.3" [[package]] name = "aws-sigv4" -version = "1.2.4" +version = "1.2.5" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", @@ -240,7 +240,7 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.60.12" +version = "0.62.0" dependencies = [ "aws-smithy-http", "aws-smithy-types", @@ -303,7 +303,7 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.1" +version = "1.7.2" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -343,7 +343,7 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.6" +version = "1.2.8" dependencies = [ "base64-simd", "bytes", @@ -367,7 +367,7 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.3" +version = "1.3.4" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -385,17 +385,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -479,9 +479,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bytes-utils" @@ -520,9 +520,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.16" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -573,18 +573,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstyle", "clap_lex", @@ -629,9 +629,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -954,9 +954,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "group" @@ -1313,11 +1313,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1501,9 +1501,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -1514,15 +1514,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -1544,9 +1544,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -1681,9 +1681,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] @@ -1790,9 +1790,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -1873,11 +1873,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1925,9 +1925,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -1947,9 +1947,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1966,9 +1966,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -2376,24 +2376,24 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "untrusted" @@ -2662,9 +2662,9 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" diff --git a/aws/rust-runtime/aws-config/Cargo.lock b/aws/rust-runtime/aws-config/Cargo.lock index f849dcd596..0b5ccfc6d4 100644 --- a/aws/rust-runtime/aws-config/Cargo.lock +++ b/aws/rust-runtime/aws-config/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" @@ -45,7 +45,7 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.6" +version = "1.5.7" dependencies = [ "aws-credential-types", "aws-runtime", @@ -298,7 +298,7 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.6" +version = "1.2.7" dependencies = [ "base64-simd", "bytes", @@ -326,7 +326,7 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.3" +version = "1.3.4" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -338,17 +338,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bytes-utils" @@ -428,9 +428,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.16" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "shlex", ] @@ -468,9 +468,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -630,9 +630,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "h2" @@ -883,11 +883,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -899,7 +899,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1049,9 +1049,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -1101,9 +1101,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags", ] @@ -1170,7 +1170,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1248,11 +1248,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1286,9 +1286,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -1308,9 +1308,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -1327,9 +1327,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1406,7 +1406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1533,7 +1533,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1691,15 +1691,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -1803,6 +1803,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1875,9 +1884,9 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zeroize" diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index bedb7d0bcc..a583475971 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-config" -version = "1.5.7" +version = "1.5.8" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 9c8d3c1b24..3e2c6726d1 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -34,6 +34,8 @@ allowed_external_types = [ "aws_smithy_runtime_api::client::result::SdkError", "aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig", "aws_smithy_types::body::SdkBody", + "aws_smithy_types::checksum_config::RequestChecksumCalculation", + "aws_smithy_types::checksum_config::ResponseChecksumValidation", "aws_smithy_types::retry", "aws_smithy_types::retry::*", "aws_smithy_types::timeout", diff --git a/aws/rust-runtime/aws-config/src/default_provider.rs b/aws/rust-runtime/aws-config/src/default_provider.rs index fceb869fb0..42e5a61739 100644 --- a/aws/rust-runtime/aws-config/src/default_provider.rs +++ b/aws/rust-runtime/aws-config/src/default_provider.rs @@ -63,3 +63,6 @@ pub mod disable_request_compression; /// Default "request minimum compression size bytes" provider chain pub mod request_min_compression_size_bytes; + +/// Default provider chains for request/response checksum configuration +pub mod checksums; diff --git a/aws/rust-runtime/aws-config/src/default_provider/checksums.rs b/aws/rust-runtime/aws-config/src/default_provider/checksums.rs new file mode 100644 index 0000000000..25da664a39 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/default_provider/checksums.rs @@ -0,0 +1,259 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::provider_config::ProviderConfig; +use aws_runtime::env_config::EnvConfigValue; +use aws_smithy_types::error::display::DisplayErrorContext; +use aws_types::sdk_config::{RequestChecksumCalculation, ResponseChecksumValidation}; +use std::str::FromStr; + +mod env { + pub(super) const REQUEST_CHECKSUM_CALCULATION: &str = "AWS_REQUEST_CHECKSUM_CALCULATION"; + pub(super) const RESPONSE_CHECKSUM_VALIDATION: &str = "AWS_RESPONSE_CHECKSUM_VALIDATION"; +} + +mod profile_key { + pub(super) const REQUEST_CHECKSUM_CALCULATION: &str = "request_checksum_calculation"; + pub(super) const RESPONSE_CHECKSUM_VALIDATION: &str = "response_checksum_validation"; +} + +/// Load the value for `request_checksum_calculation` +/// +/// This checks the following sources: +/// 1. The environment variable `AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_SUPPORTED/WHEN_REQUIRED` +/// 2. The profile key `request_checksum_calculation=WHEN_SUPPORTED/WHEN_REQUIRED` +/// +/// If invalid values are found, the provider will return `None` and an error will be logged. +pub async fn request_checksum_calculation_provider( + provider_config: &ProviderConfig, +) -> Option { + let env = provider_config.env(); + let profiles = provider_config.profile().await; + + let loaded = EnvConfigValue::new() + .env(env::REQUEST_CHECKSUM_CALCULATION) + .profile(profile_key::REQUEST_CHECKSUM_CALCULATION) + .validate(&env, profiles, RequestChecksumCalculation::from_str) + .map_err( + |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for request_checksum_calculation setting"), + ) + .unwrap_or(None); + + // request_checksum_calculation should always have a non-None value and the + // default is WhenSupported + loaded.or(Some(RequestChecksumCalculation::WhenSupported)) +} + +/// Load the value for `response_checksum_validation` +/// +/// This checks the following sources: +/// 1. The environment variable `AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_SUPPORTED/WHEN_REQUIRED` +/// 2. The profile key `response_checksum_validation=WHEN_SUPPORTED/WHEN_REQUIRED` +/// +/// If invalid values are found, the provider will return `None` and an error will be logged. +pub async fn response_checksum_validation_provider( + provider_config: &ProviderConfig, +) -> Option { + let env = provider_config.env(); + let profiles = provider_config.profile().await; + + let loaded = EnvConfigValue::new() + .env(env::RESPONSE_CHECKSUM_VALIDATION) + .profile(profile_key::RESPONSE_CHECKSUM_VALIDATION) + .validate(&env, profiles, ResponseChecksumValidation::from_str) + .map_err( + |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for response_checksum_validation setting"), + ) + .unwrap_or(None); + + // response_checksum_validation should always have a non-None value and the + // default is WhenSupported + loaded.or(Some(ResponseChecksumValidation::WhenSupported)) +} + +#[cfg(test)] +mod test { + use crate::default_provider::checksums::{ + request_checksum_calculation_provider, response_checksum_validation_provider, + }; + #[allow(deprecated)] + use crate::profile::profile_file::{ProfileFileKind, ProfileFiles}; + use crate::provider_config::ProviderConfig; + use aws_smithy_types::checksum_config::{ + RequestChecksumCalculation, ResponseChecksumValidation, + }; + use aws_types::os_shim_internal::{Env, Fs}; + use tracing_test::traced_test; + + #[tokio::test] + #[traced_test] + async fn log_error_on_invalid_value_request() { + let conf = ProviderConfig::empty().with_env(Env::from_slice(&[( + "AWS_REQUEST_CHECKSUM_CALCULATION", + "not-a-valid-value", + )])); + assert_eq!( + request_checksum_calculation_provider(&conf).await, + Some(RequestChecksumCalculation::WhenSupported) + ); + assert!(logs_contain( + "invalid value for request_checksum_calculation setting" + )); + assert!(logs_contain("AWS_REQUEST_CHECKSUM_CALCULATION")); + } + + #[tokio::test] + #[traced_test] + async fn environment_priority_request() { + let conf = ProviderConfig::empty() + .with_env(Env::from_slice(&[( + "AWS_REQUEST_CHECKSUM_CALCULATION", + "WHEN_REQUIRED", + )])) + .with_profile_config( + Some( + #[allow(deprecated)] + ProfileFiles::builder() + .with_file( + #[allow(deprecated)] + ProfileFileKind::Config, + "conf", + ) + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\nrequest_checksum_calculation = WHEN_SUPPORTED", + )])); + assert_eq!( + request_checksum_calculation_provider(&conf).await, + Some(RequestChecksumCalculation::WhenRequired) + ); + } + + #[tokio::test] + #[traced_test] + async fn profile_works_request() { + let conf = ProviderConfig::empty() + .with_profile_config( + Some( + #[allow(deprecated)] + ProfileFiles::builder() + .with_file( + #[allow(deprecated)] + ProfileFileKind::Config, + "conf", + ) + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\nrequest_checksum_calculation = WHEN_REQUIRED", + )])); + assert_eq!( + request_checksum_calculation_provider(&conf).await, + Some(RequestChecksumCalculation::WhenRequired) + ); + } + + #[tokio::test] + #[traced_test] + async fn default_works_request() { + let conf = ProviderConfig::empty(); + assert_eq!( + request_checksum_calculation_provider(&conf).await, + Some(RequestChecksumCalculation::WhenSupported) + ); + } + + #[tokio::test] + #[traced_test] + async fn log_error_on_invalid_value_response() { + let conf = ProviderConfig::empty().with_env(Env::from_slice(&[( + "AWS_RESPONSE_CHECKSUM_VALIDATION", + "not-a-valid-value", + )])); + assert_eq!( + response_checksum_validation_provider(&conf).await, + Some(ResponseChecksumValidation::WhenSupported) + ); + assert!(logs_contain( + "invalid value for response_checksum_validation setting" + )); + assert!(logs_contain("AWS_RESPONSE_CHECKSUM_VALIDATION")); + } + + #[tokio::test] + #[traced_test] + async fn environment_priority_response() { + let conf = ProviderConfig::empty() + .with_env(Env::from_slice(&[( + "AWS_RESPONSE_CHECKSUM_VALIDATION", + "WHEN_SUPPORTED", + )])) + .with_profile_config( + Some( + #[allow(deprecated)] + ProfileFiles::builder() + .with_file( + #[allow(deprecated)] + ProfileFileKind::Config, + "conf", + ) + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\response_checksum_validation = WHEN_REQUIRED", + )])); + assert_eq!( + response_checksum_validation_provider(&conf).await, + Some(ResponseChecksumValidation::WhenSupported) + ); + } + + #[tokio::test] + #[traced_test] + async fn profile_works_response() { + let conf = ProviderConfig::empty() + .with_profile_config( + Some( + #[allow(deprecated)] + ProfileFiles::builder() + .with_file( + #[allow(deprecated)] + ProfileFileKind::Config, + "conf", + ) + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\nresponse_checksum_validation = WHEN_REQUIRED", + )])); + assert_eq!( + response_checksum_validation_provider(&conf).await, + Some(ResponseChecksumValidation::WhenRequired) + ); + } + + #[tokio::test] + #[traced_test] + async fn default_works_response() { + let conf = ProviderConfig::empty(); + assert_eq!( + response_checksum_validation_provider(&conf).await, + Some(ResponseChecksumValidation::WhenSupported) + ); + } +} diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 49bfad8d6a..241c8c7549 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -228,6 +228,9 @@ mod loader { use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache}; use aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig; use aws_smithy_runtime_api::shared::IntoShared; + use aws_smithy_types::checksum_config::{ + RequestChecksumCalculation, ResponseChecksumValidation, + }; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::app_name::AppName; @@ -238,7 +241,7 @@ mod loader { use aws_types::SdkConfig; use crate::default_provider::{ - app_name, credentials, disable_request_compression, endpoint_url, + app_name, checksums, credentials, disable_request_compression, endpoint_url, ignore_configured_endpoint_urls as ignore_ep, region, request_min_compression_size_bytes, retry_config, timeout_config, use_dual_stack, use_fips, }; @@ -289,6 +292,8 @@ mod loader { env: Option, fs: Option, behavior_version: Option, + request_checksum_calculation: Option, + response_checksum_validation: Option, } impl ConfigLoader { @@ -908,6 +913,22 @@ mod loader { Some(user_cache) => Some(user_cache), }; + let request_checksum_calculation = + if let Some(request_checksum_calculation) = self.request_checksum_calculation { + Some(request_checksum_calculation) + } else { + checksums::request_checksum_calculation_provider(&conf).await + }; + + let response_checksum_validation = + if let Some(response_checksum_validation) = self.response_checksum_validation { + Some(response_checksum_validation) + } else { + checksums::response_checksum_validation_provider(&conf).await + }; + + builder.set_request_checksum_calculation(request_checksum_calculation); + builder.set_response_checksum_validation(response_checksum_validation); builder.set_identity_cache(identity_cache); builder.set_credentials_provider(credentials_provider); builder.set_token_provider(token_provider); diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index 3e010068c6..a045c1b003 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -12,14 +12,16 @@ use aws_runtime::{auth::SigV4OperationSigningConfig, content_encoding::header_va use aws_sigv4::http_request::SignableBody; use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_checksums::{body::calculate, http::HttpChecksum}; +use aws_smithy_runtime::client::sdk_feature::SmithySdkFeature; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::{ - BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, Input, + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, Input, }; use aws_smithy_runtime_api::client::interceptors::Intercept; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::checksum_config::RequestChecksumCalculation; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use aws_smithy_types::error::operation::BuildError; use http::HeaderValue; @@ -55,6 +57,8 @@ impl std::error::Error for Error {} #[derive(Debug)] struct RequestChecksumInterceptorState { checksum_algorithm: Option, + /// This value is set in the model on the `httpChecksum` trait + request_checksum_required: bool, } impl Storable for RequestChecksumInterceptorState { type Storer = StoreReplace; @@ -99,40 +103,48 @@ impl DefaultRequestChecksumOverride { } } -pub(crate) struct RequestChecksumInterceptor { +pub(crate) struct RequestChecksumInterceptor { algorithm_provider: AP, + checksum_mutator: CM, } -impl fmt::Debug for RequestChecksumInterceptor { +impl fmt::Debug for RequestChecksumInterceptor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RequestChecksumInterceptor").finish() } } -impl RequestChecksumInterceptor { - pub(crate) fn new(algorithm_provider: AP) -> Self { - Self { algorithm_provider } +impl RequestChecksumInterceptor { + pub(crate) fn new(algorithm_provider: AP, checksum_mutator: CM) -> Self { + Self { + algorithm_provider, + checksum_mutator, + } } } -impl Intercept for RequestChecksumInterceptor +impl Intercept for RequestChecksumInterceptor where - AP: Fn(&Input) -> Result, BoxError> + Send + Sync, + AP: Fn(&Input) -> Result<(Option, bool), BoxError> + Send + Sync, + CM: Fn(&mut Input, &ConfigBag) -> Result<(), BoxError> + Send + Sync, { fn name(&self) -> &'static str { "RequestChecksumInterceptor" } - fn read_before_serialization( + fn modify_before_serialization( &self, - context: &BeforeSerializationInterceptorContextRef<'_>, + context: &mut BeforeSerializationInterceptorContextMut<'_>, _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { + let _ = (self.checksum_mutator)(context.input_mut(), cfg); let checksum_algorithm = (self.algorithm_provider)(context.input())?; - let mut layer = Layer::new("RequestChecksumInterceptor"); - layer.store_put(RequestChecksumInterceptorState { checksum_algorithm }); + layer.store_put(RequestChecksumInterceptorState { + checksum_algorithm: checksum_algorithm.0, + request_checksum_required: checksum_algorithm.1, + }); cfg.push_layer(layer); Ok(()) @@ -141,7 +153,7 @@ where /// Calculate a checksum and modify the request to include the checksum as a header /// (for in-memory request bodies) or a trailer (for streaming request bodies). /// Streaming bodies must be sized or this will return an error. - fn modify_before_signing( + fn modify_before_retry_loop( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, _runtime_components: &RuntimeComponents, @@ -151,14 +163,93 @@ where .load::() .expect("set in `read_before_serialization`"); - let checksum_algorithm = incorporate_custom_default(state.checksum_algorithm, cfg); - if let Some(checksum_algorithm) = checksum_algorithm { + // This value is from the trait, but is needed for runtime logic + let request_checksum_required = state.request_checksum_required; + + // This value is set by the user on the SdkConfig to indicate their preference + // We provide a default here for users that use a client config instead of the SdkConfig + let request_checksum_calculation = cfg + .load::() + .unwrap_or(&RequestChecksumCalculation::WhenSupported); + + // Determine if we actually calculate the checksum. If the user setting is WhenSupported (the default) + // we always calculate it (because this interceptor isn't added if it isn't supported). If it is + // WhenRequired we only calculate it if the checksum is marked required on the trait. + let calculate_checksum = match request_checksum_calculation { + RequestChecksumCalculation::WhenRequired => request_checksum_required, + RequestChecksumCalculation::WhenSupported => true, + _ => true, + }; + + // Calculate the checksum if necessary + if calculate_checksum { + // If a checksum override is set in the ConfigBag we use that instead (currently only used by S3Express) + // If we have made it this far without a checksum being set we set the default as Crc32 + let checksum_algorithm = incorporate_custom_default(state.checksum_algorithm, cfg) + .unwrap_or(ChecksumAlgorithm::Crc32); + + // Set the user-agent metric for the selected checksum algorithm + match checksum_algorithm { + ChecksumAlgorithm::Crc32 => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqCrc32); + } + ChecksumAlgorithm::Crc32c => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqCrc32c); + } + #[allow(deprecated)] + ChecksumAlgorithm::Md5 => { + tracing::warn!(more_info = "Unsupported ChecksumAlgorithm MD5 set"); + } + ChecksumAlgorithm::Sha1 => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqSha1); + } + ChecksumAlgorithm::Sha256 => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqSha256); + } + unsupported => tracing::warn!( + more_info = "Unsupported value of ChecksumAlgorithm detected when setting user-agent metrics", + unsupported = ?unsupported), + } + let request = context.request_mut(); add_checksum_for_request_body(request, checksum_algorithm, cfg)?; } Ok(()) } + + /// Set the user-agent metrics for `RequestChecksumCalculation` here to avoid ownership issues + /// with the mutable borrow of cfg in `modify_before_signing` + fn read_after_serialization( + &self, + _context: &aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request_checksum_calculation = cfg + .load::() + .unwrap_or(&RequestChecksumCalculation::WhenSupported); + + match request_checksum_calculation { + RequestChecksumCalculation::WhenSupported => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqWhenSupported); + } + RequestChecksumCalculation::WhenRequired => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsReqWhenRequired); + } + unsupported => tracing::warn!( + more_info = "Unsupported value of RequestChecksumCalculation when setting user-agent metrics", + unsupported = ?unsupported), + }; + + Ok(()) + } } fn incorporate_custom_default( @@ -179,13 +270,18 @@ fn add_checksum_for_request_body( match request.body().bytes() { // Body is in-memory: read it and insert the checksum as a header. Some(data) => { - tracing::debug!("applying {checksum_algorithm:?} of the request body as a header"); let mut checksum = checksum_algorithm.into_impl(); - checksum.update(data); - request - .headers_mut() - .insert(checksum.header_name(), checksum.header_value()); + // If the header has not already been set we set it. If it was already set by the user + // we do nothing and maintain their set value. + if request.headers().get(checksum.header_name()).is_none() { + tracing::debug!("applying {checksum_algorithm:?} of the request body as a header"); + checksum.update(data); + + request + .headers_mut() + .insert(checksum.header_name(), checksum.header_value()); + } } // Body is streaming: wrap the body so it will emit a checksum as a trailer. None => { @@ -205,6 +301,13 @@ fn wrap_streaming_request_body_in_checksum_calculating_body( request: &mut HttpRequest, checksum_algorithm: ChecksumAlgorithm, ) -> Result<(), BuildError> { + let checksum = checksum_algorithm.into_impl(); + + // If the user already set the header value then do nothing and return early + if request.headers().get(checksum.header_name()).is_some() { + return Ok(()); + } + let original_body_size = request .body() .size_hint() @@ -236,7 +339,7 @@ fn wrap_streaming_request_body_in_checksum_calculating_body( headers.insert( http::header::HeaderName::from_static("x-amz-trailer"), - checksum_algorithm.into_impl().header_name(), + checksum.header_name(), ); headers.insert( diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs index 1236c9fe50..e757d667f8 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -8,14 +8,16 @@ //! Interceptor for handling Smithy `@httpChecksum` response checksumming use aws_smithy_checksums::ChecksumAlgorithm; +use aws_smithy_runtime::client::sdk_feature::SmithySdkFeature; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::{ - BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, Input, + BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextMut, Input, }; use aws_smithy_runtime_api::client::interceptors::Intercept; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::http::Headers; use aws_smithy_types::body::SdkBody; +use aws_smithy_types::checksum_config::ResponseChecksumValidation; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use std::{fmt, mem}; @@ -27,12 +29,13 @@ impl Storable for ResponseChecksumInterceptorState { type Storer = StoreReplace; } -pub(crate) struct ResponseChecksumInterceptor { +pub(crate) struct ResponseChecksumInterceptor { response_algorithms: &'static [&'static str], validation_enabled: VE, + checksum_mutator: CM, } -impl fmt::Debug for ResponseChecksumInterceptor { +impl fmt::Debug for ResponseChecksumInterceptor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ResponseChecksumInterceptor") .field("response_algorithms", &self.response_algorithms) @@ -40,38 +43,61 @@ impl fmt::Debug for ResponseChecksumInterceptor { } } -impl ResponseChecksumInterceptor { +impl ResponseChecksumInterceptor { pub(crate) fn new( response_algorithms: &'static [&'static str], validation_enabled: VE, + checksum_mutator: CM, ) -> Self { Self { response_algorithms, validation_enabled, + checksum_mutator, } } } -impl Intercept for ResponseChecksumInterceptor +impl Intercept for ResponseChecksumInterceptor where VE: Fn(&Input) -> bool + Send + Sync, + CM: Fn(&mut Input, &ConfigBag) -> Result<(), BoxError> + Send + Sync, { fn name(&self) -> &'static str { "ResponseChecksumInterceptor" } - fn read_before_serialization( + fn modify_before_serialization( &self, - context: &BeforeSerializationInterceptorContextRef<'_>, + context: &mut BeforeSerializationInterceptorContextMut<'_>, _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { + let _ = (self.checksum_mutator)(context.input_mut(), cfg); let validation_enabled = (self.validation_enabled)(context.input()); let mut layer = Layer::new("ResponseChecksumInterceptor"); layer.store_put(ResponseChecksumInterceptorState { validation_enabled }); cfg.push_layer(layer); + let response_checksum_validation = cfg + .load::() + .unwrap_or(&ResponseChecksumValidation::WhenSupported); + + // Set the user-agent feature metric for the response checksum config + match response_checksum_validation { + ResponseChecksumValidation::WhenSupported => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsResWhenSupported); + } + ResponseChecksumValidation::WhenRequired => { + cfg.interceptor_state() + .store_append(SmithySdkFeature::FlexibleChecksumsResWhenRequired); + } + unsupported => tracing::warn!( + more_info = "Unsupported value of ResponseChecksumValidation when setting user-agent metrics", + unsupported = ?unsupported), + }; + Ok(()) } @@ -85,12 +111,33 @@ where .load::() .expect("set in `read_before_serialization`"); - if state.validation_enabled { + // This value is set by the user on the SdkConfig to indicate their preference + // We provide a default here for users that use a client config instead of the SdkConfig + let response_checksum_validation = cfg + .load::() + .unwrap_or(&ResponseChecksumValidation::WhenSupported); + + // If validation has not been explicitly enabled we check the ResponseChecksumValidation + // from the SdkConfig. If it is WhenSupported (or unknown) we enable validation and if it + // is WhenRequired we leave it disabled since there is no way to indicate that a response + // checksum is required. + let validation_enabled = if !state.validation_enabled { + match response_checksum_validation { + ResponseChecksumValidation::WhenRequired => false, + ResponseChecksumValidation::WhenSupported => true, + _ => true, + } + } else { + true + }; + + if validation_enabled { let response = context.response_mut(); let maybe_checksum_headers = check_headers_for_precalculated_checksum( response.headers(), self.response_algorithms, ); + if let Some((checksum_algorithm, precalculated_checksum)) = maybe_checksum_headers { let mut body = SdkBody::taken(); mem::swap(&mut body, response.body_mut()); diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index 2265a7f3b4..9656489047 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-runtime" -version = "1.4.3" +version = "1.4.4" authors = ["AWS Rust SDK Team "] description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly." edition = "2021" diff --git a/aws/rust-runtime/aws-runtime/src/user_agent/metrics.rs b/aws/rust-runtime/aws-runtime/src/user_agent/metrics.rs index 1acaf86fc9..f625fdd3b6 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent/metrics.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent/metrics.rs @@ -126,7 +126,17 @@ iterable_enum!( AccountIdModeDisabled, AccountIdModeRequired, Sigv4aSigning, - ResolvedAccountId + ResolvedAccountId, + FlexibleChecksumsReqCrc32, + FlexibleChecksumsReqCrc32c, + FlexibleChecksumsReqCrc64, + FlexibleChecksumsReqSha1, + FlexibleChecksumsReqSha256, + FlexibleChecksumsReqWhenSupported, + FlexibleChecksumsReqWhenRequired, + FlexibleChecksumsResWhenSupported, + FlexibleChecksumsResWhenRequired, + DdbMapper ); pub(crate) trait ProvideBusinessMetric { @@ -141,6 +151,23 @@ impl ProvideBusinessMetric for SmithySdkFeature { Paginator => Some(BusinessMetric::Paginator), GzipRequestCompression => Some(BusinessMetric::GzipRequestCompression), ProtocolRpcV2Cbor => Some(BusinessMetric::ProtocolRpcV2Cbor), + FlexibleChecksumsReqCrc32 => Some(BusinessMetric::FlexibleChecksumsReqCrc32), + FlexibleChecksumsReqCrc32c => Some(BusinessMetric::FlexibleChecksumsReqCrc32c), + FlexibleChecksumsReqCrc64 => Some(BusinessMetric::FlexibleChecksumsReqCrc64), + FlexibleChecksumsReqSha1 => Some(BusinessMetric::FlexibleChecksumsReqSha1), + FlexibleChecksumsReqSha256 => Some(BusinessMetric::FlexibleChecksumsReqSha256), + FlexibleChecksumsReqWhenSupported => { + Some(BusinessMetric::FlexibleChecksumsReqWhenSupported) + } + FlexibleChecksumsReqWhenRequired => { + Some(BusinessMetric::FlexibleChecksumsReqWhenRequired) + } + FlexibleChecksumsResWhenSupported => { + Some(BusinessMetric::FlexibleChecksumsResWhenSupported) + } + FlexibleChecksumsResWhenRequired => { + Some(BusinessMetric::FlexibleChecksumsResWhenRequired) + } otherwise => { // This may occur if a customer upgrades only the `aws-smithy-runtime-api` crate // while continuing to use an outdated version of an SDK crate or the `aws-runtime` @@ -250,7 +277,17 @@ mod tests { "ACCOUNT_ID_MODE_DISABLED": "Q", "ACCOUNT_ID_MODE_REQUIRED": "R", "SIGV4A_SIGNING": "S", - "RESOLVED_ACCOUNT_ID": "T" + "RESOLVED_ACCOUNT_ID": "T", + "FLEXIBLE_CHECKSUMS_REQ_CRC32": "U", + "FLEXIBLE_CHECKSUMS_REQ_CRC32C": "V", + "FLEXIBLE_CHECKSUMS_REQ_CRC64": "W", + "FLEXIBLE_CHECKSUMS_REQ_SHA1": "X", + "FLEXIBLE_CHECKSUMS_REQ_SHA256": "Y", + "FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED": "Z", + "FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED": "a", + "FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED": "b", + "FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED": "c", + "DDB_MAPPER": "d" } "#; diff --git a/aws/rust-runtime/aws-runtime/src/user_agent/test_util.rs b/aws/rust-runtime/aws-runtime/src/user_agent/test_util.rs index a0a5679b63..de8013454f 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent/test_util.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent/test_util.rs @@ -17,13 +17,8 @@ static RE: Lazy = Lazy::new(|| Regex::new(r"m/([A-Za-z0-9+/=_,-]+)").unwr /// Refer to the end of the parent module file `user_agent.rs` for the complete ABNF specification /// of `business-metrics`. pub fn assert_ua_contains_metric_values(user_agent: &str, values: &[&str]) { - match RE.find(user_agent) { - Some(matched) => { - let csv = matched - .as_str() - .strip_prefix("m/") - .expect("prefix `m/` is guaranteed to exist by regex match"); - let metrics: Vec<&str> = csv.split(',').collect(); + match extract_ua_values(user_agent) { + Some(metrics) => { let mut missed = vec![]; for value in values.iter() { @@ -43,6 +38,18 @@ pub fn assert_ua_contains_metric_values(user_agent: &str, values: &[&str]) { } } +/// Extract the metric values from the `user_agent` string +pub fn extract_ua_values(user_agent: &str) -> Option> { + RE.find(user_agent).map(|matched| { + matched + .as_str() + .strip_prefix("m/") + .expect("prefix `m/` is guaranteed to exist by regex match") + .split(',') + .collect() + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/aws/rust-runtime/aws-sigv4/Cargo.toml b/aws/rust-runtime/aws-sigv4/Cargo.toml index 49af10cc21..0d247d5436 100644 --- a/aws/rust-runtime/aws-sigv4/Cargo.toml +++ b/aws/rust-runtime/aws-sigv4/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-sigv4" -version = "1.2.4" +version = "1.2.5" authors = ["AWS Rust SDK Team ", "David Barsky "] description = "SigV4 signer for HTTP requests and Event Stream messages." edition = "2021" diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs index 03404276d9..4622f841d9 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs @@ -31,6 +31,7 @@ pub(crate) mod header { pub(crate) const X_AMZ_DATE: &str = "x-amz-date"; pub(crate) const X_AMZ_SECURITY_TOKEN: &str = "x-amz-security-token"; pub(crate) const X_AMZ_USER_AGENT: &str = "x-amz-user-agent"; + pub(crate) const X_AMZ_CHECKSUM_MODE: &str = "x-amz-checksum-mode"; } pub(crate) mod param { @@ -293,8 +294,10 @@ impl<'a> CanonicalRequest<'a> { } if params.settings().signature_location == SignatureLocation::QueryParams { - // The X-Amz-User-Agent header should not be signed if this is for a presigned URL - if name == HeaderName::from_static(header::X_AMZ_USER_AGENT) { + // The X-Amz-User-Agent and x-amz-checksum-mode headers should not be signed if this is for a presigned URL + if name == HeaderName::from_static(header::X_AMZ_USER_AGENT) + || name == HeaderName::from_static(header::X_AMZ_CHECKSUM_MODE) + { continue; } } diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 694b8676a5..e13428e4ba 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-types" -version = "1.3.3" +version = "1.3.4" authors = ["AWS Rust SDK Team ", "Russell Cohen "] description = "Cross-service types for the AWS SDK." edition = "2021" diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 5dce405f84..ee28a1732a 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -13,6 +13,8 @@ allowed_external_types = [ "aws_smithy_runtime_api::client::identity::SharedIdentityCache", "aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig", "aws_smithy_runtime_api::http::headers::Headers", + "aws_smithy_types::checksum_config::ResponseChecksumValidation", + "aws_smithy_types::checksum_config::RequestChecksumCalculation", "aws_smithy_types::config_bag::storable::Storable", "aws_smithy_types::config_bag::storable::StoreReplace", "aws_smithy_types::config_bag::storable::Storer", diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index c26d8741be..19220fc526 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -25,6 +25,9 @@ pub use aws_smithy_runtime_api::client::http::SharedHttpClient; use aws_smithy_runtime_api::client::identity::{ResolveCachedIdentity, SharedIdentityCache}; pub use aws_smithy_runtime_api::client::stalled_stream_protection::StalledStreamProtectionConfig; use aws_smithy_runtime_api::shared::IntoShared; +pub use aws_smithy_types::checksum_config::{ + RequestChecksumCalculation, ResponseChecksumValidation, +}; pub use aws_smithy_types::retry::RetryConfig; pub use aws_smithy_types::timeout::TimeoutConfig; use std::collections::HashMap; @@ -92,6 +95,8 @@ pub struct SdkConfig { config_origins: HashMap<&'static str, Origin>, disable_request_compression: Option, request_min_compression_size_bytes: Option, + request_checksum_calculation: Option, + response_checksum_validation: Option, } /// Builder for AWS Shared Configuration @@ -120,6 +125,8 @@ pub struct Builder { config_origins: HashMap<&'static str, Origin>, disable_request_compression: Option, request_min_compression_size_bytes: Option, + request_checksum_calculation: Option, + response_checksum_validation: Option, } impl Builder { @@ -174,6 +181,54 @@ impl Builder { self } + /// Set the checksum calculation strategy to use when making requests. + /// # Examples + /// ``` + /// use aws_types::SdkConfig; + /// use aws_smithy_types::checksum_config::RequestChecksumCalculation; + /// let config = SdkConfig::builder().request_checksum_calculation(RequestChecksumCalculation::WhenSupported).build(); + /// ``` + pub fn request_checksum_calculation( + mut self, + request_checksum_calculation: RequestChecksumCalculation, + ) -> Self { + self.set_request_checksum_calculation(Some(request_checksum_calculation)); + self + } + + /// Set the checksum calculation strategy to use when making requests. + pub fn set_request_checksum_calculation( + &mut self, + request_checksum_calculation: Option, + ) -> &mut Self { + self.request_checksum_calculation = request_checksum_calculation; + self + } + + /// Set the checksum calculation strategy to use for responses. + /// # Examples + /// ``` + /// use aws_types::SdkConfig; + /// use aws_smithy_types::checksum_config::ResponseChecksumValidation; + /// let config = SdkConfig::builder().response_checksum_validation(ResponseChecksumValidation::WhenSupported).build(); + /// ``` + pub fn response_checksum_validation( + mut self, + response_checksum_validation: ResponseChecksumValidation, + ) -> Self { + self.set_response_checksum_validation(Some(response_checksum_validation)); + self + } + + /// Set the checksum calculation strategy to use for responses. + pub fn set_response_checksum_validation( + &mut self, + response_checksum_validation: Option, + ) -> &mut Self { + self.response_checksum_validation = response_checksum_validation; + self + } + /// Set the retry_config for the builder /// /// _Note:_ Retries require a sleep implementation in order to work. When enabling retry, make @@ -720,6 +775,8 @@ impl Builder { config_origins: self.config_origins, disable_request_compression: self.disable_request_compression, request_min_compression_size_bytes: self.request_min_compression_size_bytes, + request_checksum_calculation: self.request_checksum_calculation, + response_checksum_validation: self.response_checksum_validation, } } } @@ -866,6 +923,16 @@ impl SdkConfig { self.disable_request_compression } + /// Configured checksum request behavior. + pub fn request_checksum_calculation(&self) -> Option { + self.request_checksum_calculation + } + + /// Configured checksum response behavior. + pub fn response_checksum_validation(&self) -> Option { + self.response_checksum_validation + } + /// Configured minimum request compression size. pub fn request_min_compression_size_bytes(&self) -> Option { self.request_min_compression_size_bytes @@ -933,6 +1000,8 @@ impl SdkConfig { config_origins: self.config_origins, disable_request_compression: self.disable_request_compression, request_min_compression_size_bytes: self.request_min_compression_size_bytes, + request_checksum_calculation: self.request_checksum_calculation, + response_checksum_validation: self.response_checksum_validation, } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index 2794fc0295..ee14bde53f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -6,11 +6,16 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.aws.traits.HttpChecksumTrait +import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.configReexport import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -20,9 +25,13 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.orNull @@ -39,20 +48,49 @@ internal fun RuntimeConfig.awsInlineableHttpRequestChecksum() = CargoDependency.smithyHttp(this), CargoDependency.smithyRuntimeApiClient(this), CargoDependency.smithyTypes(this), + AwsCargoDependency.awsSigv4(this), + CargoDependency.TempFile.toDevDependency(), ), ) class HttpRequestChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpRequestChecksum" override val order: Byte = 0 + private val defaultAlgorithm = "CRC32" override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, ): List = baseCustomizations + HttpRequestChecksumCustomization(codegenContext, operation) + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + HttpRequestChecksumConfigCustomization(codegenContext) + + /** + * Copy the `request_checksum_calculation` value from the `SdkConfig` to the client config + */ + override fun extraSections(codegenContext: ClientCodegenContext): List = + if (!serviceHasHttpChecksumOperation(codegenContext)) { + listOf() + } else { + listOf( + adhocCustomization { section -> + rust( + """ + ${section.serviceConfigBuilder}.set_request_checksum_calculation(${section.sdkConfig}.request_checksum_calculation()); + """, + ) + }, + ) + } } +/** + * Extract the name of the operation's input member that indicates which checksum algorithm to use + */ private fun HttpChecksumTrait.requestAlgorithmMember( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -64,6 +102,9 @@ private fun HttpChecksumTrait.requestAlgorithmMember( return codegenContext.symbolProvider.toMemberName(checksumAlgorithmMemberShape) } +/** + * Extract the name of the operation's input member that indicates which checksum algorithm to use + */ private fun HttpChecksumTrait.checksumAlgorithmToStr( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -73,19 +114,16 @@ private fun HttpChecksumTrait.checksumAlgorithmToStr( val isRequestChecksumRequired = this.isRequestChecksumRequired return { - if (requestAlgorithmMember != null) { - if (isRequestChecksumRequired) { - // Checksums are required, fall back to MD5 - rust("""let checksum_algorithm = checksum_algorithm.map(|algorithm| algorithm.as_str()).or(Some("md5"));""") - } else { - // Checksums aren't required, don't set a fallback - rust("let checksum_algorithm = checksum_algorithm.map(|algorithm| algorithm.as_str());") - } - } else if (isRequestChecksumRequired) { - // Checksums are required but a user can't set one, so we set MD5 for them - rust("""let checksum_algorithm = Some("md5");""") + if (requestAlgorithmMember == null && isRequestChecksumRequired) { + // Checksums are required but a user can't set one, so we set crc32 for them + rust("""let checksum_algorithm = Some("crc32");""") + } else { + // Use checksum algo set by user or crc32 if one has not been set + rust("""let checksum_algorithm = checksum_algorithm.map(|algorithm| algorithm.as_str()).or(Some("crc32"));""") } + // Parse the checksum_algorithm type from the service's smithy model to a ChecksumAlgorithm enum from the + // aws_smithy_checksums crate. rustTemplate( """ let checksum_algorithm = match checksum_algorithm { @@ -107,6 +145,12 @@ private fun HttpChecksumTrait.checksumAlgorithmToStr( // This generator was implemented based on this spec: // https://smithy.io/2.0/aws/aws-core.html#http-request-checksums + +/** + * Calculate the checksum algorithm based on the input member identified by the trait's + * `requestAlgorithmMember`. Then instantiate an (inlineable) `http_request_checksum` + * interceptor with that checksum algorithm. + */ class HttpRequestChecksumCustomization( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, @@ -117,27 +161,71 @@ class HttpRequestChecksumCustomization( writable { // Get the `HttpChecksumTrait`, returning early if this `OperationShape` doesn't have one val checksumTrait = operationShape.getTrait() ?: return@writable - val requestAlgorithmMember = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) + val requestAlgorithmMember = + checksumTrait.requestAlgorithmMemberShape(codegenContext, operationShape) ?: return@writable + val requestAlgorithmMemberName = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) val inputShape = codegenContext.model.expectShape(operationShape.inputShape) + val requestChecksumRequired = checksumTrait.isRequestChecksumRequired + val operationName = codegenContext.symbolProvider.toSymbol(operationShape).name + val requestAlgorithmMemberInner = + if (requestAlgorithmMember.isOptional) { + codegenContext.model.expectShape(requestAlgorithmMember.target) + } else { + requestAlgorithmMember + } when (section) { is OperationSection.AdditionalInterceptors -> { - if (requestAlgorithmMember != null) { + if (requestAlgorithmMemberName != null) { section.registerInterceptor(runtimeConfig, this) { val runtimeApi = RuntimeType.smithyRuntimeApiClient(runtimeConfig) rustTemplate( """ - #{RequestChecksumInterceptor}::new(|input: &#{Input}| { + #{RequestChecksumInterceptor}::new( + |input: &#{Input}| { let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); - let checksum_algorithm = input.$requestAlgorithmMember(); + let checksum_algorithm = input.$requestAlgorithmMemberName(); #{checksum_algorithm_to_str} - #{Result}::<_, #{BoxError}>::Ok(checksum_algorithm) - }) + #{Result}::<_, #{BoxError}>::Ok((checksum_algorithm, $requestChecksumRequired)) + }, + |input: &mut #{Input}, cfg: &#{ConfigBag}| { + let input = input + .downcast_mut::<#{OperationInputType}>() + .ok_or("failed to downcast to #{OperationInputType}")?; + + // This value is set by the user on the SdkConfig to indicate their preference + let request_checksum_calculation = cfg + .load::<#{RequestChecksumCalculation}>() + .unwrap_or(&#{RequestChecksumCalculation}::WhenSupported); + + // From the httpChecksum trait + let http_checksum_required = $requestChecksumRequired; + + // If the RequestChecksumCalculation is WhenSupported and the user has not set a checksum we + // default to Crc32. If it is WhenRequired and a checksum is required by the trait we also set the + // default. In all other cases we do nothing. + match ( + request_checksum_calculation, + http_checksum_required, + input.checksum_algorithm(), + ) { + (#{RequestChecksumCalculation}::WhenSupported, _, None) + | (#{RequestChecksumCalculation}::WhenRequired, true, None) => { + input.checksum_algorithm = Some(#{ChecksumAlgoShape}::Crc32); + } + _ => {}, + } + + Ok(()) + } + + ) """, *preludeScope, "BoxError" to RuntimeType.boxError(runtimeConfig), "Input" to runtimeApi.resolve("client::interceptors::context::Input"), "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), "RequestChecksumInterceptor" to runtimeConfig.awsInlineableHttpRequestChecksum() .resolve("RequestChecksumInterceptor"), @@ -146,11 +234,129 @@ class HttpRequestChecksumCustomization( codegenContext, operationShape, ), + "RequestChecksumCalculation" to + CargoDependency.smithyTypes(runtimeConfig).toType() + .resolve("checksum_config::RequestChecksumCalculation"), + "ChecksumAlgoShape" to + codegenContext.symbolProvider.toSymbol( + requestAlgorithmMemberInner, + ), + "OperationInputType" to + codegenContext.symbolProvider.toSymbol( + operationShape.inputShape( + codegenContext.model, + ), + ), ) } } } - else -> { } + + else -> {} } } } + +/** + * Add a `request_checksum_calculation;` field to Service config. + */ +class HttpRequestChecksumConfigCustomization(private val codegenContext: ClientCodegenContext) : + NamedCustomization() { + private val rc = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + *preludeScope, + "RequestChecksumCalculation" to + configReexport( + RuntimeType.smithyTypes(rc) + .resolve("checksum_config::RequestChecksumCalculation"), + ), + ) + + override fun section(section: ServiceConfig): Writable { + // If the service contains no operations with the httpChecksum trait we return early + if (!serviceHasHttpChecksumOperation(codegenContext)) { + return emptySection + } + + // Otherwise we write the necessary sections to the service config + return when (section) { + is ServiceConfig.ConfigImpl -> + writable { + rustTemplate( + """ + /// Return a reference to the request_checksum_calculation value contained in this config, if any. + pub fn request_checksum_calculation(&self) -> #{Option}<&#{RequestChecksumCalculation}> { + self.config.load::<#{RequestChecksumCalculation}>() + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderImpl -> + writable { + rustTemplate( + """ + /// Set the [`RequestChecksumCalculation`](#{RequestChecksumCalculation}) + /// to determine when a checksum will be calculated for request payloads. + pub fn request_checksum_calculation( + mut self, + request_checksum_calculation: #{RequestChecksumCalculation} + ) -> Self { + self.set_request_checksum_calculation(#{Some}(request_checksum_calculation)); + self + } + """, + *codegenScope, + ) + + rustTemplate( + """ + /// Set the [`RequestChecksumCalculation`](#{RequestChecksumCalculation}) + /// to determine when a checksum will be calculated for request payloads. + pub fn set_request_checksum_calculation( + &mut self, + request_checksum_calculation: #{Option}<#{RequestChecksumCalculation}> + ) -> &mut Self { + self.config.store_or_unset(request_checksum_calculation); + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderFromConfigBag -> + writable { + rustTemplate( + "${section.builder}.set_request_checksum_calculation(${section.configBag}.load::<#{RequestChecksumCalculation}>().cloned());", + *codegenScope, + ) + } + + else -> emptySection + } + } +} + +/** + * Determine if the current service contains any operations with the HttpChecksum trait + */ +fun serviceHasHttpChecksumOperation(codegenContext: ClientCodegenContext): Boolean { + val index = TopDownIndex.of(codegenContext.model) + val ops = index.getContainedOperations(codegenContext.serviceShape.id) + return ops.any { it.hasTrait() } +} + +/** + * Get the top-level operation input member used to opt-in to best-effort validation of a checksum returned in + * the HTTP response of the operation. + */ +fun HttpChecksumTrait.requestAlgorithmMemberShape( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, +): MemberShape? { + val requestAlgorithmMember = this.requestAlgorithmMember.orNull() ?: return null + return operationShape.inputShape(codegenContext.model).expectMember(requestAlgorithmMember) +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index 16a4521679..0f0ef550f9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -10,17 +10,25 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.configReexport import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -42,6 +50,10 @@ private fun RuntimeConfig.awsInlineableHttpResponseChecksum() = ), ) +/** + * Get the top-level operation input member used to opt-in to best-effort validation of a checksum returned in + * the HTTP response of the operation. + */ fun HttpChecksumTrait.requestValidationModeMember( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -65,19 +77,50 @@ class HttpResponseChecksumDecorator : ClientCodegenDecorator { baseCustomizations.letIf(applies(operation)) { it + HttpResponseChecksumCustomization(codegenContext, operation) } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + HttpResponseChecksumConfigCustomization(codegenContext) + + /** + * Copy the `response_checksum_validation` value from the `SdkConfig` to the client config + */ + override fun extraSections(codegenContext: ClientCodegenContext): List = + if (!serviceHasHttpChecksumOperation(codegenContext)) { + listOf() + } else { + listOf( + adhocCustomization { section -> + rust( + """ + ${section.serviceConfigBuilder}.set_response_checksum_validation(${section.sdkConfig}.response_checksum_validation()); + """, + ) + }, + ) + } } // This generator was implemented based on this spec: // https://smithy.io/2.0/aws/aws-core.html#http-request-checksums + +/** + * Calculate the checksum algorithm based on the input member identified by the trait's + * `requestAlgorithmMember`. Then instantiate an (inlineable) `http_request_checksum` + * interceptor with that checksum algorithm. + */ class HttpResponseChecksumCustomization( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, ) : OperationCustomization() { override fun section(section: OperationSection): Writable = writable { + // Return early if this operation lacks the `httpChecksum` trait or that trait lacks a `requestValidationModeMember` val checksumTrait = operationShape.getTrait() ?: return@writable val requestValidationModeMember = checksumTrait.requestValidationModeMember(codegenContext, operationShape) ?: return@writable + val requestValidationModeName = codegenContext.symbolProvider.toSymbol(requestValidationModeMember).name val requestValidationModeMemberInner = if (requestValidationModeMember.isOptional) { codegenContext.model.expectShape(requestValidationModeMember.target) @@ -86,6 +129,7 @@ class HttpResponseChecksumCustomization( } val validationModeName = codegenContext.symbolProvider.toMemberName(requestValidationModeMember) val inputShape = codegenContext.model.expectShape(operationShape.inputShape) + val operationName = codegenContext.symbolProvider.toSymbol(operationShape).name when (section) { is OperationSection.AdditionalInterceptors -> { @@ -93,17 +137,48 @@ class HttpResponseChecksumCustomization( // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" val responseAlgorithms = checksumTrait.responseAlgorithms - .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } + .map { algorithm -> algorithm.lowercase() } + .joinToString(", ") { algorithm -> algorithm.dq() } val runtimeApi = RuntimeType.smithyRuntimeApiClient(codegenContext.runtimeConfig) rustTemplate( """ #{ResponseChecksumInterceptor}::new( [$responseAlgorithms].as_slice(), |input: &#{Input}| { - ${""/* Per [the spec](https://smithy.io/2.0/aws/aws-core.html#http-response-checksums), - we check to see if it's the `ENABLED` variant */} + ${ + ""/* Per [the spec](https://smithy.io/2.0/aws/aws-core.html#http-response-checksums), + we check to see if it's the `ENABLED` variant */ + } let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); matches!(input.$validationModeName(), #{Some}(#{ValidationModeShape}::Enabled)) + }, + |input: &mut #{Input}, cfg: &#{ConfigBag}| { + let input = input + .downcast_mut::<#{OperationInputType}>() + .ok_or("failed to downcast to #{OperationInputType}")?; + + let request_validation_enabled = + matches!(input.$requestValidationModeName(), Some(#{ValidationModeShape}::Enabled)); + + if !request_validation_enabled { + // This value is set by the user on the SdkConfig to indicate their preference + let response_checksum_validation = cfg + .load::<#{ResponseChecksumValidation}>() + .unwrap_or(&#{ResponseChecksumValidation}::WhenSupported); + + // If validation setting is WhenSupported (or unknown) we enable response checksum + // validation. If it is WhenRequired we do not enable (since there is no way to + // indicate that a response checksum is required). + ##[allow(clippy::wildcard_in_or_patterns)] + match response_checksum_validation { + #{ResponseChecksumValidation}::WhenRequired => {} + #{ResponseChecksumValidation}::WhenSupported | _ => { + input.$requestValidationModeName = Some(#{ValidationModeShape}::Enabled); + } + } + } + + #{Ok}(()) } ) """, @@ -113,7 +188,24 @@ class HttpResponseChecksumCustomization( .resolve("ResponseChecksumInterceptor"), "Input" to runtimeApi.resolve("client::interceptors::context::Input"), "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), - "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), + "ValidationModeShape" to + codegenContext.symbolProvider.toSymbol( + requestValidationModeMemberInner, + ), + "OperationInputType" to + codegenContext.symbolProvider.toSymbol( + operationShape.inputShape( + codegenContext.model, + ), + ), + "ValidationModeShape" to + codegenContext.symbolProvider.toSymbol( + requestValidationModeMemberInner, + ), + "ResponseChecksumValidation" to + CargoDependency.smithyTypes(codegenContext.runtimeConfig).toType() + .resolve("checksum_config::ResponseChecksumValidation"), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), ) } } @@ -122,3 +214,86 @@ class HttpResponseChecksumCustomization( } } } + +/** + * Add a `response_checksum_validation;` field to Service config. + */ +class HttpResponseChecksumConfigCustomization(private val codegenContext: ClientCodegenContext) : + NamedCustomization() { + private val rc = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + *preludeScope, + "ResponseChecksumValidation" to + configReexport( + RuntimeType.smithyTypes(rc) + .resolve("checksum_config::ResponseChecksumValidation"), + ), + ) + + override fun section(section: ServiceConfig): Writable { + // If the service contains no operations with the httpChecksum trait we return early + if (!serviceHasHttpChecksumOperation(codegenContext)) { + return emptySection + } + + // Otherwise we write the necessary sections to the service config + return when (section) { + is ServiceConfig.ConfigImpl -> + writable { + rustTemplate( + """ + /// Return a reference to the response_checksum_validation value contained in this config, if any. + pub fn response_checksum_validation(&self) -> #{Option}<&#{ResponseChecksumValidation}> { + self.config.load::<#{ResponseChecksumValidation}>() + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderImpl -> + writable { + rustTemplate( + """ + /// Set the [`ResponseChecksumValidation`](#{ResponseChecksumValidation}) + /// to determine when checksum validation will be performed on response payloads. + pub fn response_checksum_validation( + mut self, + response_checksum_validation: #{ResponseChecksumValidation} + ) -> Self { + self.set_response_checksum_validation(#{Some}(response_checksum_validation)); + self + } + """, + *codegenScope, + ) + + rustTemplate( + """ + /// Set the [`ResponseChecksumValidation`](#{ResponseChecksumValidation}) + /// to determine when checksum validation will be performed on response payloads. + pub fn set_response_checksum_validation( + &mut self, + response_checksum_validation: #{Option}<#{ResponseChecksumValidation}> + ) -> &mut Self { + self.config.store_or_unset(response_checksum_validation); + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderFromConfigBag -> + writable { + rustTemplate( + "${section.builder}.set_response_checksum_validation(${section.configBag}.load::<#{ResponseChecksumValidation}>().cloned());", + *codegenScope, + ) + } + + else -> emptySection + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpChecksumTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpChecksumTest.kt new file mode 100644 index 0000000000..b0fc6265ed --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpChecksumTest.kt @@ -0,0 +1,788 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Feature +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.join +import software.amazon.smithy.rust.codegen.core.rustlang.plus +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +internal class HttpChecksumTest { + companion object { + private const val PREFIX = "\$version: \"2\"" + private val model = + """ + $PREFIX + namespace test + + use aws.api#service + use aws.auth#sigv4 + use aws.protocols#httpChecksum + use aws.protocols#restJson1 + use smithy.rules#endpointRuleSet + + @service(sdkId: "dontcare") + @restJson1 + @sigv4(name: "dontcare") + @auth([sigv4]) + @endpointRuleSet({ + "version": "1.0", + "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service TestService { + version: "2023-01-01", + operations: [SomeOperation, SomeStreamingOperation] + } + + @http(uri: "/SomeOperation", method: "POST") + @optionalAuth + @httpChecksum( + requestChecksumRequired: true, + requestAlgorithmMember: "checksumAlgorithm", + requestValidationModeMember: "validationMode", + responseAlgorithms: ["CRC32", "CRC32C", "SHA1", "SHA256"] + ) + operation SomeOperation { + input: SomeInput, + output: SomeOutput + } + + @input + structure SomeInput { + @httpHeader("x-amz-request-algorithm") + checksumAlgorithm: ChecksumAlgorithm + + @httpHeader("x-amz-response-validation-mode") + validationMode: ValidationMode + + @httpPayload + @required + body: Blob + } + + @output + structure SomeOutput { + @httpPayload + body: Blob + } + + @http(uri: "/SomeStreamingOperation", method: "POST") + @optionalAuth + @httpChecksum( + requestChecksumRequired: true, + requestAlgorithmMember: "checksumAlgorithm", + requestValidationModeMember: "validationMode", + responseAlgorithms: ["CRC32", "CRC32C", "SHA1", "SHA256"] + ) + operation SomeStreamingOperation { + input: SomeStreamingInput, + output: SomeStreamingOutput + } + + @streaming + blob StreamingBlob + + @input + structure SomeStreamingInput { + @httpHeader("x-amz-request-algorithm") + checksumAlgorithm: ChecksumAlgorithm + + @httpHeader("x-amz-response-validation-mode") + validationMode: ValidationMode + + @httpPayload + @required + body: StreamingBlob + } + + @output + structure SomeStreamingOutput {} + + enum ChecksumAlgorithm { + CRC32 + CRC32C + //Value not supported by current smithy version + //CRC64NVME + SHA1 + SHA256 + } + + enum ValidationMode { + ENABLED + } + """.asSmithyModel() + } + + @Test + fun requestChecksumWorks() { + awsSdkIntegrationTest(model) { context, rustCrate -> + // Allows us to use the user-agent test-utils in aws-runtime + rustCrate.mergeFeature(Feature("test-util", true, listOf("aws-runtime/test-util"))) + val rc = context.runtimeConfig + + // Create Writables for all test types + val checksumRequestTestWritables = + checksumRequestTests.map { createRequestChecksumCalculationTest(it, context) }.join("\n") + val checksumResponseSuccTestWritables = + checksumResponseSuccTests.map { createResponseChecksumValidationSuccessTest(it, context) }.join("\n") + val checksumResponseFailTestWritables = + checksumResponseFailTests.map { createResponseChecksumValidationFailureTest(it, context) }.join("\n") + val checksumStreamingRequestTestWritables = + streamingRequestTests.map { createStreamingRequestChecksumCalculationTest(it, context) }.join("\n") + val miscTests = createMiscellaneousTests(context) + + // Shared imports for all test types + val testBase = + writable { + rustTemplate( + """ + ##![cfg(feature = "test-util")] + ##![allow(unused_imports)] + + use #{Blob}; + use #{Region}; + use #{pretty_assertions}::assert_eq; + use #{SdkBody}; + use std::io::Write; + use http_body::Body; + use #{HttpRequest}; + use #{UaAssert}; + use #{UaExtract}; + """, + *preludeScope, + "Blob" to RuntimeType.smithyTypes(rc).resolve("Blob"), + "Region" to AwsRuntimeType.awsTypes(rc).resolve("region::Region"), + "pretty_assertions" to CargoDependency.PrettyAssertions.toType(), + "SdkBody" to RuntimeType.smithyTypes(rc).resolve("body::SdkBody"), + "HttpRequest" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::HttpRequest"), + "UaAssert" to + AwsRuntimeType.awsRuntime(rc) + .resolve("user_agent::test_util::assert_ua_contains_metric_values"), + "UaExtract" to + AwsRuntimeType.awsRuntime(rc) + .resolve("user_agent::test_util::extract_ua_values"), + ) + } + + // Create one integ test per test type + rustCrate.integrationTest("request_checksums") { + testBase.plus(checksumRequestTestWritables)() + } + + rustCrate.integrationTest("response_checksums_success") { + testBase.plus(checksumResponseSuccTestWritables)() + } + + rustCrate.integrationTest("response_checksums_fail") { + testBase.plus(checksumResponseFailTestWritables)() + } + + rustCrate.integrationTest("streaming_request_checksums") { + testBase.plus(checksumStreamingRequestTestWritables)() + } + + rustCrate.integrationTest("misc_tests") { + testBase.plus(miscTests)() + } + } + } + + /** + * Generate tests where the request checksum is calculated correctly + */ + private fun createRequestChecksumCalculationTest( + testDef: RequestChecksumCalculationTest, + context: ClientCodegenContext, + ): Writable { + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + val algoLower = testDef.checksumAlgorithm.lowercase() + // If the algo is Crc32 don't explicitly set it to test that the default is correctly set + val setChecksumAlgo = + if (testDef.checksumAlgorithm != "Crc32") { + ".checksum_algorithm($moduleName::types::ChecksumAlgorithm::${testDef.checksumAlgorithm})" + } else { + "" + } + return writable { + rustTemplate( + """ + //${testDef.docs} + ##[#{tokio}::test] + async fn ${algoLower}_request_checksums_work() { + let (http_client, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + let _ = client.some_operation() + .body(Blob::new(b"${testDef.requestPayload}")) + $setChecksumAlgo + .send() + .await; + let request = rx.expect_request(); + let ${algoLower}_header = request.headers() + .get("x-amz-checksum-$algoLower") + .expect("$algoLower header should exist"); + + assert_eq!(${algoLower}_header, "${testDef.checksumHeader}"); + + let algo_header = request.headers() + .get("x-amz-request-algorithm") + .expect("algo header should exist"); + + assert_eq!(algo_header, "${testDef.algoHeader}"); + + // Check the user-agent metrics for the selected algo + assert_ua_contains_metric_values( + &request + .headers() + .get("x-amz-user-agent") + .expect("UA header should be present"), + &["${testDef.algoFeatureId}"], + ); + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + + /** + * Generate tests where the request is streaming and checksum is calculated correctly + */ + private fun createStreamingRequestChecksumCalculationTest( + testDef: StreamingRequestChecksumCalculationTest, + context: ClientCodegenContext, + ): Writable { + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + val algoLower = testDef.checksumAlgorithm.lowercase() + // If the algo is Crc32 don't explicitly set it to test that the default is correctly set + val setChecksumAlgo = + if (testDef.checksumAlgorithm != "Crc32") { + ".checksum_algorithm($moduleName::types::ChecksumAlgorithm::${testDef.checksumAlgorithm})" + } else { + "" + } + return writable { + rustTemplate( + """ + //${testDef.docs} + ##[#{tokio}::test] + async fn ${algoLower}_request_checksums_work() { + let (http_client, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + + let mut file = tempfile::NamedTempFile::new().unwrap(); + file.as_file_mut() + .write_all("${testDef.requestPayload}".as_bytes()) + .unwrap(); + + let streaming_body = aws_smithy_types::byte_stream::ByteStream::read_from() + .path(&file) + .build() + .await + .unwrap(); + + let _operation = client + .some_streaming_operation() + .body(streaming_body) + $setChecksumAlgo + .send() + .await; + + + let request = rx.expect_request(); + + let headers = request.headers(); + + assert_eq!( + headers.get("x-amz-trailer").unwrap(), + "x-amz-checksum-$algoLower", + ); + assert_eq!(headers.get("content-encoding").unwrap(), "aws-chunked"); + + let mut body = request.body().try_clone().expect("body is retryable"); + + let mut body_data = bytes::BytesMut::new(); + while let Some(data) = body.data().await { + body_data.extend_from_slice(&data.unwrap()) + } + + let body_string = std::str::from_utf8(&body_data).unwrap(); + assert!(body_string.contains("x-amz-checksum-$algoLower:${testDef.trailerChecksum}")); + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + + /** + * Generate tests where the response checksum validates successfully + */ + private fun createResponseChecksumValidationSuccessTest( + testDef: ResponseChecksumValidationSuccessTest, + context: ClientCodegenContext, + ): Writable { + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + val algoLower = testDef.checksumAlgorithm.lowercase() + return writable { + rustTemplate( + """ + //${testDef.docs} + ##[::tokio::test] + async fn ${algoLower}_response_checksums_work() { + let (http_client, _rx) = #{capture_request}(Some( + http::Response::builder() + .header("x-amz-checksum-$algoLower", "${testDef.checksumHeaderValue}") + .body(SdkBody::from("${testDef.responsePayload}")) + .unwrap(), + )); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + let res = client + .some_operation() + .body(Blob::new(b"Doesn't matter.")) + .checksum_algorithm($moduleName::types::ChecksumAlgorithm::${testDef.checksumAlgorithm}) + .validation_mode($moduleName::types::ValidationMode::Enabled) + .send() + .await; + assert!(res.is_ok()) + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + + /** + * Generate tests where the response checksum fails to validate + */ + private fun createResponseChecksumValidationFailureTest( + testDef: ResponseChecksumValidationFailureTest, + context: ClientCodegenContext, + ): Writable { + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + val algoLower = testDef.checksumAlgorithm.lowercase() + return writable { + rustTemplate( + """ + //${testDef.docs} + ##[::tokio::test] + async fn ${algoLower}_response_checksums_work() { + let (http_client, _rx) = #{capture_request}(Some( + http::Response::builder() + .header("x-amz-checksum-$algoLower", "${testDef.checksumHeaderValue}") + .body(SdkBody::from("${testDef.responsePayload}")) + .unwrap(), + )); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + let res = client + .some_operation() + .body(Blob::new(b"Doesn't matter.")) + .checksum_algorithm($moduleName::types::ChecksumAlgorithm::${testDef.checksumAlgorithm}) + .validation_mode($moduleName::types::ValidationMode::Enabled) + .send() + .await; + + assert!(res.is_err()); + + let boxed_err = res + .unwrap_err() + .into_source() + .unwrap() + .downcast::(); + let typed_err = boxed_err.as_ref().unwrap().as_ref(); + + match typed_err { + aws_smithy_checksums::body::validate::Error::ChecksumMismatch { actual, .. } => { + let calculated_checksum = aws_smithy_types::base64::encode(actual); + assert_eq!(calculated_checksum, "${testDef.calculatedChecksum}"); + } + _ => panic!("Unknown error type in checksum validation"), + }; + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + + /** + * Generate miscellaneous tests, currently mostly focused on the inclusion of the checksum config metrics in the + * user-agent header + */ + private fun createMiscellaneousTests(context: ClientCodegenContext): Writable { + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + return writable { + rustTemplate( + """ + // The following tests confirm that the user-agent business metrics header is correctly + // set for the request/response checksum config values + ##[::tokio::test] + async fn request_config_ua_required() { + let (http_client, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .request_checksum_calculation( + aws_types::sdk_config::RequestChecksumCalculation::WhenRequired, + ) + .build(); + + let client = $moduleName::Client::from_conf(config); + let _ = client + .some_operation() + .body(Blob::new(b"Doesn't matter")) + .send() + .await; + let request = rx.expect_request(); + + let sdk_metrics = extract_ua_values( + &request + .headers() + .get("x-amz-user-agent") + .expect("UA header should be present"), + ).expect("UA header should be present"); + assert!(sdk_metrics.contains(&"a")); + assert!(!sdk_metrics.contains(&"Z")); + } + + ##[::tokio::test] + async fn request_config_ua_supported() { + let (http_client, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + let _ = client + .some_operation() + .body(Blob::new(b"Doesn't matter")) + .send() + .await; + let request = rx.expect_request(); + + let sdk_metrics = extract_ua_values( + &request + .headers() + .get("x-amz-user-agent") + .expect("UA header should be present"), + ).expect("UA header should be present"); + assert!(sdk_metrics.contains(&"Z")); + assert!(!sdk_metrics.contains(&"a")); + } + + ##[::tokio::test] + async fn response_config_ua_supported() { + let (http_client, rx) = #{capture_request}(Some( + http::Response::builder() + .header("x-amz-checksum-crc32", "i9aeUg==") + .body(SdkBody::from("Hello world")) + .unwrap(), + )); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .build(); + + let client = $moduleName::Client::from_conf(config); + let _ = client + .some_operation() + .body(Blob::new(b"Doesn't matter")) + .send() + .await; + let request = rx.expect_request(); + + let sdk_metrics = extract_ua_values( + &request + .headers() + .get("x-amz-user-agent") + .expect("UA header should be present"), + ).expect("UA header should be present"); + assert!(sdk_metrics.contains(&"b")); + assert!(!sdk_metrics.contains(&"c")); + } + + ##[::tokio::test] + async fn response_config_ua_required() { + let (http_client, rx) = #{capture_request}(Some( + http::Response::builder() + .header("x-amz-checksum-crc32", "i9aeUg==") + .body(SdkBody::from("Hello world")) + .unwrap(), + )); + let config = $moduleName::Config::builder() + .region(Region::from_static("doesntmatter")) + .with_test_defaults() + .http_client(http_client) + .response_checksum_validation( + aws_types::sdk_config::ResponseChecksumValidation::WhenRequired, + ) + .build(); + + let client = $moduleName::Client::from_conf(config); + let _ = client + .some_operation() + .body(Blob::new(b"Doesn't matter")) + .send() + .await; + let request = rx.expect_request(); + + let sdk_metrics = extract_ua_values( + &request + .headers() + .get("x-amz-user-agent") + .expect("UA header should be present"), + ).expect("UA header should be present"); + assert!(sdk_metrics.contains(&"c")); + assert!(!sdk_metrics.contains(&"b")); + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } +} + +// Classes and data for test definitions + +data class RequestChecksumCalculationTest( + val docs: String, + val requestPayload: String, + val checksumAlgorithm: String, + val algoHeader: String, + val checksumHeader: String, + val algoFeatureId: String, +) + +val checksumRequestTests = + listOf( + RequestChecksumCalculationTest( + "CRC32 checksum calculation works.", + "Hello world", + "Crc32", + "CRC32", + "i9aeUg==", + "U", + ), + RequestChecksumCalculationTest( + "CRC32C checksum calculation works.", + "Hello world", + "Crc32C", + "CRC32C", + "crUfeA==", + "V", + ), + /* We do not yet support Crc64Nvme checksums + RequestChecksumCalculationTest( + "CRC64NVME checksum calculation works.", + "Hello world", + "Crc64Nvme", + "CRC64NVME", + "uc8X9yrZrD4=", + "W", + ), + */ + RequestChecksumCalculationTest( + "SHA1 checksum calculation works.", + "Hello world", + "Sha1", + "SHA1", + "e1AsOh9IyGCa4hLN+2Od7jlnP14=", + "X", + ), + RequestChecksumCalculationTest( + "SHA256 checksum calculation works.", + "Hello world", + "Sha256", + "SHA256", + "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=", + "Y", + ), + ) + +data class StreamingRequestChecksumCalculationTest( + val docs: String, + val requestPayload: String, + val checksumAlgorithm: String, + val trailerChecksum: String, +) + +val streamingRequestTests = + listOf( + StreamingRequestChecksumCalculationTest( + "CRC32 streaming checksum calculation works.", + "Hello world", + "Crc32", + "i9aeUg==", + ), + StreamingRequestChecksumCalculationTest( + "CRC32C streaming checksum calculation works.", + "Hello world", + "Crc32C", + "crUfeA==", + ), +// StreamingRequestChecksumCalculationTest( +// "CRC64NVME streaming checksum calculation works.", +// "Hello world", +// "Crc64Nvme", +// "uc8X9yrZrD4=", +// ), + StreamingRequestChecksumCalculationTest( + "SHA1 streaming checksum calculation works.", + "Hello world", + "Sha1", + "e1AsOh9IyGCa4hLN+2Od7jlnP14=", + ), + StreamingRequestChecksumCalculationTest( + "SHA256 streaming checksum calculation works.", + "Hello world", + "Sha256", + "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=", + ), + ) + +data class ResponseChecksumValidationSuccessTest( + val docs: String, + val responsePayload: String, + val checksumAlgorithm: String, + val checksumHeaderValue: String, +) + +val checksumResponseSuccTests = + listOf( + ResponseChecksumValidationSuccessTest( + "Successful payload validation with CRC32 checksum.", + "Hello world", + "Crc32", + "i9aeUg==", + ), + ResponseChecksumValidationSuccessTest( + "Successful payload validation with Crc32C checksum.", + "Hello world", + "Crc32C", + "crUfeA==", + ), + /* + ResponseChecksumValidationSuccessTest( + "Successful payload validation with Crc64Nvme checksum.", + "Hello world", + "Crc64Nvme", + "uc8X9yrZrD4=", + ),*/ + ResponseChecksumValidationSuccessTest( + "Successful payload validation with Sha1 checksum.", + "Hello world", + "Sha1", + "e1AsOh9IyGCa4hLN+2Od7jlnP14=", + ), + ResponseChecksumValidationSuccessTest( + "Successful payload validation with Sha256 checksum.", + "Hello world", + "Sha256", + "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=", + ), + ) + +data class ResponseChecksumValidationFailureTest( + val docs: String, + val responsePayload: String, + val checksumAlgorithm: String, + val checksumHeaderValue: String, + val calculatedChecksum: String, +) + +val checksumResponseFailTests = + listOf( + ResponseChecksumValidationFailureTest( + "Failed payload validation with CRC32 checksum.", + "Hello world", + "Crc32", + "bm90LWEtY2hlY2tzdW0=", + "i9aeUg==", + ), + ResponseChecksumValidationFailureTest( + "Failed payload validation with CRC32C checksum.", + "Hello world", + "Crc32C", + "bm90LWEtY2hlY2tzdW0=", + "crUfeA==", + ), + /* + ResponseChecksumValidationFailureTest( + "Failed payload validation with CRC64NVME checksum.", + "Hello world", + "Crc64Nvme", + "bm90LWEtY2hlY2tzdW0=", + "uc8X9yrZrD4=", + ),*/ + ResponseChecksumValidationFailureTest( + "Failed payload validation with SHA1 checksum.", + "Hello world", + "Sha1", + "bm90LWEtY2hlY2tzdW0=", + "e1AsOh9IyGCa4hLN+2Od7jlnP14=", + ), + ResponseChecksumValidationFailureTest( + "Failed payload validation with SHA256 checksum.", + "Hello world", + "Sha256", + "bm90LWEtY2hlY2tzdW0=", + "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=", + ), + ) diff --git a/aws/sdk/aws-models-extra/s3-tests.smithy b/aws/sdk/aws-models-extra/s3-tests.smithy index 59ced8bc10..bee98811f6 100644 --- a/aws/sdk/aws-models-extra/s3-tests.smithy +++ b/aws/sdk/aws-models-extra/s3-tests.smithy @@ -111,9 +111,7 @@ apply PutBucketLifecycleConfiguration @httpRequestTests([ protocol: "aws.protocols#restXml", uri: "/", headers: { - // we can assert this, but when this test is promoted, it can't assert - // on the exact contents - "content-md5": "JP8DTuCSH6yDC8wNGg4+mA==", + "x-amz-checksum-crc32": "11+f3g==", }, bodyMediaType: "application/xml", body: """ diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index 6b4f88c8f8..11b370ecc3 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -80,7 +80,7 @@ async fn multi_region_access_points() { let auth_header = captured_request.headers().get("AUTHORIZATION").unwrap(); // Verifies that the sigv4a signing algorithm was used, that the signing scope doesn't include a region, and that the x-amz-region-set header was signed. let expected_start = - "AWS4-ECDSA-P256-SHA256 Credential=ANOTREAL/20090213/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-user-agent, Signature="; + "AWS4-ECDSA-P256-SHA256 Credential=ANOTREAL/20090213/s3/aws4_request, SignedHeaders=host;x-amz-checksum-mode;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-user-agent, Signature="; assert!( auth_header.starts_with(expected_start), diff --git a/aws/sdk/integration-tests/s3/tests/express.rs b/aws/sdk/integration-tests/s3/tests/express.rs index 8621b6c108..44ae4c7671 100644 --- a/aws/sdk/integration-tests/s3/tests/express.rs +++ b/aws/sdk/integration-tests/s3/tests/express.rs @@ -183,7 +183,12 @@ async fn presigning() { ][..], &query_params ); - assert_eq!(presigned.headers().count(), 0); + // Presigned request has one header and that is the x-amz-checksum-mode + // header with default value ENABLED + assert_eq!(presigned.headers().count(), 1); + let headers = presigned.headers().collect::>(); + assert_eq!(headers.get(0).unwrap().0, "x-amz-checksum-mode"); + assert_eq!(headers.get(0).unwrap().1, "ENABLED"); } fn operation_request_with_checksum( diff --git a/aws/sdk/integration-tests/s3/tests/presigning.rs b/aws/sdk/integration-tests/s3/tests/presigning.rs index 768dd563bb..f887b0643d 100644 --- a/aws/sdk/integration-tests/s3/tests/presigning.rs +++ b/aws/sdk/integration-tests/s3/tests/presigning.rs @@ -100,7 +100,9 @@ async fn test_presigning() { ][..], &query_params ); - assert_eq!(presigned.headers().count(), 0); + assert_eq!(presigned.headers().count(), 1); + let headers = presigned.headers().collect::>(); + assert_eq!(headers.get("x-amz-checksum-mode").unwrap(), &"ENABLED"); } #[tokio::test] @@ -135,8 +137,8 @@ async fn test_presigning_with_payload_headers() { "X-Amz-Date=20090213T233131Z", "X-Amz-Expires=30", "X-Amz-Security-Token=notarealsessiontoken", - "X-Amz-Signature=be1d41dc392f7019750e4f5e577234fb9059dd20d15f6a99734196becce55e52", - "X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost", + "X-Amz-Signature=9403eb5961c8af066f2473f88cb9248b39fd61f8eedc33af14ec9c2d26c22974", + "X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-amz-checksum-crc32%3Bx-amz-sdk-checksum-algorithm", "x-id=PutObject" ][..], &query_params @@ -148,7 +150,9 @@ async fn test_presigning_with_payload_headers() { Some(&"application/x-test") ); assert_eq!(headers.get(CONTENT_LENGTH.as_str()), Some(&"12345")); - assert_eq!(headers.len(), 2); + assert_eq!(headers.get("x-amz-sdk-checksum-algorithm"), Some(&"CRC32")); + assert_eq!(headers.get("x-amz-checksum-crc32"), Some(&"AAAAAA==")); + assert_eq!(headers.len(), 4); } #[tokio::test] @@ -164,7 +168,7 @@ async fn test_presigned_upload_part() { }) .await; pretty_assertions::assert_eq!( - "https://bucket.s3.us-east-1.amazonaws.com/key?x-id=UploadPart&partNumber=0&uploadId=upload-id&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=content-length%3Bhost&X-Amz-Signature=a702867244f0bd1fb4d161e2a062520dcbefae3b9992d2e5366bcd61a60c6ddd&X-Amz-Security-Token=notarealsessiontoken", + "https://bucket.s3.us-east-1.amazonaws.com/key?x-id=UploadPart&partNumber=0&uploadId=upload-id&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=content-length%3Bhost%3Bx-amz-checksum-crc32%3Bx-amz-sdk-checksum-algorithm&X-Amz-Signature=0b5835e056c463d6c0963326966f6cf42c75b7a218057836274d38288e055d36&X-Amz-Security-Token=notarealsessiontoken", presigned.uri().to_string(), ); } diff --git a/aws/sdk/integration-tests/s3/tests/stalled-stream-protection.rs b/aws/sdk/integration-tests/s3/tests/stalled-stream-protection.rs index 283999d60e..920c95c1d6 100644 --- a/aws/sdk/integration-tests/s3/tests/stalled-stream-protection.rs +++ b/aws/sdk/integration-tests/s3/tests/stalled-stream-protection.rs @@ -96,6 +96,9 @@ async fn test_stalled_stream_protection_defaults_for_upload() { .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .endpoint_url(format!("http://{server_addr}")) + // The Body used here is odd and fails the body.size_hint().exact() check in the streaming branch of + // the `RequestChecksumInterceptor` + .request_checksum_calculation(aws_sdk_s3::config::RequestChecksumCalculation::WhenRequired) .build(); let client = Client::from_conf(conf); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index c2200003db..bb4c69e29f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -93,7 +93,9 @@ open class OperationGenerator( *preludeScope, "Arc" to RuntimeType.Arc, "ConcreteInput" to symbolProvider.toSymbol(operationShape.inputShape(model)), - "Input" to RuntimeType.smithyRuntimeApiClient(runtimeConfig).resolve("client::interceptors::context::Input"), + "Input" to + RuntimeType.smithyRuntimeApiClient(runtimeConfig) + .resolve("client::interceptors::context::Input"), "Operation" to symbolProvider.toSymbol(operationShape), "OperationError" to errorType, "OperationOutput" to outputType, @@ -169,7 +171,9 @@ open class OperationGenerator( } """, *codegenScope, - "Error" to RuntimeType.smithyRuntimeApiClient(runtimeConfig).resolve("client::interceptors::context::Error"), + "Error" to + RuntimeType.smithyRuntimeApiClient(runtimeConfig) + .resolve("client::interceptors::context::Error"), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "OrchestratorError" to RuntimeType.smithyRuntimeApiClient(runtimeConfig) diff --git a/rust-runtime/Cargo.lock b/rust-runtime/Cargo.lock index 5373a5e3ca..82ae4896e7 100644 --- a/rust-runtime/Cargo.lock +++ b/rust-runtime/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -152,7 +146,7 @@ checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", "zeroize", ] @@ -172,9 +166,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" +checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -185,9 +179,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.20.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703" +checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" dependencies = [ "bindgen", "cc", @@ -200,18 +194,18 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" +checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-eventstream 0.60.4", - "aws-smithy-http 0.60.10", - "aws-smithy-runtime 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-eventstream 0.60.5 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-http 0.60.11 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-runtime 1.7.1", "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", "aws-types", "bytes", "fastrand", @@ -226,23 +220,23 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.48.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a545b16c05af9302b0b4b38a7584f6f323749e407169aa3e9b210e7c0a808d" +checksum = "c09fd4b5c7ed75f52b913b4f3ff0501dae7f8cb9125f6d45db4553980cbc0528" dependencies = [ "ahash", "aws-credential-types", "aws-runtime", "aws-sigv4", "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-checksums 0.60.12 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-eventstream 0.60.4", - "aws-smithy-http 0.60.10", + "aws-smithy-checksums 0.60.12", + "aws-smithy-eventstream 0.60.5 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-http 0.60.11 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-json 0.60.7 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-runtime 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-runtime 1.7.1", "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", - "aws-smithy-xml 0.60.8", + "aws-smithy-types 1.2.6", + "aws-smithy-xml 0.60.9 (registry+https://github.com/rust-lang/crates.io-index)", "aws-types", "bytes", "fastrand", @@ -261,15 +255,15 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +checksum = "cc8db6904450bafe7473c6ca9123f88cc11089e41a025408f992db4e22d3be68" dependencies = [ "aws-credential-types", - "aws-smithy-eventstream 0.60.4", - "aws-smithy-http 0.60.10", + "aws-smithy-eventstream 0.60.5 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-http 0.60.11 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", "bytes", "crypto-bigint 0.5.5", "form_urlencoded", @@ -314,7 +308,7 @@ dependencies = [ name = "aws-smithy-cbor" version = "0.60.7" dependencies = [ - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "criterion", "minicbor", ] @@ -322,11 +316,12 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598b1689d001c4d4dc3cb386adb07d37786783aee3ac4b324bcadac116bf3d23" dependencies = [ - "aws-smithy-http 0.60.11", + "aws-smithy-http 0.60.11 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-types 1.2.6", "bytes", - "bytes-utils", "crc32c", "crc32fast", "hex", @@ -334,23 +329,19 @@ dependencies = [ "http-body 0.4.6", "md-5", "pin-project-lite", - "pretty_assertions", "sha1", "sha2", - "tokio", "tracing", - "tracing-test", ] [[package]] name = "aws-smithy-checksums" -version = "0.60.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598b1689d001c4d4dc3cb386adb07d37786783aee3ac4b324bcadac116bf3d23" +version = "0.62.0" dependencies = [ - "aws-smithy-http 0.60.10", - "aws-smithy-types 1.2.4", + "aws-smithy-http 0.60.11", + "aws-smithy-types 1.2.8", "bytes", + "bytes-utils", "crc32c", "crc32fast", "hex", @@ -358,9 +349,12 @@ dependencies = [ "http-body 0.4.6", "md-5", "pin-project-lite", + "pretty_assertions", "sha1", "sha2", + "tokio", "tracing", + "tracing-test", ] [[package]] @@ -372,7 +366,7 @@ name = "aws-smithy-compression" version = "0.0.2" dependencies = [ "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "bytes", "bytes-utils", "flate2", @@ -390,25 +384,25 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.60.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6363078f927f612b970edf9d1903ef5cef9a64d1e8423525ebb1f0a1633c858" +version = "0.60.5" dependencies = [ - "aws-smithy-types 1.2.4", + "arbitrary", + "aws-smithy-types 1.2.8", "bytes", + "bytes-utils", "crc32fast", + "derive_arbitrary", ] [[package]] name = "aws-smithy-eventstream" version = "0.60.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef7d0a272725f87e51ba2bf89f8c21e4df61b9e49ae1ac367a6d69916ef7c90" dependencies = [ - "arbitrary", "aws-smithy-types 1.2.6", "bytes", - "bytes-utils", "crc32fast", - "derive_arbitrary", ] [[package]] @@ -416,9 +410,9 @@ name = "aws-smithy-experimental" version = "0.1.4" dependencies = [ "aws-smithy-async 1.2.1", - "aws-smithy-runtime 1.7.1", + "aws-smithy-runtime 1.7.2", "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "h2 0.4.6", "http 1.1.0", "hyper 1.4.1", @@ -426,7 +420,7 @@ dependencies = [ "hyper-util", "once_cell", "pin-project-lite", - "rustls 0.23.12", + "rustls 0.23.13", "tokio", "tower", "tracing", @@ -434,46 +428,46 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" +version = "0.60.11" dependencies = [ - "aws-smithy-eventstream 0.60.4", - "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "async-stream", + "aws-smithy-eventstream 0.60.5", + "aws-smithy-runtime-api 1.7.2", + "aws-smithy-types 1.2.8", "bytes", "bytes-utils", "futures-core", + "futures-util", "http 0.2.12", "http-body 0.4.6", + "hyper 0.14.30", "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", + "proptest", + "tokio", "tracing", ] [[package]] name = "aws-smithy-http" version = "0.60.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" dependencies = [ - "async-stream", - "aws-smithy-eventstream 0.60.5", - "aws-smithy-runtime-api 1.7.2", + "aws-smithy-eventstream 0.60.5 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-types 1.2.6", "bytes", "bytes-utils", "futures-core", - "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", - "proptest", - "tokio", "tracing", ] @@ -489,7 +483,7 @@ dependencies = [ "aws-smithy-http 0.60.11", "aws-smithy-json 0.60.7", "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "aws-smithy-xml 0.60.9", "bytes", "futures-util", @@ -519,7 +513,7 @@ dependencies = [ "aws-smithy-http 0.60.11", "aws-smithy-http-server", "aws-smithy-json 0.60.7", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "aws-smithy-xml 0.60.9", "bytes", "futures", @@ -559,7 +553,7 @@ version = "0.60.3" name = "aws-smithy-json" version = "0.60.7" dependencies = [ - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "proptest", "serde_json", ] @@ -570,7 +564,7 @@ version = "0.60.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" dependencies = [ - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", ] [[package]] @@ -579,7 +573,7 @@ version = "0.2.1" dependencies = [ "aws-sdk-s3", "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "tokio", ] @@ -623,26 +617,25 @@ dependencies = [ name = "aws-smithy-query" version = "0.60.7" dependencies = [ - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "urlencoding", ] [[package]] name = "aws-smithy-runtime" version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ - "approx", - "aws-smithy-async 1.2.1", - "aws-smithy-http 0.60.11", - "aws-smithy-protocol-test 0.62.0", - "aws-smithy-runtime-api 1.7.2", + "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-http 0.60.11 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-protocol-test 0.62.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-types 1.2.6", "bytes", "fastrand", - "futures-util", "h2 0.3.26", "http 0.2.12", - "http 1.1.0", "http-body 0.4.6", "http-body 1.0.1", "httparse", @@ -652,31 +645,30 @@ dependencies = [ "once_cell", "pin-project-lite", "pin-utils", - "pretty_assertions", "rustls 0.21.12", "serde", "serde_json", "tokio", "tracing", "tracing-subscriber", - "tracing-test", ] [[package]] name = "aws-smithy-runtime" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +version = "1.7.2" dependencies = [ - "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-http 0.60.10", - "aws-smithy-protocol-test 0.62.0 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "approx", + "aws-smithy-async 1.2.1", + "aws-smithy-http 0.60.11", + "aws-smithy-protocol-test 0.62.0", + "aws-smithy-runtime-api 1.7.2", + "aws-smithy-types 1.2.8", "bytes", "fastrand", + "futures-util", "h2 0.3.26", "http 0.2.12", + "http 1.1.0", "http-body 0.4.6", "http-body 1.0.1", "httparse", @@ -686,12 +678,14 @@ dependencies = [ "once_cell", "pin-project-lite", "pin-utils", + "pretty_assertions", "rustls 0.21.12", "serde", "serde_json", "tokio", "tracing", "tracing-subscriber", + "tracing-test", ] [[package]] @@ -699,7 +693,7 @@ name = "aws-smithy-runtime-api" version = "1.7.2" dependencies = [ "aws-smithy-async 1.2.1", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "bytes", "http 0.2.12", "http 1.1.0", @@ -717,7 +711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" dependencies = [ "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", "bytes", "http 0.2.12", "http 1.1.0", @@ -729,9 +723,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.4" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" +checksum = "03701449087215b5369c7ea17fef0dd5d24cb93439ec5af0c7615f58c3f22605" dependencies = [ "base64-simd", "bytes", @@ -755,7 +749,7 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.6" +version = "1.2.8" dependencies = [ "base64 0.13.1", "base64-simd", @@ -792,7 +786,7 @@ name = "aws-smithy-types-convert" version = "0.60.8" dependencies = [ "aws-smithy-async 1.2.1", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "chrono", "futures-core", "time", @@ -804,7 +798,7 @@ version = "0.1.3" dependencies = [ "aws-smithy-http 0.60.11", "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "bytes", "http 1.1.0", "tracing", @@ -813,20 +807,20 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +version = "0.60.9" dependencies = [ + "aws-smithy-protocol-test 0.62.0", + "base64 0.13.1", + "proptest", "xmlparser", ] [[package]] name = "aws-smithy-xml" version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" dependencies = [ - "aws-smithy-protocol-test 0.62.0", - "base64 0.13.1", - "proptest", "xmlparser", ] @@ -839,7 +833,7 @@ dependencies = [ "aws-credential-types", "aws-smithy-async 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "aws-smithy-runtime-api 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aws-smithy-types 1.2.4", + "aws-smithy-types 1.2.6", "rustc_version", "tracing", ] @@ -862,17 +856,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -1003,9 +997,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -1047,9 +1041,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.16" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -1135,18 +1129,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstyle", "clap_lex 0.7.2", @@ -1209,9 +1203,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1243,7 +1237,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.17", + "clap 4.5.18", "criterion-plot", "is-terminal", "itertools 0.10.5", @@ -1505,7 +1499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -1641,9 +1635,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -1931,7 +1925,7 @@ dependencies = [ "http 1.1.0", "hyper 1.4.1", "hyper-util", - "rustls 0.23.12", + "rustls 0.23.13", "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", @@ -1941,9 +1935,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -2004,9 +1998,9 @@ dependencies = [ "aws-smithy-compression", "aws-smithy-http 0.60.11", "aws-smithy-json 0.60.7", - "aws-smithy-runtime 1.7.1", + "aws-smithy-runtime 1.7.2", "aws-smithy-runtime-api 1.7.2", - "aws-smithy-types 1.2.6", + "aws-smithy-types 1.2.8", "aws-smithy-xml 0.60.9", "bytes", "fastrand", @@ -2250,9 +2244,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minicbor" -version = "0.24.2" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f8e213c36148d828083ae01948eed271d03f95f7e72571fa242d78184029af2" +checksum = "29be4f60e41fde478b36998b88821946aafac540e53591e76db53921a0cc225b" dependencies = [ "half 2.4.1", "minicbor-derive", @@ -2260,9 +2254,9 @@ dependencies = [ [[package]] name = "minicbor-derive" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297ad6022c004a6c54f0fb28bbd2306314d5a4edc767e56b4b4cc48fc2371654" +checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" dependencies = [ "proc-macro2", "quote", @@ -2275,15 +2269,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -2531,9 +2516,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -2544,15 +2529,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -2574,9 +2559,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -2832,9 +2817,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -2962,9 +2947,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -2987,15 +2972,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "aws-lc-rs", "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.7", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] @@ -3062,9 +3047,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "aws-lc-rs", "ring 0.17.8", @@ -3101,11 +3086,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3153,9 +3138,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -3175,9 +3160,9 @@ checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -3194,9 +3179,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -3565,7 +3550,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.12", + "rustls 0.23.13", "rustls-pki-types", "tokio", ] @@ -3801,15 +3786,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -4127,9 +4112,9 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yasna" @@ -4166,17 +4151,3 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] diff --git a/rust-runtime/aws-smithy-checksums/Cargo.toml b/rust-runtime/aws-smithy-checksums/Cargo.toml index 5c7a6b8a01..97b295130c 100644 --- a/rust-runtime/aws-smithy-checksums/Cargo.toml +++ b/rust-runtime/aws-smithy-checksums/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-checksums" -version = "0.60.12" +version = "0.62.0" authors = [ "AWS Rust SDK Team ", "Zelda Hessler ", diff --git a/rust-runtime/aws-smithy-checksums/src/lib.rs b/rust-runtime/aws-smithy-checksums/src/lib.rs index e969918dd7..b900bac5fa 100644 --- a/rust-runtime/aws-smithy-checksums/src/lib.rs +++ b/rust-runtime/aws-smithy-checksums/src/lib.rs @@ -17,6 +17,7 @@ //! Checksum calculation and verification callbacks. use crate::error::UnknownChecksumAlgorithmError; + use bytes::Bytes; use std::str::FromStr; @@ -33,9 +34,11 @@ pub const MD5_NAME: &str = "md5"; /// We only support checksum calculation and validation for these checksum algorithms. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] pub enum ChecksumAlgorithm { Crc32, Crc32c, + #[deprecated] Md5, Sha1, Sha256, @@ -49,7 +52,6 @@ impl FromStr for ChecksumAlgorithm { /// - "crc32c" /// - "sha1" /// - "sha256" - /// - "md5" /// /// Passing an invalid name will return an error. fn from_str(checksum_algorithm: &str) -> Result { @@ -62,7 +64,8 @@ impl FromStr for ChecksumAlgorithm { } else if checksum_algorithm.eq_ignore_ascii_case(SHA_256_NAME) { Ok(Self::Sha256) } else if checksum_algorithm.eq_ignore_ascii_case(MD5_NAME) { - Ok(Self::Md5) + // MD5 is now an alias for the default Crc32 since it is deprecated + Ok(Self::Crc32) } else { Err(UnknownChecksumAlgorithmError::new(checksum_algorithm)) } @@ -75,7 +78,8 @@ impl ChecksumAlgorithm { match self { Self::Crc32 => Box::::default(), Self::Crc32c => Box::::default(), - Self::Md5 => Box::::default(), + #[allow(deprecated)] + Self::Md5 => Box::::default(), Self::Sha1 => Box::::default(), Self::Sha256 => Box::::default(), } @@ -86,6 +90,7 @@ impl ChecksumAlgorithm { match self { Self::Crc32 => CRC_32_NAME, Self::Crc32c => CRC_32_C_NAME, + #[allow(deprecated)] Self::Md5 => MD5_NAME, Self::Sha1 => SHA_1_NAME, Self::Sha256 => SHA_256_NAME, diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index de05b3ea38..b53f1d7805 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-runtime" -version = "1.7.1" +version = "1.7.2" authors = ["AWS Rust SDK Team ", "Zelda Hessler "] description = "The new smithy runtime crate" edition = "2021" diff --git a/rust-runtime/aws-smithy-runtime/src/client/sdk_feature.rs b/rust-runtime/aws-smithy-runtime/src/client/sdk_feature.rs index 5a8ab95a13..a6c4f04ad5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/sdk_feature.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/sdk_feature.rs @@ -12,6 +12,15 @@ pub enum SmithySdkFeature { Paginator, GzipRequestCompression, ProtocolRpcV2Cbor, + FlexibleChecksumsReqCrc32, + FlexibleChecksumsReqCrc32c, + FlexibleChecksumsReqCrc64, + FlexibleChecksumsReqSha1, + FlexibleChecksumsReqSha256, + FlexibleChecksumsReqWhenSupported, + FlexibleChecksumsReqWhenRequired, + FlexibleChecksumsResWhenSupported, + FlexibleChecksumsResWhenRequired, } impl Storable for SmithySdkFeature { diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 7b52b8b6d8..3fd9186d0f 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-types" -version = "1.2.7" +version = "1.2.8" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", diff --git a/rust-runtime/aws-smithy-types/src/checksum_config.rs b/rust-runtime/aws-smithy-types/src/checksum_config.rs new file mode 100644 index 0000000000..555939b8c1 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/checksum_config.rs @@ -0,0 +1,161 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Types that allow users to indicate their preferences for checksum calculation and validation + +// Note: These types would likely make more sense in `aws-smithy-checksums` and were originally +// added there. But we have lints protecting against exporting non-stable types from stable crates +// and the checksums crate is not yet 1.0, so these types cannot live there for now. In the future +// if we do decide to 1.0 the checksums crate we can move these types there and re-export them here +// to maintain the current behavior. + +use std::error::Error; +use std::fmt; +use std::str::FromStr; + +use crate::config_bag::{Storable, StoreReplace}; + +// Valid names for RequestChecksumCalculation and ResponseChecksumValidation +const WHEN_SUPPORTED: &str = "when_supported"; +const WHEN_REQUIRED: &str = "when_required"; + +/// Determines when a checksum will be calculated for request payloads. Values are: +/// * [RequestChecksumCalculation::WhenSupported] - (default) When set, a checksum will be +/// calculated for all request payloads of operations modeled with the +/// `httpChecksum` trait where `requestChecksumRequired` is `true` and/or a +/// `requestAlgorithmMember` is modeled. +/// * [RequestChecksumCalculation::WhenRequired] - When set, a checksum will only be calculated for +/// request payloads of operations modeled with the `httpChecksum` trait where +/// `requestChecksumRequired` is `true` or where a requestAlgorithmMember +/// is modeled and supplied. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum RequestChecksumCalculation { + /// Calculate request checksums when they are supported. + WhenSupported, + /// Caulculate request checksums only when they are required. + WhenRequired, +} + +impl Storable for RequestChecksumCalculation { + type Storer = StoreReplace; +} + +impl FromStr for RequestChecksumCalculation { + type Err = UnknownRequestChecksumCalculationError; + + fn from_str(request_checksum_calculation: &str) -> Result { + if request_checksum_calculation.eq_ignore_ascii_case(WHEN_SUPPORTED) { + Ok(Self::WhenSupported) + } else if request_checksum_calculation.eq_ignore_ascii_case(WHEN_REQUIRED) { + Ok(Self::WhenRequired) + } else { + Err(UnknownRequestChecksumCalculationError::new( + request_checksum_calculation, + )) + } + } +} + +/// Determines when checksum validation will be performed on response payloads. Values are: +/// * [ResponseChecksumValidation::WhenSupported] - (default) When set, checksum validation is performed on all +/// response payloads of operations modeled with the `httpChecksum` trait where +/// `responseAlgorithms` is modeled, except when no modeled checksum algorithms +/// are supported. +/// * [ResponseChecksumValidation::WhenRequired] - When set, checksum validation is not performed on +/// response payloads of operations unless the checksum algorithm is supported and +/// the `requestValidationModeMember` member is set to `ENABLED`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum ResponseChecksumValidation { + /// Validate response checksums when they are supported. + WhenSupported, + /// Validate response checksums only when they are required. + WhenRequired, +} + +impl Storable for ResponseChecksumValidation { + type Storer = StoreReplace; +} + +impl FromStr for ResponseChecksumValidation { + type Err = UnknownResponseChecksumValidationError; + + fn from_str(response_checksum_validation: &str) -> Result { + if response_checksum_validation.eq_ignore_ascii_case(WHEN_SUPPORTED) { + Ok(Self::WhenSupported) + } else if response_checksum_validation.eq_ignore_ascii_case(WHEN_REQUIRED) { + Ok(Self::WhenRequired) + } else { + Err(UnknownResponseChecksumValidationError::new( + response_checksum_validation, + )) + } + } +} + +/// Unknown setting for `request_checksum_calculation` +#[derive(Debug)] +#[non_exhaustive] +pub struct UnknownRequestChecksumCalculationError { + request_checksum_calculation: String, +} + +impl UnknownRequestChecksumCalculationError { + pub(crate) fn new(request_checksum_calculation: impl Into) -> Self { + Self { + request_checksum_calculation: request_checksum_calculation.into(), + } + } + + /// The unknown value + pub fn request_checksum_calculation(&self) -> &str { + &self.request_checksum_calculation + } +} + +impl fmt::Display for UnknownRequestChecksumCalculationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + r#"unknown request_checksum_calculation value "{}", please pass a known name ("when_supported", "when_required")"#, + self.request_checksum_calculation + ) + } +} + +impl Error for UnknownRequestChecksumCalculationError {} + +/// Unknown setting for `response_checksum_validation` +#[derive(Debug)] +#[non_exhaustive] +pub struct UnknownResponseChecksumValidationError { + response_checksum_validation: String, +} + +impl UnknownResponseChecksumValidationError { + pub(crate) fn new(response_checksum_validation: impl Into) -> Self { + Self { + response_checksum_validation: response_checksum_validation.into(), + } + } + + /// The unknown value + pub fn response_checksum_validation(&self) -> &str { + &self.response_checksum_validation + } +} + +impl fmt::Display for UnknownResponseChecksumValidationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + r#"unknown response_checksum_validation value "{}", please pass a known name ("when_supported", "when_required")"#, + self.response_checksum_validation + ) + } +} + +impl Error for UnknownResponseChecksumValidationError {} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index ed53de191b..5af73e1fb1 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -20,6 +20,7 @@ pub mod base64; pub mod body; pub mod byte_stream; +pub mod checksum_config; /// A typemap for storing configuration. pub mod config_bag; pub mod date_time;