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 07f0ac32556e98b4ee0153018bb7da7e6e549033..dbf3c1350f0bab8aae3467f072288103c6daf168 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 4a52f8dee5d91ccad3a2cfea581a0f6baa788551..1858ccaa4911ae932b0dcce438026be6265a60c7 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 f78f75d1e4430341617e92ab55a8e6b05af56ee6..9437ff2f0a90e27c69d28908f8f7a899614d8403 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 0b57bfa1f4201518ea640e612574151d36e25d8b..6b09169b18c0fe07834ea247a0a3e4100d0abea8 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 5afeaa1e4a58503871fb4484f7d7800b468db370..284cabc8a3eafda417084f77ec1fd95f5e8afb2f 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 8721674c9d0c041bf9454cccda904f3635084789..0a490e6b7904a4afd15e78461ff28a23d9ce2641 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 12b103238c2edbe57a2c25f8b8c3aafcc9dc05a4..31960d7cd6b78aad58e6fd7101ce8ab956bee25f 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 01eb893127f74a446290cc94eae0c13df112c6b9..1d9d75ee34bb44c321652b7f967ab22aadaaeac6 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 096b6de41a5d6cef14ca1fc13c73764cac0215e4..a1113977a6e76a778dc90e49d5a3433ded9e3a67 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 2787bcab2cf2065b8c128bc875a8d2e566874579..9f96f32b0110b44c4518aec11f30dc4f15baaf46 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 0000000000000000000000000000000000000000..6397794b8fcbf4bab36e74fdd12ed939dd0bd172 --- /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)) + } +}