AWS SDK

AWS SDK

rev. d06a46cae0f385cdae37a9f8264db3469a090ab5

Files changed:

tmp-codegen-diff/aws-sdk/Cargo.lock

@@ -414,414 +473,474 @@
  434    434   
 "aws-types",
  435    435   
 "bytes",
  436    436   
 "bytes-utils",
  437    437   
 "convert_case",
  438    438   
 "fastrand 2.3.0",
  439    439   
 "futures-util",
  440    440   
 "http 0.2.12",
  441    441   
 "http 1.4.0",
  442    442   
 "http-body 0.4.6",
  443    443   
 "http-body 1.0.1",
         444  +
 "http-body-util",
  444    445   
 "percent-encoding",
  445    446   
 "pin-project-lite",
  446    447   
 "proptest",
  447    448   
 "regex-lite",
  448    449   
 "serde",
  449    450   
 "serde_json",
  450    451   
 "tokio",
  451    452   
 "tracing",
  452    453   
 "tracing-subscriber",
  453    454   
 "tracing-test",
@@ -543,544 +602,604 @@
  563    564   
 "aws-smithy-http",
  564    565   
 "aws-smithy-json",
  565    566   
 "aws-smithy-observability",
  566    567   
 "aws-smithy-runtime",
  567    568   
 "aws-smithy-runtime-api",
  568    569   
 "aws-smithy-types",
  569    570   
 "aws-types",
  570    571   
 "bytes",
  571    572   
 "fastrand 2.3.0",
  572    573   
 "http 0.2.12",
         574  +
 "http 1.4.0",
  573    575   
 "proptest",
  574    576   
 "regex-lite",
  575    577   
 "tokio",
  576    578   
 "tracing",
  577    579   
]
  578    580   
  579    581   
[[package]]
  580    582   
name = "aws-sdk-dynamodb"
  581    583   
version = "0.0.0-local"
  582    584   
dependencies = [
@@ -630,632 +689,692 @@
  650    652   
 "aws-smithy-http",
  651    653   
 "aws-smithy-json",
  652    654   
 "aws-smithy-observability",
  653    655   
 "aws-smithy-runtime",
  654    656   
 "aws-smithy-runtime-api",
  655    657   
 "aws-smithy-types",
  656    658   
 "aws-types",
  657    659   
 "bytes",
  658    660   
 "fastrand 2.3.0",
  659    661   
 "http 0.2.12",
         662  +
 "http 1.4.0",
  660    663   
 "proptest",
  661    664   
 "regex-lite",
  662    665   
 "tokio",
  663    666   
 "tracing",
  664    667   
]
  665    668   
  666    669   
[[package]]
  667    670   
name = "aws-sdk-glacier"
  668    671   
version = "0.0.0-local"
  669    672   
dependencies = [
@@ -814,817 +873,877 @@
  834    837   
 "aws-smithy-http-client",
  835    838   
 "aws-smithy-json",
  836    839   
 "aws-smithy-observability",
  837    840   
 "aws-smithy-runtime",
  838    841   
 "aws-smithy-runtime-api",
  839    842   
 "aws-smithy-types",
  840    843   
 "aws-smithy-xml",
  841    844   
 "aws-types",
  842    845   
 "fastrand 2.3.0",
  843    846   
 "http 0.2.12",
         847  +
 "http 1.4.0",
  844    848   
 "pretty_assertions",
  845    849   
 "proptest",
  846    850   
 "regex-lite",
  847    851   
 "tokio",
  848    852   
 "tracing",
  849    853   
 "tracing-test",
  850    854   
]
  851    855   
  852    856   
[[package]]
  853    857   
name = "aws-sdk-s3"
@@ -923,927 +1028,1035 @@
  943    947   
 "aws-smithy-http",
  944    948   
 "aws-smithy-json",
  945    949   
 "aws-smithy-observability",
  946    950   
 "aws-smithy-runtime",
  947    951   
 "aws-smithy-runtime-api",
  948    952   
 "aws-smithy-types",
  949    953   
 "aws-types",
  950    954   
 "bytes",
  951    955   
 "fastrand 2.3.0",
  952    956   
 "http 0.2.12",
         957  +
 "http 1.4.0",
  953    958   
 "proptest",
  954    959   
 "regex-lite",
  955    960   
 "tokio",
  956    961   
 "tracing",
  957    962   
]
  958    963   
  959    964   
[[package]]
  960    965   
name = "aws-sdk-sso"
  961    966   
version = "0.0.0-local"
  962    967   
dependencies = [
  963    968   
 "aws-credential-types",
  964    969   
 "aws-runtime",
  965    970   
 "aws-smithy-async",
  966    971   
 "aws-smithy-http",
  967    972   
 "aws-smithy-json",
  968    973   
 "aws-smithy-observability",
  969    974   
 "aws-smithy-runtime",
  970    975   
 "aws-smithy-runtime-api",
  971    976   
 "aws-smithy-types",
  972    977   
 "aws-types",
  973    978   
 "bytes",
  974    979   
 "fastrand 2.3.0",
  975    980   
 "http 0.2.12",
         981  +
 "http 1.4.0",
  976    982   
 "proptest",
  977    983   
 "regex-lite",
  978    984   
 "tokio",
  979    985   
 "tracing",
  980    986   
]
  981    987   
  982    988   
[[package]]
  983    989   
name = "aws-sdk-ssooidc"
  984    990   
version = "0.0.0-local"
  985    991   
dependencies = [
  986    992   
 "aws-credential-types",
  987    993   
 "aws-runtime",
  988    994   
 "aws-smithy-async",
  989    995   
 "aws-smithy-http",
  990    996   
 "aws-smithy-json",
  991    997   
 "aws-smithy-observability",
  992    998   
 "aws-smithy-runtime",
  993    999   
 "aws-smithy-runtime-api",
  994   1000   
 "aws-smithy-types",
  995   1001   
 "aws-types",
  996   1002   
 "bytes",
  997   1003   
 "fastrand 2.3.0",
  998   1004   
 "http 0.2.12",
        1005  +
 "http 1.4.0",
  999   1006   
 "proptest",
 1000   1007   
 "regex-lite",
 1001   1008   
 "tokio",
 1002   1009   
 "tracing",
 1003   1010   
]
 1004   1011   
 1005   1012   
[[package]]
 1006   1013   
name = "aws-sdk-sts"
 1007   1014   
version = "0.0.0-local"
 1008   1015   
dependencies = [
@@ -1053,1060 +1112,1120 @@
 1073   1080   
 "aws-smithy-http",
 1074   1081   
 "aws-smithy-json",
 1075   1082   
 "aws-smithy-observability",
 1076   1083   
 "aws-smithy-runtime",
 1077   1084   
 "aws-smithy-runtime-api",
 1078   1085   
 "aws-smithy-types",
 1079   1086   
 "aws-types",
 1080   1087   
 "bytes",
 1081   1088   
 "fastrand 2.3.0",
 1082   1089   
 "http 0.2.12",
        1090  +
 "http 1.4.0",
 1083   1091   
 "proptest",
 1084   1092   
 "regex-lite",
 1085   1093   
 "tokio",
 1086   1094   
 "tracing",
 1087   1095   
]
 1088   1096   
 1089   1097   
[[package]]
 1090   1098   
name = "aws-sdk-transcribestreaming"
 1091   1099   
version = "0.0.0-local"
 1092   1100   
