Unverified Commit 4afa8a3c authored by david-perez's avatar david-perez Committed by GitHub
Browse files

Revamp error system (#1233)

This commit rewrites the server's error system so that it can render
accurate protocol-specific HTTP responses adhering to the malformed
requests protocol tests' expectations. It also makes 400+ of the tests
relating to request deserialization pass.

Previously, we were rendering the `SmithyRejection` type in our HTTP responses
directly as we desired. However, AWS protocols' errors are not as granular:
many different request deserialization failure causes are conflated and
signaled to clients under a single `SerializationException` in the response.
This commit introduces a new concept, the `RuntimeEror` type, which groups
rejections and renders the responses that these protocols expect. Rejection
types now become an internal detail of the framework that only serve for us
maintainers to keep track and inventory all possible error causes in great
detail.

Extension types have also been renamed to better reflect their purpose.

For more information about this patch, read the added documentation to the
`rejection`, `extension`, and `runtime_error` modules.
parent e6d09ea6
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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")
}
+30 −19
Original line number Diff line number Diff line
@@ -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<B, Fun, Fut> #{ServerOperationHandler}::Handler<B, (), $inputName> 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::<S>::from_request(&mut req).await {
                    """
                    let state = match $serverCrate::extension::extract_extension(&mut req).await {
                        Ok(v) => v,
                    Err(r) => $storeErrorInExtensions
                        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<Output = $outputType> + Send,
            B: $serverCrate::body::HttpBody + Send + 'static, $streamingBodyTraitBounds
            B::Data: Send,
            B::Error: Into<$serverCrate::BoxError>,
            $serverCrate::rejection::SmithyRejection: From<<B as $serverCrate::body::HttpBody>::Error>
            $serverCrate::rejection::RequestRejection: From<<B as $serverCrate::body::HttpBody>::Error>
        """.trimIndent()
    }
}
+23 −492

File changed.

Preview size limit exceeded, changes collapsed.

+68 −59
Original line number Diff line number Diff line
@@ -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<<B as #{SmithyHttpServer}::body::HttpBody>::Error>
                #{RequestRejection} : From<<B as #{SmithyHttpServer}::body::HttpBody>::Error>
            {
                type Rejection = #{SmithyRejection};
                type Rejection = #{RuntimeError};
                async fn from_request(req: &mut #{AxumCore}::extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
                    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<B>(
                    ##[allow(unused_variables)] request: &mut #{AxumCore}::extract::RequestParts<B>
                ) -> std::result::Result<
                    #{I},
                    #{SmithyRejection}
                    #{RequestRejection}
                >
                where
                    B: #{SmithyHttpServer}::body::HttpBody + Send, ${streamingBodyTraitBounds(operationShape)}
                    B::Data: Send,
                    B::Error: Into<#{SmithyHttpServer}::BoxError>,
                    #{SmithyRejection}: From<<B as #{SmithyHttpServer}::body::HttpBody>::Error>
                    #{RequestRejection}: From<<B as #{SmithyHttpServer}::body::HttpBody>::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<i32>`.
            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::<i32>::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 -> {
+1 −0
Original line number Diff line number Diff line
@@ -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 }
Loading