Unverified Commit f89f9417 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Add support for the httpchecksum required trait (#523)

parent 0f534f00
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ $version: "1.0"

namespace com.amazonaws.s3
use smithy.test#httpResponseTests
use smithy.test#httpRequestTests

apply NotFound @httpResponseTests([
    {
@@ -9,6 +10,7 @@ apply NotFound @httpResponseTests([
        documentation: "This test case validates https://github.com/awslabs/smithy-rs/issues/456",
        params: {
        },
        bodyMediaType: "application/xml",
        body: "",
        protocol: "aws.protocols#restXml",
        code: 404,
@@ -31,6 +33,42 @@ apply GetBucketLocation @httpResponseTests([
        params: {
            "LocationConstraint": "us-west-2"
        },
        bodyMediaType: "application/xml",
        protocol: "aws.protocols#restXml"
    }
])

apply PutBucketLifecycleConfiguration @httpRequestTests([
    {
        id: "PutBucketLifecycleConfiguration",
        documentation: "This test validates that the content md5 header is set correctly",
        method: "PUT",
        protocol: "aws.protocols#restXml",
        uri: "/test-bucket",
        headers: {
            // we can assert this, but when this test is promoted, it can't assert
            // on the exact contents
            "content-md5": "b14bbeb8064f913b40c4975a03ef6e4a",
        },
        bodyMediaType: "application/xml",
        body: """
        <LifecycleConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">
            <Rule xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">
                <Expiration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">
                    <Days xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">1</Days>
                </Expiration>
                <ID xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">Expire</ID>
                <Status xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">Enabled</Status>
            </Rule>
        </LifecycleConfiguration>
        """,
        params: {
            "Bucket": "test-bucket",
            "LifecycleConfiguration": {
                "Rules": [
                    {"Expiration": { "Days": 1 }, "Status": "Enabled", "ID": "Expire" },
                ]
            }
        }
    }
])
+29 −1
Original line number Diff line number Diff line
@@ -12,7 +12,13 @@ use smithy.test#httpRequestTests
@restXml
service RestXmlExtras {
    version: "2019-12-16",
    operations: [AttributeParty, XmlMapsFlattenedNestedXmlNamespace, EnumKeys, PrimitiveIntOpXml]
    operations: [
        AttributeParty,
        XmlMapsFlattenedNestedXmlNamespace,
        EnumKeys,
        PrimitiveIntOpXml,
        ChecksumRequired
    ]
}

@httpRequestTests([{
@@ -175,3 +181,25 @@ map XmlMapsNestedNestedNamespaceInputOutputMap {
    @xmlName("V")
    value: String
}

@httpRequestTests([{
    id: "ChecksumRequiredHeader",
    method: "POST",
    body: "<ChecksumRequiredInput><field>hello</field></ChecksumRequiredInput>",
    uri: "/ChecksumRequired",
    bodyMediaType: "application/xml",
    params: {
        field: "hello"
    },
    headers: { "Content-Md5": "240240a9803ad7032101319e42a45c31" },
    protocol: "aws.protocols#restXml"
}])
@httpChecksumRequired
@http(uri: "/ChecksumRequired", method: "POST")
operation ChecksumRequired {
    input: ChecksumRequiredInput
}

structure ChecksumRequiredInput {
    field: String
}
+1 −0
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@ data class CargoDependency(
    }

    companion object {
        val Md5 = CargoDependency("md5", CratesIo("0.7"))
        val FastRand = CargoDependency("fastrand", CratesIo("1"))
        val Http: CargoDependency = CargoDependency("http", CratesIo("0.2"))
        val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4"), optional = true)
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

package software.amazon.smithy.rust.codegen.smithy.customizations

import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.Writable
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.rustlang.writable
import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization
import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection
import software.amazon.smithy.rust.codegen.smithy.generators.ProtocolConfig
import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError
import software.amazon.smithy.rust.codegen.util.hasStreamingMember
import software.amazon.smithy.rust.codegen.util.hasTrait
import software.amazon.smithy.rust.codegen.util.inputShape

class HttpChecksumRequiredGenerator(
    private val protocolConfig: ProtocolConfig,
    private val operationShape: OperationShape
) : OperationCustomization() {
    override fun section(section: OperationSection): Writable {
        if (!operationShape.hasTrait<HttpChecksumRequiredTrait>()) {
            return emptySection
        }
        if (operationShape.inputShape(protocolConfig.model).hasStreamingMember(protocolConfig.model)) {
            throw CodegenException("HttpChecksum required cannot be applied to a streaming shape")
        }
        return when (section) {
            is OperationSection.MutateRequest -> writable {
                rustTemplate(
                    """
                    ${section.request} = ${section.request}.augment(|mut req, _| {
                        let data = req
                            .body()
                            .bytes()
                            .ok_or_else(||#{BuildError}::SerializationError(
                                "checksum can only be computed for non-streaming operations".into())
                            )?;
                        let checksum = #{md5}::compute(data);
                        req.headers_mut().insert(
                            #{http}::header::HeaderName::from_static("content-md5"),
                            format!("{:x}", checksum).parse().expect("checksum is valid header value")
                        );
                        Result::<_, #{BuildError}>::Ok(req)
                    })?;
                """,
                    "md5" to CargoDependency.Md5.asType(),
                    "http" to CargoDependency.Http.asType(),
                    "BuildError" to protocolConfig.runtimeConfig.operationBuildError()
                )
            }
            else -> emptySection
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.rust.codegen.smithy.customizations.AllowClippyLints
import software.amazon.smithy.rust.codegen.smithy.customizations.CrateVersionGenerator
import software.amazon.smithy.rust.codegen.smithy.customizations.EndpointPrefixGenerator
import software.amazon.smithy.rust.codegen.smithy.customizations.HttpChecksumRequiredGenerator
import software.amazon.smithy.rust.codegen.smithy.customizations.IdempotencyTokenGenerator
import software.amazon.smithy.rust.codegen.smithy.customizations.SmithyTypesPubUseGenerator
import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization
@@ -30,7 +31,7 @@ class RequiredCustomizations : RustCodegenDecorator {
        return baseCustomizations + IdempotencyTokenGenerator(protocolConfig, operation) + EndpointPrefixGenerator(
            protocolConfig,
            operation
        )
        ) + HttpChecksumRequiredGenerator(protocolConfig, operation)
    }

    override fun libRsCustomizations(
Loading