From 4afa8a3c8089b8c0cfe0100fec83f6412eac30e7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 11 Mar 2022 13:39:27 +0100 Subject: [PATCH] Revamp error system (#1233) This commit rewrites the server's error system so that it can render accurate protocol-specific HTTP responses adhering to the malformed requests protocol tests' expectations. It also makes 400+ of the tests relating to request deserialization pass. Previously, we were rendering the `SmithyRejection` type in our HTTP responses directly as we desired. However, AWS protocols' errors are not as granular: many different request deserialization failure causes are conflated and signaled to clients under a single `SerializationException` in the response. This commit introduces a new concept, the `RuntimeEror` type, which groups rejections and renders the responses that these protocols expect. Rejection types now become an internal detail of the framework that only serve for us maintainers to keep track and inventory all possible error causes in great detail. Extension types have also been renamed to better reflect their purpose. For more information about this patch, read the added documentation to the `rejection`, `extension`, and `runtime_error` modules. --- .../server/smithy/ServerRuntimeType.kt | 9 + .../ServerOperationHandlerGenerator.kt | 49 +- .../protocol/ServerProtocolTestGenerator.kt | 515 +----------------- .../protocols/ServerHttpProtocolGenerator.kt | 127 +++-- .../aws-smithy-http-server/Cargo.toml | 1 + .../aws-smithy-http-server/src/extension.rs | 111 ++-- .../aws-smithy-http-server/src/lib.rs | 14 +- .../aws-smithy-http-server/src/macros.rs | 169 +----- .../aws-smithy-http-server/src/protocols.rs | 32 +- .../aws-smithy-http-server/src/rejection.rs | 439 ++++++++------- .../src/runtime_error.rs | 106 ++++ 11 files changed, 563 insertions(+), 1009 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/runtime_error.rs diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index 07f0ac325..dbf3c1350 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -30,4 +30,13 @@ object ServerRuntimeType { fun serverOperationHandler(runtimeConfig: RuntimeConfig) = forInlineDependency(ServerInlineDependency.serverOperationHandler(runtimeConfig)) + + fun RuntimeError(runtimeConfig: RuntimeConfig) = + RuntimeType("RuntimeError", CargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::runtime_error") + + fun RequestRejection(runtimeConfig: RuntimeConfig) = + RuntimeType("RequestRejection", CargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::rejection") + + fun ResponseRejection(runtimeConfig: RuntimeConfig) = + RuntimeType("ResponseRejection", CargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::rejection") } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt index 4a52f8dee..1858ccaa4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asType -import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency @@ -21,6 +20,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.util.hasStreamingMember import software.amazon.smithy.rust.codegen.util.inputShape import software.amazon.smithy.rust.codegen.util.outputShape +import software.amazon.smithy.rust.codegen.util.toPascalCase /** * ServerOperationHandlerGenerator @@ -32,6 +32,7 @@ class ServerOperationHandlerGenerator( private val serverCrate = "aws_smithy_http_server" private val service = codegenContext.serviceShape private val model = codegenContext.model + private val protocol = codegenContext.protocol private val symbolProvider = codegenContext.symbolProvider private val operationNames = operations.map { symbolProvider.toSymbol(it).name } private val runtimeConfig = codegenContext.runtimeConfig @@ -43,7 +44,6 @@ class ServerOperationHandlerGenerator( "FuturesUtil" to ServerCargoDependency.FuturesUtil.asType(), "SmithyHttp" to CargoDependency.SmithyHttp(runtimeConfig).asType(), "SmithyHttpServer" to CargoDependency.SmithyHttpServer(runtimeConfig).asType(), - "SmithyRejection" to ServerHttpProtocolGenerator.smithyRejection(runtimeConfig), "Phantom" to ServerRuntimeType.Phantom, "ServerOperationHandler" to ServerRuntimeType.serverOperationHandler(runtimeConfig), "http" to RuntimeType.http, @@ -69,13 +69,6 @@ class ServerOperationHandlerGenerator( } else { "impl #{ServerOperationHandler}::Handler for Fun" } - val storeErrorInExtensions = """{ - let error = aws_smithy_http_server::ExtensionRejection::new(r.to_string()); - let mut response = r.into_response(); - response.extensions_mut().insert(error); - return response.map($serverCrate::body::boxed); - } - """.trimIndent() writer.rustBlockTemplate( """ ##[#{AsyncTrait}::async_trait] @@ -86,15 +79,28 @@ class ServerOperationHandlerGenerator( *codegenScope ) { val callImpl = if (state) { - """let state = match $serverCrate::Extension::::from_request(&mut req).await { - Ok(v) => v, - Err(r) => $storeErrorInExtensions + """ + let state = match $serverCrate::extension::extract_extension(&mut req).await { + Ok(v) => v, + Err(extension_not_found_rejection) => { + let extension = $serverCrate::extension::RuntimeErrorExtension::new(extension_not_found_rejection.to_string()); + let runtime_error = $serverCrate::runtime_error::RuntimeError { + protocol: #{SmithyHttpServer}::protocols::Protocol::${protocol.name.toPascalCase()}, + kind: extension_not_found_rejection.into(), + }; + let mut response = runtime_error.into_response(); + response.extensions_mut().insert(extension); + return response.map($serverCrate::body::boxed); + } }; let input_inner = input_wrapper.into(); - let output_inner = self(input_inner, state).await;""" + let output_inner = self(input_inner, state).await; + """.trimIndent() } else { - """let input_inner = input_wrapper.into(); - let output_inner = self(input_inner).await;""" + """ + let input_inner = input_wrapper.into(); + let output_inner = self(input_inner).await; + """.trimIndent() } rustTemplate( """ @@ -105,11 +111,17 @@ class ServerOperationHandlerGenerator( use #{AxumCore}::response::IntoResponse; let input_wrapper = match $inputWrapperName::from_request(&mut req).await { Ok(v) => v, - Err(r) => $storeErrorInExtensions + Err(runtime_error) => { + return runtime_error.into_response().map($serverCrate::body::boxed); + } }; $callImpl let output_wrapper: $outputWrapperName = output_inner.into(); - output_wrapper.into_response().map(#{SmithyHttpServer}::body::boxed) + let mut response = output_wrapper.into_response(); + response.extensions_mut().insert( + #{SmithyHttpServer}::extension::OperationExtension::new("${operation.id.namespace}", "$operationName") + ); + response.map(#{SmithyHttpServer}::body::boxed) } """, *codegenScope @@ -145,8 +157,7 @@ class ServerOperationHandlerGenerator( Fut: std::future::Future + Send, B: $serverCrate::body::HttpBody + Send + 'static, $streamingBodyTraitBounds B::Data: Send, - B::Error: Into<$serverCrate::BoxError>, - $serverCrate::rejection::SmithyRejection: From<::Error> + $serverCrate::rejection::RequestRejection: From<::Error> """.trimIndent() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index f78f75d1e..9437ff2f0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -455,7 +455,12 @@ class ServerProtocolTestGenerator( checkHeaders(rustWriter, "&http_response.headers()", testCase.headers) checkForbidHeaders(rustWriter, "&http_response.headers()", testCase.forbidHeaders) checkRequiredHeaders(rustWriter, "&http_response.headers()", testCase.requireHeaders) - checkHttpResponseExtensions(rustWriter) + + // We can't check that the `OperationExtension` is set in the response, because it is set in the implementation + // of the operation `Handler` trait, a code path that does not get exercised when we don't have a request to + // invoke it with (like in the case of an `httpResponseTest` test case). + // checkHttpOperationExtension(rustWriter) + // If no request body is defined, then no assertions are made about the body of the message. if (testCase.body.isPresent) { checkBody(rustWriter, testCase.body.get(), testCase.bodyMediaType.orNull()) @@ -465,7 +470,14 @@ class ServerProtocolTestGenerator( private fun checkResponse(rustWriter: RustWriter, testCase: HttpMalformedResponseDefinition) { checkStatusCode(rustWriter, testCase.code) checkHeaders(rustWriter, "&http_response.headers()", testCase.headers) - checkHttpResponseExtensions(rustWriter) + + // We can't check that the `OperationExtension` is set in the response, because it is set in the implementation + // of the operation `Handler` trait, a code path that does not get exercised by `httpRequestTest` test cases. + // TODO(https://github.com/awslabs/smithy-rs/issues/1212): We could change test case generation so as to `call()` + // the operation handler trait implementation instead of directly calling `from_request()`, or we could run an + // actual service. + // checkHttpOperationExtension(rustWriter) + // If no request body is defined, then no assertions are made about the body of the message. if (testCase.body.isEmpty) return @@ -512,18 +524,18 @@ class ServerProtocolTestGenerator( } } - private fun checkHttpResponseExtensions(rustWriter: RustWriter) { + private fun checkHttpOperationExtension(rustWriter: RustWriter) { rustWriter.rustTemplate( """ - let response_extensions = http_response.extensions() - .get::<#{SmithyHttpServer}::ResponseExtensions>() - .expect("extension `ResponseExtensions` not found"); + let operation_extension = http_response.extensions() + .get::<#{SmithyHttpServer}::extension::OperationExtension>() + .expect("extension `OperationExtension` not found"); """.trimIndent(), *codegenScope ) rustWriter.writeWithNoFormatting( """ - assert_eq!(response_extensions.operation(), format!("{}#{}", "${operationShape.id.namespace}", "${operationSymbol.name}")); + assert_eq!(operation_extension.operation(), format!("{}#{}", "${operationShape.id.namespace}", "${operationSymbol.name}")); """.trimIndent() ) } @@ -651,532 +663,51 @@ class ServerProtocolTestGenerator( FailingTest(RestJson, "RestJsonWithBodyExpectsApplicationJsonAccept", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsImpliedAccept", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsModeledAccept", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case0", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedBlobInvalidBase64_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case15", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case16", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case17", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case18", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case19", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case20", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case21", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanBadLiteral_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case15", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case16", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case17", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case18", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case19", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case20", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case21", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case22", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case23", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyBooleanStringCoercion_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case15", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case16", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case17", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case18", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case19", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case20", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case21", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderBooleanStringCoercion_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case15", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case16", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case17", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case18", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case19", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case20", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case21", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathBooleanStringCoercion_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case15", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case16", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case17", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case18", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case19", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case20", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case21", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryBooleanStringCoercion_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case5", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case7", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case9", TestType.MalformedRequest), + FailingTest(RestJson, "RestJsonBodyByteMalformedValueRejected_case10", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteUnderflowOverflow_case0", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteUnderflowOverflow_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteUnderflowOverflow_case2", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyByteUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyByteUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderByteUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathByteUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryByteUnderflowOverflow_case4", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithBodyExpectsApplicationJsonContentType", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsImpliedContentType", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsModeledContentType", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithoutBodyExpectsEmptyContentType", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyDoubleMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderDoubleMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderDoubleMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderDoubleMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathDoubleMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathDoubleMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathDoubleMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryDoubleMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryDoubleMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryDoubleMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyFloatMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderFloatMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderFloatMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderFloatMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathFloatMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathFloatMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathFloatMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryFloatMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryFloatMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryFloatMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case5", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case7", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case9", TestType.MalformedRequest), + FailingTest(RestJson, "RestJsonBodyIntegerMalformedValueRejected_case10", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyIntegerUnderflowOverflow_case0", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyIntegerUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyIntegerUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderIntegerUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathIntegerUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryIntegerUnderflowOverflow_case2", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyMalformedListNullItem", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedListUnclosed", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyMalformedMapNullKey", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyMalformedMapNullValue", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case5", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case7", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyLongUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderLongUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathLongUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryLongUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonInvalidJsonBody_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonTechnicallyValidJsonBody_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonTechnicallyValidJsonBody_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonTechnicallyValidJsonBody_case2", TestType.MalformedRequest), + FailingTest(RestJson, "RestJsonBodyLongMalformedValueRejected_case10", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonMalformedSetDuplicateItems", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonMalformedSetNullItem", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case5", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case7", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case9", TestType.MalformedRequest), + FailingTest(RestJson, "RestJsonBodyShortMalformedValueRejected_case10", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortUnderflowOverflow_case0", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortUnderflowOverflow_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortUnderflowOverflow_case2", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyShortUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyShortUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderShortUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathShortUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortMalformedValueRejected_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortUnderflowOverflow_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortUnderflowOverflow_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortUnderflowOverflow_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortUnderflowOverflow_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryShortUnderflowOverflow_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderMalformedStringInvalidBase64MediaType_case0", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonHeaderMalformedStringInvalidBase64MediaType_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderMalformedStringInvalidBase64MediaType_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderMalformedStringInvalidBase64MediaType_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsDifferent8601Formats_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsHttpDate_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyTimestampDateTimeRejectsUTCOffsets_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case4", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case6", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case8", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsMalformedEpochSeconds_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsStringifiedEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampDefaultRejectsStringifiedEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampHttpDateRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampHttpDateRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampHttpDateRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampHttpDateRejectsEpoch_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonBodyTimestampHttpDateRejectsEpoch_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case12", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsDifferent8601Formats_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDateTimeRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDefaultRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDefaultRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDefaultRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDefaultRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampDefaultRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonHeaderTimestampEpochRejectsMalformedValues_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case12", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsDifferent8601Formats_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsHttpDate_case1", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonPathTimestampDefaultRejectsUTCOffsets", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampEpochRejectsMalformedValues_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampHttpDateRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampHttpDateRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampHttpDateRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampHttpDateRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonPathTimestampHttpDateRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case10", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case11", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case12", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case13", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case14", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case7", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case8", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsDifferent8601Formats_case9", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampDefaultRejectsUTCOffsets", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsHttpDate_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsHttpDate_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case3", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case4", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case5", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampEpochRejectsMalformedValues_case6", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampHttpDateRejectsDateTime_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampHttpDateRejectsDateTime_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampHttpDateRejectsDateTime_case2", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampHttpDateRejectsEpochSeconds_case0", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonQueryTimestampHttpDateRejectsEpochSeconds_case1", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonMalformedUnionKnownAndUnknownFieldsSet", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonMalformedUnionMultipleFieldsSet", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonMalformedUnionNoFieldsSet", TestType.MalformedRequest), - FailingTest(RestJson, "RestJsonMalformedUnionValueIsArray", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case1", TestType.MalformedRequest), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt index 0b57bfa1f..6b09169b1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpProtocolGenerator.kt @@ -37,7 +37,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerRequestBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator import software.amazon.smithy.rust.codegen.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol @@ -62,6 +61,7 @@ import software.amazon.smithy.rust.codegen.util.hasStreamingMember import software.amazon.smithy.rust.codegen.util.inputShape import software.amazon.smithy.rust.codegen.util.isStreaming import software.amazon.smithy.rust.codegen.util.outputShape +import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase import java.util.logging.Logger @@ -89,12 +89,6 @@ class ServerHttpProtocolGenerator( companion object { const val OPERATION_INPUT_WRAPPER_SUFFIX = "OperationInputWrapper" const val OPERATION_OUTPUT_WRAPPER_SUFFIX = "OperationOutputWrapper" - - fun smithyRejection(runtimeConfig: RuntimeConfig) = RuntimeType( - "SmithyRejection", - dependency = CargoDependency.SmithyHttpServer(runtimeConfig), - namespace = "aws_smithy_http_server::rejection" - ) } } @@ -129,7 +123,9 @@ private class ServerHttpProtocolImplGenerator( "SerdeUrlEncoded" to ServerCargoDependency.SerdeUrlEncoded.asType(), "SmithyHttp" to CargoDependency.SmithyHttp(runtimeConfig).asType(), "SmithyHttpServer" to CargoDependency.SmithyHttpServer(runtimeConfig).asType(), - "SmithyRejection" to ServerHttpProtocolGenerator.smithyRejection(runtimeConfig), + "RuntimeError" to ServerRuntimeType.RuntimeError(runtimeConfig), + "RequestRejection" to ServerRuntimeType.RequestRejection(runtimeConfig), + "ResponseRejection" to ServerRuntimeType.ResponseRejection(runtimeConfig), "http" to RuntimeType.http ) @@ -167,12 +163,19 @@ private class ServerHttpProtocolImplGenerator( where B: #{SmithyHttpServer}::body::HttpBody + Send, ${streamingBodyTraitBounds(operationShape)} B::Data: Send, - B::Error: Into<#{SmithyHttpServer}::BoxError>, - #{SmithyRejection}: From<::Error> + #{RequestRejection} : From<::Error> { - type Rejection = #{SmithyRejection}; + type Rejection = #{RuntimeError}; async fn from_request(req: &mut #{AxumCore}::extract::RequestParts) -> Result { - Ok($inputName(#{parse_request}(req).await?)) + #{parse_request}(req) + .await + .map($inputName) + .map_err( + |err| #{RuntimeError} { + protocol: #{SmithyHttpServer}::protocols::Protocol::${codegenContext.protocol.name.toPascalCase()}, + kind: err.into() + } + ) } } """.trimIndent(), @@ -185,36 +188,39 @@ private class ServerHttpProtocolImplGenerator( val outputName = "${operationName}${ServerHttpProtocolGenerator.OPERATION_OUTPUT_WRAPPER_SUFFIX}" val errorSymbol = operationShape.errorSymbol(symbolProvider) - val httpExtensions = setHttpExtensions(operationShape) if (operationShape.errors.isNotEmpty()) { // The output of fallible operations is a `Result` which we convert into an // isomorphic `enum` type we control that can in turn be converted into a response. val intoResponseImpl = """ - let mut response = match self { + match self { Self::Output(o) => { match #{serialize_response}(o) { Ok(response) => response, Err(e) => { - e.into_response() + #{RuntimeError} { + protocol: #{SmithyHttpServer}::protocols::Protocol::${codegenContext.protocol.name.toPascalCase()}, + kind: e.into() + }.into_response() } } }, Self::Error(err) => { match #{serialize_error}(&err) { Ok(mut response) => { - response.extensions_mut().insert(aws_smithy_http_server::ExtensionModeledError::new(err.name())); + response.extensions_mut().insert(aws_smithy_http_server::extension::ModeledErrorExtension::new(err.name())); response }, Err(e) => { - e.into_response() + #{RuntimeError} { + protocol: #{SmithyHttpServer}::protocols::Protocol::${codegenContext.protocol.name.toPascalCase()}, + kind: e.into() + }.into_response() } } } - }; - $httpExtensions - response + } """.trimIndent() rustTemplate( @@ -241,12 +247,15 @@ private class ServerHttpProtocolImplGenerator( // a "wrapper" unit `struct` type we control that can in turn be converted into a response. val intoResponseImpl = """ - let mut response = match #{serialize_response}(self.0) { + match #{serialize_response}(self.0) { Ok(response) => response, - Err(e) => e.into_response() - }; - $httpExtensions - response + Err(e) => { + #{RuntimeError} { + protocol: #{SmithyHttpServer}::protocols::Protocol::${codegenContext.protocol.name.toPascalCase()}, + kind: e.into() + }.into_response() + } + } """.trimIndent() rustTemplate( @@ -307,17 +316,6 @@ private class ServerHttpProtocolImplGenerator( ) } - /* - * Set `http::Extensions` for the current request. They can be used later for things like metrics, logging... - */ - private fun setHttpExtensions(operationShape: OperationShape): String { - val namespace = operationShape.id.namespace - val operationName = symbolProvider.toSymbol(operationShape).name - return """ - response.extensions_mut().insert(#{SmithyHttpServer}::ResponseExtensions::new("$namespace", "$operationName")); - """.trimIndent() - } - private fun serverParseRequest(operationShape: OperationShape): RuntimeType { val fnName = "parse_${operationShape.id.name.toSnakeCase()}_request" val inputShape = operationShape.inputShape(model) @@ -325,19 +323,19 @@ private class ServerHttpProtocolImplGenerator( return RuntimeType.forInlineFun(fnName, operationDeserModule) { Attribute.Custom("allow(clippy::unnecessary_wraps)").render(it) + // The last conversion trait bound is needed by the `hyper::body::to_bytes(body).await?` call. it.rustBlockTemplate( """ pub async fn $fnName( ##[allow(unused_variables)] request: &mut #{AxumCore}::extract::RequestParts ) -> std::result::Result< #{I}, - #{SmithyRejection} + #{RequestRejection} > where B: #{SmithyHttpServer}::body::HttpBody + Send, ${streamingBodyTraitBounds(operationShape)} B::Data: Send, - B::Error: Into<#{SmithyHttpServer}::BoxError>, - #{SmithyRejection}: From<::Error> + #{RequestRejection}: From<::Error> """.trimIndent(), *codegenScope, "I" to inputSymbol, @@ -370,7 +368,7 @@ private class ServerHttpProtocolImplGenerator( ##[allow(unused_variables)] output: #{O} ) -> std::result::Result< #{AxumCore}::response::Response, - #{SmithyRejection} + #{ResponseRejection} > """.trimIndent(), *codegenScope, @@ -392,7 +390,7 @@ private class ServerHttpProtocolImplGenerator( return RuntimeType.forInlineFun(fnName, operationSerModule) { Attribute.Custom("allow(clippy::unnecessary_wraps)").render(it) it.rustBlockTemplate( - "pub fn $fnName(error: &#{E}) -> std::result::Result<#{AxumCore}::response::Response, #{SmithyRejection}>", + "pub fn $fnName(error: &#{E}) -> std::result::Result<#{AxumCore}::response::Response, #{ResponseRejection}>", *codegenScope, "E" to errorSymbol ) { @@ -554,12 +552,15 @@ private class ServerHttpProtocolImplGenerator( check(binding.location == HttpLocation.RESPONSE_CODE) return writable { val memberName = symbolProvider.toMemberName(binding.member) + // TODO(https://github.com/awslabs/smithy-rs/issues/1229): This code is problematic for two reasons: + // 1. We're not falling back to the `http` trait if no `output.$memberName` is `None`. + // 2. It only works when `output.$memberName` is of type `Option`. rustTemplate( """ let status = output.$memberName - .ok_or_else(|| #{SmithyHttpServer}::rejection::Serialize::from("$memberName missing or empty"))?; + .ok_or(#{ResponseRejection}::MissingHttpStatusCode)?; let http_status: u16 = std::convert::TryFrom::::try_from(status) - .map_err(|_| #{SmithyHttpServer}::rejection::Serialize::from("invalid status code"))?; + .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; """.trimIndent(), *codegenScope, ) @@ -581,7 +582,7 @@ private class ServerHttpProtocolImplGenerator( val contentTypeCheck = getContentTypeCheck() rustTemplate( """ - let body = request.take_body().ok_or(#{SmithyHttpServer}::rejection::BodyAlreadyExtracted)?; + let body = request.take_body().ok_or(#{RequestRejection}::BodyAlreadyExtracted)?; let bytes = #{Hyper}::body::to_bytes(body).await?; if !bytes.is_empty() { #{SmithyHttpServer}::protocols::$contentTypeCheck(request)?; @@ -616,7 +617,6 @@ private class ServerHttpProtocolImplGenerator( httpBindingGenerator: ServerRequestBindingGenerator, structuredDataParser: StructuredDataParserGenerator, ): Writable? { - val errorSymbol = getDeserializeErrorSymbol(binding) return when (binding.location) { HttpLocation.HEADER -> writable { serverRenderHeaderParser(this, binding, operationShape) } HttpLocation.PREFIX_HEADERS -> writable { serverRenderPrefixHeadersParser(this, binding, operationShape) } @@ -626,7 +626,7 @@ private class ServerHttpProtocolImplGenerator( rustTemplate( """ { - let body = request.take_body().ok_or(#{SmithyHttpServer}::rejection::BodyAlreadyExtracted)?; + let body = request.take_body().ok_or(#{RequestRejection}::BodyAlreadyExtracted)?; Some(body.into()) } """.trimIndent(), @@ -637,6 +637,7 @@ private class ServerHttpProtocolImplGenerator( val structureShapeHandler: RustWriter.(String) -> Unit = { body -> rust("#T($body)", structuredDataParser.payloadParser(binding.member)) } + val errorSymbol = getDeserializePayloadErrorSymbol(binding) val deserializer = httpBindingGenerator.generateDeserializePayloadFn( operationShape, binding, @@ -647,7 +648,7 @@ private class ServerHttpProtocolImplGenerator( rustTemplate( """ { - let body = request.take_body().ok_or(#{SmithyHttpServer}::rejection::BodyAlreadyExtracted)?; + let body = request.take_body().ok_or(#{RequestRejection}::BodyAlreadyExtracted)?; let bytes = #{Hyper}::body::to_bytes(body).await?; #{Deserializer}(&bytes)? } @@ -719,11 +720,10 @@ private class ServerHttpProtocolImplGenerator( if (greedyLabelIndex >= 0 && greedyLabelIndex + 1 < httpTrait.uri.segments.size) { rustTemplate( """ - if !input_string.ends_with(${restAfterGreedyLabel.dq()}) { - return std::result::Result::Err(#{SmithyRejection}::Deserialize( - aws_smithy_http_server::rejection::Deserialize::from_err(format!("Postfix not found: {}", ${restAfterGreedyLabel.dq()})))); + if !input_string.ends_with("$restAfterGreedyLabel") { + return Err(#{RequestRejection}::UriPatternGreedyLabelPostfixNotFound); } - let input_string = &input_string[..(input_string.len() - ${restAfterGreedyLabel.dq()}.len())]; + let input_string = &input_string[..(input_string.len() - "$restAfterGreedyLabel".len())]; """.trimIndent(), *codegenScope ) @@ -941,7 +941,7 @@ private class ServerHttpProtocolImplGenerator( val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) writer.rustTemplate( """ - #{deserializer}(request.headers().ok_or(#{SmithyHttpServer}::rejection::HeadersAlreadyExtracted)?)? + #{deserializer}(request.headers().ok_or(#{RequestRejection}::HeadersAlreadyExtracted)?)? """.trimIndent(), "deserializer" to deserializer, *codegenScope @@ -960,13 +960,15 @@ private class ServerHttpProtocolImplGenerator( val deserializer = httpBindingGenerator.generateDeserializePrefixHeadersFn(binding) writer.rustTemplate( """ - #{deserializer}(request.headers().ok_or(#{SmithyHttpServer}::rejection::HeadersAlreadyExtracted)?)? + #{deserializer}(request.headers().ok_or(#{RequestRejection}::HeadersAlreadyExtracted)?)? """.trimIndent(), "deserializer" to deserializer, *codegenScope ) } + // TODO(https://github.com/awslabs/smithy-rs/issues/1231): If this function was called to parse a query string + // key value pair, we don't need to percent-decode it _again_. private fun generateParsePercentEncodedStrFn(binding: HttpBindingDescriptor): RuntimeType { // HTTP bindings we support that contain percent-encoded data. check(binding.location == HttpLocation.LABEL || binding.location == HttpLocation.QUERY) @@ -984,7 +986,7 @@ private class ServerHttpProtocolImplGenerator( val fnName = generateParseStrFnName(binding) return RuntimeType.forInlineFun(fnName, operationDeserModule) { writer -> writer.rustBlockTemplate( - "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{SmithyRejection}>", + "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{RequestRejection}>", *codegenScope, "O" to output, ) { @@ -1015,7 +1017,7 @@ private class ServerHttpProtocolImplGenerator( val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat) return RuntimeType.forInlineFun(fnName, operationDeserModule) { writer -> writer.rustBlockTemplate( - "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{SmithyRejection}>", + "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{RequestRejection}>", *codegenScope, "O" to output, ) { @@ -1032,13 +1034,14 @@ private class ServerHttpProtocolImplGenerator( } } - // TODO These functions can be replaced with the ones in https://docs.rs/aws-smithy-types/latest/aws_smithy_types/primitive/trait.Parse.html + // Function to parse a string as the data type generated for boolean, byte, short, integer, long, float, or double shapes. + // TODO(https://github.com/awslabs/smithy-rs/issues/1232): This function can be replaced by https://docs.rs/aws-smithy-types/latest/aws_smithy_types/primitive/trait.Parse.html private fun generateParseStrAsPrimitiveFn(binding: HttpBindingDescriptor): RuntimeType { val output = symbolProvider.toSymbol(binding.member) val fnName = generateParseStrFnName(binding) return RuntimeType.forInlineFun(fnName, operationDeserModule) { writer -> writer.rustBlockTemplate( - "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{SmithyRejection}>", + "pub fn $fnName(value: &str) -> std::result::Result<#{O}, #{RequestRejection}>", *codegenScope, "O" to output, ) { @@ -1073,9 +1076,15 @@ private class ServerHttpProtocolImplGenerator( } } - private fun getDeserializeErrorSymbol(binding: HttpBindingDescriptor): RuntimeType { + /** + * Returns the error type of the function that deserializes a non-streaming HTTP payload (a byte slab) into the + * shape targeted by the `httpPayload` trait. + */ + private fun getDeserializePayloadErrorSymbol(binding: HttpBindingDescriptor): RuntimeType { + check(binding.location == HttpLocation.PAYLOAD) + if (model.expectShape(binding.member.target) is StringShape) { - return CargoDependency.SmithyHttpServer(runtimeConfig).asType().member("rejection").member("SmithyRejection") + return ServerRuntimeType.RequestRejection(runtimeConfig) } when (codegenContext.protocol) { RestJson1Trait.ID -> { diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 5afeaa1e4..284cabc8a 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -32,6 +32,7 @@ nom = "7" pin-project-lite = "0.2" regex = "1.0" serde_urlencoded = "0.7" +strum_macros = "0.24" thiserror = "1" tokio = { version = "1.0", features = ["full"] } tower = { version = "0.4.11", features = ["util", "make"], default-features = false } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 8721674c9..0a490e6b7 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -32,24 +32,39 @@ * DEALINGS IN THE SOFTWARE. */ -//! Extension extraction to share state across handlers. - -use super::rejection::{ExtensionHandlingRejection, ExtensionsAlreadyExtracted, MissingExtension}; -use async_trait::async_trait; -use axum_core::extract::{FromRequest, RequestParts}; +//! Extension types. +//! +//! Extension types are types that are stored in and extracted from _both_ requests and +//! responses. +//! +//! There is only one _generic_ extension type _for requests_, [`Extension`]. +//! +//! On the other hand, the server SDK uses multiple concrete extension types for responses in order +//! to store a variety of information, like the operation that was executed, the operation error +//! that got returned, or the runtime error that happened, among others. The information stored in +//! these types may be useful to [`tower::Layer`]s that post-process the response: for instance, a +//! particular metrics layer implementation might want to emit metrics about the number of times an +//! an operation got executed. +//! +//! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html + +use axum_core::extract::RequestParts; use std::ops::Deref; -/// Extension type used to store information in HTTP responses. +/// Extension type used to store information about Smithy operations in HTTP responses. +/// This extension type is set when it has been correctly determined that the request should be +/// routed to a particular operation. The operation handler might not even get invoked because the +/// request fails to deserialize into the modeled operation input. #[derive(Debug, Clone)] -pub struct ResponseExtensions { +pub struct OperationExtension { /// Smithy model namespace. namespace: &'static str, /// Smithy operation name. operation_name: &'static str, } -impl ResponseExtensions { - /// Creates a new `ResponseExtensions`. +impl OperationExtension { + /// Creates a new `OperationExtension`. pub fn new(namespace: &'static str, operation_name: &'static str) -> Self { Self { namespace, @@ -63,25 +78,26 @@ impl ResponseExtensions { } } -/// Extension type used to store the type of user defined error returned by an operation. +/// Extension type used to store the type of user-modeled error returned by an operation handler. /// These are modeled errors, defined in the Smithy model. #[derive(Debug, Clone)] -pub struct ExtensionModeledError(&'static str); -impl_extension_new_and_deref!(ExtensionModeledError); +pub struct ModeledErrorExtension(&'static str); +impl_extension_new_and_deref!(ModeledErrorExtension); -/// Extension type used to store the type of framework error caught during execution. -/// These are unmodeled error, or rejection, defined in the runtime crates. +/// Extension type used to store the _name_ of the [`crate::runtime_error::RuntimeError`] that +/// occurred during request handling (see [`crate::runtime_error::RuntimeErrorKind::name`]). +/// These are _unmodeled_ errors; the operation handler was not invoked. #[derive(Debug, Clone)] -pub struct ExtensionRejection(String); +pub struct RuntimeErrorExtension(String); -impl ExtensionRejection { - /// Returns a new `ExtensionRejection`. - pub fn new(value: String) -> ExtensionRejection { - ExtensionRejection(value) +impl RuntimeErrorExtension { + /// Creates a new `RuntimeErrorExtension`. + pub fn new(value: String) -> RuntimeErrorExtension { + RuntimeErrorExtension(value) } } -impl Deref for ExtensionRejection { +impl Deref for RuntimeErrorExtension { type Target = String; fn deref(&self) -> &Self::Target { @@ -89,7 +105,7 @@ impl Deref for ExtensionRejection { } } -/// Extractor that gets a value from [request extensions]. +/// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. /// @@ -97,34 +113,9 @@ impl Deref for ExtensionRejection { /// Server Error` response. /// /// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Extension(pub T); -#[async_trait] -impl FromRequest for Extension -where - T: Clone + Send + Sync + 'static, - B: Send, -{ - type Rejection = ExtensionHandlingRejection; - - async fn from_request(req: &mut RequestParts) -> Result { - let value = req - .extensions() - .ok_or(ExtensionsAlreadyExtracted)? - .get::() - .ok_or_else(|| { - MissingExtension::from_err(format!( - "Extension of type `{}` was not found. Perhaps you forgot to add it?", - std::any::type_name::() - )) - }) - .map(|x| x.clone())?; - - Ok(Extension(value)) - } -} - impl Deref for Extension { type Target = T; @@ -132,3 +123,29 @@ impl Deref for Extension { &self.0 } } + +/// Extract an [`Extension`] from a request. +/// This is essentially the implementation of `FromRequest` for `Extension`, but with a +/// protocol-agnostic rejection type. The actual code-generated implementation simply delegates to +/// this function and converts the rejection type into a [`crate::runtime_error::RuntimeError`]. +pub async fn extract_extension( + req: &mut RequestParts, +) -> Result, crate::rejection::RequestExtensionNotFoundRejection> +where + T: Clone + Send + Sync + 'static, + B: Send, +{ + let value = req + .extensions() + .ok_or(crate::rejection::RequestExtensionNotFoundRejection::ExtensionsAlreadyExtracted)? + .get::() + .ok_or_else(|| { + crate::rejection::RequestExtensionNotFoundRejection::MissingExtension(format!( + "Extension of type `{}` was not found. Perhaps you forgot to add it?", + std::any::type_name::() + )) + }) + .map(|x| x.clone())?; + + Ok(Extension(value)) +} diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 12b103238..31960d7cd 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -11,25 +11,27 @@ pub(crate) mod macros; pub mod body; -pub mod error; -mod extension; +pub(crate) mod error; +pub mod extension; pub mod routing; #[doc(hidden)] pub mod protocols; +#[doc(hidden)] pub mod rejection; +#[doc(hidden)] +pub mod runtime_error; #[doc(inline)] -pub use self::error::Error; -#[doc(inline)] -pub use self::extension::{Extension, ExtensionModeledError, ExtensionRejection, ResponseExtensions}; +pub(crate) use self::error::Error; +pub use self::extension::Extension; #[doc(inline)] pub use self::routing::Router; #[doc(inline)] pub use tower_http::add_extension::{AddExtension, AddExtensionLayer}; /// Alias for a type-erased error type. -pub use axum_core::BoxError; +pub(crate) use axum_core::BoxError; #[cfg(test)] mod test_helpers; diff --git a/rust-runtime/aws-smithy-http-server/src/macros.rs b/rust-runtime/aws-smithy-http-server/src/macros.rs index 01eb89312..1d9d75ee3 100644 --- a/rust-runtime/aws-smithy-http-server/src/macros.rs +++ b/rust-runtime/aws-smithy-http-server/src/macros.rs @@ -34,155 +34,6 @@ //! Macros implementation. -// Define a single rejection type -macro_rules! define_rejection { - ( - #[status = $status:ident] - #[body = $body:expr] - $(#[$m:meta])* - pub struct $name:ident; - ) => { - $(#[$m])* - #[derive(Debug)] - pub struct $name; - - impl axum_core::response::IntoResponse for $name { - fn into_response(self) -> axum_core::response::Response { - let mut res = http::Response::new(axum_core::body::boxed(http_body::Full::from($body))); - *res.status_mut() = http::StatusCode::$status; - res.extensions_mut().insert( - crate::ExtensionRejection::new(self.to_string()) - ); - res - } - } - - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", $body) - } - } - - impl std::error::Error for $name {} - - impl Default for $name { - fn default() -> Self { - Self - } - } - }; - - ( - #[status = $status:ident] - #[body = $body:expr] - $(#[$m:meta])* - pub struct $name:ident (Error); - ) => { - $(#[$m])* - #[derive(Debug)] - pub struct $name(crate::Error); - - impl $name { - pub fn from_err(err: E) -> Self - where - E: Into<$crate::BoxError>, - { - Self(crate::Error::new(err)) - } - } - - impl axum_core::response::IntoResponse for $name { - - fn into_response(self) -> axum_core::response::Response { - let body = http_body::Full::from(format!(concat!($body, ": {}"), self.0)); - let body = $crate::body::boxed(body); - let mut res = - http::Response::new(body); - *res.status_mut() = http::StatusCode::$status; - res - } - } - - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", $body) - } - } - - impl std::error::Error for $name { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.0) - } - } - - impl From<&str> for $name { - fn from(src: &str) -> Self { - Self(Err(src).expect("Unable to convert string into error")) - } - } - }; -} - -// Define a composite rejection type -macro_rules! composite_rejection { - ( - $(#[$m:meta])* - pub enum $name:ident { - $($variant:ident),+ - $(,)? - } - ) => { - $(#[$m])* - #[derive(Debug)] - pub enum $name { - $( - #[allow(missing_docs, deprecated)] - $variant($variant) - ),+ - } - - impl axum_core::response::IntoResponse for $name { - - fn into_response(self) -> axum_core::response::Response { - match self { - $( - Self::$variant(inner) => inner.into_response(), - )+ - } - } - } - - $( - #[allow(deprecated)] - impl From<$variant> for $name { - fn from(inner: $variant) -> Self { - Self::$variant(inner) - } - } - )+ - - impl std::fmt::Display for $name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - $( - Self::$variant(inner) => write!(f, "{}", inner), - )+ - } - } - } - - impl std::error::Error for $name { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - $( - Self::$variant(inner) => Some(inner), - )+ - } - } - } - } -} - /// Define a type that implements [`std::future::Future`]. #[macro_export] macro_rules! opaque_future { @@ -259,3 +110,23 @@ macro_rules! impl_extension_new_and_deref { impl_deref!($name); }; } + +macro_rules! convert_to_request_rejection { + ($from:ty, $to:ident) => { + impl From<$from> for RequestRejection { + fn from(err: $from) -> Self { + Self::$to(crate::Error::new(err)) + } + } + }; +} + +macro_rules! convert_to_response_rejection { + ($from:ty, $to:ident) => { + impl From<$from> for ResponseRejection { + fn from(err: $from) -> Self { + Self::$to(crate::Error::new(err)) + } + } + }; +} diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocols.rs index 096b6de41..a1113977a 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocols.rs @@ -4,47 +4,53 @@ */ //! Protocol helpers. -use crate::rejection::{ContentTypeRejection, MimeParsingFailed, MissingJsonContentType, MissingXmlContentType}; +use crate::rejection::RequestRejection; use axum_core::extract::RequestParts; +#[derive(Debug)] +pub enum Protocol { + RestJson1, + RestXml, +} + /// Validate that the request had the standard JSON content-type header. -pub fn check_json_content_type(req: &RequestParts) -> Result<(), ContentTypeRejection> { +pub fn check_json_content_type(req: &RequestParts) -> Result<(), RequestRejection> { let mime = req .headers() - .ok_or(MissingJsonContentType)? + .ok_or(RequestRejection::MissingJsonContentType)? .get(http::header::CONTENT_TYPE) - .ok_or(MissingJsonContentType)? + .ok_or(RequestRejection::MissingJsonContentType)? .to_str() - .map_err(|_| MissingJsonContentType)? + .map_err(|_| RequestRejection::MissingJsonContentType)? .parse::() - .map_err(|_| MimeParsingFailed)?; + .map_err(|_| RequestRejection::MimeParse)?; if mime.type_() == "application" && (mime.subtype() == "json" || mime.suffix().filter(|name| *name == "json").is_some()) { Ok(()) } else { - Err(ContentTypeRejection::MissingJsonContentType(MissingJsonContentType)) + Err(RequestRejection::MissingJsonContentType) } } /// Validate that the request had the standard XML content-type header. -pub fn check_xml_content_type(req: &RequestParts) -> Result<(), ContentTypeRejection> { +pub fn check_xml_content_type(req: &RequestParts) -> Result<(), RequestRejection> { let mime = req .headers() - .ok_or(MissingXmlContentType)? + .ok_or(RequestRejection::MissingXmlContentType)? .get(http::header::CONTENT_TYPE) - .ok_or(MissingXmlContentType)? + .ok_or(RequestRejection::MissingXmlContentType)? .to_str() - .map_err(|_| MissingXmlContentType)? + .map_err(|_| RequestRejection::MissingXmlContentType)? .parse::() - .map_err(|_| MimeParsingFailed)?; + .map_err(|_| RequestRejection::MimeParse)?; if mime.type_() == "application" && (mime.subtype() == "xml" || mime.suffix().filter(|name| *name == "xml").is_some()) { Ok(()) } else { - Err(ContentTypeRejection::MissingXmlContentType(MissingXmlContentType)) + Err(RequestRejection::MissingXmlContentType) } } diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 2787bcab2..9f96f32b0 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -3,231 +3,222 @@ * SPDX-License-Identifier: Apache-2.0. */ -//! Rejection response types. -define_rejection! { - #[status = INTERNAL_SERVER_ERROR] - #[body = "Cannot have two request body extractors for a single request"] - /// Rejection type used if you try and extract the request body more than - /// once. - pub struct BodyAlreadyExtracted; -} - -define_rejection! { - #[status = INTERNAL_SERVER_ERROR] - #[body = "Headers taken by other extractor"] - /// Rejection type used if the headers have been taken by another extractor. - pub struct HeadersAlreadyExtracted; -} - -define_rejection! { - #[status = BAD_REQUEST] - #[body = "Request deserialize failed"] - /// Rejection type used if the request deserialization encountered errors. - pub struct Deserialize(Error); -} - -define_rejection! { - #[status = INTERNAL_SERVER_ERROR] - #[body = "Response serialize failed"] - /// Rejection type used if the response serialization encountered errors. - pub struct Serialize(Error); -} - -define_rejection! { - #[status = BAD_REQUEST] - #[body = "Request body does not contain valid UTF-8"] - /// Rejection type used when buffering the request into a [`String`] if the - /// body doesn't contain valid UTF-8. - pub struct InvalidUtf8(Error); -} - -define_rejection! { - // TODO: we probably want to be more specific as the http::Error enum has many variants - #[status = INTERNAL_SERVER_ERROR] - #[body = "Error handling HTTP request"] - /// Rejection type used when there is an error handling the HTTP request. - pub struct Http(Error); -} - -define_rejection! { - // TODO: we probably want to be more specific as the header parsing can have many variants - #[status = BAD_REQUEST] - #[body = "Error parsing headers"] - /// Rejection type used if the any of the header parsing fails. - pub struct HeadersParse(Error); -} - -define_rejection! { - #[status = BAD_REQUEST] - #[body = "Expected `Content-Type: application/json`"] - /// Rejection type used if the JSON `Content-Type` header is missing. - pub struct MissingJsonContentType; -} - -define_rejection! { - #[status = BAD_REQUEST] - #[body = "Expected `Content-Type: application/xml`"] - /// Rejection type used if the XML `Content-Type` header is missing. - pub struct MissingXmlContentType; -} - -define_rejection! { - #[status = BAD_REQUEST] - #[body = "Failed to parse request MIME type"] - /// Rejection type used if the MIME type parsing failed. - pub struct MimeParsingFailed; -} - -define_rejection! { - #[status = INTERNAL_SERVER_ERROR] - #[body = "Extensions taken by other extractor"] - /// Rejection used if the request extension has been taken by another - /// extractor. - pub struct ExtensionsAlreadyExtracted; -} - -define_rejection! { - #[status = INTERNAL_SERVER_ERROR] - #[body = "Missing request extension"] - /// Rejection type for [`Extension`](super::Extension) if an expected - /// request extension was not found. - pub struct MissingExtension(Error); -} - -composite_rejection! { - /// Rejection used for `Content-Type` errors such as missing `Content-Type` - /// header, MIME parse issues, etc. - pub enum ContentTypeRejection { - MissingJsonContentType, - MissingXmlContentType, - MimeParsingFailed, - } -} - -composite_rejection! { - /// Rejection used for [`Extension`](super::Extension). - /// - /// Contains one variant for each way the [`Extension`](super::Extension) extractor - /// can fail. - pub enum ExtensionHandlingRejection { - MissingExtension, - ExtensionsAlreadyExtracted, - } -} - -composite_rejection! { - /// General rejection type used by `smithy-rs` auto-generated extractors and responders. - /// - /// Contains one variant for each way extracting and responding can fail. - /// - /// This rejection type also aggregates all the errors that come from other `smithy-rs` runtime - /// crates, allowing a nice integration with serialization, deserialization, and builder types - /// generated by the codegen. - pub enum SmithyRejection { - Serialize, - Deserialize, - InvalidUtf8, - Http, - HeadersParse, - ContentTypeRejection, - BodyAlreadyExtracted, - HeadersAlreadyExtracted, - ExtensionsAlreadyExtracted, - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_json::deserialize::Error) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_xml::decode::XmlError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_http::operation::BuildError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: std::num::ParseIntError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: std::num::ParseFloatError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: std::str::ParseBoolError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_types::date_time::DateTimeParseError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_types::date_time::DateTimeFormatError) -> Self { - SmithyRejection::Serialize(Serialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_types::primitive::PrimitiveParseError) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: aws_smithy_http::operation::SerializationError) -> Self { - SmithyRejection::Serialize(Serialize::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: std::str::Utf8Error) -> Self { - SmithyRejection::InvalidUtf8(InvalidUtf8::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: http::Error) -> Self { - SmithyRejection::Http(Http::from_err(err)) - } -} - -impl From for SmithyRejection { - fn from(err: hyper::Error) -> Self { - SmithyRejection::Http(Http::from_err(err)) +//! Rejection types. +//! +//! This module contains types that are commonly used as the `E` error type in functions that +//! handle requests and responses that return `Result` throughout the framework. These +//! include functions to deserialize incoming requests and serialize outgoing responses. +//! +//! All types end with `Rejection`. There are three types: +//! +//! 1. [`RequestRejection`]s are used when the framework fails to deserialize the request into the +//! corresponding operation input. +//! 1. [`RequestExtensionNotFoundRejection`]s are used when the framework fails to deserialize from +//! the request's extensions a particular [`crate::Extension`] that was expected to be found. +//! 1. [`ResponseRejection`]s are used when the framework fails to serialize the operation +//! output into a response. +//! +//! They are called _rejection_ types and not _error_ types to signal that the input was _rejected_ +//! (as opposed to it causing a recoverable error that would need to be handled, or an +//! unrecoverable error). For example, a [`RequestRejection`] simply means that the request was +//! rejected; there isn't really anything wrong with the service itself that the service +//! implementer would need to handle. +//! +//! Rejection types are an _internal_ detail about the framework: they can be added, removed, and +//! modified at any time without causing breaking changes. They are not surfaced to clients or the +//! service implementer in any way (including this documentation): indeed, they can't be converted +//! into responses. They serve as a mechanism to keep track of all the possible errors that can +//! occur when processing a request or a response, in far more detail than what AWS protocols need +//! to. This is why they are so granular: other (possibly protocol-specific) error types (like +//! [`crate::runtime_error::RuntimeError`]) can "group" them when exposing errors to +//! clients while the framework does not need to sacrifice fidelity in private error handling +//! routines, and future-proofing itself at the same time (for example, we might want to record +//! metrics about rejection types). +//! +//! Rejection types implement [`std::error::Error`], and some take in type-erased boxed errors +//! (`crate::Error`) to represent their underlying causes, so they can be composed with other types +//! that take in (possibly type-erased) [`std::error::Error`]s, like +//! [`crate::runtime_error::RuntimeError`], thus allowing us to represent the full +//! error chain. + +use strum_macros::Display; + +/// Rejection used for when failing to extract an [`crate::Extension`] from an incoming [request's +/// extensions]. Contains one variant for each way the extractor can fail. +/// +/// [request's extensions]: https://docs.rs/http/latest/http/struct.Extensions.html +#[derive(Debug, Display)] +pub enum RequestExtensionNotFoundRejection { + /// Used when a particular [`crate::Extension`] was expected to be found in the request but we + /// did not find it. + /// This most likely means the service implementer simply forgot to add a [`tower::Layer`] that + /// registers the particular extension in their service to incoming requests. + MissingExtension(String), + // Used when the request extensions have already been taken by another extractor. + ExtensionsAlreadyExtracted, +} + +impl std::error::Error for RequestExtensionNotFoundRejection {} + +/// Errors that can occur when serializing the operation output provided by the service implementer +/// into an HTTP response. +#[derive(Debug, Display)] +pub enum ResponseRejection { + /// Used when `httpResponseCode` targets an optional member, and the service implementer sets + /// it to `None`. + MissingHttpStatusCode, + + /// Used when the service implementer provides an integer outside the 100-999 range for a + /// member targeted by `httpResponseCode`. + InvalidHttpStatusCode, + + /// Used when an invalid HTTP header value (a value that cannot be parsed as an + /// `[http::header::HeaderValue]`) is provided for a shape member bound to an HTTP header with + /// `httpHeader` or `httpPrefixHeaders`. + /// Used when failing to serialize an `httpPayload`-bound struct into an HTTP response body. + Build(crate::Error), + + /// Used when failing to serialize a struct into a `String` for the HTTP response body (for + /// example, converting a struct into a JSON-encoded `String`). + Serialization(crate::Error), + + /// Used when consuming an [`http::response::Builder`] into the constructed [`http::Response`] + /// when calling [`http::response::Builder::body`]. + /// This error can happen if an invalid HTTP header value (a value that cannot be parsed as an + /// `[http::header::HeaderValue]`) is used for the protocol-specific response `Content-Type` + /// header, or for additional protocol-specific headers (like `X-Amzn-Errortype` to signal + /// errors in RestJson1). + Http(crate::Error), +} + +impl std::error::Error for ResponseRejection {} + +convert_to_response_rejection!(aws_smithy_http::operation::BuildError, Build); +convert_to_response_rejection!(aws_smithy_http::operation::SerializationError, Serialization); +convert_to_response_rejection!(http::Error, Http); + +/// Errors that can occur when deserializing an HTTP request into an _operation input_, the input +/// that is passed as the first argument to operation handlers. To deserialize into the service's +/// registered state, a different rejection type is used, [`RequestExtensionNotFoundRejection`]. +/// +/// This type allows us to easily keep track of all the possible errors that can occur in the +/// lifecycle of an incoming HTTP request. +/// +/// Many inner code-generated and runtime deserialization functions use this as their error type, when they can +/// only instantiate a subset of the variants (most likely a single one). For example, the +/// functions that check the `Content-Type` header in `[crate::protocols]` can only return three of +/// the variants: `MissingJsonContentType`, `MissingXmlContentType`, and `MimeParse`. +/// This is a deliberate design choice to keep code generation simple. After all, this type is an +/// inner detail of the framework the service implementer does not interact with. It allows us to +/// easily keep track of all the possible errors that can occur in the lifecycle of an incoming +/// HTTP request. +/// +/// If a variant takes in a value, it represents the underlying cause of the error. This inner +/// value should be of the type-erased boxed error type `[crate::Error]`. In practice, some of the +/// variants that take in a value are only instantiated with errors of a single type in the +/// generated code. For example, `UriPatternMismatch` is only instantiated with an error coming +/// from a `nom` parser, `nom::Err>`. This is reflected in the converters +/// below that convert from one of these very specific error types into one of the variants. For +/// example, the `RequestRejection` implements `From` to construct the `HttpBody` +/// variant. This is a deliberate design choice to make the code simpler and less prone to changes. +/// +// The variants are _roughly_ sorted in the order in which the HTTP request is processed. +#[derive(Debug, Display)] +pub enum RequestRejection { + /// Used when attempting to take the request's body, and it has already been taken (presumably + /// by an outer `Service` that handled the request before us). + BodyAlreadyExtracted, + + /// Used when failing to convert non-streaming requests into a byte slab with + /// `hyper::body::to_bytes`. + HttpBody(crate::Error), + + // These are used when checking the `Content-Type` header. + MissingJsonContentType, + MissingXmlContentType, + MimeParse, + + /// Used when failing to deserialize the HTTP body's bytes into a JSON document conforming to + /// the modeled input it should represent. + JsonDeserialize(crate::Error), + /// Used when failing to deserialize the HTTP body's bytes into a XML conforming to the modeled + /// input it should represent. + XmlDeserialize(crate::Error), + + /// Used when attempting to take the request's headers, and they have already been taken (presumably + /// by an outer `Service` that handled the request before us). + HeadersAlreadyExtracted, + + /// Used when failing to parse HTTP headers that are bound to input members with the `httpHeader` + /// or the `httpPrefixHeaders` traits. + HeaderParse(crate::Error), + + /// Used when the URI pattern has a literal after the greedy label, and it is not found in the + /// request's URL. + UriPatternGreedyLabelPostfixNotFound, + /// Used when the `nom` parser's input does not match the URI pattern. + UriPatternMismatch(crate::Error), + + /// Used when percent-decoding URL query string. + /// Used when percent-decoding URI path label. + InvalidUtf8(crate::Error), + + /// Used when failing to deserialize strings from a URL query string and from URI path labels + /// into an [`aws_smithy_types::DateTime`]. + DateTimeParse(crate::Error), + + /// Used when failing to deserialize strings from a URL query string and from URI path labels + /// into "primitive" types. + PrimitiveParse(crate::Error), + + // The following three variants are used when failing to deserialize strings from a URL query + // string and URI path labels into "primitive" types. + // TODO(https://github.com/awslabs/smithy-rs/issues/1232): They should be removed and + // conflated into the `PrimitiveParse` variant above after this issue is resolved. + IntParse(crate::Error), + FloatParse(crate::Error), + BoolParse(crate::Error), + + // TODO(https://github.com/awslabs/smithy-rs/issues/1243): In theory, we could get rid of this + // error, but it would be a lot of effort for comparatively low benefit. + /// Used when consuming the input struct builder. + Build(crate::Error), +} + +impl std::error::Error for RequestRejection {} + +// These converters are solely to make code-generation simpler. They convert from a specific error +// type (from a runtime/third-party crate or the standard library) into a variant of the +// [`crate::rejection::RequestRejection`] enum holding the type-erased boxed [`crate::Error`] +// type. Generated functions that use [crate::rejection::RequestRejection] can thus use `?` to +// bubble up instead of having to sprinkle things like [`Result::map_err`] everywhere. + +convert_to_request_rejection!(aws_smithy_json::deserialize::Error, JsonDeserialize); +convert_to_request_rejection!(aws_smithy_xml::decode::XmlError, XmlDeserialize); +convert_to_request_rejection!(aws_smithy_http::operation::BuildError, Build); +convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); +convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); +convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); +convert_to_request_rejection!(std::str::ParseBoolError, BoolParse); +convert_to_request_rejection!(std::num::ParseFloatError, FloatParse); +convert_to_request_rejection!(std::num::ParseIntError, IntParse); +convert_to_request_rejection!(serde_urlencoded::de::Error, InvalidUtf8); + +impl From>> for RequestRejection { + fn from(err: nom::Err>) -> Self { + Self::UriPatternMismatch(crate::Error::new(err.to_owned())) } } -impl From for SmithyRejection { - fn from(err: aws_smithy_http::header::ParseError) -> Self { - SmithyRejection::HeadersParse(HeadersParse::from_err(err)) - } -} +// Used when calling +// [`percent_encoding::percent_decode_str`](https://docs.rs/percent-encoding/latest/percent_encoding/fn.percent_decode_str.html) +// and bubbling up. +// This can happen when the percent-encoded data in e.g. a query string decodes to bytes that are +// not a well-formed UTF-8 string. +convert_to_request_rejection!(std::str::Utf8Error, InvalidUtf8); -impl From for SmithyRejection { - fn from(err: serde_urlencoded::de::Error) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err)) - } -} - -impl From>> for SmithyRejection { - fn from(err: nom::Err>) -> Self { - SmithyRejection::Deserialize(Deserialize::from_err(err.to_owned())) - } -} +// `[crate::body::Body]` is `[hyper::Body]`, whose associated `Error` type is `[hyper::Error]`. We +// need this converter for when we convert the body into bytes in the framework, since protocol +// tests use `[crate::body::Body]` as their body type when constructing requests (and almost +// everyone will run a Hyper-based server in their services). +convert_to_request_rejection!(hyper::Error, HttpBody); diff --git a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs new file mode 100644 index 000000000..6397794b8 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +//! Runtime error type. +//! +//! This module contains [`RuntimeError`] type. +//! +//! As opposed to rejection types (see [`crate::rejection`]), which are an internal detail about +//! the framework, `RuntimeError` is surfaced to clients in HTTP responses: indeed, it implements +//! [`axum_core::response::IntoResponse`]. Rejections can be "grouped" and converted into a +//! specific `RuntimeError` kind: for example, all request rejections due to serialization issues +//! can be conflated under the [`RuntimeErrorKind::Serialization`] enum variant. +//! +//! The HTTP response representation of the specific `RuntimeError` can be protocol-specific: for +//! example, the runtime error in the RestJson1 protocol sets the `X-Amzn-Errortype` header. +//! +//! Generated code works always works with [`crate::rejection`] types when deserializing requests +//! and serializing response. Just before a response needs to be sent, the generated code looks up +//! and converts into the corresponding `RuntimeError`, and then it uses the its +//! [`axum_core::response::IntoResponse`] implementation to render and send a response. + +use crate::protocols::Protocol; + +#[derive(Debug)] +pub enum RuntimeErrorKind { + // UnknownOperation, + /// Request failed to deserialize or response failed to serialize. + Serialization(crate::Error), + /// As of writing, this variant can only occur upon failure to extract an + /// [`crate::extension::Extension`] from the request. + InternalFailure(crate::Error), + // UnsupportedMediaType, + // NotAcceptable, +} + +/// String representation of the runtime error type. +/// Used as the value of the `X-Amzn-Errortype` header in RestJson1. +/// Used as the value passed to construct an [`crate::extension::RuntimeErrorExtension`]. +impl RuntimeErrorKind { + pub fn name(&self) -> &'static str { + match self { + RuntimeErrorKind::Serialization(_) => "SerializationException", + RuntimeErrorKind::InternalFailure(_) => "InternalFailureException", + } + } +} + +#[derive(Debug)] +pub struct RuntimeError { + pub protocol: Protocol, + pub kind: RuntimeErrorKind, +} + +impl axum_core::response::IntoResponse for RuntimeError { + fn into_response(self) -> axum_core::response::Response { + let status_code = match self.kind { + RuntimeErrorKind::Serialization(_) => http::StatusCode::BAD_REQUEST, + RuntimeErrorKind::InternalFailure(_) => http::StatusCode::INTERNAL_SERVER_ERROR, + }; + + let body = crate::body::to_boxed(match self.protocol { + Protocol::RestJson1 => "{}", + Protocol::RestXml => "", + }); + + let mut builder = http::Response::builder(); + builder = builder.status(status_code); + + match self.protocol { + Protocol::RestJson1 => { + builder = builder + .header("Content-Type", "application/json") + .header("X-Amzn-Errortype", self.kind.name()); + } + Protocol::RestXml => { + builder = builder.header("Content-Type", "application/xml"); + } + } + + builder = builder.extension(crate::extension::RuntimeErrorExtension::new(String::from( + self.kind.name(), + ))); + + builder.body(body).expect("invalid HTTP response for `RuntimeError`; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + } +} + +impl From for RuntimeErrorKind { + fn from(err: crate::rejection::RequestExtensionNotFoundRejection) -> Self { + RuntimeErrorKind::InternalFailure(crate::Error::new(err)) + } +} + +impl From for RuntimeErrorKind { + fn from(err: crate::rejection::ResponseRejection) -> Self { + RuntimeErrorKind::Serialization(crate::Error::new(err)) + } +} + +impl From for RuntimeErrorKind { + fn from(err: crate::rejection::RequestRejection) -> Self { + RuntimeErrorKind::Serialization(crate::Error::new(err)) + } +} -- GitLab