dependencies = [
@@ -1159,1167 +1357,1364 @@
 1179   1187   
[[package]]
 1180   1188   
name = "aws-smithy-checksums"
 1181   1189   
version = "0.63.13"
 1182   1190   
dependencies = [
 1183   1191   
 "aws-smithy-http",
 1184   1192   
 "aws-smithy-types",
 1185   1193   
 "bytes",
 1186   1194   
 "bytes-utils",
 1187   1195   
 "crc-fast",
 1188   1196   
 "hex",
 1189         -
 "http 0.2.12",
 1190         -
 "http-body 0.4.6",
        1197  +
 "http 1.4.0",
        1198  +
 "http-body 1.0.1",
        1199  +
 "http-body-util",
 1191   1200   
 "md-5",
 1192   1201   
 "pin-project-lite",
 1193   1202   
 "pretty_assertions",
 1194   1203   
 "sha1",
 1195   1204   
 "sha2",
 1196   1205   
 "tokio",
 1197   1206   
 "tracing",
 1198   1207   
 "tracing-test",
 1199   1208   
]
 1200   1209   
 1201   1210   
[[package]]
 1202   1211   
name = "aws-smithy-compression"
 1203   1212   
version = "0.0.7"
 1204   1213   
dependencies = [
 1205   1214   
 "aws-smithy-runtime-api",
 1206   1215   
 "aws-smithy-types",
 1207   1216   
 "bytes",
 1208   1217   
 "bytes-utils",
 1209   1218   
 "flate2",
 1210   1219   
 "futures-util",
 1211         -
 "http 0.2.12",
 1212   1220   
 "http 1.4.0",
 1213         -
 "http-body 0.4.6",
 1214   1221   
 "http-body 1.0.1",
 1215   1222   
 "http-body-util",
 1216   1223   
 "pin-project-lite",
 1217   1224   
 "pretty_assertions",
 1218   1225   
 "tokio",
 1219   1226   
 "tracing",
 1220   1227   
]
 1221   1228   
 1222   1229   
[[package]]
 1223   1230   
name = "aws-smithy-dns"
 1224   1231   
version = "0.1.5"
 1225   1232   
dependencies = [
 1226   1233   
 "aws-smithy-runtime-api",
 1227   1234   
 "criterion",
 1228   1235   
 "hickory-resolver",
 1229   1236   
 "tokio",
 1230   1237   
]
 1231   1238   
 1232   1239   
[[package]]
 1233   1240   
name = "aws-smithy-eventstream"
 1234   1241   
version = "0.60.14"
 1235   1242   
dependencies = [
 1236   1243   
 "arbitrary",
 1237   1244   
 "aws-smithy-types",
 1238   1245   
 "bytes",
 1239   1246   
 "bytes-utils",
 1240   1247   
 "crc32fast",
 1241   1248   
 "criterion",
 1242   1249   
 "derive_arbitrary",
 1243   1250   
 "jemallocator",
 1244   1251   
 "mimalloc",
 1245   1252   
]
 1246   1253   
 1247   1254   
[[package]]
 1248   1255   
name = "aws-smithy-experimental"
 1249   1256   
version = "0.2.2"
 1250   1257   
 1251   1258   
[[package]]
 1252   1259   
name = "aws-smithy-http"
 1253         -
version = "0.62.6"
        1260  +
version = "0.63.0"
 1254   1261   
dependencies = [
 1255   1262   
 "async-stream",
 1256   1263   
 "aws-smithy-eventstream",
 1257   1264   
 "aws-smithy-runtime-api",
 1258   1265   
 "aws-smithy-types",
 1259   1266   
 "bytes",
 1260   1267   
 "bytes-utils",
 1261   1268   
 "futures-core",
 1262   1269   
 "futures-util",
 1263         -
 "http 0.2.12",
 1264   1270   
 "http 1.4.0",
 1265         -
 "http-body 0.4.6",
 1266         -
 "hyper 0.14.32",
        1271  +
 "http-body 1.0.1",
        1272  +
 "http-body-util",
        1273  +
 "hyper 1.8.1",
 1267   1274   
 "percent-encoding",
 1268   1275   
 "pin-project-lite",
 1269   1276   
 "pin-utils",
 1270   1277   
 "proptest",
 1271   1278   
 "tokio",
 1272   1279   
 "tracing",
 1273   1280   
]
 1274   1281   
 1275   1282   
[[package]]
 1276   1283   
name = "aws-smithy-http-client"
 1277   1284   
version = "1.1.5"
 1278   1285   
dependencies = [
 1279   1286   
 "aws-smithy-async",
 1280   1287   
 "aws-smithy-protocol-test",
 1281   1288   
 "aws-smithy-runtime-api",
 1282   1289   
 "aws-smithy-types",
 1283   1290   
 "base64 0.22.1",
 1284   1291   
 "bytes",
 1285   1292   
 "h2 0.3.27",
 1286   1293   
 "h2 0.4.13",
 1287   1294   
 "http 0.2.12",
 1288   1295   
 "http 1.4.0",
 1289   1296   
 "http-body 0.4.6",
 1290   1297   
 "http-body 1.0.1",
 1291   1298   
 "http-body-util",
 1292   1299   
 "hyper 0.14.32",
 1293   1300   
 "hyper 1.8.1",
 1294   1301   
 "hyper-rustls 0.24.2",
 1295   1302   
 "hyper-rustls 0.27.7",
 1296   1303   
 "hyper-util",
 1297   1304   
 "indexmap",
 1298   1305   
 "pin-project-lite",
 1299   1306   
 "rustls 0.21.12",
 1300   1307   
 "rustls 0.23.36",
 1301   1308   
 "rustls-native-certs",
 1302   1309   
 "rustls-pemfile",
 1303   1310   
 "rustls-pki-types",
 1304   1311   
 "s2n-tls",
 1305   1312   
 "s2n-tls-hyper",
 1306   1313   
 "s2n-tls-tokio",
 1307   1314   
 "serde",
 1308   1315   
 "serde_json",
 1309   1316   
 "serial_test",
 1310   1317   
 "tokio",
 1311   1318   
 "tokio-rustls 0.26.4",
 1312   1319   
 "tower",
 1313   1320   
 "tracing",
 1314   1321   
]
 1315   1322   
 1316   1323   
[[package]]
 1317   1324   
name = "aws-smithy-json"
 1318         -
version = "0.61.9"
        1325  +
version = "0.62.0"
 1319   1326   
dependencies = [
 1320   1327   
 "aws-smithy-types",
 1321   1328   
 "proptest",
 1322   1329   
 "serde_json",
 1323   1330   
]
 1324   1331   
 1325   1332   
[[package]]
 1326   1333   
name = "aws-smithy-legacy-http"
 1327         -
version = "0.62.6"
        1334  +
version = "0.62.7"
 1328   1335   
dependencies = [
 1329   1336   
 "async-stream",
 1330   1337   
 "aws-smithy-eventstream",
 1331   1338   
 "aws-smithy-runtime-api",
 1332   1339   
 "aws-smithy-types",
 1333   1340   
 "bytes",
 1334   1341   
 "bytes-utils",
 1335   1342   
 "futures-core",
 1336   1343   
 "futures-util",
 1337   1344   
 "http 0.2.12",
@@ -1365,1372 +1457,1466 @@
 1385   1392   
[[package]]
 1386   1393   
name = "aws-smithy-protocol-test"
 1387   1394   
version = "0.63.7"
 1388   1395   
dependencies = [
 1389   1396   
 "assert-json-diff",
 1390   1397   
 "aws-smithy-runtime-api",
 1391   1398   
 "base64-simd",
 1392   1399   
 "cbor-diag",
 1393   1400   
 "ciborium",
 1394   1401   
 "http 0.2.12",
        1402  +
 "http 1.4.0",
 1395   1403   
 "pretty_assertions",
 1396   1404   
 "regex-lite",
 1397   1405   
 "roxmltree",
 1398   1406   
 "serde_json",
 1399   1407   
 "thiserror 2.0.17",
 1400   1408   
]
 1401   1409   
 1402   1410   
[[package]]
 1403   1411   
name = "aws-smithy-query"
 1404   1412   
version = "0.60.9"
 1405   1413   
dependencies = [
 1406   1414   
 "aws-smithy-types",
 1407   1415   
 "urlencoding",
 1408   1416   
]
 1409   1417   
 1410   1418   
[[package]]
 1411   1419   
name = "aws-smithy-runtime"
 1412   1420   
version = "1.9.8"
 1413   1421   
dependencies = [
 1414   1422   
 "approx",
 1415   1423   
 "aws-smithy-async",
 1416   1424   
 "aws-smithy-http",
 1417   1425   
 "aws-smithy-http-client",
 1418   1426   
 "aws-smithy-observability",
 1419   1427   
 "aws-smithy-runtime-api",
 1420   1428   
 "aws-smithy-types",
 1421   1429   
 "bytes",
 1422   1430   
 "fastrand 2.3.0",
 1423   1431   
 "futures-util",
 1424   1432   
 "http 0.2.12",
 1425   1433   
 "http 1.4.0",
 1426   1434   
 "http-body 0.4.6",
 1427   1435   
 "http-body 1.0.1",
        1436  +
 "http-body-util",
 1428   1437   
 "hyper 0.14.32",
 1429   1438   
 "pin-project-lite",
 1430   1439   
 "pin-utils",
 1431   1440   
 "pretty_assertions",
 1432   1441   
 "tokio",
 1433   1442   
 "tracing",
 1434   1443   
 "tracing-subscriber",
 1435   1444   
 "tracing-test",
 1436   1445   
]
 1437   1446   

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

@@ -19,19 +110,110 @@
   39     39   
url = "2.5.4"
   40     40   
fastrand = "2.3.0"
   41     41   
   42     42   
[dependencies.aws-credential-types]
   43     43   
path = "../aws-credential-types"
   44     44   
features = ["test-util"]
   45     45   
version = "1.2.11"
   46     46   
   47     47   
[dependencies.aws-runtime]
   48     48   
path = "../aws-runtime"
   49         -
version = "1.5.18"
          49  +
version = "1.6.0"
   50     50   
   51     51   
[dependencies.aws-sdk-sts]
   52     52   
path = "../sts"
   53     53   
default-features = false
   54     54   
version = "0.0.0-local"
   55     55   
   56     56   
[dependencies.aws-smithy-async]
   57     57   
path = "../aws-smithy-async"
   58         -
version = "1.2.7"
          58  +
version = "1.2.8"
   59     59   
   60     60   
[dependencies.aws-smithy-http]
   61     61   
path = "../aws-smithy-http"
   62         -
version = "0.62.6"
          62  +
version = "0.63.0"
   63     63   
   64     64   
[dependencies.aws-smithy-json]
   65     65   
path = "../aws-smithy-json"
   66         -
version = "0.61.9"
          66  +
version = "0.62.0"
   67     67   
   68     68   
[dependencies.aws-smithy-runtime]
   69     69   
path = "../aws-smithy-runtime"
   70     70   
features = ["client"]
   71         -
version = "1.9.8"
          71  +
version = "1.10.0"
   72     72   
   73     73   
[dependencies.aws-smithy-runtime-api]
   74     74   
path = "../aws-smithy-runtime-api"
   75     75   
features = ["client"]
   76         -
version = "1.10.0"
          76  +
version = "1.11.0"
   77     77   
   78     78   
[dependencies.aws-smithy-types]
   79     79   
path = "../aws-smithy-types"
   80         -
version = "1.3.6"
          80  +
version = "1.4.0"
   81     81   
   82     82   
[dependencies.aws-types]
   83     83   
path = "../aws-types"
   84     84   
version = "1.3.11"
   85     85   
   86     86   
[dependencies.time]
   87     87   
version = "0.3.4"
   88     88   
features = ["parsing"]
   89     89   
   90     90   
[dependencies.tokio]
@@ -126,126 +187,187 @@
  146    146   
features = ["std", "std_rng"]
  147    147   
optional = true
  148    148   
  149    149   
[dev-dependencies]
  150    150   
tracing-test = "0.2.4"
  151    151   
serde_json = "1"
  152    152   
  153    153   
[dev-dependencies.aws-smithy-async]
  154    154   
path = "../aws-smithy-async"
  155    155   
features = ["rt-tokio", "test-util"]
  156         -
version = "1.2.7"
         156  +
version = "1.2.8"
  157    157   
  158    158   
[dev-dependencies.aws-smithy-http-client]
  159    159   
path = "../aws-smithy-http-client"
  160    160   
features = ["default-client", "test-util"]
  161         -
version = "1.1.5"
         161  +
version = "1.1.6"
  162    162   
  163    163   
[dev-dependencies.aws-smithy-runtime]
  164    164   
path = "../aws-smithy-runtime"
  165    165   
features = ["client", "test-util"]
  166         -
version = "1.9.8"
         166  +
version = "1.10.0"
  167    167   
  168    168   
[dev-dependencies.aws-smithy-runtime-api]
  169    169   
path = "../aws-smithy-runtime-api"
  170    170   
features = ["test-util"]
  171         -
version = "1.10.0"
         171  +
version = "1.11.0"
  172    172   
  173    173   
[dev-dependencies.futures-util]
  174    174   
version = "0.3.29"
  175    175   
default-features = false
  176    176   
  177    177   
[dev-dependencies.tracing-subscriber]
  178    178   
version = "0.3.16"
  179    179   
features = ["fmt", "json"]
  180    180   
  181    181   
[dev-dependencies.tokio]

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

@@ -1,1 +50,50 @@
   19     19   
   20     20   
[features]
   21     21   
hardcoded-credentials = []
   22     22   
test-util = ["aws-smithy-runtime-api/test-util"]
   23     23   
   24     24   
[dependencies]
   25     25   
zeroize = "1.7.0"
   26     26   
   27     27   
[dependencies.aws-smithy-async]
   28     28   
path = "../aws-smithy-async"
   29         -
version = "1.2.7"
          29  +
version = "1.2.8"
   30     30   
   31     31   
[dependencies.aws-smithy-types]
   32     32   
path = "../aws-smithy-types"
   33         -
version = "1.3.6"
          33  +
version = "1.4.0"
   34     34   
   35     35   
[dependencies.aws-smithy-runtime-api]
   36     36   
path = "../aws-smithy-runtime-api"
   37     37   
features = ["client", "http-auth"]
   38         -
version = "1.10.0"
          38  +
version = "1.11.0"
   39     39   
   40     40   
[dev-dependencies]
   41     41   
async-trait = "0.1.74"
   42     42   
   43     43   
[dev-dependencies.aws-smithy-runtime-api]
   44     44   
path = "../aws-smithy-runtime-api"
   45     45   
features = ["test-util"]
   46         -
version = "1.10.0"
          46  +
version = "1.11.0"
   47     47   
   48     48   
[dev-dependencies.tokio]
   49     49   
version = "1.23.1"
   50     50   
features = ["full", "test-util", "rt"]

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

@@ -1,1 +145,145 @@
    1      1   
# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
    2      2   
[package]
    3      3   
name = "aws-runtime"
    4         -
version = "1.5.18"
           4  +
version = "1.6.0"
    5      5   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
    6      6   
description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly."
    7      7   
edition = "2021"
    8      8   
license = "Apache-2.0"
    9      9   
repository = "https://github.com/smithy-lang/smithy-rs"
   10     10   
rust-version = "1.88"
   11     11   
[package.metadata.docs.rs]
   12     12   
all-features = true
   13     13   
targets = ["x86_64-unknown-linux-gnu"]
   14     14   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   15     15   
rustdoc-args = ["--cfg", "docsrs"]
   16     16   
   17     17   
[package.metadata.smithy-rs-release-tooling]
   18     18   
stable = true
   19     19   
   20     20   
[features]
   21     21   
event-stream = ["dep:aws-smithy-eventstream", "aws-sigv4/sign-eventstream"]
   22     22   
http-02x = []
   23         -
http-1x = ["dep:http-1x", "dep:http-body-1x"]
          23  +
http-1x = []
   24     24   
test-util = ["dep:regex-lite"]
   25     25   
sigv4a = ["aws-sigv4/sigv4a"]
   26     26   
   27     27   
[dependencies]
   28     28   
bytes = "1.10.0"
   29     29   
fastrand = "2.3.0"
   30     30   
percent-encoding = "2.3.1"
   31     31   
pin-project-lite = "0.2.14"
   32     32   
tracing = "0.1.40"
   33     33   
   34     34   
[dependencies.aws-credential-types]
   35     35   
path = "../aws-credential-types"
   36     36   
version = "1.2.11"
   37     37   
   38     38   
[dependencies.aws-sigv4]
   39     39   
path = "../aws-sigv4"
   40     40   
features = ["http0-compat"]
   41         -
version = "1.3.7"
          41  +
version = "1.3.8"
   42     42   
   43     43   
[dependencies.aws-smithy-async]
   44     44   
path = "../aws-smithy-async"
   45         -
version = "1.2.7"
          45  +
version = "1.2.8"
   46     46   
   47     47   
[dependencies.aws-smithy-eventstream]
   48     48   
path = "../aws-smithy-eventstream"
   49     49   
optional = true
   50         -
version = "0.60.14"
          50  +
version = "0.60.15"
   51     51   
   52     52   
[dependencies.aws-smithy-http]
   53     53   
path = "../aws-smithy-http"
   54         -
version = "0.62.6"
          54  +
version = "0.63.0"
   55     55   
   56     56   
[dependencies.aws-smithy-runtime]
   57     57   
path = "../aws-smithy-runtime"
   58     58   
features = ["client"]
   59         -
version = "1.9.8"
          59  +
version = "1.10.0"
   60     60   
   61     61   
[dependencies.aws-smithy-runtime-api]
   62     62   
path = "../aws-smithy-runtime-api"
   63         -
features = ["client"]
   64         -
version = "1.10.0"
          63  +
features = ["client", "http-1x"]
          64  +
version = "1.11.0"
   65     65   
   66     66   
[dependencies.aws-smithy-types]
   67     67   
path = "../aws-smithy-types"
   68         -
version = "1.3.6"
          68  +
features = ["http-body-1-x"]
          69  +
version = "1.4.0"
   69     70   
   70     71   
[dependencies.aws-types]
   71     72   
path = "../aws-types"
   72     73   
version = "1.3.11"
   73     74   
   74     75   
[dependencies.http-02x]
   75     76   
package = "http"
   76         -
version = "0.2.9"
          77  +
version = "0.2.12"
   77     78   
   78     79   
[dependencies.http-body-04x]
   79     80   
package = "http-body"
   80         -
version = "0.4.5"
          81  +
version = "0.4.6"
   81     82   
   82     83   
[dependencies.http-1x]
   83     84   
package = "http"
   84         -
version = "1.1.0"
   85         -
optional = true
          85  +
version = "1.3.1"
   86     86   
   87     87   
[dependencies.http-body-1x]
   88     88   
package = "http-body"
   89         -
version = "1.0.0"
   90         -
optional = true
          89  +
version = "1.0.1"
   91     90   
   92     91   
[dependencies.regex-lite]
   93     92   
version = "0.1.5"
   94     93   
optional = true
   95     94   
   96     95   
[dependencies.uuid]
   97     96   
version = "1"
   98     97   
   99     98   
[dev-dependencies]
  100     99   
arbitrary = "1.3"
  101    100   
bytes-utils = "0.1.2"
  102    101   
convert_case = "0.6.0"
         102  +
http-body-util = "0.1.3"
  103    103   
proptest = "1.2"
  104    104   
serde_json = "1"
  105    105   
tracing-test = "0.2.4"
  106    106   
  107    107   
[dev-dependencies.aws-credential-types]
  108    108   
path = "../aws-credential-types"
  109    109   
features = ["test-util"]
  110    110   
version = "1.2.11"
  111    111   
  112    112   
[dev-dependencies.aws-smithy-async]
  113    113   
path = "../aws-smithy-async"
  114    114   
features = ["test-util"]
  115         -
version = "1.2.7"
         115  +
version = "1.2.8"
  116    116   
  117    117   
[dev-dependencies.aws-smithy-protocol-test]
  118    118   
path = "../aws-smithy-protocol-test"
  119         -
version = "0.63.7"
         119  +
version = "0.63.8"
  120    120   
  121    121   
[dev-dependencies.aws-smithy-runtime-api]
  122    122   
path = "../aws-smithy-runtime-api"
  123         -
features = ["test-util"]
  124         -
version = "1.10.0"
         123  +
features = ["test-util", "http-1x"]
         124  +
version = "1.11.0"
  125    125   
  126    126   
[dev-dependencies.aws-smithy-types]
  127    127   
path = "../aws-smithy-types"
  128    128   
features = ["test-util"]
  129         -
version = "1.3.6"
         129  +
version = "1.4.0"
  130    130   
  131    131   
[dev-dependencies.futures-util]
  132    132   
version = "0.3.29"
  133    133   
default-features = false
  134    134   
  135    135   
[dev-dependencies.serde]
  136    136   
version = "1"
  137    137   
features = ["derive"]
  138    138   
  139    139   
[dev-dependencies.tokio]

tmp-codegen-diff/aws-sdk/sdk/aws-runtime/src/auth.rs

@@ -269,269 +329,329 @@
  289    289   
        .and_then(Document::as_object)
  290    290   
        .and_then(|config| config.get(field_name))
  291    291   
}
  292    292   
  293    293   
fn apply_signing_instructions(
  294    294   
    instructions: SigningInstructions,
  295    295   
    request: &mut HttpRequest,
  296    296   
) -> Result<(), BoxError> {
  297    297   
    let (new_headers, new_query) = instructions.into_parts();
  298    298   
    for header in new_headers.into_iter() {
  299         -
        let mut value = http_02x::HeaderValue::from_str(header.value()).unwrap();
         299  +
        let mut value = http_1x::HeaderValue::from_str(header.value()).unwrap();
  300    300   
        value.set_sensitive(header.sensitive());
  301    301   
        request.headers_mut().insert(header.name(), value);
  302    302   
    }
  303    303   
  304    304   
    if !new_query.is_empty() {
  305    305   
        let mut query = aws_smithy_http::query_writer::QueryWriter::new_from_string(request.uri())?;
  306    306   
        for (name, value) in new_query {
  307    307   
            query.insert(name, &value);
  308    308   
        }
  309    309   
        request.set_uri(query.build_uri())?;

tmp-codegen-diff/aws-sdk/sdk/aws-runtime/src/content_encoding.rs

@@ -1,1 +46,48 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
use aws_smithy_types::config_bag::{Storable, StoreReplace};
    7      7   
use bytes::{Bytes, BytesMut};
    8         -
use http_02x::{HeaderMap, HeaderValue};
    9         -
use http_body_04x::{Body, SizeHint};
   10      8   
use pin_project_lite::pin_project;
   11      9   
   12     10   
use std::pin::Pin;
   13     11   
use std::task::{Context, Poll};
   14     12   
   15     13   
const CRLF: &str = "\r\n";
          14  +
const CRLF_RAW: &[u8] = b"\r\n";
          15  +
   16     16   
const CHUNK_TERMINATOR: &str = "0\r\n";
          17  +
const CHUNK_TERMINATOR_RAW: &[u8] = b"0\r\n";
          18  +
   17     19   
const TRAILER_SEPARATOR: &[u8] = b":";
   18     20   
   19     21   
/// Content encoding header value constants
   20     22   
pub mod header_value {
   21     23   
    /// Header value denoting "aws-chunked" encoding
   22     24   
    pub const AWS_CHUNKED: &str = "aws-chunked";
   23     25   
}
   24     26   
   25     27   
/// Options used when constructing an [`AwsChunkedBody`].
   26     28   
#[derive(Clone, Debug, Default)]
@@ -140,142 +644,1158 @@
  160    162   
    pub fn new(body: Inner, options: AwsChunkedBodyOptions) -> Self {
  161    163   
        Self {
  162    164   
            inner: body,
  163    165   
            state: AwsChunkedBodyState::WritingChunkSize,
  164    166   
            options,
  165    167   
            inner_body_bytes_read_so_far: 0,
  166    168   
        }
  167    169   
    }
  168    170   
}
  169    171   
  170         -
fn get_unsigned_chunk_bytes_length(payload_length: u64) -> u64 {
  171         -
    let hex_repr_len = int_log16(payload_length);
  172         -
    hex_repr_len + CRLF.len() as u64 + payload_length + CRLF.len() as u64
  173         -
}
  174         -
  175         -
/// Writes trailers out into a `string` and then converts that `String` to a `Bytes` before
  176         -
/// returning.
  177         -
///
  178         -
/// - Trailer names are separated by a single colon only, no space.
  179         -
/// - Trailer names with multiple values will be written out one line per value, with the name
  180         -
///   appearing on each line.
  181         -
fn trailers_as_aws_chunked_bytes(
  182         -
    trailer_map: Option<HeaderMap>,
  183         -
    estimated_length: u64,
  184         -
) -> BytesMut {
  185         -
    if let Some(trailer_map) = trailer_map {
  186         -
        let mut current_header_name = None;
  187         -
        let mut trailers = BytesMut::with_capacity(estimated_length.try_into().unwrap_or_default());
  188         -
  189         -
        for (header_name, header_value) in trailer_map.into_iter() {
  190         -
            // When a header has multiple values, the name only comes up in iteration the first time
  191         -
            // we see it. Therefore, we need to keep track of the last name we saw and fall back to
  192         -
            // it when `header_name == None`.
  193         -
            current_header_name = header_name.or(current_header_name);
  194         -
  195         -
            // In practice, this will always exist, but `if let` is nicer than unwrap
  196         -
            if let Some(header_name) = current_header_name.as_ref() {
  197         -
                trailers.extend_from_slice(header_name.as_ref());
  198         -
                trailers.extend_from_slice(TRAILER_SEPARATOR);
  199         -
                trailers.extend_from_slice(header_value.as_bytes());
  200         -
                trailers.extend_from_slice(CRLF.as_bytes());
  201         -
            }
  202         -
        }
  203         -
  204         -
        trailers
  205         -
    } else {
  206         -
        BytesMut::new()
  207         -
    }
  208         -
}
  209         -
  210         -
/// Given an optional `HeaderMap`, calculate the total number of bytes required to represent the
  211         -
/// `HeaderMap`. If no `HeaderMap` is given as input, return 0.
  212         -
///
  213         -
/// - Trailer names are separated by a single colon only, no space.
  214         -
/// - Trailer names with multiple values will be written out one line per value, with the name
  215         -
///   appearing on each line.
  216         -
fn total_rendered_length_of_trailers(trailer_map: Option<&HeaderMap>) -> u64 {
  217         -
    match trailer_map {
  218         -
        Some(trailer_map) => trailer_map
  219         -
            .iter()
  220         -
            .map(|(trailer_name, trailer_value)| {
  221         -
                trailer_name.as_str().len()
  222         -
                    + TRAILER_SEPARATOR.len()
  223         -
                    + trailer_value.len()
  224         -
                    + CRLF.len()
  225         -
            })
  226         -
            .sum::<usize>() as u64,
  227         -
        None => 0,
  228         -
    }
  229         -
}
  230         -
  231         -
impl<Inner> Body for AwsChunkedBody<Inner>
         172  +
impl<Inner> http_body_04x::Body for AwsChunkedBody<Inner>
  232    173   
where
  233         -
    Inner: Body<Data = Bytes, Error = aws_smithy_types::body::Error>,
         174  +
    Inner: http_body_04x::Body<Data = Bytes, Error = aws_smithy_types::body::Error>,
  234    175   
{
  235    176   
    type Data = Bytes;
  236    177   
    type Error = aws_smithy_types::body::Error;
  237    178   
  238    179   
    fn poll_data(
  239    180   
        self: Pin<&mut Self>,
  240    181   
        cx: &mut Context<'_>,
  241    182   
    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
  242    183   
        tracing::trace!(state = ?self.state, "polling AwsChunkedBody");
  243    184   
        let mut this = self.project();
  244    185   
  245    186   
        match *this.state {
  246    187   
            AwsChunkedBodyState::WritingChunkSize => {
  247    188   
                if this.options.stream_length == 0 {
  248    189   
                    // If the stream is empty, we skip to writing trailers after writing the CHUNK_TERMINATOR.
  249    190   
                    *this.state = AwsChunkedBodyState::WritingTrailers;
  250    191   
                    tracing::trace!("stream is empty, writing chunk terminator");
  251    192   
                    Poll::Ready(Some(Ok(Bytes::from([CHUNK_TERMINATOR].concat()))))
  252    193   
                } else {
  253    194   
                    *this.state = AwsChunkedBodyState::WritingChunk;
  254    195   
                    // A chunk must be prefixed by chunk size in hexadecimal
  255    196   
                    let chunk_size = format!("{:X?}{CRLF}", this.options.stream_length);
  256    197   
                    tracing::trace!(%chunk_size, "writing chunk size");
  257    198   
                    let chunk_size = Bytes::from(chunk_size);
  258    199   
                    Poll::Ready(Some(Ok(chunk_size)))
  259    200   
                }
  260    201   
            }
  261    202   
            AwsChunkedBodyState::WritingChunk => match this.inner.poll_data(cx) {
  262    203   
                Poll::Ready(Some(Ok(data))) => {
  263    204   
                    tracing::trace!(len = data.len(), "writing chunk data");
  264    205   
                    *this.inner_body_bytes_read_so_far += data.len();
  265    206   
                    Poll::Ready(Some(Ok(data)))
  266    207   
                }
  267    208   
                Poll::Ready(None) => {
  268    209   
                    let actual_stream_length = *this.inner_body_bytes_read_so_far as u64;
  269    210   
                    let expected_stream_length = this.options.stream_length;
  270    211   
                    if actual_stream_length != expected_stream_length {
  271    212   
                        let err = Box::new(AwsChunkedBodyError::StreamLengthMismatch {
  272    213   
                            actual: actual_stream_length,
  273    214   
                            expected: expected_stream_length,
  274    215   
                        });
  275    216   
                        return Poll::Ready(Some(Err(err)));
  276    217   
                    };
  277    218   
  278    219   
                    tracing::trace!("no more chunk data, writing CRLF and chunk terminator");
  279    220   
                    *this.state = AwsChunkedBodyState::WritingTrailers;
  280    221   
                    // Since we wrote chunk data, we end it with a CRLF and since we only write
  281    222   
                    // a single chunk, we write the CHUNK_TERMINATOR immediately after
  282    223   
                    Poll::Ready(Some(Ok(Bytes::from([CRLF, CHUNK_TERMINATOR].concat()))))
  283    224   
                }
  284    225   
                Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
  285    226   
                Poll::Pending => Poll::Pending,
  286    227   
            },
  287    228   
            AwsChunkedBodyState::WritingTrailers => {
  288    229   
                return match this.inner.poll_trailers(cx) {
  289    230   
                    Poll::Ready(Ok(trailers)) => {
  290    231   
                        *this.state = AwsChunkedBodyState::Closed;
  291         -
                        let expected_length = total_rendered_length_of_trailers(trailers.as_ref());
         232  +
                        let expected_length =
         233  +
                            http_02x_utils::total_rendered_length_of_trailers(trailers.as_ref());
  292    234   
                        let actual_length = this.options.total_trailer_length();
  293    235   
  294    236   
                        if expected_length != actual_length {
  295    237   
                            let err =
  296    238   
                                Box::new(AwsChunkedBodyError::ReportedTrailerLengthMismatch {
  297    239   
                                    actual: actual_length,
  298    240   
                                    expected: expected_length,
  299    241   
                                });
  300    242   
                            return Poll::Ready(Some(Err(err)));
  301    243   
                        }
  302    244   
  303         -
                        let mut trailers =
  304         -
                            trailers_as_aws_chunked_bytes(trailers, actual_length + 1);
         245  +
                        let mut trailers = http_02x_utils::trailers_as_aws_chunked_bytes(
         246  +
                            trailers,
         247  +
                            actual_length + 1,
         248  +
                        );
  305    249   
                        // Insert the final CRLF to close the body
  306    250   
                        trailers.extend_from_slice(CRLF.as_bytes());
  307    251   
  308    252   
                        Poll::Ready(Some(Ok(trailers.into())))
  309    253   
                    }
  310    254   
                    Poll::Pending => Poll::Pending,
  311    255   
                    Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
  312    256   
                };
  313    257   
            }
  314    258   
            AwsChunkedBodyState::Closed => Poll::Ready(None),
  315    259   
        }
  316    260   
    }
  317    261   
  318    262   
    fn poll_trailers(
  319    263   
        self: Pin<&mut Self>,
  320    264   
        _cx: &mut Context<'_>,
  321         -
    ) -> Poll<Result<Option<HeaderMap<HeaderValue>>, Self::Error>> {
         265  +
    ) -> Poll<Result<Option<http_02x::HeaderMap<http_02x::HeaderValue>>, Self::Error>> {
  322    266   
        // Trailers were already appended to the body because of the content encoding scheme
  323    267   
        Poll::Ready(Ok(None))
  324    268   
    }
  325    269   
  326    270   
    fn is_end_stream(&self) -> bool {
  327    271   
        self.state == AwsChunkedBodyState::Closed
  328    272   
    }
  329    273   
  330         -
    fn size_hint(&self) -> SizeHint {
  331         -
        SizeHint::with_exact(self.options.encoded_length())
         274  +
    fn size_hint(&self) -> http_body_04x::SizeHint {
         275  +
        http_body_04x::SizeHint::with_exact(self.options.encoded_length())
         276  +
    }
         277  +
}
         278  +
         279  +
/// Utility functions to help with the [http_body_04x::Body] trait implementation
         280  +
mod http_02x_utils {
         281  +
    use super::{CRLF, TRAILER_SEPARATOR};
         282  +
    use bytes::BytesMut;
         283  +
    use http_02x::HeaderMap;
         284  +
         285  +
    /// Writes trailers out into a `string` and then converts that `String` to a `Bytes` before
         286  +
    /// returning.
         287  +
    ///
         288  +
    /// - Trailer names are separated by a single colon only, no space.
         289  +
    /// - Trailer names with multiple values will be written out one line per value, with the name
         290  +
    ///   appearing on each line.
         291  +
    pub(super) fn trailers_as_aws_chunked_bytes(
         292  +
        trailer_map: Option<HeaderMap>,
         293  +
        estimated_length: u64,
         294  +
    ) -> BytesMut {
         295  +
        if let Some(trailer_map) = trailer_map {
         296  +
            let mut current_header_name = None;
         297  +
            let mut trailers =
         298  +
                BytesMut::with_capacity(estimated_length.try_into().unwrap_or_default());
         299  +
         300  +
            for (header_name, header_value) in trailer_map.into_iter() {
         301  +
                // When a header has multiple values, the name only comes up in iteration the first time
         302  +
                // we see it. Therefore, we need to keep track of the last name we saw and fall back to
         303  +
                // it when `header_name == None`.
         304  +
                current_header_name = header_name.or(current_header_name);
         305  +
         306  +
                // In practice, this will always exist, but `if let` is nicer than unwrap
         307  +
                if let Some(header_name) = current_header_name.as_ref() {
         308  +
                    trailers.extend_from_slice(header_name.as_ref());
         309  +
                    trailers.extend_from_slice(TRAILER_SEPARATOR);
         310  +
                    trailers.extend_from_slice(header_value.as_bytes());
         311  +
                    trailers.extend_from_slice(CRLF.as_bytes());
         312  +
                }
         313  +
            }
         314  +
         315  +
            trailers
         316  +
        } else {
         317  +
            BytesMut::new()
         318  +
        }
         319  +
    }
         320  +
         321  +
    /// Given an optional `HeaderMap`, calculate the total number of bytes required to represent the
         322  +
    /// `HeaderMap`. If no `HeaderMap` is given as input, return 0.
         323  +
    ///
         324  +
    /// - Trailer names are separated by a single colon only, no space.
         325  +
    /// - Trailer names with multiple values will be written out one line per value, with the name
         326  +
    ///   appearing on each line.
         327  +
    pub(super) fn total_rendered_length_of_trailers(trailer_map: Option<&HeaderMap>) -> u64 {
         328  +
        match trailer_map {
         329  +
            Some(trailer_map) => trailer_map
         330  +
                .iter()
         331  +
                .map(|(trailer_name, trailer_value)| {
         332  +
                    trailer_name.as_str().len()
         333  +
                        + TRAILER_SEPARATOR.len()
         334  +
                        + trailer_value.len()
         335  +
                        + CRLF.len()
         336  +
                })
         337  +
                .sum::<usize>() as u64,
         338  +
            None => 0,
         339  +
        }
         340  +
    }
         341  +
}
         342  +
         343  +
const UNREACHABLE_STATES: &str = "These states already short circuited";
         344  +
         345  +
/// Implementing the [http_body_1x::Body] trait
         346  +
impl<Inner> http_body_1x::Body for AwsChunkedBody<Inner>
         347  +
where
         348  +
    Inner: http_body_1x::Body<Data = Bytes, Error = aws_smithy_types::body::Error>,
         349  +
{
         350  +
    type Data = Bytes;
         351  +
    type Error = aws_smithy_types::body::Error;
         352  +
         353  +
    fn is_end_stream(&self) -> bool {
         354  +
        self.state == AwsChunkedBodyState::Closed
         355  +
    }
         356  +
         357  +
    fn size_hint(&self) -> http_body_1x::SizeHint {
         358  +
        http_body_1x::SizeHint::with_exact(self.options.encoded_length())
         359  +
    }
         360  +
         361  +
    fn poll_frame(
         362  +
        self: Pin<&mut Self>,
         363  +
        cx: &mut Context<'_>,
         364  +
    ) -> Poll<Option<Result<http_body_1x::Frame<Self::Data>, Self::Error>>> {
         365  +
        tracing::trace!(state = ?self.state, "polling AwsChunkedBody");
         366  +
        let mut this = self.project();
         367  +
         368  +
        // Both `WritingChunkSize` and `Closed` states short circuit without polling the inner body
         369  +
         370  +
        // Initial setup, we do not poll the inner body here
         371  +
        if *this.state == AwsChunkedBodyState::WritingChunkSize {
         372  +
            if this.options.stream_length == 0 {
         373  +
                // If the stream is empty, we skip to writing trailers after writing the CHUNK_TERMINATOR.
         374  +
                tracing::trace!("stream is empty, writing chunk terminator");
         375  +
                let frame = http_body_1x::Frame::data(Bytes::from(CHUNK_TERMINATOR));
         376  +
                *this.state = AwsChunkedBodyState::WritingTrailers;
         377  +
                return Poll::Ready(Some(Ok(frame)));
         378  +
            } else {
         379  +
                // A chunk must be prefixed by chunk size in hexadecimal
         380  +
                let chunk_size = format!(
         381  +
                    "{:X?}{}",
         382  +
                    this.options.stream_length,
         383  +
                    std::str::from_utf8(CRLF_RAW).unwrap()
         384  +
                );
         385  +
                tracing::trace!(%chunk_size, "writing chunk size");
         386  +
                let chunk_size = http_body_1x::Frame::data(Bytes::from(chunk_size));
         387  +
                *this.state = AwsChunkedBodyState::WritingChunk;
         388  +
                return Poll::Ready(Some(Ok(chunk_size)));
         389  +
            }
         390  +
        }
         391  +
         392  +
        // Polled after completion
         393  +
        if *this.state == AwsChunkedBodyState::Closed {
         394  +
            return Poll::Ready(None);
         395  +
        }
         396  +
         397  +
        // For all other states we must poll the inner body
         398  +
        let maybe_frame = this.inner.poll_frame(cx);
         399  +
        tracing::trace!(poll_state = ?maybe_frame, "Polling InnerBody");
         400  +
         401  +
        match maybe_frame {
         402  +
            Poll::Ready(Some(Ok(frame))) => match *this.state {
         403  +
                // Both data chunks and trailers are written as Frame::data so we treat these states similarly
         404  +
                // Importantly we cannot know that the body data of the InnerBody is exhausted until we see a
         405  +
                // trailer frame or a Poll::Ready(None)
         406  +
                AwsChunkedBodyState::WritingChunk => {
         407  +
                    if frame.is_data() {
         408  +
                        let data = frame.data_ref().expect("Data frame has data");
         409  +
                        tracing::trace!(len = data.len(), "Writing chunk data");
         410  +
                        *this.inner_body_bytes_read_so_far += data.len();
         411  +
                        Poll::Ready(Some(Ok(frame)))
         412  +
                    } else {
         413  +
                        tracing::trace!(
         414  +
                            "No more chunk data, writing CRLF + CHUNK_TERMINATOR to end the data, and the first trailer frame"
         415  +
                        );
         416  +
         417  +
                        // We exhausted the body data, now check if the length is correct
         418  +
                        if let Err(poll_stream_len_err) =
         419  +
                            http_1x_utils::check_for_stream_length_mismatch(
         420  +
                                *this.inner_body_bytes_read_so_far as u64,
         421  +
                                this.options.stream_length,
         422  +
                            )
         423  +
                        {
         424  +
                            return poll_stream_len_err;
         425  +
                        }
         426  +
         427  +
                        *this.state = AwsChunkedBodyState::WritingTrailers;
         428  +
                        let trailers = frame.trailers_ref();
         429  +
         430  +
                        // NOTE: there is a subtle logic bug here (which is present in the http-02x implementation as well)
         431  +
                        // The check for this error assumes that all trailers will come in a single trailer frame. Currently
         432  +
                        // I believe this will always be the case since the only thing we send trailers for in AwsChunked is
         433  +
                        // streaming checksums and that is a single trailer value. But it might not always be true. We should
         434  +
                        // fix this bug when we update the behavior here to match the actual spec.
         435  +
                        // The fix probably looks like returning Poll::Pending while we buffer all of the trailers and then
         436  +
                        // comparing the actual length to the expected length before returning a final frame containing all
         437  +
                        // of the trailers.
         438  +
                        let actual_length: u64 =
         439  +
                            http_1x_utils::total_rendered_length_of_trailers(trailers);
         440  +
                        let expected_length = this.options.total_trailer_length();
         441  +
                        if expected_length != actual_length {
         442  +
                            let err =
         443  +
                                Box::new(AwsChunkedBodyError::ReportedTrailerLengthMismatch {
         444  +
                                    actual: actual_length,
         445  +
                                    expected: expected_length,
         446  +
                                });
         447  +
                            return Poll::Ready(Some(Err(err)));
         448  +
                        }
         449  +
         450  +
                        // Capacity = actual_length (in case all of the trailers specified in  come in AwsChunkedBodyOptions
         451  +
                        // come in the first trailer frame which is going to be the case most of the time in practice) + 7
         452  +
                        // (2 + 3) for the initial CRLF + CHUNK_TERMINATOR to end the chunked data + 2 for the final CRLF
         453  +
                        // ending the trailers section.
         454  +
                        let mut buf = BytesMut::with_capacity(actual_length as usize + 7);
         455  +
                        // End the final data chunk
         456  +
                        buf.extend_from_slice(&[CRLF_RAW, CHUNK_TERMINATOR_RAW].concat());
         457  +
         458  +
                        // We transform the trailers into raw bytes. We can't write them with Frame::trailers
         459  +
                        // since we must include the CRLF + CHUNK_TERMINATOR that end the body and the CRLFs
         460  +
                        // after each trailer, so we write them as Frame::data
         461  +
                        let trailers = http_1x_utils::trailers_as_aws_chunked_bytes(trailers, buf);
         462  +
                        Poll::Ready(Some(Ok(http_body_1x::Frame::data(trailers.into()))))
         463  +
                    }
         464  +
                }
         465  +
                AwsChunkedBodyState::WritingTrailers => {
         466  +
                    let trailers = frame.trailers_ref();
         467  +
                    let actual_length: u64 =
         468  +
                        http_1x_utils::total_rendered_length_of_trailers(trailers);
         469  +
                    let buf = BytesMut::with_capacity(actual_length as usize + 7);
         470  +
                    let trailers = http_1x_utils::trailers_as_aws_chunked_bytes(trailers, buf);
         471  +
                    Poll::Ready(Some(Ok(http_body_1x::Frame::data(trailers.into()))))
         472  +
                }
         473  +
                AwsChunkedBodyState::Closed | AwsChunkedBodyState::WritingChunkSize => {
         474  +
                    unreachable!("{}", UNREACHABLE_STATES)
         475  +
                }
         476  +
            },
         477  +
            // InnerBody data exhausted, add finalizing bytes depending on current state
         478  +
            Poll::Ready(None) => {
         479  +
                let trailers = match *this.state {
         480  +
                    AwsChunkedBodyState::WritingChunk => {
         481  +
                        // We exhausted the body data, now check if the length is correct
         482  +
                        if let Err(poll_stream_len_err) =
         483  +
                            http_1x_utils::check_for_stream_length_mismatch(
         484  +
                                *this.inner_body_bytes_read_so_far as u64,
         485  +
                                this.options.stream_length,
         486  +
                            )
         487  +
                        {
         488  +
                            return poll_stream_len_err;
         489  +
                        }
         490  +
         491  +
                        // Since we exhausted the body data, but are still in the WritingChunk state we did
         492  +
                        // not poll any trailer frames and we write the CRLF + Chunk terminator to begin the
         493  +
                        // trailer section plus a single final CRLF to end the (empty) trailer section
         494  +
                        let mut trailers = BytesMut::with_capacity(7);
         495  +
                        trailers.extend_from_slice(
         496  +
                            &[CRLF_RAW, CHUNK_TERMINATOR_RAW, CRLF_RAW].concat(),
         497  +
                        );
         498  +
                        trailers
         499  +
                    }
         500  +
                    AwsChunkedBodyState::WritingTrailers => {
         501  +
                        let mut trailers = BytesMut::with_capacity(2);
         502  +
                        trailers.extend_from_slice(CRLF_RAW);
         503  +
                        trailers
         504  +
                    }
         505  +
                    AwsChunkedBodyState::Closed | AwsChunkedBodyState::WritingChunkSize => {
         506  +
                        unreachable!("{}", UNREACHABLE_STATES)
         507  +
                    }
         508  +
                };
         509  +
         510  +
                let frame = http_body_1x::Frame::data(trailers.into());
         511  +
                *this.state = AwsChunkedBodyState::Closed;
         512  +
                Poll::Ready(Some(Ok(frame)))
         513  +
            }
         514  +
            // Passthrough states
         515  +
            Poll::Pending => Poll::Pending,
         516  +
            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
         517  +
        }
         518  +
    }
         519  +
}
         520  +
/// Utility functions to help with the [http_body_1x::Body] trait implementation
         521  +
mod http_1x_utils {
         522  +
    use std::task::Poll;
         523  +
         524  +
    use super::{CRLF_RAW, TRAILER_SEPARATOR};
         525  +
    use bytes::{Bytes, BytesMut};
         526  +
    use http_1x::{HeaderMap, HeaderName};
         527  +
         528  +
    /// Writes trailers out into a `string` and then converts that `String` to a `Bytes` before
         529  +
    /// returning.
         530  +
    ///
         531  +
    /// - Trailer names are separated by a single colon only, no space.
         532  +
    /// - Trailer names with multiple values will be written out one line per value, with the name
         533  +
    ///   appearing on each line.
         534  +
    pub(super) fn trailers_as_aws_chunked_bytes(
         535  +
        trailer_map: Option<&HeaderMap>,
         536  +
        mut buffer: BytesMut,
         537  +
    ) -> BytesMut {
         538  +
        if let Some(trailer_map) = trailer_map {
         539  +
            let mut current_header_name: Option<HeaderName> = None;
         540  +
         541  +
            for (header_name, header_value) in trailer_map.clone().into_iter() {
         542  +
                // When a header has multiple values, the name only comes up in iteration the first time
         543  +
                // we see it. Therefore, we need to keep track of the last name we saw and fall back to
         544  +
                // it when `header_name == None`.
         545  +
                current_header_name = header_name.or(current_header_name);
         546  +
         547  +
                // In practice, this will always exist, but `if let` is nicer than unwrap
         548  +
                if let Some(header_name) = current_header_name.as_ref() {
         549  +
                    buffer.extend_from_slice(header_name.as_ref());
         550  +
                    buffer.extend_from_slice(TRAILER_SEPARATOR);
         551  +
                    buffer.extend_from_slice(header_value.as_bytes());
         552  +
                    buffer.extend_from_slice(CRLF_RAW);
         553  +
                }
         554  +
            }
         555  +
         556  +
            buffer
         557  +
        } else {
         558  +
            buffer
         559  +
        }
         560  +
    }
         561  +
         562  +
    /// Given an optional `HeaderMap`, calculate the total number of bytes required to represent the
         563  +
    /// `HeaderMap`. If no `HeaderMap` is given as input, return 0.
         564  +
    ///
         565  +
    /// - Trailer names are separated by a single colon only, no space.
         566  +
    /// - Trailer names with multiple values will be written out one line per value, with the name
         567  +
    ///   appearing on each line.
         568  +
    pub(super) fn total_rendered_length_of_trailers(trailer_map: Option<&HeaderMap>) -> u64 {
         569  +
        match trailer_map {
         570  +
            Some(trailer_map) => trailer_map
         571  +
                .iter()
         572  +
                .map(|(trailer_name, trailer_value)| {
         573  +
                    trailer_name.as_str().len()
         574  +
                        + TRAILER_SEPARATOR.len()
         575  +
                        + trailer_value.len()
         576  +
                        + CRLF_RAW.len()
         577  +
                })
         578  +
                .sum::<usize>() as u64,
         579  +
            None => 0,
         580  +
        }
         581  +
    }
         582  +
         583  +
    /// This is an ugly return type, but in practice it just returns `Ok(())` if the values match
         584  +
    /// and `Err(Poll::Ready(Some(Err(AwsChunkedBodyError::StreamLengthMismatch))))` if they don't
         585  +
    #[allow(clippy::type_complexity)]
         586  +
    pub(super) fn check_for_stream_length_mismatch(
         587  +
        actual_stream_length: u64,
         588  +
        expected_stream_length: u64,
         589  +
    ) -> Result<(), Poll<Option<Result<http_body_1x::Frame<Bytes>, aws_smithy_types::body::Error>>>>
         590  +
    {
         591  +
        if actual_stream_length != expected_stream_length {
         592  +
            let err = Box::new(super::AwsChunkedBodyError::StreamLengthMismatch {
         593  +
                actual: actual_stream_length,
         594  +
                expected: expected_stream_length,
         595  +
            });
         596  +
            return Err(Poll::Ready(Some(Err(err))));
         597  +
        };
         598  +
         599  +
        Ok(())
  332    600   
    }
  333    601   
}
  334    602   
  335    603   
/// Errors related to `AwsChunkedBody`
  336    604   
#[derive(Debug)]
  337    605   
enum AwsChunkedBodyError {
  338    606   
    /// Error that occurs when the sum of `trailer_lengths` set when creating an `AwsChunkedBody` is
  339    607   
    /// not equal to the actual length of the trailers returned by the inner `http_body::Body`
  340    608   
    /// implementor. These trailer lengths are necessary in order to correctly calculate the total
  341    609   
    /// size of the body for setting the content length header.
  342    610   
    ReportedTrailerLengthMismatch { actual: u64, expected: u64 },
  343    611   
    /// Error that occurs when the `stream_length` set when creating an `AwsChunkedBody` is not
  344    612   
    /// equal to the actual length of the body returned by the inner `http_body::Body` implementor.
  345    613   
    /// `stream_length` must be correct in order to set an accurate content length header.
  346    614   
    StreamLengthMismatch { actual: u64, expected: u64 },
  347    615   
}
  348    616   
  349    617   
impl std::fmt::Display for AwsChunkedBodyError {
  350    618   
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  351    619   
        match self {
  352    620   
            Self::ReportedTrailerLengthMismatch { actual, expected } => {
  353    621   
                write!(f, "When creating this AwsChunkedBody, length of trailers was reported as {expected}. However, when double checking during trailer encoding, length was found to be {actual} instead.")
  354    622   
            }
  355    623   
            Self::StreamLengthMismatch { actual, expected } => {
  356    624   
                write!(f, "When creating this AwsChunkedBody, stream length was reported as {expected}. However, when double checking during body encoding, length was found to be {actual} instead.")
  357    625   
            }
  358    626   
        }
  359    627   
    }
  360    628   
}
  361    629   
  362    630   
impl std::error::Error for AwsChunkedBodyError {}
  363    631   
  364    632   
// Used for finding how many hexadecimal digits it takes to represent a base 10 integer
  365    633   
fn int_log16<T>(mut i: T) -> u64
  366    634   
where
  367    635   
    T: std::ops::DivAssign + PartialOrd + From<u8> + Copy,
  368    636   
{
  369    637   
    let mut len = 0;
  370    638   
    let zero = T::from(0);
  371    639   
    let sixteen = T::from(16);
  372    640   
  373    641   
    while i > zero {
  374    642   
        i /= sixteen;
  375    643   
        len += 1;
  376    644   
    }
  377    645   
  378    646   
    len
  379    647   
}
  380    648   
         649  +
fn get_unsigned_chunk_bytes_length(payload_length: u64) -> u64 {
         650  +
    let hex_repr_len = int_log16(payload_length);
         651  +
    hex_repr_len + CRLF.len() as u64 + payload_length + CRLF.len() as u64
         652  +
}
         653  +
  381    654   
#[cfg(test)]
  382    655   
mod tests {
  383         -
    use super::{
  384         -
        total_rendered_length_of_trailers, trailers_as_aws_chunked_bytes, AwsChunkedBody,
  385         -
        AwsChunkedBodyOptions, CHUNK_TERMINATOR, CRLF,
  386         -
    };
  387         -
  388         -
    use aws_smithy_types::body::SdkBody;
  389         -
    use bytes::{Buf, Bytes};
  390         -
    use bytes_utils::SegmentedBuf;
  391         -
    use http_02x::{HeaderMap, HeaderValue};
  392         -
    use http_body_04x::{Body, SizeHint};
  393         -
    use pin_project_lite::pin_project;
  394         -
  395         -
    use std::io::Read;
  396         -
    use std::pin::Pin;
  397         -
    use std::task::{Context, Poll};
  398         -
    use std::time::Duration;
  399         -
  400         -
    pin_project! {
  401         -
        struct SputteringBody {
  402         -
            parts: Vec<Option<Bytes>>,
  403         -
            cursor: usize,
  404         -
            delay_in_millis: u64,
  405         -
        }
  406         -
    }
  407    656   
  408         -
    impl SputteringBody {
  409         -
        fn len(&self) -> usize {
  410         -
            self.parts.iter().flatten().map(|b| b.len()).sum()
  411         -
        }
  412         -
    }
         657  +
    #[cfg(test)]
         658  +
    mod http_02x_tests {
         659  +
        use super::super::{
         660  +
            http_02x_utils::{total_rendered_length_of_trailers, trailers_as_aws_chunked_bytes},
         661  +
            AwsChunkedBody, AwsChunkedBodyOptions, CHUNK_TERMINATOR, CRLF,
         662  +
        };
  413    663   
  414         -
    impl Body for SputteringBody {
  415         -
        type Data = Bytes;
  416         -
        type Error = aws_smithy_types::body::Error;
         664  +
        use aws_smithy_types::body::SdkBody;
         665  +
        use bytes::{Buf, Bytes};
         666  +
        use bytes_utils::SegmentedBuf;
         667  +
        use http_02x::{HeaderMap, HeaderValue};
         668  +
        use http_body_04x::{Body, SizeHint};
         669  +
        use pin_project_lite::pin_project;
         670  +
         671  +
        use std::io::Read;
         672  +
        use std::pin::Pin;
         673  +
        use std::task::{Context, Poll};
         674  +
        use std::time::Duration;
         675  +
         676  +
        pin_project! {
         677  +
            struct SputteringBody {
         678  +
                parts: Vec<Option<Bytes>>,
         679  +
                cursor: usize,
         680  +
                delay_in_millis: u64,
         681  +
            }
         682  +
        }
  417    683   
  418         -
        fn poll_data(
  419         -
            self: Pin<&mut Self>,
  420         -
            cx: &mut Context<'_>,
  421         -
        ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
  422         -
            if self.cursor == self.parts.len() {
  423         -
                return Poll::Ready(None);
         684  +
        impl SputteringBody {
         685  +
            fn len(&self) -> usize {
         686  +
                self.parts.iter().flatten().map(|b| b.len()).sum()
  424    687   
            }
         688  +
        }
  425    689   
  426         -
            let this = self.project();
  427         -
            let delay_in_millis = *this.delay_in_millis;
  428         -
            let next_part = this.parts.get_mut(*this.cursor).unwrap().take();
  429         -
  430         -
            match next_part {
  431         -
                None => {
  432         -
                    *this.cursor += 1;
  433         -
                    let waker = cx.waker().clone();
  434         -
                    tokio::spawn(async move {
  435         -
                        tokio::time::sleep(Duration::from_millis(delay_in_millis)).await;
  436         -
                        waker.wake();
  437         -
                    });
  438         -
                    Poll::Pending
         690  +
        impl Body for SputteringBody {
         691  +
            type Data = Bytes;
         692  +
            type Error = aws_smithy_types::body::Error;
         693  +
         694  +
            fn poll_data(
         695  +
                self: Pin<&mut Self>,
         696  +
                cx: &mut Context<'_>,
         697  +
            ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
         698  +
                if self.cursor == self.parts.len() {
         699  +
                    return Poll::Ready(None);
  439    700   
                }
  440         -
                Some(data) => {
  441         -
                    *this.cursor += 1;
  442         -
                    Poll::Ready(Some(Ok(data)))
         701  +
         702  +
                let this = self.project();
         703  +
                let delay_in_millis = *this.delay_in_millis;
         704  +
                let next_part = this.parts.get_mut(*this.cursor).unwrap().take();
         705  +
         706  +
                match next_part {
         707  +
                    None => {
         708  +
                        *this.cursor += 1;
         709  +
                        let waker = cx.waker().clone();
         710  +
                        tokio::spawn(async move {
         711  +
                            tokio::time::sleep(Duration::from_millis(delay_in_millis)).await;
         712  +
                            waker.wake();
         713  +
                        });
         714  +
                        Poll::Pending
         715  +
                    }
         716  +
                    Some(data) => {
         717  +
                        *this.cursor += 1;
         718  +
                        Poll::Ready(Some(Ok(data)))
         719  +
                    }
  443    720   
                }
  444    721   
            }
  445         -
        }
  446    722   
  447         -
        fn poll_trailers(
  448         -
            self: Pin<&mut Self>,
  449         -
            _cx: &mut Context<'_>,
  450         -
        ) -> Poll<Result<Option<HeaderMap<HeaderValue>>, Self::Error>> {
  451         -
            Poll::Ready(Ok(None))
         723  +
            fn poll_trailers(
         724  +
                self: Pin<&mut Self>,
         725  +
                _cx: &mut Context<'_>,
         726  +
            ) -> Poll<Result<Option<HeaderMap<HeaderValue>>, Self::Error>> {
         727  +
                Poll::Ready(Ok(None))
         728  +
            }
         729  +
         730  +
            fn is_end_stream(&self) -> bool {
         731  +
                false
         732  +
            }
         733  +
         734  +
            fn size_hint(&self) -> SizeHint {
         735  +
                SizeHint::new()
         736  +
            }
  452    737   
        }
  453    738   
  454         -
        fn is_end_stream(&self) -> bool {
  455         -
            false
         739  +
        #[tokio::test]
         740  +
        async fn test_aws_chunked_encoding() {
         741  +
            let test_fut = async {
         742  +
                let input_str = "Hello world";
         743  +
                let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, Vec::new());
         744  +
                let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
         745  +
         746  +
                let mut output = SegmentedBuf::new();
         747  +
                while let Some(buf) = body.data().await {
         748  +
                    output.push(buf.unwrap());
         749  +
                }
         750  +
         751  +
                let mut actual_output = String::new();
         752  +
                output
         753  +
                    .reader()
         754  +
                    .read_to_string(&mut actual_output)
         755  +
                    .expect("Doesn't cause IO errors");
         756  +
         757  +
                let expected_output = "B\r\nHello world\r\n0\r\n\r\n";
         758  +
         759  +
                assert_eq!(expected_output, actual_output);
         760  +
                assert!(
         761  +
                    body.trailers()
         762  +
                        .await
         763  +
                        .expect("no errors occurred during trailer polling")
         764  +
                        .is_none(),
         765  +
                    "aws-chunked encoded bodies don't have normal HTTP trailers"
         766  +
                );
         767  +
         768  +
                // You can insert a `tokio::time::sleep` here to verify the timeout works as intended
         769  +
            };
         770  +
         771  +
            let timeout_duration = Duration::from_secs(3);
         772  +
            if tokio::time::timeout(timeout_duration, test_fut)
         773  +
                .await
         774  +
                .is_err()
         775  +
            {
         776  +
                panic!("test_aws_chunked_encoding timed out after {timeout_duration:?}");
         777  +
            }
  456    778   
        }
  457    779   
  458         -
        fn size_hint(&self) -> SizeHint {
  459         -
            SizeHint::new()
         780  +
        #[tokio::test]
         781  +
        async fn test_aws_chunked_encoding_sputtering_body() {
         782  +
            let test_fut = async {
         783  +
                let input = SputteringBody {
         784  +
                    parts: vec![
         785  +
                        Some(Bytes::from_static(b"chunk 1, ")),
         786  +
                        None,
         787  +
                        Some(Bytes::from_static(b"chunk 2, ")),
         788  +
                        Some(Bytes::from_static(b"chunk 3, ")),
         789  +
                        None,
         790  +
                        None,
         791  +
                        Some(Bytes::from_static(b"chunk 4, ")),
         792  +
                        Some(Bytes::from_static(b"chunk 5, ")),
         793  +
                        Some(Bytes::from_static(b"chunk 6")),
         794  +
                    ],
         795  +
                    cursor: 0,
         796  +
                    delay_in_millis: 500,
         797  +
                };
         798  +
                let opts = AwsChunkedBodyOptions::new(input.len() as u64, Vec::new());
         799  +
                let mut body = AwsChunkedBody::new(input, opts);
         800  +
         801  +
                let mut output = SegmentedBuf::new();
         802  +
                while let Some(buf) = body.data().await {
         803  +
                    output.push(buf.unwrap());
         804  +
                }
         805  +
         806  +
                let mut actual_output = String::new();
         807  +
                output
         808  +
                    .reader()
         809  +
                    .read_to_string(&mut actual_output)
         810  +
                    .expect("Doesn't cause IO errors");
         811  +
         812  +
                let expected_output =
         813  +
                    "34\r\nchunk 1, chunk 2, chunk 3, chunk 4, chunk 5, chunk 6\r\n0\r\n\r\n";
         814  +
         815  +
                assert_eq!(expected_output, actual_output);
         816  +
                assert!(
         817  +
                    body.trailers()
         818  +
                        .await
         819  +
                        .expect("no errors occurred during trailer polling")
         820  +
                        .is_none(),
         821  +
                    "aws-chunked encoded bodies don't have normal HTTP trailers"
         822  +
                );
         823  +
            };
         824  +
         825  +
            let timeout_duration = Duration::from_secs(3);
         826  +
            if tokio::time::timeout(timeout_duration, test_fut)
         827  +
                .await
         828  +
                .is_err()
         829  +
            {
         830  +
                panic!(
         831  +
                "test_aws_chunked_encoding_sputtering_body timed out after {timeout_duration:?}"
         832  +
            );
         833  +
            }
  460    834   
        }
  461         -
    }
  462    835   
  463         -
    #[tokio::test]
  464         -
    async fn test_aws_chunked_encoding() {
  465         -
        let test_fut = async {
         836  +
        #[tokio::test]
         837  +
        #[should_panic = "called `Result::unwrap()` on an `Err` value: ReportedTrailerLengthMismatch { actual: 44, expected: 0 }"]
         838  +
        async fn test_aws_chunked_encoding_incorrect_trailer_length_panic() {
  466    839   
            let input_str = "Hello world";
  467         -
            let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, Vec::new());
         840  +
            // Test body has no trailers, so this length is incorrect and will trigger an assert panic
         841  +
            // When the panic occurs, it will actually expect a length of 44. This is because, when using
         842  +
            // aws-chunked encoding, each trailer will end with a CRLF which is 2 bytes long.
         843  +
            let wrong_trailer_len = 42;
         844  +
            let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, vec![wrong_trailer_len]);
  468    845   
            let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
  469    846   
  470         -
            let mut output = SegmentedBuf::new();
         847  +
            // We don't care about the body contents but we have to read it all before checking for trailers
  471    848   
            while let Some(buf) = body.data().await {
  472         -
                output.push(buf.unwrap());
         849  +
                drop(buf.unwrap());
  473    850   
            }
  474    851   
  475         -
            let mut actual_output = String::new();
  476         -
            output
  477         -
                .reader()
  478         -
                .read_to_string(&mut actual_output)
  479         -
                .expect("Doesn't cause IO errors");
  480         -
  481         -
            let expected_output = "B\r\nHello world\r\n0\r\n\r\n";
  482         -
  483         -
            assert_eq!(expected_output, actual_output);
  484    852   
            assert!(
  485    853   
                body.trailers()
  486    854   
                    .await
  487    855   
                    .expect("no errors occurred during trailer polling")
  488    856   
                    .is_none(),
  489    857   
                "aws-chunked encoded bodies don't have normal HTTP trailers"
  490    858   
            );
  491         -
  492         -
            // You can insert a `tokio::time::sleep` here to verify the timeout works as intended
  493         -
        };
  494         -
  495         -
        let timeout_duration = Duration::from_secs(3);
  496         -
        if tokio::time::timeout(timeout_duration, test_fut)
  497         -
            .await
  498         -
            .is_err()
  499         -
        {
  500         -
            panic!("test_aws_chunked_encoding timed out after {timeout_duration:?}");
  501    859   
        }
  502         -
    }
  503    860   
  504         -
    #[tokio::test]
  505         -
    async fn test_aws_chunked_encoding_sputtering_body() {
  506         -
        let test_fut = async {
  507         -
            let input = SputteringBody {
  508         -
                parts: vec![
  509         -
                    Some(Bytes::from_static(b"chunk 1, ")),
  510         -
                    None,
  511         -
                    Some(Bytes::from_static(b"chunk 2, ")),
  512         -
                    Some(Bytes::from_static(b"chunk 3, ")),
  513         -
                    None,
  514         -
                    None,
  515         -
                    Some(Bytes::from_static(b"chunk 4, ")),
  516         -
                    Some(Bytes::from_static(b"chunk 5, ")),
  517         -
                    Some(Bytes::from_static(b"chunk 6")),
  518         -
                ],
  519         -
                cursor: 0,
  520         -
                delay_in_millis: 500,
  521         -
            };
  522         -
            let opts = AwsChunkedBodyOptions::new(input.len() as u64, Vec::new());
  523         -
            let mut body = AwsChunkedBody::new(input, opts);
         861  +
        #[tokio::test]
         862  +
        async fn test_aws_chunked_encoding_empty_body() {
         863  +
            let input_str = "";
         864  +
            let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, Vec::new());
         865  +
            let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
  524    866   
  525    867   
            let mut output = SegmentedBuf::new();
  526    868   
            while let Some(buf) = body.data().await {
  527    869   
                output.push(buf.unwrap());
  528    870   
            }
  529    871   
  530    872   
            let mut actual_output = String::new();
  531    873   
            output
  532    874   
                .reader()
  533    875   
                .read_to_string(&mut actual_output)
  534    876   
                .expect("Doesn't cause IO errors");
  535    877   
  536         -
            let expected_output =
  537         -
                "34\r\nchunk 1, chunk 2, chunk 3, chunk 4, chunk 5, chunk 6\r\n0\r\n\r\n";
         878  +
            let expected_output = [CHUNK_TERMINATOR, CRLF].concat();
  538    879   
  539    880   
            assert_eq!(expected_output, actual_output);
  540    881   
            assert!(
  541    882   
                body.trailers()
  542    883   
                    .await
  543    884   
                    .expect("no errors occurred during trailer polling")
  544    885   
                    .is_none(),
  545    886   
                "aws-chunked encoded bodies don't have normal HTTP trailers"
  546    887   
            );
  547         -
        };
         888  +
        }
  548    889   
  549         -
        let timeout_duration = Duration::from_secs(3);
  550         -
        if tokio::time::timeout(timeout_duration, test_fut)
  551         -
            .await
  552         -
            .is_err()
  553         -
        {
  554         -
            panic!(
  555         -
                "test_aws_chunked_encoding_sputtering_body timed out after {timeout_duration:?}"
  556         -
            );
         890  +
        #[tokio::test]
         891  +
        async fn test_total_rendered_length_of_trailers() {
         892  +
            let mut trailers = HeaderMap::new();
         893  +
         894  +
            trailers.insert("empty_value", HeaderValue::from_static(""));
         895  +
         896  +
            trailers.insert("single_value", HeaderValue::from_static("value 1"));
         897  +
         898  +
            trailers.insert("two_values", HeaderValue::from_static("value 1"));
         899  +
            trailers.append("two_values", HeaderValue::from_static("value 2"));
         900  +
         901  +
            trailers.insert("three_values", HeaderValue::from_static("value 1"));
         902  +
            trailers.append("three_values", HeaderValue::from_static("value 2"));
         903  +
            trailers.append("three_values", HeaderValue::from_static("value 3"));
         904  +
         905  +
            let trailers = Some(trailers);
         906  +
            let actual_length = total_rendered_length_of_trailers(trailers.as_ref());
         907  +
            let expected_length =
         908  +
                (trailers_as_aws_chunked_bytes(trailers, actual_length).len()) as u64;
         909  +
         910  +
            assert_eq!(expected_length, actual_length);
         911  +
        }
         912  +
         913  +
        #[tokio::test]
         914  +
        async fn test_total_rendered_length_of_empty_trailers() {
         915  +
            let trailers = Some(HeaderMap::new());
         916  +
            let actual_length = total_rendered_length_of_trailers(trailers.as_ref());
         917  +
            let expected_length =
         918  +
                (trailers_as_aws_chunked_bytes(trailers, actual_length).len()) as u64;
         919  +
         920  +
            assert_eq!(expected_length, actual_length);
  557    921   
        }
  558    922   
    }
  559    923   
  560         -
    #[tokio::test]
  561         -
    #[should_panic = "called `Result::unwrap()` on an `Err` value: ReportedTrailerLengthMismatch { actual: 44, expected: 0 }"]
  562         -
    async fn test_aws_chunked_encoding_incorrect_trailer_length_panic() {
  563         -
        let input_str = "Hello world";
  564         -
        // Test body has no trailers, so this length is incorrect and will trigger an assert panic
  565         -
        // When the panic occurs, it will actually expect a length of 44. This is because, when using
  566         -
        // aws-chunked encoding, each trailer will end with a CRLF which is 2 bytes long.
  567         -
        let wrong_trailer_len = 42;
  568         -
        let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, vec![wrong_trailer_len]);
  569         -
        let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
         924  +
    #[cfg(test)]
         925  +
    mod http_1x_tests {
         926  +
        use super::super::{
         927  +
            http_1x_utils::{total_rendered_length_of_trailers, trailers_as_aws_chunked_bytes},
         928  +
            AwsChunkedBody, AwsChunkedBodyOptions, CHUNK_TERMINATOR_RAW, CRLF_RAW,
         929  +
        };
  570    930   
  571         -
        // We don't care about the body contents but we have to read it all before checking for trailers
  572         -
        while let Some(buf) = body.data().await {
  573         -
            drop(buf.unwrap());
         931  +
        use aws_smithy_types::body::SdkBody;
         932  +
        use bytes::{Buf, Bytes, BytesMut};
         933  +
        use bytes_utils::SegmentedBuf;
         934  +
        use http_1x::{HeaderMap, HeaderValue};
         935  +
        use http_body_1x::{Body, Frame, SizeHint};
         936  +
        use http_body_util::BodyExt;
         937  +
        use pin_project_lite::pin_project;
         938  +
         939  +
        use std::io::Read;
         940  +
        use std::pin::Pin;
         941  +
        use std::task::{Context, Poll};
         942  +
        use std::time::Duration;
         943  +
         944  +
        pin_project! {
         945  +
            struct SputteringBody {
         946  +
                parts: Vec<Option<Bytes>>,
         947  +
                cursor: usize,
         948  +
                delay_in_millis: u64,
         949  +
            }
  574    950   
        }
  575    951   
  576         -
        assert!(
  577         -
            body.trailers()
  578         -
                .await
  579         -
                .expect("no errors occurred during trailer polling")
  580         -
                .is_none(),
  581         -
            "aws-chunked encoded bodies don't have normal HTTP trailers"
  582         -
        );
  583         -
    }
         952  +
        impl SputteringBody {
         953  +
            fn len(&self) -> usize {
         954  +
                self.parts.iter().flatten().map(|b| b.len()).sum()
         955  +
            }
         956  +
        }
         957  +
         958  +
        impl Body for SputteringBody {
         959  +
            type Data = Bytes;
         960  +
            type Error = aws_smithy_types::body::Error;
  584    961   
  585         -
    #[tokio::test]
  586         -
    async fn test_aws_chunked_encoding_empty_body() {
  587         -
        let input_str = "";
  588         -
        let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, Vec::new());
  589         -
        let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
         962  +
            fn poll_frame(
         963  +
                self: Pin<&mut Self>,
         964  +
                cx: &mut Context<'_>,
         965  +
            ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
         966  +
                if self.cursor == self.parts.len() {
         967  +
                    return Poll::Ready(None);
         968  +
                }
  590    969   
  591         -
        let mut output = SegmentedBuf::new();
  592         -
        while let Some(buf) = body.data().await {
  593         -
            output.push(buf.unwrap());
         970  +
                let this = self.project();
         971  +
                let delay_in_millis = *this.delay_in_millis;
         972  +
                let next_part = this.parts.get_mut(*this.cursor).unwrap().take();
         973  +
         974  +
                match next_part {
         975  +
                    None => {
         976  +
                        *this.cursor += 1;
         977  +
                        let waker = cx.waker().clone();
         978  +
                        tokio::spawn(async move {
         979  +
                            tokio::time::sleep(Duration::from_millis(delay_in_millis)).await;
         980  +
                            waker.wake();
         981  +
                        });
         982  +
                        Poll::Pending
         983  +
                    }
         984  +
                    Some(data) => {
         985  +
                        *this.cursor += 1;
         986  +
                        let frame = Frame::data(data);
         987  +
                        Poll::Ready(Some(Ok(frame)))
         988  +
                    }
         989  +
                }
         990  +
            }
         991  +
         992  +
            fn is_end_stream(&self) -> bool {
         993  +
                false
         994  +
            }
         995  +
         996  +
            fn size_hint(&self) -> SizeHint {
         997  +
                SizeHint::new()
         998  +
            }
  594    999   
        }
  595   1000   
  596         -
        let mut actual_output = String::new();
  597         -
        output
  598         -
            .reader()
  599         -
            .read_to_string(&mut actual_output)
  600         -
            .expect("Doesn't cause IO errors");
        1001  +
        #[tokio::test]
        1002  +
        async fn test_aws_chunked_encoding() {
        1003  +
            let test_fut = async {
        1004  +
                let input_str = "Hello world";
        1005  +
                let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, vec![]);
        1006  +
                let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
        1007  +
        1008  +
                let mut output = SegmentedBuf::new();
        1009  +
                while let Some(Ok(buf)) = body.frame().await {
        1010  +
                    output.push(buf.into_data().unwrap());
        1011  +
                }
        1012  +
        1013  +
                let mut actual_output = String::new();
        1014  +
                output
        1015  +
                    .reader()
        1016  +
                    .read_to_string(&mut actual_output)
        1017  +
                    .expect("Doesn't cause IO errors");
  601   1018   
  602         -
        let expected_output = [CHUNK_TERMINATOR, CRLF].concat();
        1019  +
                let expected_output = "B\r\nHello world\r\n0\r\n\r\n";
  603   1020   
  604         -
        assert_eq!(expected_output, actual_output);
  605         -
        assert!(
  606         -
            body.trailers()
        1021  +
                assert_eq!(expected_output, actual_output);
        1022  +
        1023  +
                // You can insert a `tokio::time::sleep` here to verify the timeout works as intended
        1024  +
            };
        1025  +
        1026  +
            let timeout_duration = Duration::from_secs(3);
        1027  +
            if tokio::time::timeout(timeout_duration, test_fut)
  607   1028   
                .await
  608         -
                .expect("no errors occurred during trailer polling")
  609         -
                .is_none(),
  610         -
            "aws-chunked encoded bodies don't have normal HTTP trailers"
  611         -
        );
  612         -
    }
        1029  +
                .is_err()
        1030  +
            {
        1031  +
                panic!("test_aws_chunked_encoding timed out after {timeout_duration:?}");
        1032  +
            }
        1033  +
        }
  613   1034   
  614         -
    #[tokio::test]
  615         -
    async fn test_total_rendered_length_of_trailers() {
  616         -
        let mut trailers = HeaderMap::new();
        1035  +
        #[tokio::test]
        1036  +
        async fn test_aws_chunked_encoding_sputtering_body() {
        1037  +
            let test_fut = async {
        1038  +
                let input = SputteringBody {
        1039  +
                    parts: vec![
        1040  +
                        Some(Bytes::from_static(b"chunk 1, ")),
        1041  +
                        None,
        1042  +
                        Some(Bytes::from_static(b"chunk 2, ")),
        1043  +
                        Some(Bytes::from_static(b"chunk 3, ")),
        1044  +
                        None,
        1045  +
                        None,
        1046  +
                        Some(Bytes::from_static(b"chunk 4, ")),
        1047  +
                        Some(Bytes::from_static(b"chunk 5, ")),
        1048  +
                        Some(Bytes::from_static(b"chunk 6")),
        1049  +
                    ],
        1050  +
                    cursor: 0,
        1051  +
                    delay_in_millis: 500,
        1052  +
                };
        1053  +
                let opts = AwsChunkedBodyOptions::new(input.len() as u64, vec![]);
        1054  +
                let mut body = AwsChunkedBody::new(input, opts);
  617   1055   
  618         -
        trailers.insert("empty_value", HeaderValue::from_static(""));
        1056  +
                let mut output = SegmentedBuf::new();
        1057  +
                while let Some(Ok(buf)) = body.frame().await {
        1058  +
                    output.push(buf.into_data().unwrap());
        1059  +
                }
  619   1060   
  620         -
        trailers.insert("single_value", HeaderValue::from_static("value 1"));
        1061  +
                let mut actual_output = String::new();
        1062  +
                output
        1063  +
                    .reader()
        1064  +
                    .read_to_string(&mut actual_output)
        1065  +
                    .expect("Doesn't cause IO errors");
  621   1066   
  622         -
        trailers.insert("two_values", HeaderValue::from_static("value 1"));
  623         -
        trailers.append("two_values", HeaderValue::from_static("value 2"));
        1067  +
                let expected_output =
        1068  +
                    "34\r\nchunk 1, chunk 2, chunk 3, chunk 4, chunk 5, chunk 6\r\n0\r\n\r\n";
  624   1069   
  625         -
        trailers.insert("three_values", HeaderValue::from_static("value 1"));
  626         -
        trailers.append("three_values", HeaderValue::from_static("value 2"));
  627         -
        trailers.append("three_values", HeaderValue::from_static("value 3"));
        1070  +
                assert_eq!(expected_output, actual_output);
        1071  +
            };
  628   1072   
  629         -
        let trailers = Some(trailers);
  630         -
        let actual_length = total_rendered_length_of_trailers(trailers.as_ref());
  631         -
        let expected_length = (trailers_as_aws_chunked_bytes(trailers, actual_length).len()) as u64;
        1073  +
            let timeout_duration = Duration::from_secs(3);
        1074  +
            if tokio::time::timeout(timeout_duration, test_fut)
        1075  +
                .await
        1076  +
                .is_err()
        1077  +
            {
        1078  +
                panic!(
        1079  +
                "test_aws_chunked_encoding_sputtering_body timed out after {timeout_duration:?}"
        1080  +
            );
        1081  +
            }
        1082  +
        }
  632   1083   
  633         -
        assert_eq!(expected_length, actual_length);
  634         -
    }
        1084  +
        #[tokio::test]
        1085  +
        async fn test_aws_chunked_encoding_incorrect_trailer_length_panic() {
        1086  +
            let input_str = "Hello world";
        1087  +
            // Test body has no trailers, so this length is incorrect and will trigger an assert panic
        1088  +
            // When the panic occurs, it will actually expect a length of 44. This is because, when using
        1089  +
            // aws-chunked encoding, each trailer will end with a CRLF which is 2 bytes long.
        1090  +
            let wrong_trailer_len = 42;
        1091  +
            let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, vec![wrong_trailer_len]);
        1092  +
            let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
        1093  +
        1094  +
            // We don't care about the body contents but we have to read it all before checking for trailers
        1095  +
            while let Some(Ok(frame)) = body.frame().await {
        1096  +
                assert!(!frame.is_trailers());
        1097  +
            }
        1098  +
        }
        1099  +
        1100  +
        #[tokio::test]
        1101  +
        async fn test_aws_chunked_encoding_empty_body() {
        1102  +
            let input_str = "";
        1103  +
            let opts = AwsChunkedBodyOptions::new(input_str.len() as u64, vec![]);
        1104  +
            let mut body = AwsChunkedBody::new(SdkBody::from(input_str), opts);
  635   1105   
  636         -
    #[tokio::test]
  637         -
    async fn test_total_rendered_length_of_empty_trailers() {
  638         -
        let trailers = Some(HeaderMap::new());
  639         -
        let actual_length = total_rendered_length_of_trailers(trailers.as_ref());
  640         -
        let expected_length = (trailers_as_aws_chunked_bytes(trailers, actual_length).len()) as u64;
        1106  +
            let mut output = SegmentedBuf::new();
        1107  +
            while let Some(Ok(frame)) = body.frame().await {
        1108  +
                output.push(frame.into_data().unwrap());
        1109  +
            }
        1110  +
        1111  +
            let mut actual_output = String::new();
        1112  +
            output
        1113  +
                .reader()
        1114  +
                .read_to_string(&mut actual_output)
        1115  +
                .expect("Doesn't cause IO errors");
  641   1116   
  642         -
        assert_eq!(expected_length, actual_length);
        1117  +
            let actual_output = std::str::from_utf8(actual_output.as_bytes()).unwrap();
        1118  +
            let expected_output = [CHUNK_TERMINATOR_RAW, CRLF_RAW].concat();
        1119  +
            let expected_output = std::str::from_utf8(&expected_output).unwrap();
        1120  +
        1121  +
            assert_eq!(expected_output, actual_output);
        1122  +
        }
        1123  +
        1124  +
        #[tokio::test]
        1125  +
        async fn test_total_rendered_length_of_trailers() {
        1126  +
            let mut trailers = HeaderMap::new();
        1127  +
        1128  +
            trailers.insert("empty_value", HeaderValue::from_static(""));
        1129  +
        1130  +
            trailers.insert("single_value", HeaderValue::from_static("value 1"));
        1131  +
        1132  +
            trailers.insert("two_values", HeaderValue::from_static("value 1"));
        1133  +
            trailers.append("two_values", HeaderValue::from_static("value 2"));
        1134  +
        1135  +
            trailers.insert("three_values", HeaderValue::from_static("value 1"));
        1136  +
            trailers.append("three_values", HeaderValue::from_static("value 2"));
        1137  +
            trailers.append("three_values", HeaderValue::from_static("value 3"));
        1138  +
        1139  +
            let trailers = Some(&trailers);
        1140  +
            let actual_length = total_rendered_length_of_trailers(trailers);
        1141  +
            let buf = BytesMut::with_capacity(actual_length as usize);
        1142  +
            let expected_length = (trailers_as_aws_chunked_bytes(trailers, buf).len()) as u64;
        1143  +
        1144  +
            assert_eq!(expected_length, actual_length);
        1145  +
        }
        1146  +
        1147  +
        #[tokio::test]
        1148  +
        async fn test_total_rendered_length_of_empty_trailers() {
        1149  +
            let header_map = HeaderMap::new();
        1150  +
            let trailers = Some(&header_map);
        1151  +
            let actual_length = total_rendered_length_of_trailers(trailers);
        1152  +
            let buf = BytesMut::with_capacity(actual_length as usize);
        1153  +
            let expected_length = (trailers_as_aws_chunked_bytes(trailers, buf).len()) as u64;
        1154  +
        1155  +
            assert_eq!(expected_length, actual_length);
        1156  +
        }
  643   1157   
    }
  644   1158   
}