AWS SDK

AWS SDK

rev. c5ffb8777856626a5189f6a35b193fa88ecbb358 (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-json/src/codec/deserializer.rs

@@ -1039,1039 +1109,1125 @@
 1059   1059   
        let n = s.parse::<i64>().map_err(|e| SerdeError::InvalidInput {
 1060   1060   
            message: e.to_string(),
 1061   1061   
        })?;
 1062   1062   
        self.advance_by(len);
 1063   1063   
        Ok(n)
 1064   1064   
    }
 1065   1065   
 1066   1066   
    fn read_float_value(&mut self) -> Result<f64, SerdeError> {
 1067   1067   
        self.skip_whitespace();
 1068   1068   
        let rem = self.remaining();
 1069         -
        // Handle string-encoded special float values: "NaN", "Infinity", "-Infinity"
        1069  +
        // A string may only carry a non-finite value, which JSON cannot express as a number:
        1070  +
        // `"NaN"`, `"Infinity"`, `"-Infinity"`. A quoted finite number such as `"123"` is a
        1071  +
        // type mismatch; the restJson1 protocol tests
        1072  +
        // `RestJsonBodyFloatMalformedValueRejected_case0` and
        1073  +
        // `RestJsonBodyDoubleMalformedValueRejected_case0` require it to be rejected. This
        1074  +
        // mirrors the token-based parser (`expect_number_or_null`), which parses the string
        1075  +
        // with `aws_smithy_types::primitive::Parse` and then rejects finite values, so the
        1076  +
        // spellings Rust's `f64::from_str` accepts for non-finite values (e.g. `"nan"`) are
        1077  +
        // accepted here too.
 1070   1078   
        if rem.first() == Some(&b'"') {
 1071   1079   
            let s = self.read_string(&aws_smithy_schema::prelude::STRING)?;
 1072         -
            return match s.as_str() {
 1073         -
                "NaN" => Ok(f64::NAN),
 1074         -
                "Infinity" => Ok(f64::INFINITY),
 1075         -
                "-Infinity" => Ok(f64::NEG_INFINITY),
 1076         -
                _ => s.parse::<f64>().map_err(|e| SerdeError::InvalidInput {
        1080  +
            let value = match s.as_str() {
        1081  +
                "NaN" => f64::NAN,
        1082  +
                "Infinity" => f64::INFINITY,
        1083  +
                "-Infinity" => f64::NEG_INFINITY,
        1084  +
                other => other.parse::<f64>().map_err(|e| SerdeError::InvalidInput {
 1077   1085   
                    message: e.to_string(),
 1078         -
                }),
        1086  +
                })?,
 1079   1087   
            };
        1088  +
            if value.is_finite() {
        1089  +
                return Err(SerdeError::TypeMismatch {
        1090  +
                    message: format!(
        1091  +
                        "only `Infinity`, `-Infinity`, `NaN` can represent a float as a string but found `{s}`"
        1092  +
                    ),
        1093  +
                });
        1094  +
            }
        1095  +
            return Ok(value);
 1080   1096   
        }
 1081   1097   
        let mut len = 0;
 1082   1098   
        for &b in rem {
 1083   1099   
            if b.is_ascii_digit() || b == b'-' || b == b'+' || b == b'.' || b == b'e' || b == b'E' {
 1084   1100   
                len += 1;
 1085   1101   
            } else {
 1086   1102   
                break;
 1087   1103   
            }
 1088   1104   
        }
 1089   1105   
        if len == 0 {
@@ -2424,2440 +2454,2543 @@
 2444   2460   
        #[test]
 2445   2461   
        fn documents_check_separators() {
 2446   2462   
            assert!(deser(br#"{"a":[1,2],"b":{"c":true}}"#)
 2447   2463   
                .read_document(&INT)
 2448   2464   
                .is_ok());
 2449   2465   
            assert!(deser(br#"{"a":1,}"#).read_document(&INT).is_err());
 2450   2466   
            assert!(deser(br#"[1,,2]"#).read_document(&INT).is_err());
 2451   2467   
            assert!(deser(br#"{,"a":1}"#).read_document(&INT).is_err());
 2452   2468   
        }
 2453   2469   
    }
        2470  +
        2471  +
    mod float_strings {
        2472  +
        //! A float or double member may carry a string only for a non-finite value
        2473  +
        //! (`"NaN"`, `"Infinity"`, `"-Infinity"`). Smithy protocol tests
        2474  +
        //! `RestJsonBodyFloatMalformedValueRejected_case0` and
        2475  +
        //! `RestJsonBodyDoubleMalformedValueRejected_case0` (`"123"`) require quoted finite
        2476  +
        //! numbers to be rejected.
        2477  +
        use super::*;
        2478  +
        use aws_smithy_schema::{shape_id, ShapeType};
        2479  +
        2480  +
        static F: Schema =
        2481  +
            Schema::new_member(shape_id!("test", "Input", "f"), ShapeType::Float, "f", 0);
        2482  +
        static D: Schema =
        2483  +
            Schema::new_member(shape_id!("test", "Input", "d"), ShapeType::Double, "d", 1);
        2484  +
        static INPUT: Schema =
        2485  +
            Schema::new_struct(shape_id!("test", "Input"), ShapeType::Structure, &[&F, &D]);
        2486  +
        2487  +
        fn read(body: &[u8]) -> Result<(Option<f32>, Option<f64>), SerdeError> {
        2488  +
            let mut deser = JsonDeserializer::new(body, Arc::new(JsonCodecSettings::default()));
        2489  +
            let (mut f, mut d) = (None, None);
        2490  +
            deser.read_struct(&INPUT, &mut |m, x| {
        2491  +
                match m.member_index() {
        2492  +
                    Some(0) => f = Some(x.read_float(m)?),
        2493  +
                    Some(1) => d = Some(x.read_double(m)?),
        2494  +
                    _ => {}
        2495  +
                }
        2496  +
                Ok(())
        2497  +
            })?;
        2498  +
            Ok((f, d))
        2499  +
        }
        2500  +
        2501  +
        #[test]
        2502  +
        fn numbers_and_the_three_special_strings_are_read() {
        2503  +
            assert_eq!(
        2504  +
                read(br#"{"f": 1.5, "d": -2}"#).unwrap(),
        2505  +
                (Some(1.5), Some(-2.0))
        2506  +
            );
        2507  +
            let (f, d) = read(br#"{"f": "NaN", "d": "-Infinity"}"#).unwrap();
        2508  +
            assert!(f.unwrap().is_nan());
        2509  +
            assert_eq!(d, Some(f64::NEG_INFINITY));
        2510  +
            assert_eq!(
        2511  +
                read(br#"{"f": "Infinity"}"#).unwrap().0,
        2512  +
                Some(f32::INFINITY)
        2513  +
            );
        2514  +
        }
        2515  +
        2516  +
        #[test]
        2517  +
        fn quoted_finite_numbers_are_rejected() {
        2518  +
            assert!(read(br#"{"f": "123"}"#).is_err());
        2519  +
            assert!(read(br#"{"d": "123"}"#).is_err());
        2520  +
            assert!(read(br#"{"d": "1.5e3"}"#).is_err());
        2521  +
            assert!(read(br#"{"d": "-0"}"#).is_err());
        2522  +
        }
        2523  +
        2524  +
        #[test]
        2525  +
        fn non_numeric_strings_are_rejected() {
        2526  +
            assert!(read(br#"{"d": "abc"}"#).is_err());
        2527  +
            assert!(read(br#"{"d": ""}"#).is_err());
        2528  +
            assert!(read(br#"{"d": "1 2"}"#).is_err());
        2529  +
        }
        2530  +
        2531  +
        #[test]
        2532  +
        fn alternative_non_finite_spellings_match_the_legacy_parser() {
        2533  +
            // `aws_smithy_types::primitive::Parse` falls back to `f64::from_str`, which
        2534  +
            // accepts these case-insensitively; only finite results are rejected.
        2535  +
            assert!(read(br#"{"f": "nan"}"#).unwrap().0.unwrap().is_nan());
        2536  +
            assert_eq!(read(br#"{"d": "inf"}"#).unwrap().1, Some(f64::INFINITY));
        2537  +
            assert_eq!(
        2538  +
                read(br#"{"d": "-infinity"}"#).unwrap().1,
        2539  +
                Some(f64::NEG_INFINITY)
        2540  +
            );
        2541  +
        }
        2542  +
    }
 2454   2543   
}

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-json/src/protocol/aws_json_rpc.rs

@@ -194,194 +243,278 @@
  214    214   
            SdkBody::from(r#"{ "int": 10 }"#),
  215    215   
        );
  216    216   
        let mut deser = AwsJsonRpcProtocol::aws_json_1_0("TestService")
  217    217   
            .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
  218    218   
            .unwrap();
  219    219   
        deser
  220    220   
            .read_struct(&TEST_SCHEMA, &mut |_, _| Ok(()))
  221    221   
            .expect("well-formed body is accepted");
  222    222   
    }
  223    223   
         224  +
    /// A quoted number for a double member must be rejected through the real response path.
         225  +
    /// This is the body of Smithy protocol test `RestJsonBodyDoubleMalformedValueRejected_case0`.
         226  +
    #[test]
         227  +
    fn quoted_number_for_double_member_is_rejected() {
         228  +
        use aws_smithy_runtime_api::http::Response;
         229  +
        use aws_smithy_types::body::SdkBody;
         230  +
         231  +
        static DOUBLE: aws_smithy_schema::Schema = aws_smithy_schema::Schema::new_member(
         232  +
            shape_id!("test", "Output", "doubleInBody"),
         233  +
            ShapeType::Double,
         234  +
            "doubleInBody",
         235  +
            0,
         236  +
        );
         237  +
        static OUTPUT: aws_smithy_schema::Schema = aws_smithy_schema::Schema::new_struct(
         238  +
            shape_id!("test", "Output"),
         239  +
            ShapeType::Structure,
         240  +
            &[&DOUBLE],
         241  +
        );
         242  +
        let read = |body: &str| {
         243  +
            let response = Response::new(200u16.try_into().unwrap(), SdkBody::from(body));
         244  +
            let mut deser = AwsJsonRpcProtocol::aws_json_1_0("TestService")
         245  +
                .deserialize_response(&response, &OUTPUT, &ConfigBag::base())
         246  +
                .unwrap();
         247  +
            let mut value = None;
         248  +
            deser
         249  +
                .read_struct(&OUTPUT, &mut |member, d| {
         250  +
                    value = Some(d.read_double(member)?);
         251  +
                    Ok(())
         252  +
                })
         253  +
                .map(|()| value)
         254  +
        };
         255  +
        assert_eq!(read(r#"{ "doubleInBody" : 123 }"#).unwrap(), Some(123.0));
         256  +
        assert!(read(r#"{ "doubleInBody" : "123" }"#).is_err());
         257  +
    }
         258  +
  224    259   
    #[test]
  225    260   
    fn json_1_0_protocol_id() {
  226    261   
        assert_eq!(
  227    262   
            AwsJsonRpcProtocol::aws_json_1_0("Svc")
  228    263   
                .protocol_id()
  229    264   
                .as_str(),
  230    265   
            "aws.protocols#awsJson1_0"
  231    266   
        );
  232    267   
    }
  233    268