Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

s3: Adds customization to disable s3 auto gzip decode #748

Merged
merged 4 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,18 @@ protected static GoDependency aws(String relativePath, String alias) {
*
* @param moduleImportPath the module path within aws sdk to be added as go mod dependency.
* @param relativePath the relative path which will be used as import path relative to aws sdk path.
* @param version the version of the aws module dependency to be imported
* @param alias the go import alias.
* @return GoDependency
*/
protected static GoDependency awsModuleDep(
String moduleImportPath,
String relativePath,
String version,
String alias
) {
moduleImportPath = AWS_SOURCE_PATH+ "/" + moduleImportPath;
return module(moduleImportPath, relativePath, Versions.AWS_SDK, alias);
return module(moduleImportPath, relativePath, version, alias);
}

protected static GoDependency module(
Expand All @@ -79,6 +81,6 @@ protected static GoDependency module(
}

private static final class Versions {
private static final String AWS_SDK = "v0.0.0-20200928200900-9b4f334f82b2";
private static final String AWS_SDK = "v0.25.0";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,18 @@ public final class AwsCustomGoDependency extends AwsGoDependency {
"service/apigateway/internal/customizations", "agcust");
public static final GoDependency GLACIER_CUSTOMIZATION = aws(
"service/glacier/internal/customizations", "glaciercust");

public static final GoDependency S3_SHARED_CUSTOMIZATION = awsModuleDep(
"service/internal/s3shared", null, "s3shared");
"service/internal/s3shared", null, Versions.INTERNAL_S3SHARED, "s3shared");
public static final GoDependency ACCEPT_ENCODING_CUSTOMIZATION = awsModuleDep(
"service/internal/accept-encoding", null, Versions.INTERNAL_ACCEPTENCODING, "acceptencodingcust");

private AwsCustomGoDependency() {
super();
}

private static final class Versions {
private static final String INTERNAL_S3SHARED = "v0.1.0";
private static final String INTERNAL_ACCEPTENCODING = "v0.0.0-20200930084954-897dfb99530c";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ private void writeMiddlewareHelper(GoWriter writer) {
writer.openBlock("func $L(stack *middleware.Stack, options Options) {", "}", GZIP_ADDER, () -> {
writer.write("$T(stack, $T{Enable: options.$L})",
SymbolUtils.createValueSymbolBuilder(GZIP_INTERNAL_ADDER,
AwsCustomGoDependency.DYNAMODB_CUSTOMIZATION).build(),
AwsCustomGoDependency.ACCEPT_ENCODING_CUSTOMIZATION).build(),
SymbolUtils.createValueSymbolBuilder(GZIP_INTERNAL_ADDER + "Options",
AwsCustomGoDependency.DYNAMODB_CUSTOMIZATION).build(),
AwsCustomGoDependency.ACCEPT_ENCODING_CUSTOMIZATION).build(),
GZIP_CLIENT_OPTION
);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package software.amazon.smithy.aws.go.codegen.customization;

import java.util.List;
import java.util.logging.Logger;
import software.amazon.smithy.aws.go.codegen.AddAwsConfigFields;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.go.codegen.GoDelegator;
import software.amazon.smithy.go.codegen.GoSettings;
import software.amazon.smithy.go.codegen.GoWriter;
import software.amazon.smithy.go.codegen.SymbolUtils;
import software.amazon.smithy.go.codegen.integration.ConfigField;
import software.amazon.smithy.go.codegen.integration.GoIntegration;
import software.amazon.smithy.go.codegen.integration.MiddlewareRegistrar;
import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.utils.ListUtils;

/**
* S3AcceptEncodingGzip adds a customization for s3 client to disable
* auto decoding of GZip content by Golang HTTP Client.
*
* This customization provides an option on the S3 client options to enable
* AcceptEncoding for GZIP. The flag if set, will enable auto decompression of
* GZIP by the S3 Client.
*
* By default, the client's auto decompression of GZIP content is turned off.
*/
public class S3AcceptEncodingGzip implements GoIntegration {
private static final Logger LOGGER = Logger.getLogger(AddAwsConfigFields.class.getName());

private static final String GZIP_DISABLE = "disableAcceptEncodingGzip";
private static final String GZIP_INTERNAL_ADDER = "AddAcceptEncodingGzip";

/**
* Gets the sort order of the customization from -128 to 127, with lowest
* executed first.
*
* @return Returns the sort order, defaults to -50.
*/
@Override
public byte getOrder() {
return 127;
}

@Override
public void writeAdditionalFiles(
GoSettings settings,
Model model,
SymbolProvider symbolProvider,
GoDelegator goDelegator
) {
if (!isServiceS3(model, settings.getService(model))) {
return;
}

goDelegator.useShapeWriter(settings.getService(model), this::writeMiddlewareHelper);
}

private void writeMiddlewareHelper(GoWriter writer) {
writer.openBlock("func $L(stack *middleware.Stack) {", "}", GZIP_DISABLE, () -> {
writer.write("$T(stack, $T{})",
SymbolUtils.createValueSymbolBuilder(GZIP_INTERNAL_ADDER,
AwsCustomGoDependency.ACCEPT_ENCODING_CUSTOMIZATION).build(),
SymbolUtils.createValueSymbolBuilder(GZIP_INTERNAL_ADDER + "Options",
AwsCustomGoDependency.ACCEPT_ENCODING_CUSTOMIZATION).build()
);
});
writer.insertTrailingNewline();
}

@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return ListUtils.of(
// register disableAcceptEncodingGzip middleware
RuntimeClientPlugin.builder()
.servicePredicate(S3AcceptEncodingGzip::isServiceS3)
.registerMiddleware(MiddlewareRegistrar.builder()
.resolvedFunction(SymbolUtils.createValueSymbolBuilder(GZIP_DISABLE)
.build())
.build()
)
.build()
);
}

/**
* Return true if service is S3.
*
* @param model the model used for generation.
* @param service the service shape for which default HTTP Client is generated.
* @return true if service is S3
*/
private static boolean isServiceS3(Model model, ServiceShape service) {
return service.expectTrait(ServiceTrait.class).getSdkId().equalsIgnoreCase("S3");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ software.amazon.smithy.aws.go.codegen.customization.GlacierCustomizations
software.amazon.smithy.aws.go.codegen.customization.S3ResponseErrorWrapper
software.amazon.smithy.aws.go.codegen.customization.S3MetadataRetriever
software.amazon.smithy.aws.go.codegen.customization.S3ContentSHA256Header
software.amazon.smithy.aws.go.codegen.customization.BackfillS3ObjectSizeMemberShapeType
software.amazon.smithy.aws.go.codegen.customization.BackfillS3ObjectSizeMemberShapeType
software.amazon.smithy.aws.go.codegen.customization.S3AcceptEncodingGzip
3 changes: 2 additions & 1 deletion service/dynamodb/api_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions service/dynamodb/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ go 1.15

require (
github.com/aws/aws-sdk-go-v2 v0.25.0
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v0.0.0-20200930084954-897dfb99530c
github.com/awslabs/smithy-go v0.1.0
)

replace github.com/aws/aws-sdk-go-v2 => ../../

replace github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding => ../../service/internal/accept-encoding/
86 changes: 40 additions & 46 deletions service/dynamodb/internal/customizations/doc.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
// Package customizations provides customizations for the Amazon DynamoDB API client.
//
// The DynamoDB API client uses two customizations, response checksum validation,
// and manual content-encoding: gzip support.
//
// Middleware layering
//
// Checksum validation needs to be performed first in deserialization chain
// on top of gzip decompression. Since the behavior of Deserialization is
// in reverse order to the other stack steps its easier to consider that
// "after" means "before".
//
// HTTP Response -> Checksum -> gzip decompress -> deserialize
//
// Response checksum validation
//
// DynamoDB responses can include a X-Amz-Crc32 header with the CRC32 checksum
// value of the response body. If the response body is content-encoding: gzip, the
// checksum is of the gzipped response content.
//
// If the header is present, the SDK should validate that the response payload
// computed CRC32 checksum matches the value provided in the header. The checksum
// header is based on the original payload provided returned by the service. Which
// means that if the response is gzipped the checksum is of the gzipped response,
// not the decompressed response bytes.
//
// Customization option:
// DisableValidateResponseChecksum (Enabled by Default)
//
// Accept encoding gzip
//
// The Go HTTP client automatically supports accept-encoding and content-encoding
// gzip by default. This default behavior is not desired by the SDK, and prevents
// validating the response body's checksum. To prevent this the SDK must manually
// control usage of content-encoding gzip.
//
// To control content-encoding, the SDK must always set the `Accept-Encoding`
// header to a value. This prevents the HTTP client from using gzip automatically.
// When gzip is enabled on the API client, the SDK's customization will control
// decompressing the gzip data in order to not break the checksum validation. When
// gzip is disabled, the API client will disable gzip, preventing the HTTP
// client's default behavior.
//
// Customization option:
// EnableAcceptEncodingGzip (Disabled by Default)
//
/*
Package customizations provides customizations for the Amazon DynamoDB API client.

The DynamoDB API client uses two customizations, response checksum validation,
and manual content-encoding: gzip support.

Middleware layering

Checksum validation needs to be performed first in deserialization chain
on top of gzip decompression. Since the behavior of Deserialization is
in reverse order to the other stack steps its easier to consider that
"after" means "before".

HTTP Response -> Checksum -> gzip decompress -> deserialize

Response checksum validation

DynamoDB responses can include a X-Amz-Crc32 header with the CRC32 checksum
value of the response body. If the response body is content-encoding: gzip, the
checksum is of the gzipped response content.

If the header is present, the SDK should validate that the response payload
computed CRC32 checksum matches the value provided in the header. The checksum
header is based on the original payload provided returned by the service. Which
means that if the response is gzipped the checksum is of the gzipped response,
not the decompressed response bytes.

Customization option:
DisableValidateResponseChecksum (Enabled by Default)

Accept encoding gzip

For customization around accept encoding, dynamodb client uses the middlewares
defined at service/internal/accept-encoding. Please refer to the documentation for
`accept-encoding` package for more details.

Customization option:
EnableAcceptEncodingGzip (Disabled by Default)

*/
package customizations
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package customizations
package acceptencoding

import (
"compress/gzip"
"context"
"fmt"
"io"

smithy "github.com/awslabs/smithy-go"
"github.com/awslabs/smithy-go"
"github.com/awslabs/smithy-go/middleware"
smithyhttp "github.com/awslabs/smithy-go/transport/http"
)
Expand All @@ -25,26 +25,26 @@ type AddAcceptEncodingGzipOptions struct {
// computed without disabling GZIP support.
func AddAcceptEncodingGzip(stack *middleware.Stack, options AddAcceptEncodingGzipOptions) {
if options.Enable {
stack.Finalize.Add(&AcceptEncodingGzipMiddleware{}, middleware.Before)
stack.Finalize.Add(&EnableGzipMiddleware{}, middleware.Before)
stack.Deserialize.Insert(&DecompressGzipMiddleware{}, "OperationDeserializer", middleware.After)
return
}

stack.Finalize.Add(&DisableAcceptEncodingGzipMiddleware{}, middleware.Before)
stack.Finalize.Add(&DisableGzipMiddleware{}, middleware.Before)
}

// DisableAcceptEncodingGzipMiddleware provides the middleware that will
// DisableGzipMiddleware provides the middleware that will
// disable the underlying http client automatically enabling for gzip
// decompress content-encoding support.
type DisableAcceptEncodingGzipMiddleware struct{}
type DisableGzipMiddleware struct{}

// ID returns the id for the middleware.
func (*DisableAcceptEncodingGzipMiddleware) ID() string {
return "DynamoDB:DisableAcceptEncodingGzipMiddleware"
func (*DisableGzipMiddleware) ID() string {
return "DisableAcceptEncodingGzipMiddleware"
}

// HandleFinalize implements the FinalizeMiddlware interface.
func (*DisableAcceptEncodingGzipMiddleware) HandleFinalize(
// HandleFinalize implements the FinalizeMiddleware interface.
func (*DisableGzipMiddleware) HandleFinalize(
ctx context.Context, input middleware.FinalizeInput, next middleware.FinalizeHandler,
) (
output middleware.FinalizeOutput, metadata middleware.Metadata, err error,
Expand All @@ -63,16 +63,16 @@ func (*DisableAcceptEncodingGzipMiddleware) HandleFinalize(
return next.HandleFinalize(ctx, input)
}

// AcceptEncodingGzipMiddleware provides a middleware to enable support for
// EnableGzipMiddleware provides a middleware to enable support for
// gzip responses, with manual decompression. This prevents the underlying HTTP
// client from performing the gzip decompression automatically.
type AcceptEncodingGzipMiddleware struct{}
type EnableGzipMiddleware struct{}

// ID returns the id for the middleware.
func (*AcceptEncodingGzipMiddleware) ID() string { return "DynamoDB:AcceptEncodingGzipMiddleware" }
func (*EnableGzipMiddleware) ID() string { return "AcceptEncodingGzipMiddleware" }

// HandleFinalize implements the FinalizeMiddlware interface.
func (*AcceptEncodingGzipMiddleware) HandleFinalize(
func (*EnableGzipMiddleware) HandleFinalize(
ctx context.Context, input middleware.FinalizeInput, next middleware.FinalizeHandler,
) (
output middleware.FinalizeOutput, metadata middleware.Metadata, err error,
Expand All @@ -96,7 +96,7 @@ func (*AcceptEncodingGzipMiddleware) HandleFinalize(
type DecompressGzipMiddleware struct{}

// ID returns the id for the middleware.
func (*DecompressGzipMiddleware) ID() string { return "DynamoDB:DecompressGzipMiddleware" }
func (*DecompressGzipMiddleware) ID() string { return "DecompressGzipMiddleware" }

// HandleDeserialize implements the DeserializeMiddlware interface.
func (*DecompressGzipMiddleware) HandleDeserialize(
Expand Down
Loading