AWS SDK

AWS SDK

rev. ee474c7509d7728618c23068f3741e8e5b339ef9 (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/settings.rs

@@ -1,1 +36,36 @@
    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         -
use http0::header::{AUTHORIZATION, TRANSFER_ENCODING, USER_AGENT};
           6  +
use http::header::{AUTHORIZATION, TRANSFER_ENCODING, USER_AGENT};
    7      7   
use std::borrow::Cow;
    8      8   
use std::time::Duration;
    9      9   
   10     10   
const HEADER_NAME_X_RAY_TRACE_ID: &str = "x-amzn-trace-id";
   11     11   
   12     12   
/// HTTP-specific signing settings
   13     13   
#[derive(Clone, Debug, PartialEq)]
   14     14   
#[non_exhaustive]
   15     15   
pub struct SigningSettings {
   16     16   
    /// Specifies how to encode the request URL when signing. Some services do not decode

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/sign.rs

@@ -1,1 +47,47 @@
    7      7   
use super::{PayloadChecksumKind, SignatureLocation};
    8      8   
use crate::http_request::canonical_request::header;
    9      9   
use crate::http_request::canonical_request::param;
   10     10   
use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
   11     11   
use crate::http_request::error::CanonicalRequestError;
   12     12   
use crate::http_request::SigningParams;
   13     13   
use crate::sign::v4;
   14     14   
#[cfg(feature = "sigv4a")]
   15     15   
use crate::sign::v4a;
   16     16   
use crate::{SignatureVersion, SigningOutput};
   17         -
use http0::Uri;
          17  +
use http::Uri;
   18     18   
use std::borrow::Cow;
   19     19   
use std::fmt::{Debug, Formatter};
   20     20   
use std::str;
   21     21   
   22     22   
const LOG_SIGNABLE_BODY: &str = "LOG_SIGNABLE_BODY";
   23     23   
   24     24   
/// Represents all of the information necessary to sign an HTTP request.
   25     25   
#[derive(Debug)]
   26     26   
#[non_exhaustive]
   27     27   
pub struct SignableRequest<'a> {
@@ -179,179 +243,248 @@
  199    199   
    /// Applies the instructions to the given `request`.
  200    200   
    pub fn apply_to_request_http0x<B>(self, request: &mut http0::Request<B>) {
  201    201   
        let (new_headers, new_query) = self.into_parts();
  202    202   
        for header in new_headers.into_iter() {
  203    203   
            let mut value = http0::HeaderValue::from_str(&header.value).unwrap();
  204    204   
            value.set_sensitive(header.sensitive);
  205    205   
            request.headers_mut().insert(header.key, value);
  206    206   
        }
  207    207   
  208    208   
        if !new_query.is_empty() {
  209         -
            let mut query = aws_smithy_http::query_writer::QueryWriter::new(request.uri());
         209  +
            let mut query = aws_smithy_http::query_writer::QueryWriter::new_from_string(
         210  +
                &request.uri().to_string(),
         211  +
            )
         212  +
            .expect("unreachable: URI is valid");
  210    213   
            for (name, value) in new_query {
  211    214   
                query.insert(name, &value);
  212    215   
            }
  213         -
            *request.uri_mut() = query.build_uri();
         216  +
            let query_uri = query.build_uri().to_string();
         217  +
            let query_http0 = query_uri.parse::<http0::Uri>().expect("URI is valid");
         218  +
            *request.uri_mut() = query_http0;
  214    219   
        }
  215    220   
    }
  216    221   
  217    222   
    #[cfg(any(feature = "http1", test))]
  218    223   
    /// Applies the instructions to the given `request`.
  219    224   
    pub fn apply_to_request_http1x<B>(self, request: &mut http::Request<B>) {
  220    225   
        // TODO(https://github.com/smithy-lang/smithy-rs/issues/3367): Update query writer to reduce
  221    226   
        // allocations
  222    227   
        let (new_headers, new_query) = self.into_parts();
  223    228   
        for header in new_headers.into_iter() {
@@ -487,492 +547,552 @@
  507    512   
mod tests {
  508    513   
    use crate::date_time::test_parsers::parse_date_time;
  509    514   
    use crate::http_request::sign::{add_header, SignableRequest};
  510    515   
    use crate::http_request::test::SigningSuiteTest;
  511    516   
    use crate::http_request::{
  512    517   
        sign, SessionTokenMode, SignableBody, SignatureLocation, SigningInstructions,
  513    518   
        SigningSettings,
  514    519   
    };
  515    520   
    use crate::sign::v4;
  516    521   
    use aws_credential_types::Credentials;
  517         -
    use http0::{HeaderValue, Request};
         522  +
    use http::{HeaderValue, Request};
  518    523   
    use pretty_assertions::assert_eq;
  519    524   
    use proptest::proptest;
  520    525   
    use std::borrow::Cow;
  521    526   
    use std::iter;
  522    527   
  523    528   
    macro_rules! assert_req_eq {
  524    529   
        (http: $expected:expr, $actual:expr) => {
  525    530   
            let mut expected = ($expected).map(|_b|"body");
  526    531   
            let mut actual = ($actual).map(|_b|"body");
  527    532   
            make_headers_comparable(&mut expected);
@@ -741,746 +954,959 @@
  761    766   
  762    767   
        let original = test.request();
  763    768   
        let signable = SignableRequest::from(&original);
  764    769   
        let out = sign(signable, &params).unwrap();
  765    770   
        assert_eq!(
  766    771   
            "57d157672191bac40bae387e48bbe14b15303c001fdbb01f4abf295dccb09705",
  767    772   
            out.signature
  768    773   
        );
  769    774   
  770    775   
        let mut signed = original.as_http_request();
  771         -
        out.output.apply_to_request_http0x(&mut signed);
         776  +
        out.output.apply_to_request_http1x(&mut signed);
  772    777   
  773    778   
        let expected = test.signed_request(SignatureLocation::Headers);
  774    779   
        assert_req_eq!(expected, signed);
  775    780   
    }
  776    781   
  777    782   
    #[test]
  778    783   
    fn test_sign_headers_utf8() {
  779    784   
        let settings = SigningSettings::default();
  780    785   
        let identity = &Credentials::for_tests().into();
  781    786   
        let params = v4::SigningParams {
  782    787   
            identity,
  783    788   
            region: "us-east-1",
  784    789   
            name: "service",
  785    790   
            time: parse_date_time("20150830T123600Z").unwrap(),
  786    791   
            settings,
  787    792   
        }
  788    793   
        .into();
  789    794   
  790         -
        let original = http0::Request::builder()
         795  +
        let original = http::Request::builder()
  791    796   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  792    797   
            .header("some-header", HeaderValue::from_str("テスト").unwrap())
  793    798   
            .body("")
  794    799   
            .unwrap()
  795    800   
            .into();
  796    801   
        let signable = SignableRequest::from(&original);
  797    802   
        let out = sign(signable, &params).unwrap();
  798    803   
        assert_eq!(
  799    804   
            "55e16b31f9bde5fd04f9d3b780dd2b5e5f11a5219001f91a8ca9ec83eaf1618f",
  800    805   
            out.signature
  801    806   
        );
  802    807   
  803    808   
        let mut signed = original.as_http_request();
  804         -
        out.output.apply_to_request_http0x(&mut signed);
         809  +
        out.output.apply_to_request_http1x(&mut signed);
  805    810   
  806         -
        let expected = http0::Request::builder()
         811  +
        let expected = http::Request::builder()
  807    812   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  808    813   
            .header("some-header", HeaderValue::from_str("テスト").unwrap())
  809    814   
            .header(
  810    815   
                "x-amz-date",
  811    816   
                HeaderValue::from_str("20150830T123600Z").unwrap(),
  812    817   
            )
  813    818   
            .header(
  814    819   
                "authorization",
  815    820   
                HeaderValue::from_str(
  816    821   
                    "AWS4-HMAC-SHA256 \
  817    822   
                        Credential=ANOTREAL/20150830/us-east-1/service/aws4_request, \
  818    823   
                        SignedHeaders=host;some-header;x-amz-date, \
  819    824   
                        Signature=55e16b31f9bde5fd04f9d3b780dd2b5e5f11a5219001f91a8ca9ec83eaf1618f",
  820    825   
                )
  821    826   
                .unwrap(),
  822    827   
            )
  823    828   
            .body("")
  824    829   
            .unwrap();
  825    830   
        assert_req_eq!(http: expected, signed);
  826    831   
    }
  827    832   
  828    833   
    #[test]
  829    834   
    fn test_sign_headers_excluding_session_token() {
  830    835   
        let settings = SigningSettings {
  831    836   
            session_token_mode: SessionTokenMode::Exclude,
  832    837   
            ..Default::default()
  833    838   
        };
  834    839   
        let identity = &Credentials::for_tests_with_session_token().into();
  835    840   
        let params = v4::SigningParams {
  836    841   
            identity,
  837    842   
            region: "us-east-1",
  838    843   
            name: "service",
  839    844   
            time: parse_date_time("20150830T123600Z").unwrap(),
  840    845   
            settings,
  841    846   
        }
  842    847   
        .into();
  843    848   
  844         -
        let original = http0::Request::builder()
         849  +
        let original = http::Request::builder()
  845    850   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  846    851   
            .body("")
  847    852   
            .unwrap()
  848    853   
            .into();
  849    854   
        let out_without_session_token = sign(SignableRequest::from(&original), &params).unwrap();
  850    855   
  851    856   
        let out_with_session_token_but_excluded =
  852    857   
            sign(SignableRequest::from(&original), &params).unwrap();
  853    858   
        assert_eq!(
  854    859   
            "ab32de057edf094958d178b3c91f3c8d5c296d526b11da991cd5773d09cea560",
  855    860   
            out_with_session_token_but_excluded.signature
  856    861   
        );
  857    862   
        assert_eq!(
  858    863   
            out_with_session_token_but_excluded.signature,
  859    864   
            out_without_session_token.signature
  860    865   
        );
  861    866   
  862    867   
        let mut signed = original.as_http_request();
  863    868   
        out_with_session_token_but_excluded
  864    869   
            .output
  865         -
            .apply_to_request_http0x(&mut signed);
         870  +
            .apply_to_request_http1x(&mut signed);
  866    871   
  867         -
        let expected = http0::Request::builder()
         872  +
        let expected = http::Request::builder()
  868    873   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  869    874   
            .header(
  870    875   
                "x-amz-date",
  871    876   
                HeaderValue::from_str("20150830T123600Z").unwrap(),
  872    877   
            )
  873    878   
            .header(
  874    879   
                "authorization",
  875    880   
                HeaderValue::from_str(
  876    881   
                    "AWS4-HMAC-SHA256 \
  877    882   
                        Credential=ANOTREAL/20150830/us-east-1/service/aws4_request, \
  878    883   
                        SignedHeaders=host;x-amz-date, \
  879    884   
                        Signature=ab32de057edf094958d178b3c91f3c8d5c296d526b11da991cd5773d09cea560",
  880    885   
                )
  881    886   
                .unwrap(),
  882    887   
            )
  883    888   
            .header(
  884    889   
                "x-amz-security-token",
  885    890   
                HeaderValue::from_str("notarealsessiontoken").unwrap(),
  886    891   
            )
  887    892   
            .body(b"")
  888    893   
            .unwrap();
  889    894   
        assert_req_eq!(http: expected, signed);
  890    895   
    }
  891    896   
  892    897   
    #[test]
  893    898   
    fn test_sign_headers_space_trimming() {
  894    899   
        let settings = SigningSettings::default();
  895    900   
        let identity = &Credentials::for_tests().into();
  896    901   
        let params = v4::SigningParams {
  897    902   
            identity,
  898    903   
            region: "us-east-1",
  899    904   
            name: "service",
  900    905   
            time: parse_date_time("20150830T123600Z").unwrap(),
  901    906   
            settings,
  902    907   
        }
  903    908   
        .into();
  904    909   
  905         -
        let original = http0::Request::builder()
         910  +
        let original = http::Request::builder()
  906    911   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  907    912   
            .header(
  908    913   
                "some-header",
  909    914   
                HeaderValue::from_str("  test  test   ").unwrap(),
  910    915   
            )
  911    916   
            .body("")
  912    917   
            .unwrap()
  913    918   
            .into();
  914    919   
        let signable = SignableRequest::from(&original);
  915    920   
        let out = sign(signable, &params).unwrap();
  916    921   
        assert_eq!(
  917    922   
            "244f2a0db34c97a528f22715fe01b2417b7750c8a95c7fc104a3c48d81d84c08",
  918    923   
            out.signature
  919    924   
        );
  920    925   
  921    926   
        let mut signed = original.as_http_request();
  922         -
        out.output.apply_to_request_http0x(&mut signed);
         927  +
        out.output.apply_to_request_http1x(&mut signed);
  923    928   
  924         -
        let expected = http0::Request::builder()
         929  +
        let expected = http::Request::builder()
  925    930   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  926    931   
            .header(
  927    932   
                "some-header",
  928    933   
                HeaderValue::from_str("  test  test   ").unwrap(),
  929    934   
            )
  930    935   
            .header(
  931    936   
                "x-amz-date",
  932    937   
                HeaderValue::from_str("20150830T123600Z").unwrap(),
  933    938   
            )
  934    939   
            .header(
@@ -957,962 +1042,1047 @@
  977    982   
        }
  978    983   
    }
  979    984   
  980    985   
    #[test]
  981    986   
    fn apply_signing_instructions_headers() {
  982    987   
        let mut headers = vec![];
  983    988   
        add_header(&mut headers, "some-header", "foo", false);
  984    989   
        add_header(&mut headers, "some-other-header", "bar", false);
  985    990   
        let instructions = SigningInstructions::new(headers, vec![]);
  986    991   
  987         -
        let mut request = http0::Request::builder()
         992  +
        let mut request = http::Request::builder()
  988    993   
            .uri("https://some-endpoint.some-region.amazonaws.com")
  989    994   
            .body("")
  990    995   
            .unwrap();
  991    996   
  992         -
        instructions.apply_to_request_http0x(&mut request);
         997  +
        instructions.apply_to_request_http1x(&mut request);
  993    998   
  994    999   
        let get_header = |n: &str| request.headers().get(n).unwrap().to_str().unwrap();
  995   1000   
        assert_eq!("foo", get_header("some-header"));
  996   1001   
        assert_eq!("bar", get_header("some-other-header"));
  997   1002   
    }
  998   1003   
  999   1004   
    #[test]
 1000   1005   
    fn apply_signing_instructions_query_params() {
 1001   1006   
        let params = vec![
 1002   1007   
            ("some-param", Cow::Borrowed("f&o?o")),
 1003   1008   
            ("some-other-param?", Cow::Borrowed("bar")),
 1004   1009   
        ];
 1005   1010   
        let instructions = SigningInstructions::new(vec![], params);
 1006   1011   
 1007         -
        let mut request = http0::Request::builder()
        1012  +
        let mut request = http::Request::builder()
 1008   1013   
            .uri("https://some-endpoint.some-region.amazonaws.com/some/path")
 1009   1014   
            .body("")
 1010   1015   
            .unwrap();
 1011   1016   
 1012         -
        instructions.apply_to_request_http0x(&mut request);
        1017  +
        instructions.apply_to_request_http1x(&mut request);
 1013   1018   
 1014   1019   
        assert_eq!(
 1015   1020   
            "/some/path?some-param=f%26o%3Fo&some-other-param%3F=bar",
 1016   1021   
            request.uri().path_and_query().unwrap().to_string()
 1017   1022   
        );
 1018   1023   
    }
 1019   1024   
 1020   1025   
    #[test]
 1021   1026   
    fn apply_signing_instructions_query_params_http_1x() {
 1022   1027   
        let params = vec![

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/test.rs

@@ -1,1 +45,45 @@
    5      5   
    6      6   
//! Functions shared between the tests of several modules.
    7      7   
    8      8   
use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
    9      9   
use crate::http_request::{
   10     10   
    PayloadChecksumKind, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation,
   11     11   
    SigningSettings,
   12     12   
};
   13     13   
use aws_credential_types::Credentials;
   14     14   
use aws_smithy_runtime_api::client::identity::Identity;
   15         -
use http0::{Method, Uri};
          15  +
use http::Uri;
   16     16   
use std::borrow::Cow;
   17     17   
use std::error::Error as StdError;
   18     18   
use std::time::{Duration, SystemTime};
   19     19   
use time::format_description::well_known::Rfc3339;
   20     20   
use time::OffsetDateTime;
   21     21   
   22     22   
/// Common test suite collection
   23     23   
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
   24     24   
enum Collection {
   25     25   
    V4,
@@ -121,121 +231,231 @@
  141    141   
        form_urlencoded::parse(expected.query().unwrap_or_default().as_bytes()).collect();
  142    142   
    expected_params.sort();
  143    143   
  144    144   
    let mut actual_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
  145    145   
        form_urlencoded::parse(actual.query().unwrap_or_default().as_bytes()).collect();
  146    146   
    actual_params.sort();
  147    147   
  148    148   
    assert_eq!(expected_params, actual_params);
  149    149   
}
  150    150   
  151         -
fn assert_requests_eq(expected: TestRequest, actual: http0::Request<&str>) {
         151  +
fn assert_requests_eq(expected: TestRequest, actual: http::Request<&str>) {
  152    152   
    let expected = expected.as_http_request();
  153    153   
    let actual = actual;
  154    154   
    assert_eq!(expected.method(), actual.method());
  155    155   
    assert_eq!(
  156    156   
        expected.headers().len(),
  157    157   
        actual.headers().len(),
  158    158   
        "extra or missing headers"
  159    159   
    );
  160    160   
    assert_eq!(expected.headers(), actual.headers(), "headers mismatch");
  161    161   
    assert_uri_eq(expected.uri(), actual.uri());
  162    162   
    assert_eq!(*expected.body(), *actual.body(), "body mismatch");
  163    163   
}
  164    164   
  165    165   
/// Run the given test from the v4 suite for the given signature location
  166    166   
pub(crate) fn run_v4_test(test_name: &'static str, signature_location: SignatureLocation) {
  167    167   
    let test = SigningSuiteTest::v4(test_name);
  168    168   
    let tc = test.context();
  169    169   
    let params = new_v4_signing_params_from_context(&tc, signature_location);
  170    170   
  171    171   
    let req = test.request();
  172    172   
    let expected_creq = test.canonical_request(signature_location);
  173    173   
    let signable_req = SignableRequest::from(&req);
  174    174   
    let actual_creq = CanonicalRequest::from(&signable_req, &params).unwrap();
  175    175   
  176    176   
    // check canonical request
  177    177   
    assert_eq!(
  178    178   
        expected_creq,
  179    179   
        actual_creq.to_string(),
  180    180   
        "canonical request didn't match (signature location: {signature_location:?})"
  181    181   
    );
  182    182   
  183    183   
    let expected_string_to_sign = test.string_to_sign(signature_location);
  184    184   
    let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
  185    185   
    let actual_string_to_sign = StringToSign::new_v4(
  186    186   
        *params.time(),
  187    187   
        params.region().unwrap(),
  188    188   
        params.name(),
  189    189   
        hashed_creq,
  190    190   
    )
  191    191   
    .to_string();
  192    192   
  193    193   
    // check string to sign
  194    194   
    assert_eq!(
  195    195   
        expected_string_to_sign, actual_string_to_sign,
  196    196   
        "'string to sign' didn't match (signature location: {signature_location:?})"
  197    197   
    );
  198    198   
  199    199   
    let out = crate::http_request::sign(signable_req, &params).unwrap();
  200    200   
    let mut signed = req.as_http_request();
  201         -
    out.output.apply_to_request_http0x(&mut signed);
         201  +
    out.output.apply_to_request_http1x(&mut signed);
  202    202   
  203    203   
    // check signature
  204    204   
    assert_eq!(
  205    205   
        test.signature(signature_location),
  206    206   
        out.signature,
  207    207   
        "signature didn't match (signature location: {signature_location:?})"
  208    208   
    );
  209    209   
  210    210   
    let expected = test.signed_request(signature_location);
  211    211   
    assert_requests_eq(expected, signed);
@@ -337,337 +397,397 @@
  357    357   
        .to_string();
  358    358   
  359    359   
        assert_eq!(
  360    360   
            expected_string_to_sign, actual_string_to_sign,
  361    361   
            "'string to sign' didn't match (signature location: {signature_location:?})"
  362    362   
        );
  363    363   
  364    364   
        let out = sign(signable_req, &params).unwrap();
  365    365   
        // Sigv4a signatures are non-deterministic, so we can't compare the signature directly.
  366    366   
        out.output
  367         -
            .apply_to_request_http0x(&mut req.as_http_request());
         367  +
            .apply_to_request_http1x(&mut req.as_http_request());
  368    368   
  369    369   
        let creds = params.credentials().unwrap();
  370    370   
        let signing_key =
  371    371   
            v4a::generate_signing_key(creds.access_key_id(), creds.secret_access_key());
  372    372   
        let sig = DerSignature::from_bytes(&hex::decode(out.signature).unwrap()).unwrap();
  373    373   
        let sig = sig
  374    374   
            .try_into()
  375    375   
            .expect("DER-style signatures are always convertible into fixed-size signatures");
  376    376   
  377    377   
        let signing_key = SigningKey::from_bytes(signing_key.as_ref()).unwrap();
@@ -442,442 +539,566 @@
  462    462   
            TestSignedBody::Bytes(data) => SignableBody::Bytes(data.as_slice()),
  463    463   
        }
  464    464   
    }
  465    465   
}
  466    466   
  467    467   
impl TestRequest {
  468    468   
    pub(crate) fn set_body(&mut self, body: SignableBody<'static>) {
  469    469   
        self.body = TestSignedBody::Signable(body);
  470    470   
    }
  471    471   
  472         -
    pub(crate) fn as_http_request(&self) -> http0::Request<&'static str> {
  473         -
        let mut builder = http0::Request::builder()
         472  +
    pub(crate) fn as_http_request(&self) -> http::Request<&'static str> {
         473  +
        let mut builder = http::Request::builder()
  474    474   
            .uri(&self.uri)
  475         -
            .method(Method::from_bytes(self.method.as_bytes()).unwrap());
         475  +
            .method(http::Method::from_bytes(self.method.as_bytes()).unwrap());
  476    476   
        for (k, v) in &self.headers {
  477    477   
            builder = builder.header(k, v);
  478    478   
        }
  479    479   
        builder.body("body").unwrap()
  480    480   
    }
  481    481   
}
  482    482   
  483    483   
impl<B: AsRef<[u8]>> From<http0::Request<B>> for TestRequest {
  484    484   
    fn from(value: http0::Request<B>) -> Self {
  485    485   
        let invalid = value
  486    486   
            .headers()
  487    487   
            .values()
  488    488   
            .find(|h| std::str::from_utf8(h.as_bytes()).is_err());
  489    489   
        if let Some(invalid) = invalid {
  490    490   
            panic!("invalid header: {:?}", invalid);
  491    491   
        }
  492    492   
        Self {
  493    493   
            uri: value.uri().to_string(),
  494    494   
            method: value.method().to_string(),
  495    495   
            headers: value
  496    496   
                .headers()
  497    497   
                .iter()
  498    498   
                .map(|(k, v)| {
  499    499   
                    (
  500    500   
                        k.to_string(),
  501    501   
                        String::from_utf8(v.as_bytes().to_vec()).unwrap(),
  502    502   
                    )
  503    503   
                })
  504    504   
                .collect::<Vec<_>>(),
  505    505   
            body: TestSignedBody::Bytes(value.body().as_ref().to_vec()),
  506    506   
        }
  507    507   
    }
  508    508   
}
  509    509   
         510  +
impl<B: AsRef<[u8]>> From<http::Request<B>> for TestRequest {
         511  +
    fn from(value: http::Request<B>) -> Self {
         512  +
        let invalid = value
         513  +
            .headers()
         514  +
            .values()
         515  +
            .find(|h| std::str::from_utf8(h.as_bytes()).is_err());
         516  +
        if let Some(invalid) = invalid {
         517  +
            panic!("invalid header: {:?}", invalid);
         518  +
        }
         519  +
        Self {
         520  +
            uri: value.uri().to_string(),
         521  +
            method: value.method().to_string(),
         522  +
            headers: value
         523  +
                .headers()
         524  +
                .iter()
         525  +
                .map(|(k, v)| {
         526  +
                    (
         527  +
                        k.to_string(),
         528  +
                        String::from_utf8(v.as_bytes().to_vec()).unwrap(),
         529  +
                    )
         530  +
                })
         531  +
                .collect::<Vec<_>>(),
         532  +
            body: TestSignedBody::Bytes(value.body().as_ref().to_vec()),
         533  +
        }
         534  +
    }
         535  +
}
         536  +
  510    537   
impl<'a> From<&'a TestRequest> for SignableRequest<'a> {
  511    538   
    fn from(request: &'a TestRequest) -> SignableRequest<'a> {
  512    539   
        SignableRequest::new(
  513    540   
            &request.method,
  514    541   
            &request.uri,
  515    542   
            request
  516    543   
                .headers
  517    544   
                .iter()
  518    545   
                .map(|(k, v)| (k.as_str(), v.as_str())),
  519    546   
            request.body.as_signable_body(),

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

@@ -1,1 +41,41 @@
    1      1   
[package]
    2      2   
name = "aws-smithy-async"
    3         -
version = "1.2.7"
           3  +
version = "1.2.8"
    4      4   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "John DiSanti <jdisanti@amazon.com>"]
    5      5   
description = "Async runtime agnostic abstractions for smithy-rs."
    6      6   
edition = "2021"
    7      7   
license = "Apache-2.0"
    8      8   
repository = "https://github.com/smithy-lang/smithy-rs"
    9      9   
rust-version = "1.88"
   10     10   
   11     11   
[features]
   12     12   
rt-tokio = ["tokio/time"]
   13     13   
test-util = ["rt-tokio", "tokio/rt"]
   14     14   
   15     15   
[dependencies]
   16     16   
pin-project-lite = "0.2.14"
   17         -
tokio = { version = "1.40.0", features = ["sync"] }
          17  +
tokio = { version = "1.46.0", features = ["sync"] }
   18     18   
futures-util = { version = "0.3.29", default-features = false }
   19     19   
   20     20   
[dev-dependencies]
   21     21   
pin-utils = "0.1"
   22     22   
tokio = { version = "1.23.1", features = ["rt", "macros", "test-util"] }
   23     23   
tokio-test = "0.4.2"
   24     24   
   25     25   
# futures-util is used by `now_or_later`, for instance, but the tooling
   26     26   
# reports a false positive, saying it is unused.
   27     27   
[package.metadata.cargo-udeps.ignore]

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

@@ -1,1 +33,33 @@
   20     20   
all-features = true
   21     21   
targets = ["x86_64-unknown-linux-gnu"]
   22     22   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   23     23   
rustdoc-args = ["--cfg", "docsrs"]
   24     24   
[dependencies.minicbor]
   25     25   
version = "0.24.2"
   26     26   
features = ["alloc", "half"]
   27     27   
   28     28   
[dependencies.aws-smithy-types]
   29     29   
path = "../aws-smithy-types"
   30         -
version = "1.3.6"
          30  +
version = "1.4.0"
   31     31   
   32     32   
[dev-dependencies]
   33     33   
criterion = "0.5.1"

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

@@ -1,1 +44,52 @@
    1      1   
# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
    2      2   
[package]
    3      3   
name = "aws-smithy-checksums"
    4         -
version = "0.63.13"
           4  +
version = "0.64.0"
    5      5   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Zelda Hessler <zhessler@amazon.com>"]
    6      6   
description = "Checksum calculation and verification callbacks"
    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   
[dependencies]
   18     18   
bytes = "1.10.0"
   19     19   
crc-fast = "~1.9.0"
   20     20   
hex = "0.4.3"
   21         -
http = "0.2.9"
   22         -
http-body = "0.4.5"
          21  +
http-body-util = "0.1.3"
   23     22   
md-5 = "0.10"
   24     23   
pin-project-lite = "0.2.14"
   25     24   
sha1 = "0.10"
   26     25   
sha2 = "0.10"
   27     26   
tracing = "0.1.40"
   28     27   
   29     28   
[dependencies.aws-smithy-http]
   30     29   
path = "../aws-smithy-http"
   31         -
version = "0.62.6"
          30  +
version = "0.63.0"
   32     31   
   33     32   
[dependencies.aws-smithy-types]
   34     33   
path = "../aws-smithy-types"
   35         -
version = "1.3.6"
          34  +
features = ["http-body-1-x"]
          35  +
version = "1.4.0"
          36  +
          37  +
[dependencies.http-1x]
          38  +
package = "http"
          39  +
version = "1.3.1"
          40  +
          41  +
[dependencies.http-body-1x]
          42  +
package = "http-body"
          43  +
version = "1.0.1"
   36     44   
   37     45   
[dev-dependencies]
   38     46   
bytes-utils = "0.1.2"
   39     47   
pretty_assertions = "1.3"
   40     48   
tracing-test = "0.2.1"
   41     49   
   42     50   
[dev-dependencies.tokio]
   43     51   
version = "1.23.1"
   44     52   
features = ["macros", "rt"]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/src/body/cache.rs

@@ -1,1 +38,38 @@
    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   
//! Checksum caching functionality.
    7      7   
    8         -
use http::HeaderMap;
           8  +
use http_1x::HeaderMap;
    9      9   
use std::sync::{Arc, Mutex};
   10     10   
   11     11   
/// A cache for storing previously calculated checksums.
   12     12   
#[derive(Debug, Clone)]
   13     13   
pub struct ChecksumCache {
   14     14   
    inner: Arc<Mutex<Option<HeaderMap>>>,
   15     15   
}
   16     16   
   17     17   
impl ChecksumCache {
   18     18   
    /// Create a new empty checksum cache.

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/src/body/calculate.rs

@@ -1,1 +188,197 @@
    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   
//! Functionality for calculating the checksum of an HTTP body and emitting it as trailers.
    7      7   
    8      8   
use super::ChecksumCache;
    9      9   
use crate::http::HttpChecksum;
   10     10   
   11         -
use aws_smithy_http::header::append_merge_header_maps;
          11  +
use aws_smithy_http::header::append_merge_header_maps_http_1x;
   12     12   
use aws_smithy_types::body::SdkBody;
   13         -
   14         -
use http::HeaderMap;
   15         -
use http_body::SizeHint;
   16     13   
use pin_project_lite::pin_project;
   17         -
   18     14   
use std::pin::Pin;
   19     15   
use std::task::{Context, Poll};
   20     16   
use tracing::warn;
   21     17   
   22     18   
pin_project! {
   23     19   
    /// A body-wrapper that will calculate the `InnerBody`'s checksum and emit it as a trailer.
   24     20   
    pub struct ChecksumBody<InnerBody> {
   25     21   
            #[pin]
   26     22   
            body: InnerBody,
   27     23   
            checksum: Option<Box<dyn HttpChecksum>>,
          24  +
            written_trailers: bool,
   28     25   
            cache: Option<ChecksumCache>
   29     26   
    }
   30     27   
}
   31     28   
   32     29   
impl ChecksumBody<SdkBody> {
   33     30   
    /// Given an `SdkBody` and a `Box<dyn HttpChecksum>`, create a new `ChecksumBody<SdkBody>`.
   34     31   
    pub fn new(body: SdkBody, checksum: Box<dyn HttpChecksum>) -> Self {
   35     32   
        Self {
   36     33   
            body,
   37     34   
            checksum: Some(checksum),
          35  +
            written_trailers: false,
   38     36   
            cache: None,
   39     37   
        }
   40     38   
    }
   41     39   
   42     40   
    /// Configure a cache for this body.
   43     41   
    ///
   44     42   
    /// When used across multiple requests (e.g. retries) a cached checksum previously
   45     43   
    /// calculated will be favored if available.
   46     44   
    pub fn with_cache(self, cache: ChecksumCache) -> Self {
   47     45   
        Self {
   48     46   
            body: self.body,
   49     47   
            checksum: self.checksum,
          48  +
            written_trailers: false,
   50     49   
            cache: Some(cache),
   51     50   
        }
   52     51   
    }
   53         -
}
   54         -
   55         -
impl http_body::Body for ChecksumBody<SdkBody> {
   56         -
    type Data = bytes::Bytes;
   57         -
    type Error = aws_smithy_types::body::Error;
   58         -
   59         -
    fn poll_data(
   60         -
        self: Pin<&mut Self>,
   61         -
        cx: &mut Context<'_>,
   62         -
    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
   63         -
        let this = self.project();
   64         -
        match this.checksum {
   65         -
            Some(checksum) => {
   66         -
                let poll_res = this.body.poll_data(cx);
   67         -
                if let Poll::Ready(Some(Ok(data))) = &poll_res {
   68         -
                    checksum.update(data);
   69         -
                }
   70         -
   71         -
                poll_res
   72         -
            }
   73         -
            None => unreachable!("This can only fail if poll_data is called again after poll_trailers, which is invalid"),
   74         -
        }
   75         -
    }
   76         -
   77         -
    fn poll_trailers(
   78         -
        self: Pin<&mut Self>,
   79         -
        cx: &mut Context<'_>,
   80         -
    ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
   81         -
        let this = self.project();
   82         -
        let poll_res = this.body.poll_trailers(cx);
   83     52   
   84         -
        if let Poll::Ready(Ok(maybe_inner_trailers)) = poll_res {
   85         -
            let checksum_headers = if let Some(checksum) = this.checksum.take() {
          53  +
    // It would be nicer if this could take &self, but I couldn't make that
          54  +
    // work out with the Pin/Projection types, so its a static method for now
          55  +
    fn extract_or_set_cached_headers(
          56  +
        maybe_cache: &Option<ChecksumCache>,
          57  +
        checksum: Box<dyn HttpChecksum>,
          58  +
    ) -> http_1x::HeaderMap {
   86     59   
        let calculated_headers = checksum.headers();
   87         -
   88         -
                if let Some(cache) = this.cache {
          60  +
        if let Some(cache) = maybe_cache {
   89     61   
            if let Some(cached_headers) = cache.get() {
   90     62   
                if cached_headers != calculated_headers {
   91     63   
                    warn!(cached = ?cached_headers, calculated = ?calculated_headers, "calculated checksum differs from cached checksum!");
   92     64   
                }
   93     65   
                cached_headers
   94     66   
            } else {
   95     67   
                cache.set(calculated_headers.clone());
   96     68   
                calculated_headers
   97     69   
            }
   98     70   
        } else {
   99     71   
            calculated_headers
  100     72   
        }
          73  +
    }
          74  +
}
          75  +
          76  +
impl http_body_1x::Body for ChecksumBody<SdkBody> {
          77  +
    type Data = bytes::Bytes;
          78  +
    type Error = aws_smithy_types::body::Error;
          79  +
          80  +
    fn poll_frame(
          81  +
        self: Pin<&mut Self>,
          82  +
        cx: &mut Context<'_>,
          83  +
    ) -> Poll<Option<Result<http_body_1x::Frame<Self::Data>, Self::Error>>> {
          84  +
        let this = self.project();
          85  +
        let poll_res = this.body.poll_frame(cx);
          86  +
          87  +
        match &poll_res {
          88  +
            Poll::Ready(Some(Ok(frame))) => {
          89  +
                // Update checksum for data frames
          90  +
                if frame.is_data() {
          91  +
                    if let Some(checksum) = this.checksum {
          92  +
                        checksum.update(frame.data_ref().expect("Data frame has data"));
          93  +
                    }
  101     94   
                } else {
  102         -
                return Poll::Ready(Ok(None));
          95  +
                    // Add checksum trailer to other trailers if necessary
          96  +
                    let checksum_headers = if let Some(checksum) = this.checksum.take() {
          97  +
                        ChecksumBody::extract_or_set_cached_headers(this.cache, checksum)
          98  +
                    } else {
          99  +
                        return Poll::Ready(None);
  103    100   
                    };
  104         -
  105         -
            return match maybe_inner_trailers {
  106         -
                Some(inner_trailers) => Poll::Ready(Ok(Some(append_merge_header_maps(
  107         -
                    inner_trailers,
  108         -
                    checksum_headers,
  109         -
                )))),
  110         -
                None => Poll::Ready(Ok(Some(checksum_headers))),
         101  +
                    let trailers = frame
         102  +
                        .trailers_ref()
         103  +
                        .expect("Trailers frame has trailers")
         104  +
                        .clone();
         105  +
                    *this.written_trailers = true;
         106  +
                    return Poll::Ready(Some(Ok(http_body_1x::Frame::trailers(
         107  +
                        append_merge_header_maps_http_1x(trailers, checksum_headers),
         108  +
                    ))));
         109  +
                }
         110  +
            }
         111  +
            Poll::Ready(None) => {
         112  +
                // If the trailers have not already been written (because there were no existing
         113  +
                // trailers on the body) we write them here
         114  +
                if !*this.written_trailers {
         115  +
                    let checksum_headers = if let Some(checksum) = this.checksum.take() {
         116  +
                        ChecksumBody::extract_or_set_cached_headers(this.cache, checksum)
         117  +
                    } else {
         118  +
                        return Poll::Ready(None);
  111    119   
                    };
         120  +
                    let trailers = http_1x::HeaderMap::new();
         121  +
                    return Poll::Ready(Some(Ok(http_body_1x::Frame::trailers(
         122  +
                        append_merge_header_maps_http_1x(trailers, checksum_headers),
         123  +
                    ))));
  112    124   
                }
  113         -
  114         -
        poll_res
  115         -
    }
  116         -
  117         -
    fn is_end_stream(&self) -> bool {
  118         -
        // If inner body is finished and we've already consumed the checksum then we must be
  119         -
        // at the end of the stream.
  120         -
        self.body.is_end_stream() && self.checksum.is_none()
  121    125   
            }
  122         -
  123         -
    fn size_hint(&self) -> SizeHint {
  124         -
        self.body.size_hint()
         126  +
            _ => {}
         127  +
        };
         128  +
        poll_res
  125    129   
    }
  126    130   
}
  127    131   
  128    132   
#[cfg(test)]
  129    133   
mod tests {
  130    134   
    use super::ChecksumBody;
  131    135   
    use crate::{http::CRC_32_HEADER_NAME, ChecksumAlgorithm, CRC_32_NAME};
  132    136   
    use aws_smithy_types::base64;
  133    137   
    use aws_smithy_types::body::SdkBody;
  134    138   
    use bytes::Buf;
  135    139   
    use bytes_utils::SegmentedBuf;
  136         -
    use http_body::Body;
         140  +
    use http_1x::HeaderMap;
         141  +
    use http_body_util::BodyExt;
  137    142   
    use std::fmt::Write;
  138    143   
    use std::io::Read;
  139    144   
  140         -
    fn header_value_as_checksum_string(header_value: &http::HeaderValue) -> String {
         145  +
    fn header_value_as_checksum_string(header_value: &http_1x::HeaderValue) -> String {
  141    146   
        let decoded_checksum = base64::decode(header_value.to_str().unwrap()).unwrap();
  142    147   
        let decoded_checksum = decoded_checksum
  143    148   
            .into_iter()
  144    149   
            .fold(String::new(), |mut acc, byte| {
  145    150   
                write!(acc, "{byte:02X?}").expect("string will always be writeable");
  146    151   
                acc
  147    152   
            });
  148    153   
  149    154   
        format!("0x{decoded_checksum}")
  150    155   
    }
  151    156   
  152    157   
    #[tokio::test]
  153    158   
    async fn test_checksum_body() {
  154    159   
        let input_text = "This is some test text for an SdkBody";
  155    160   
        let body = SdkBody::from(input_text);
  156    161   
        let checksum = CRC_32_NAME
  157    162   
            .parse::<ChecksumAlgorithm>()
  158    163   
            .unwrap()
  159    164   
            .into_impl();
  160    165   
        let mut body = ChecksumBody::new(body, checksum);
  161    166   
  162         -
        let mut output = SegmentedBuf::new();
  163         -
        while let Some(buf) = body.data().await {
  164         -
            output.push(buf.unwrap());
         167  +
        let mut output_data = SegmentedBuf::new();
         168  +
        let mut trailers = HeaderMap::new();
         169  +
        while let Some(buf) = body.frame().await {
         170  +
            let buf = buf.unwrap();
         171  +
            if buf.is_data() {
         172  +
                output_data.push(buf.into_data().unwrap());
         173  +
            } else if buf.is_trailers() {
         174  +
                let map = buf.into_trailers().unwrap();
         175  +
                map.into_iter().for_each(|(k, v)| {
         176  +
                    trailers.insert(k.unwrap(), v);
         177  +
                });
         178  +
            }
  165    179   
        }
  166    180   
  167    181   
        let mut output_text = String::new();
  168         -
        output
         182  +
        output_data
  169    183   
            .reader()
  170    184   
            .read_to_string(&mut output_text)
  171    185   
            .expect("Doesn't cause IO errors");
  172    186   
        // Verify data is complete and unaltered
  173    187   
        assert_eq!(input_text, output_text);
  174    188   
  175         -
        let trailers = body
  176         -
            .trailers()
  177         -
            .await
  178         -
            .expect("checksum generation was without error")
  179         -
            .expect("trailers were set");
  180    189   
        let checksum_trailer = trailers
  181    190   
            .get(CRC_32_HEADER_NAME)
  182    191   
            .expect("trailers contain crc32 checksum");
  183    192   
        let checksum_trailer = header_value_as_checksum_string(checksum_trailer);
  184    193   
  185    194   
        // Known correct checksum for the input "This is some test text for an SdkBody"
  186    195   
        assert_eq!("0x99B01F72", checksum_trailer);
  187    196   
    }
  188    197   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/src/body/validate.rs

@@ -1,1 +223,208 @@
    4      4   
 */
    5      5   
    6      6   
//! Functionality for validating an HTTP body against a given precalculated checksum and emitting an
    7      7   
//! error if it doesn't match.
    8      8   
    9      9   
use crate::http::HttpChecksum;
   10     10   
   11     11   
use aws_smithy_types::body::SdkBody;
   12     12   
   13     13   
use bytes::Bytes;
   14         -
use http::{HeaderMap, HeaderValue};
   15         -
use http_body::SizeHint;
   16     14   
use pin_project_lite::pin_project;
   17     15   
   18     16   
use std::fmt::Display;
   19     17   
use std::pin::Pin;
   20     18   
use std::task::{Context, Poll};
   21     19   
   22     20   
pin_project! {
   23     21   
    /// A body-wrapper that will calculate the `InnerBody`'s checksum and emit an error if it
   24     22   
    /// doesn't match the precalculated checksum.
   25     23   
    pub struct ChecksumBody<InnerBody> {
   26     24   
        #[pin]
   27     25   
        inner: InnerBody,
   28     26   
        checksum: Option<Box<dyn HttpChecksum>>,
   29     27   
        precalculated_checksum: Bytes,
   30     28   
    }
   31     29   
}
   32     30   
   33     31   
impl ChecksumBody<SdkBody> {
   34     32   
    /// Given an `SdkBody`, a `Box<dyn HttpChecksum>`, and a precalculated checksum represented
   35     33   
    /// as `Bytes`, create a new `ChecksumBody<SdkBody>`.
   36     34   
    pub fn new(
   37     35   
        body: SdkBody,
   38     36   
        checksum: Box<dyn HttpChecksum>,
   39     37   
        precalculated_checksum: Bytes,
   40     38   
    ) -> Self {
   41     39   
        Self {
   42     40   
            inner: body,
   43     41   
            checksum: Some(checksum),
   44     42   
            precalculated_checksum,
   45     43   
        }
   46     44   
    }
   47     45   
   48     46   
    fn poll_inner(
   49     47   
        self: Pin<&mut Self>,
   50     48   
        cx: &mut Context<'_>,
   51         -
    ) -> Poll<Option<Result<Bytes, aws_smithy_types::body::Error>>> {
   52         -
        use http_body::Body;
          49  +
    ) -> Poll<Option<Result<http_body_1x::Frame<Bytes>, aws_smithy_types::body::Error>>> {
          50  +
        use http_body_1x::Body;
   53     51   
   54     52   
        let this = self.project();
   55     53   
        let checksum = this.checksum;
   56     54   
   57         -
        match this.inner.poll_data(cx) {
   58         -
            Poll::Ready(Some(Ok(data))) => {
          55  +
        match this.inner.poll_frame(cx) {
          56  +
            Poll::Ready(Some(Ok(frame))) => {
          57  +
                let data = frame.data_ref().expect("Data frame should have data");
   59     58   
                tracing::trace!(
   60     59   
                    "reading {} bytes from the body and updating the checksum calculation",
   61     60   
                    data.len()
   62     61   
                );
   63     62   
                let checksum = match checksum.as_mut() {
   64     63   
                    Some(checksum) => checksum,
   65     64   
                    None => {
   66     65   
                        unreachable!("The checksum must exist because it's only taken out once the inner body has been completely polled.");
   67     66   
                    }
   68     67   
                };
   69     68   
   70         -
                checksum.update(&data);
   71         -
                Poll::Ready(Some(Ok(data)))
          69  +
                checksum.update(data);
          70  +
                Poll::Ready(Some(Ok(frame)))
   72     71   
            }
   73     72   
            // Once the inner body has stopped returning data, check the checksum
   74     73   
            // and return an error if it doesn't match.
   75     74   
            Poll::Ready(None) => {
   76     75   
                tracing::trace!("finished reading from body, calculating final checksum");
   77     76   
                let checksum = match checksum.take() {
   78     77   
                    Some(checksum) => checksum,
   79     78   
                    None => {
   80     79   
                        // If the checksum was already taken and this was polled again anyways,
   81     80   
                        // then return nothing
   82     81   
                        return Poll::Ready(None);
   83     82   
                    }
   84     83   
                };
   85     84   
   86     85   
                let actual_checksum = checksum.finalize();
   87     86   
                if *this.precalculated_checksum == actual_checksum {
   88     87   
                    Poll::Ready(None)
   89     88   
                } else {
   90     89   
                    // So many parens it's starting to look like LISP
   91     90   
                    Poll::Ready(Some(Err(Box::new(Error::ChecksumMismatch {
   92     91   
                        expected: this.precalculated_checksum.clone(),
   93     92   
                        actual: actual_checksum,
   94     93   
                    }))))
   95     94   
                }
   96     95   
            }
   97     96   
            Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
   98     97   
            Poll::Pending => Poll::Pending,
   99     98   
        }
  100     99   
    }
  101    100   
}
  102    101   
  103    102   
/// Errors related to checksum calculation and validation
  104    103   
#[derive(Debug, Eq, PartialEq)]
  105    104   
#[non_exhaustive]
  106    105   
pub enum Error {
  107    106   
    /// The actual checksum didn't match the expected checksum. The checksummed data has been
  108    107   
    /// altered since the expected checksum was calculated.
  109    108   
    ChecksumMismatch { expected: Bytes, actual: Bytes },
  110    109   
}
  111    110   
  112    111   
impl Display for Error {
  113    112   
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
  114    113   
        match self {
  115    114   
            Error::ChecksumMismatch { expected, actual } => write!(
  116    115   
                f,
  117    116   
                "body checksum mismatch. expected body checksum to be {} but it was {}",
  118    117   
                hex::encode(expected),
  119    118   
                hex::encode(actual)
  120    119   
            ),
  121    120   
        }
  122    121   
    }
  123    122   
}
  124    123   
  125    124   
impl std::error::Error for Error {}
  126    125   
  127         -
impl http_body::Body for ChecksumBody<SdkBody> {
         126  +
impl http_body_1x::Body for ChecksumBody<SdkBody> {
  128    127   
    type Data = Bytes;
  129    128   
    type Error = aws_smithy_types::body::Error;
  130    129   
  131         -
    fn poll_data(
         130  +
    fn poll_frame(
  132    131   
        self: Pin<&mut Self>,
  133    132   
        cx: &mut Context<'_>,
  134         -
    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
         133  +
    ) -> Poll<Option<Result<http_body_1x::Frame<Self::Data>, Self::Error>>> {
  135    134   
        self.poll_inner(cx)
  136    135   
    }
  137         -
  138         -
    fn poll_trailers(
  139         -
        self: Pin<&mut Self>,
  140         -
        cx: &mut Context<'_>,
  141         -
    ) -> Poll<Result<Option<HeaderMap<HeaderValue>>, Self::Error>> {
  142         -
        self.project().inner.poll_trailers(cx)
  143         -
    }
  144         -
  145         -
    fn is_end_stream(&self) -> bool {
  146         -
        self.checksum.is_none()
  147         -
    }
  148         -
  149         -
    fn size_hint(&self) -> SizeHint {
  150         -
        self.inner.size_hint()
  151         -
    }
  152    136   
}
  153    137   
  154    138   
#[cfg(test)]
  155    139   
mod tests {
  156    140   
    use crate::body::validate::{ChecksumBody, Error};
  157    141   
    use crate::ChecksumAlgorithm;
  158    142   
    use aws_smithy_types::body::SdkBody;
  159    143   
    use bytes::{Buf, Bytes};
  160    144   
    use bytes_utils::SegmentedBuf;
  161         -
    use http_body::Body;
         145  +
    use http_body_util::BodyExt;
  162    146   
    use std::io::Read;
  163    147   
  164    148   
    fn calculate_crc32_checksum(input: &str) -> Bytes {
  165    149   
        let checksum =
  166    150   
            crc_fast::checksum(crc_fast::CrcAlgorithm::Crc32IsoHdlc, input.as_bytes()) as u32;
  167    151   
  168    152   
        Bytes::copy_from_slice(&checksum.to_be_bytes())
  169    153   
    }
  170    154   
  171    155   
    #[tokio::test]
  172    156   
    async fn test_checksum_validated_body_errors_on_mismatch() {
  173    157   
        let input_text = "This is some test text for an SdkBody";
  174    158   
        let actual_checksum = calculate_crc32_checksum(input_text);
  175    159   
        let body = SdkBody::from(input_text);
  176    160   
        let non_matching_checksum = Bytes::copy_from_slice(&[0x00, 0x00, 0x00, 0x00]);
  177    161   
        let mut body = ChecksumBody::new(
  178    162   
            body,
  179    163   
            "crc32".parse::<ChecksumAlgorithm>().unwrap().into_impl(),
  180    164   
            non_matching_checksum.clone(),
  181    165   
        );
  182    166   
  183         -
        while let Some(data) = body.data().await {
         167  +
        while let Some(data) = body.frame().await {
  184    168   
            match data {
  185    169   
                Ok(_) => { /* Do nothing */ }
  186    170   
                Err(e) => {
  187    171   
                    match e.downcast_ref::<Error>().unwrap() {
  188    172   
                        Error::ChecksumMismatch { expected, actual } => {
  189    173   
                            assert_eq!(expected, &non_matching_checksum);
  190    174   
                            assert_eq!(actual, &actual_checksum);
  191    175   
                        }
  192    176   
                    }
  193    177   
  194    178   
                    return;
  195    179   
                }
  196    180   
            }
  197    181   
        }
  198    182   
  199    183   
        panic!("didn't hit expected error condition");
  200    184   
    }
  201    185   
  202    186   
    #[tokio::test]
  203    187   
    async fn test_checksum_validated_body_succeeds_on_match() {
  204    188   
        let input_text = "This is some test text for an SdkBody";
  205    189   
        let actual_checksum = calculate_crc32_checksum(input_text);
  206    190   
        let body = SdkBody::from(input_text);
  207    191   
        let http_checksum = "crc32".parse::<ChecksumAlgorithm>().unwrap().into_impl();
  208    192   
        let mut body = ChecksumBody::new(body, http_checksum, actual_checksum);
  209    193   
  210    194   
        let mut output = SegmentedBuf::new();
  211         -
        while let Some(buf) = body.data().await {
  212         -
            output.push(buf.unwrap());
         195  +
        while let Some(buf) = body.frame().await {
         196  +
            let data = buf.unwrap().into_data().unwrap();
         197  +
            output.push(data);
  213    198   
        }
  214    199   
  215    200   
        let mut output_text = String::new();
  216    201   
        output
  217    202   
            .reader()
  218    203   
            .read_to_string(&mut output_text)
  219    204   
            .expect("Doesn't cause IO errors");
  220    205   
        // Verify data is complete and unaltered
  221    206   
        assert_eq!(input_text, output_text);
  222    207   
    }

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/src/http.rs

@@ -1,1 +87,86 @@
    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   
//! Checksum support for HTTP requests and responses.
    7      7   
    8      8   
use aws_smithy_types::base64;
    9         -
use http::header::{HeaderMap, HeaderValue};
   10      9   
   11     10   
use crate::Crc64Nvme;
   12     11   
use crate::{
   13     12   
    Checksum, Crc32, Crc32c, Md5, Sha1, Sha256, CRC_32_C_NAME, CRC_32_NAME, CRC_64_NVME_NAME,
   14     13   
    SHA_1_NAME, SHA_256_NAME,
   15     14   
};
   16     15   
   17     16   
pub const CRC_32_HEADER_NAME: &str = "x-amz-checksum-crc32";
   18     17   
pub const CRC_32_C_HEADER_NAME: &str = "x-amz-checksum-crc32c";
   19     18   
pub const SHA_1_HEADER_NAME: &str = "x-amz-checksum-sha1";
   20     19   
pub const SHA_256_HEADER_NAME: &str = "x-amz-checksum-sha256";
   21     20   
pub const CRC_64_NVME_HEADER_NAME: &str = "x-amz-checksum-crc64nvme";
   22     21   
   23     22   
// Preserved for compatibility purposes. This should never be used by users, only within smithy-rs
   24     23   
#[warn(dead_code)]
   25     24   
pub(crate) static MD5_HEADER_NAME: &str = "content-md5";
   26     25   
   27     26   
/// When a response has to be checksum-verified, we have to check possible headers until we find the
   28     27   
/// header with the precalculated checksum. Because a service may send back multiple headers, we have
   29     28   
/// to check them in order based on how fast each checksum is to calculate.
   30     29   
pub const CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER: [&str; 5] = [
   31     30   
    CRC_64_NVME_NAME,
   32     31   
    CRC_32_C_NAME,
   33     32   
    CRC_32_NAME,
   34     33   
    SHA_1_NAME,
   35     34   
    SHA_256_NAME,
   36     35   
];
   37     36   
   38     37   
/// Checksum algorithms are use to validate the integrity of data. Structs that implement this trait
   39     38   
/// can be used as checksum calculators. This trait requires Send + Sync because these checksums are
   40     39   
/// often used in a threaded context.
   41     40   
pub trait HttpChecksum: Checksum + Send + Sync {
   42         -
    /// Either return this checksum as a `HeaderMap` containing one HTTP header, or return an error
          41  +
    /// Either return this checksum as a http-02x `HeaderMap` containing one HTTP header, or return an error
   43     42   
    /// describing why checksum calculation failed.
   44         -
    fn headers(self: Box<Self>) -> HeaderMap<HeaderValue> {
   45         -
        let mut header_map = HeaderMap::new();
          43  +
    fn headers(self: Box<Self>) -> http_1x::HeaderMap<http_1x::HeaderValue> {
          44  +
        let mut header_map = http_1x::HeaderMap::new();
   46     45   
        header_map.insert(self.header_name(), self.header_value());
   47     46   
   48     47   
        header_map
   49     48   
    }
   50     49   
   51     50   
    /// Return the `HeaderName` used to represent this checksum algorithm
   52     51   
    fn header_name(&self) -> &'static str;
   53     52   
   54         -
    /// Return the calculated checksum as a base64-encoded `HeaderValue`
   55         -
    fn header_value(self: Box<Self>) -> HeaderValue {
          53  +
    /// Return the calculated checksum as a base64-encoded http-02x `HeaderValue`
          54  +
    fn header_value(self: Box<Self>) -> http_1x::HeaderValue {
   56     55   
        let hash = self.finalize();
   57         -
        HeaderValue::from_str(&base64::encode(&hash[..]))
          56  +
        http_1x::HeaderValue::from_str(&base64::encode(&hash[..]))
   58     57   
            .expect("base64 encoded bytes are always valid header values")
   59     58   
    }
   60     59   
   61     60   
    /// Return the total size of
   62     61   
    /// - The `HeaderName`
   63     62   
    /// - The header name/value separator
   64     63   
    /// - The base64-encoded `HeaderValue`
   65     64   
    fn size(&self) -> u64 {
   66     65   
        let trailer_name_size_in_bytes = self.header_name().len();
   67     66   
        let base64_encoded_checksum_size_in_bytes =

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/src/lib.rs

@@ -350,350 +410,410 @@
  370    370   
            CRC_32_C_HEADER_NAME, CRC_32_HEADER_NAME, MD5_HEADER_NAME, SHA_1_HEADER_NAME,
  371    371   
            SHA_256_HEADER_NAME,
  372    372   
        },
  373    373   
        Crc32, Crc32c, Md5, Sha1, Sha256,
  374    374   
    };
  375    375   
  376    376   
    use crate::http::HttpChecksum;
  377    377   
    use crate::ChecksumAlgorithm;
  378    378   
  379    379   
    use aws_smithy_types::base64;
  380         -
    use http::HeaderValue;
         380  +
    use http_1x::HeaderValue;
  381    381   
    use pretty_assertions::assert_eq;
  382    382   
    use std::fmt::Write;
  383    383   
  384    384   
    const TEST_DATA: &str = r#"test data"#;
  385    385   
  386    386   
    fn base64_encoded_checksum_to_hex_string(header_value: &HeaderValue) -> String {
  387    387   
        let decoded_checksum = base64::decode(header_value.to_str().unwrap()).unwrap();
  388    388   
        let decoded_checksum = decoded_checksum
  389    389   
            .into_iter()
  390    390   
            .fold(String::new(), |mut acc, byte| {

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

@@ -1,1 +66,51 @@
    1      1   
# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
    2      2   
[package]
    3      3   
name = "aws-smithy-compression"
    4         -
version = "0.0.7"
           4  +
version = "0.0.8"
    5      5   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Zelda Hessler <zhessler@amazon.com>"]
    6      6   
description = "Request compression for smithy clients."
    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   
[features]
   18         -
http-body-0-4-x = ["dep:http-body-0-4", "dep:http-0-2", "aws-smithy-types/http-body-0-4-x"]
   19         -
http-body-1-x = ["dep:http-body-1-0", "dep:http-1-0", "dep:http-body-util", "aws-smithy-types/http-body-1-x"]
   20     18   
   21     19   
[dependencies]
   22     20   
bytes = "1.10.0"
   23     21   
flate2 = "1.0.30"
   24     22   
futures-util = "0.3"
   25     23   
pin-project-lite = "0.2.14"
   26     24   
tracing = "0.1.40"
   27     25   
   28     26   
[dependencies.aws-smithy-types]
   29     27   
path = "../aws-smithy-types"
   30         -
version = "1.3.6"
          28  +
version = "1.4.0"
   31     29   
   32     30   
[dependencies.aws-smithy-runtime-api]
   33     31   
path = "../aws-smithy-runtime-api"
   34         -
version = "1.10.0"
          32  +
version = "1.11.0"
   35     33   
   36         -
[dependencies.http-0-2]
          34  +
[dependencies.http-1x]
   37     35   
package = "http"
   38         -
version = "0.2.9"
   39         -
optional = true
          36  +
version = "1.3.1"
   40     37   
   41         -
[dependencies.http-1-0]
   42         -
package = "http"
   43         -
version = "1"
   44         -
optional = true
   45         -
   46         -
[dependencies.http-body-0-4]
   47         -
package = "http-body"
   48         -
version = "0.4.5"
   49         -
optional = true
   50         -
   51         -
[dependencies.http-body-1-0]
          38  +
[dependencies.http-body-1x]
   52     39   
package = "http-body"
   53         -
version = "1"
   54         -
optional = true
          40  +
version = "1.0.1"
   55     41   
   56     42   
[dependencies.http-body-util]
   57         -
version = "0.1.2"
   58         -
optional = true
          43  +
version = "0.1.3"
   59     44   
   60     45   
[dev-dependencies]
   61     46   
bytes-utils = "0.1.2"
   62     47   
pretty_assertions = "1.3"
   63     48   
   64     49   
[dev-dependencies.tokio]
   65     50   
version = "1.23.1"
   66     51   
features = ["macros", "rt"]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-compression/src/body.rs

@@ -9,9 +264,154 @@
   29     29   
        /// Given an [`SdkBody`] and a `Box<dyn CompressRequest>`, create a new `CompressedBody<SdkBody, CR>`.
   30     30   
        pub fn new(body: SdkBody, compress_request: CR) -> Self {
   31     31   
            Self {
   32     32   
                body,
   33     33   
                compress_request,
   34     34   
                is_end_stream: false,
   35     35   
            }
   36     36   
        }
   37     37   
    }
   38     38   
   39         -
    /// Support for the `http-body-0-4` and `http-0-2` crates.
   40         -
    #[cfg(feature = "http-body-0-4-x")]
   41         -
    pub mod http_body_0_4_x {
   42         -
        use super::CompressedBody;
   43         -
        use crate::http::http_body_0_4_x::CompressRequest;
   44         -
        use aws_smithy_runtime_api::box_error::BoxError;
   45         -
        use aws_smithy_types::body::SdkBody;
   46         -
        use http_0_2::HeaderMap;
   47         -
        use http_body_0_4::{Body, SizeHint};
   48         -
        use std::pin::Pin;
   49         -
        use std::task::{Context, Poll};
   50         -
   51         -
        impl Body for CompressedBody<SdkBody, Box<dyn CompressRequest>> {
   52         -
            type Data = bytes::Bytes;
   53         -
            type Error = aws_smithy_types::body::Error;
   54         -
   55         -
            fn poll_data(
   56         -
                self: Pin<&mut Self>,
   57         -
                cx: &mut Context<'_>,
   58         -
            ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
   59         -
                let this = self.project();
   60         -
                match this.body.poll_data(cx)? {
   61         -
                    Poll::Ready(Some(data)) => {
   62         -
                        let mut out = Vec::new();
   63         -
                        this.compress_request.compress_bytes(&data[..], &mut out)?;
   64         -
                        Poll::Ready(Some(Ok(out.into())))
   65         -
                    }
   66         -
                    Poll::Ready(None) => {
   67         -
                        *this.is_end_stream = true;
   68         -
                        Poll::Ready(None)
   69         -
                    }
   70         -
                    Poll::Pending => Poll::Pending,
   71         -
                }
   72         -
            }
   73         -
   74         -
            fn poll_trailers(
   75         -
                self: Pin<&mut Self>,
   76         -
                cx: &mut Context<'_>,
   77         -
            ) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
   78         -
                let this = self.project();
   79         -
                this.body.poll_trailers(cx)
   80         -
            }
   81         -
   82         -
            fn is_end_stream(&self) -> bool {
   83         -
                self.is_end_stream
   84         -
            }
   85         -
   86         -
            fn size_hint(&self) -> SizeHint {
   87         -
                // We can't return a hint because we don't know exactly how
   88         -
                // compression will affect the content length
   89         -
                SizeHint::default()
   90         -
            }
   91         -
        }
   92         -
   93         -
        impl CompressedBody<SdkBody, Box<dyn CompressRequest>> {
   94         -
            /// Consumes this `CompressedBody` and returns an [`SdkBody`] containing the compressed data.
   95         -
            ///
   96         -
            /// This *requires* that the inner `SdkBody` is in-memory (i.e. not streaming). Otherwise, an error is returned.
   97         -
            /// If compression fails, an error is returned.
   98         -
            pub fn into_compressed_sdk_body(mut self) -> Result<SdkBody, BoxError> {
   99         -
                let mut compressed_body = Vec::new();
  100         -
                let bytes = self.body.bytes().ok_or_else(|| "`into_compressed_sdk_body` requires that the inner body is 'in-memory', but it was streaming".to_string())?;
  101         -
  102         -
                self.compress_request
  103         -
                    .compress_bytes(bytes, &mut compressed_body)?;
  104         -
                Ok(SdkBody::from(compressed_body))
  105         -
            }
  106         -
        }
  107         -
    }
  108         -
  109     39   
    /// Support for the `http-body-1-0` and `http-1-0` crates.
  110         -
    #[cfg(feature = "http-body-1-x")]
  111     40   
    pub mod http_body_1_x {
  112     41   
        use crate::body::compress::CompressedBody;
  113         -
        use crate::http::http_body_1_x::CompressRequest;
          42  +
        use crate::http::CompressRequest;
          43  +
        use aws_smithy_runtime_api::box_error::BoxError;
  114     44   
        use aws_smithy_types::body::SdkBody;
  115         -
        use http_body_1_0::{Body, Frame, SizeHint};
          45  +
        use http_body_1x::{Body, Frame, SizeHint};
  116     46   
        use std::pin::Pin;
  117     47   
        use std::task::{ready, Context, Poll};
  118     48   
  119     49   
        impl Body for CompressedBody<SdkBody, Box<dyn CompressRequest>> {
  120     50   
            type Data = bytes::Bytes;
  121     51   
            type Error = aws_smithy_types::body::Error;
  122     52   
  123     53   
            fn poll_frame(
  124     54   
                mut self: Pin<&mut Self>,
  125     55   
                cx: &mut Context<'_>,
  126     56   
            ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
  127     57   
                let this = self.as_mut().project();
  128     58   
                Poll::Ready(match ready!(this.body.poll_frame(cx)) {
  129     59   
                    Some(Ok(f)) => {
  130     60   
                        if f.is_data() {
  131     61   
                            let d = f.into_data().expect("we checked for data first");
  132     62   
                            let mut out = Vec::new();
  133     63   
                            this.compress_request.compress_bytes(&d, &mut out)?;
  134     64   
                            Some(Ok(Frame::data(out.into())))
  135     65   
                        } else if f.is_trailers() {
  136     66   
                            // Trailers don't get compressed.
  137     67   
                            Some(Ok(f))
  138     68   
                        } else {
  139     69   
                            unreachable!("Frame is either data or trailers")
  140     70   
                        }
  141     71   
                    }
  142     72   
                    None => {
  143     73   
                        *this.is_end_stream = true;
  144     74   
                        None
  145     75   
                    }
  146     76   
                    other => other,
  147     77   
                })
  148     78   
            }
  149     79   
  150     80   
            fn is_end_stream(&self) -> bool {
  151     81   
                self.is_end_stream
  152     82   
            }
  153     83   
  154     84   
            fn size_hint(&self) -> SizeHint {
  155     85   
                // We can't return a hint because we don't know exactly how
  156     86   
                // compression will affect the content length
  157     87   
                SizeHint::default()
  158     88   
            }
  159     89   
        }
          90  +
        impl CompressedBody<SdkBody, Box<dyn CompressRequest>> {
          91  +
            /// Consumes this `CompressedBody` and returns an [`SdkBody`] containing the compressed data.
          92  +
            ///
          93  +
            /// This *requires* that the inner `SdkBody` is in-memory (i.e. not streaming). Otherwise, an error is returned.
          94  +
            /// If compression fails, an error is returned.
          95  +
            pub fn into_compressed_sdk_body(mut self) -> Result<SdkBody, BoxError> {
          96  +
                let mut compressed_body = Vec::new();
          97  +
                let bytes = self.body.bytes().ok_or_else(|| "`into_compressed_sdk_body` requires that the inner body is 'in-memory', but it was streaming".to_string())?;
          98  +
          99  +
                self.compress_request
         100  +
                    .compress_bytes(bytes, &mut compressed_body)?;
         101  +
                Ok(SdkBody::from(compressed_body))
         102  +
            }
         103  +
        }
  160    104   
    }
  161    105   
}
  162    106   
  163         -
#[cfg(any(feature = "http-body-0-4-x", feature = "http-body-1-x"))]
  164    107   
#[cfg(test)]
  165    108   
mod test {
  166    109   
    use crate::body::compress::CompressedBody;
  167    110   
    use crate::{CompressionAlgorithm, CompressionOptions};
  168    111   
    use aws_smithy_types::body::SdkBody;
  169    112   
    use bytes::Buf;
  170    113   
    use bytes_utils::SegmentedBuf;
  171    114   
    use std::io::Read;
  172    115   
    const UNCOMPRESSED_INPUT: &[u8] = b"hello world";
  173    116   
    const COMPRESSED_OUTPUT: &[u8] = &[
  174    117   
        31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 203, 72, 205, 201, 201, 87, 40, 207, 47, 202, 73, 1, 0,
  175    118   
        133, 17, 74, 13, 11, 0, 0, 0,
  176    119   
    ];
  177    120   
  178         -
    #[cfg(feature = "http-body-0-4-x")]
  179         -
    mod http_body_0_4_x {
  180         -
        use super::*;
  181         -
        use http_body_0_4::Body;
  182         -
  183         -
        #[tokio::test]
  184         -
        async fn test_body_is_compressed() {
  185         -
            let compression_options = CompressionOptions::default()
  186         -
                .with_min_compression_size_bytes(0)
  187         -
                .unwrap();
  188         -
            let compress_request =
  189         -
                CompressionAlgorithm::Gzip.into_impl_http_body_0_4_x(&compression_options);
  190         -
            let body = SdkBody::from(UNCOMPRESSED_INPUT);
  191         -
            let mut compressed_body = CompressedBody::new(body, compress_request);
  192         -
  193         -
            let mut output = SegmentedBuf::new();
  194         -
            while let Some(buf) = compressed_body.data().await {
  195         -
                output.push(buf.unwrap());
  196         -
            }
  197         -
  198         -
            let mut actual_output = Vec::new();
  199         -
            output
  200         -
                .reader()
  201         -
                .read_to_end(&mut actual_output)
  202         -
                .expect("Doesn't cause IO errors");
  203         -
            // Verify data is compressed as expected
  204         -
            assert_eq!(COMPRESSED_OUTPUT, actual_output);
  205         -
        }
  206         -
  207         -
        #[tokio::test]
  208         -
        async fn test_into_compressed_sdk_body() {
  209         -
            let compression_options = CompressionOptions::default()
  210         -
                .with_min_compression_size_bytes(0)
  211         -
                .unwrap();
  212         -
            let compress_request =
  213         -
                CompressionAlgorithm::Gzip.into_impl_http_body_0_4_x(&compression_options);
  214         -
            let body = SdkBody::from(UNCOMPRESSED_INPUT);
  215         -
            let compressed_sdk_body = CompressedBody::new(body, compress_request)
  216         -
                .into_compressed_sdk_body()
  217         -
                .unwrap();
  218         -
  219         -
            // Verify data is compressed as expected
  220         -
            assert_eq!(
  221         -
                COMPRESSED_OUTPUT,
  222         -
                compressed_sdk_body.bytes().expect("body is in-memory")
  223         -
            );
  224         -
        }
  225         -
    }
  226         -
  227         -
    #[cfg(feature = "http-body-1-x")]
  228         -
    mod http_body_1_x {
  229         -
        use super::*;
  230    121   
    use http_body_util::BodyExt;
  231    122   
  232    123   
    #[tokio::test]
  233    124   
    async fn test_body_is_compressed() {
  234    125   
        let compression_options = CompressionOptions::default()
  235    126   
            .with_min_compression_size_bytes(0)
  236    127   
            .unwrap();
  237    128   
        let compress_request =
  238    129   
            CompressionAlgorithm::Gzip.into_impl_http_body_1_x(&compression_options);
  239    130   
        let body = SdkBody::from(UNCOMPRESSED_INPUT);
  240    131   
        let mut compressed_body = CompressedBody::new(body, compress_request);
  241    132   
  242    133   
        let mut output = SegmentedBuf::new();
  243    134   
  244    135   
        loop {
  245    136   
            let data = match compressed_body.frame().await {
  246    137   
                Some(Ok(frame)) => frame.into_data(),
  247    138   
                Some(Err(e)) => panic!("Error: {}", e),
  248    139   
                // No more frames, break out of loop
  249    140   
                None => break,
  250    141   
            }
  251    142   
            .expect("frame is OK");
  252    143   
            output.push(data);
  253    144   
        }
  254    145   
  255    146   
        let mut actual_output = Vec::new();
  256    147   
        output
  257    148   
            .reader()
  258    149   
            .read_to_end(&mut actual_output)
  259    150   
            .expect("Doesn't cause IO errors");
  260    151   
        // Verify data is compressed as expected
  261    152   
        assert_eq!(COMPRESSED_OUTPUT, actual_output);
  262    153   
    }
  263         -
    }
  264    154   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-compression/src/gzip.rs

@@ -1,1 +80,65 @@
    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  +
use crate::http::CompressRequest;
    6      7   
use crate::{Compress, CompressionOptions};
    7      8   
use aws_smithy_runtime_api::box_error::BoxError;
    8      9   
use flate2::write::GzEncoder;
    9     10   
use std::io::prelude::*;
   10     11   
   11     12   
#[derive(Debug, Default, Clone, PartialEq, Eq)]
   12     13   
pub(crate) struct Gzip {
   13     14   
    compression: flate2::Compression,
   14     15   
}
   15     16   
   16     17   
impl Gzip {
   17     18   
    fn compress_bytes(&self, bytes: &[u8], writer: impl Write) -> Result<(), BoxError> {
   18     19   
        let mut encoder = GzEncoder::new(writer, self.compression);
   19     20   
        encoder.write_all(bytes)?;
   20     21   
        encoder.try_finish()?;
   21     22   
   22     23   
        Ok(())
   23     24   
    }
   24     25   
}
   25     26   
   26     27   
impl Compress for Gzip {
   27     28   
    fn compress_bytes(&mut self, bytes: &[u8], writer: &mut dyn Write) -> Result<(), BoxError> {
   28     29   
        Gzip::compress_bytes(self, bytes, writer)
   29     30   
    }
   30     31   
}
   31     32   
   32         -
#[cfg(feature = "http-body-0-4-x")]
   33         -
mod http_body_0_4_x {
   34         -
    use crate::http::http_body_0_4_x::CompressRequest;
   35         -
   36         -
    impl CompressRequest for super::Gzip {
   37         -
        fn header_value(&self) -> http_0_2::HeaderValue {
   38         -
            http_0_2::HeaderValue::from_static("gzip")
   39         -
        }
   40         -
    }
   41         -
}
   42         -
   43         -
#[cfg(feature = "http-body-1-x")]
   44         -
mod http_body_1_x {
   45         -
    use crate::http::http_body_1_x::CompressRequest;
   46         -
   47         -
    impl CompressRequest for super::Gzip {
   48         -
        fn header_value(&self) -> http_1_0::HeaderValue {
   49         -
            http_1_0::HeaderValue::from_static("gzip")
   50         -
        }
          33  +
impl CompressRequest for Gzip {
          34  +
    fn header_value(&self) -> http_1x::HeaderValue {
          35  +
        http_1x::HeaderValue::from_static("gzip")
   51     36   
    }
   52     37   
}
   53     38   
   54     39   
impl From<&CompressionOptions> for Gzip {
   55     40   
    fn from(options: &CompressionOptions) -> Self {
   56     41   
        Gzip {
   57     42   
            compression: flate2::Compression::new(options.level),
   58     43   
        }
   59     44   
    }
   60     45   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-compression/src/http.rs

@@ -1,1 +84,42 @@
    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   
//! Checksum support for HTTP requests and responses.
    7      7   
    8         -
/// Support for the `http-body-0-4` and `http-0-2` crates.
    9         -
#[cfg(feature = "http-body-0-4-x")]
   10         -
pub mod http_body_0_4_x {
   11         -
    use crate::Compress;
   12         -
    use http_0_2::header::{HeaderName, HeaderValue};
   13         -
   14         -
    /// Implementors of this trait can be used to compress HTTP requests.
   15         -
    pub trait CompressRequest: Compress + CloneCompressRequest {
   16         -
        /// Return the header name for the content-encoding header.
   17         -
        fn header_name(&self) -> HeaderName {
   18         -
            HeaderName::from_static("content-encoding")
   19         -
        }
   20         -
   21         -
        /// Return the header value for the content-encoding header.
   22         -
        fn header_value(&self) -> HeaderValue;
   23         -
    }
   24         -
   25         -
    /// Enables CompressRequest implementors to be cloned.
   26         -
    pub trait CloneCompressRequest {
   27         -
        /// Clone this request compressor.
   28         -
        fn clone_request_compressor(&self) -> Box<dyn CompressRequest>;
   29         -
    }
   30         -
   31         -
    impl<T> CloneCompressRequest for T
   32         -
    where
   33         -
        T: CompressRequest + Clone + 'static,
   34         -
    {
   35         -
        fn clone_request_compressor(&self) -> Box<dyn CompressRequest> {
   36         -
            Box::new(self.clone())
   37         -
        }
   38         -
    }
   39         -
   40         -
    impl Clone for Box<dyn CompressRequest> {
   41         -
        fn clone(&self) -> Self {
   42         -
            self.clone_request_compressor()
   43         -
        }
   44         -
    }
   45         -
}
   46         -
   47      8   
/// Support for the `http-body-1-0` and `http-1-0` crates.
   48         -
#[cfg(feature = "http-body-1-x")]
   49         -
pub mod http_body_1_x {
   50         -
    use crate::Compress;
   51         -
    use http_1_0::header::{HeaderName, HeaderValue};
           9  +
use crate::Compress;
          10  +
use http_1x::header::{HeaderName, HeaderValue};
   52     11   
   53         -
    /// Implementors of this trait can be used to compress HTTP requests.
   54         -
    pub trait CompressRequest: Compress + CloneCompressRequest {
          12  +
/// Implementors of this trait can be used to compress HTTP requests.
          13  +
pub trait CompressRequest: Compress + CloneCompressRequest {
   55     14   
    /// Return the header name for the content-encoding header.
   56     15   
    fn header_name(&self) -> HeaderName {
   57     16   
        HeaderName::from_static("content-encoding")
   58     17   
    }
   59     18   
   60     19   
    /// Return the header value for the content-encoding header.
   61     20   
    fn header_value(&self) -> HeaderValue;
   62         -
    }
          21  +
}
   63     22   
   64         -
    /// Enables CompressRequest implementors to be cloned.
   65         -
    pub trait CloneCompressRequest {
          23  +
/// Enables CompressRequest implementors to be cloned.
          24  +
pub trait CloneCompressRequest {
   66     25   
    /// Clone this request compressor.
   67     26   
    fn clone_request_compressor(&self) -> Box<dyn CompressRequest>;
   68         -
    }
          27  +
}
   69     28   
   70         -
    impl<T> CloneCompressRequest for T
   71         -
    where
          29  +
impl<T> CloneCompressRequest for T
          30  +
where
   72     31   
    T: CompressRequest + Clone + 'static,
   73         -
    {
          32  +
{
   74     33   
    fn clone_request_compressor(&self) -> Box<dyn CompressRequest> {
   75     34   
        Box::new(self.clone())
   76     35   
    }
   77         -
    }
          36  +
}
   78     37   
   79         -
    impl Clone for Box<dyn CompressRequest> {
          38  +
impl Clone for Box<dyn CompressRequest> {
   80     39   
    fn clone(&self) -> Self {
   81     40   
        self.clone_request_compressor()
   82     41   
    }
   83         -
    }
   84     42   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-compression/src/lib.rs

@@ -136,136 +212,200 @@
  156    156   
    fn from_str(compression_algorithm: &str) -> Result<Self, Self::Err> {
  157    157   
        if compression_algorithm.eq_ignore_ascii_case(GZIP_NAME) {
  158    158   
            Ok(Self::Gzip)
  159    159   
        } else {
  160    160   
            Err(format!("unknown compression algorithm `{compression_algorithm}`").into())
  161    161   
        }
  162    162   
    }
  163    163   
}
  164    164   
  165    165   
impl CompressionAlgorithm {
  166         -
    #[cfg(feature = "http-body-0-4-x")]
  167         -
    /// Return the `HttpChecksum` implementor for this algorithm.
  168         -
    pub fn into_impl_http_body_0_4_x(
  169         -
        self,
  170         -
        options: &CompressionOptions,
  171         -
    ) -> Box<dyn http::http_body_0_4_x::CompressRequest> {
  172         -
        match self {
  173         -
            Self::Gzip => Box::new(gzip::Gzip::from(options)),
  174         -
        }
  175         -
    }
  176         -
  177         -
    #[cfg(feature = "http-body-1-x")]
  178    166   
    /// Return the `HttpChecksum` implementor for this algorithm.
  179    167   
    pub fn into_impl_http_body_1_x(
  180    168   
        self,
  181    169   
        options: &CompressionOptions,
  182         -
    ) -> Box<dyn http::http_body_1_x::CompressRequest> {
         170  +
    ) -> Box<dyn http::CompressRequest> {
  183    171   
        match self {
  184    172   
            Self::Gzip => Box::new(gzip::Gzip::from(options)),
  185    173   
        }
  186    174   
    }
  187    175   
  188    176   
    /// Return the name of this algorithm in string form
  189    177   
    pub fn as_str(&self) -> &'static str {
  190    178   
        match self {
  191    179   
            Self::Gzip { .. } => GZIP_NAME,
  192    180   
        }