Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

* Generated HTTP clients expose request compression controls for operations marked with the `requestCompression` trait.

## v0.6.0

### Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3106,7 +3106,6 @@ async def test_aws_json10_client_error_corrects_with_default_values_when_server_
assert deep_equal(actual, expected)


@mark.xfail()
async def test_sdk_applied_content_encoding_aws_json1_0_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -3180,7 +3179,6 @@ async def test_sdk_applied_content_encoding_aws_json1_0_request_put_with_content
)


@mark.xfail()
async def test_sdk_appends_gzip_and_ignores_http_provided_encoding_aws_json1_0_request_put_with_content_encoding() -> (
None
):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5746,7 +5746,6 @@ async def test_put_and_get_inline_documents_input_response_put_and_get_inline_do
assert deep_equal(actual, expected)


@mark.xfail()
async def test_sdk_applied_content_encoding_aws_json1_1_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -5820,7 +5819,6 @@ async def test_sdk_applied_content_encoding_aws_json1_1_request_put_with_content
)


@mark.xfail()
async def test_sdk_appends_gzip_and_ignores_http_provided_encoding_aws_json1_1_request_put_with_content_encoding() -> (
None
):
Expand Down
2 changes: 0 additions & 2 deletions codegen-output/aws-query/tests/test_awsquery_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,6 @@ async def test_query_no_input_and_output_response_no_input_and_output() -> None:
assert deep_equal(actual, expected)


@mark.xfail()
async def test_sdk_applied_content_encoding_aws_query_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -1178,7 +1177,6 @@ async def test_sdk_applied_content_encoding_aws_query_request_put_with_content_e
)


@mark.xfail()
async def test_sdk_appends_gzip_and_ignores_http_provided_encoding_aws_query_request_put_with_content_encoding() -> (
None
):
Expand Down
2 changes: 0 additions & 2 deletions codegen-output/ec2-query/tests/test_ec2query_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,6 @@ async def test_ec2_query_no_input_and_output_response_no_input_and_output() -> N
assert deep_equal(actual, expected)


@mark.xfail()
async def test_sdk_applied_content_encoding_ec2_query_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -913,7 +912,6 @@ async def test_sdk_applied_content_encoding_ec2_query_request_put_with_content_e
)


@mark.xfail()
async def test_sdk_appends_gzip_and_ignores_http_provided_encoding_ec2_query_request_put_with_content_encoding() -> (
None
):
Expand Down
2 changes: 0 additions & 2 deletions codegen-output/rest-json-1/tests/test_restjson_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -10786,7 +10786,6 @@ async def test_post_union_with_json_name_response3_response_post_union_with_json
assert deep_equal(actual, expected)


@mark.xfail()
async def test_sdk_applied_content_encoding_rest_json1_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -10860,7 +10859,6 @@ async def test_sdk_applied_content_encoding_rest_json1_request_put_with_content_
)


@mark.xfail()
async def test_sdk_appended_gzip_after_provided_encoding_rest_json1_request_put_with_content_encoding() -> (
None
):
Expand Down
2 changes: 0 additions & 2 deletions codegen-output/rest-xml/tests/test_restxml_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5714,7 +5714,6 @@ async def test_rest_xml_serializes_empty_string_request_omits_null_serializes_em
)


@mark.xfail()
async def test_sdk_applied_content_encoding_rest_xml_request_put_with_content_encoding() -> (
None
):
Expand Down Expand Up @@ -5788,7 +5787,6 @@ async def test_sdk_applied_content_encoding_rest_xml_request_put_with_content_en
)


@mark.xfail()
async def test_sdk_appended_gzip_after_provided_encoding_rest_xml_request_put_with_content_encoding() -> (
None
):
Expand Down
26 changes: 26 additions & 0 deletions codegen-output/rpcv2-cbor/src/rpcv2cbor/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class Config:
auth_schemes: dict[ShapeID, AuthScheme[Any, Any, Any, Any]]
"""A map of auth scheme ids to auth schemes."""

disable_request_compression: bool
"""Disable compression on operations that advertise support."""

endpoint_resolver: EndpointResolver
"""
The endpoint resolver used to resolve the final endpoint per-operation
Expand Down Expand Up @@ -122,6 +125,15 @@ class Config:
the service endpoint.
"""

request_compression_encoding: str | None
"""Explicitly request gzip regardless of size; unsupported operations fail."""

request_min_compression_size_bytes: int
"""
Minimum buffered request size for automatic compression; zero compresses
all.
"""

retry_strategy: RetryStrategy | RetryStrategyOptions | None
"""
The retry strategy or options for configuring retry behavior. Can be
Expand Down Expand Up @@ -150,6 +162,7 @@ def __init__(
*,
auth_scheme_resolver: AuthSchemeResolver | None = None,
auth_schemes: dict[ShapeID, AuthScheme[Any, Any, Any, Any]] | None = None,
disable_request_compression: bool | None = None,
endpoint_resolver: EndpointResolver | None = None,
endpoint_uri: str | URI | None = None,
http_request_config: HTTPRequestConfiguration | None = None,
Expand All @@ -158,13 +171,20 @@ def __init__(
| ProtocolConstructor[ClientProtocol[Any, Any]]
| None = None,
region: str | None = None,
request_compression_encoding: str | None = None,
request_min_compression_size_bytes: int | None = None,
retry_strategy: RetryStrategy | RetryStrategyOptions | None = None,
sdk_ua_app_id: str | None = None,
transport: ClientTransport[Any, Any] | None = None,
user_agent_extra: str | None = None,
):
self.auth_scheme_resolver = auth_scheme_resolver or DefaultAuthResolver()
self.auth_schemes = auth_schemes or {}
self.disable_request_compression = (
disable_request_compression
if disable_request_compression is not None
else False
)
self.endpoint_resolver = endpoint_resolver or StandardRegionalEndpointsResolver(
endpoint_prefix="RpcV2Protocol"
)
Expand All @@ -177,6 +197,12 @@ def __init__(
"ClientProtocol[Any, Any]", protocol
) or RpcV2CborClientProtocol(_PROTOCOL_SETTINGS)
self.region = region
self.request_compression_encoding = request_compression_encoding
self.request_min_compression_size_bytes = (
request_min_compression_size_bytes
if request_min_compression_size_bytes is not None
else 10240
)
self.retry_strategy = retry_strategy
self.sdk_ua_app_id = sdk_ua_app_id
self.transport = transport or AIOHTTPClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
@SmithyInternalApi
public final class AwsJson10ProtocolGenerator implements ProtocolGenerator {
private static final Set<String> TESTS_TO_SKIP = Set.of(
// TODO: support the request compression trait.
"SDKAppliedContentEncoding_awsJson1_0",
"SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0",

// TODO: fix default value behavior for JSON RPC.
"AwsJson10ClientPopulatesDefaultValuesInInput",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
@SmithyInternalApi
public final class AwsJson11ProtocolGenerator implements ProtocolGenerator {
private static final Set<String> TESTS_TO_SKIP = Set.of(
// TODO: support the request compression trait.
"SDKAppliedContentEncoding_awsJson1_1",
"SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1",

// TODO: support the endpoint trait.
"AwsJson11EndpointTrait",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
@SmithyInternalApi
public final class AwsQueryProtocolGenerator implements ProtocolGenerator {
private static final Set<String> TESTS_TO_SKIP = Set.of(
// TODO: support the request compression trait
// https://smithy.io/2.0/spec/behavior-traits.html#smithy-api-requestcompression-trait
"SDKAppliedContentEncoding_awsQuery",
"SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery",

// TODO: support idempotency token autofill
"QueryProtocolIdempotencyTokenAutoFill",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
@SmithyInternalApi
public final class Ec2QueryProtocolGenerator implements ProtocolGenerator {
private static final Set<String> TESTS_TO_SKIP = Set.of(
// TODO: support the request compression trait
// https://smithy.io/2.0/spec/behavior-traits.html#smithy-api-requestcompression-trait
"SDKAppliedContentEncoding_ec2Query",
"SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query",

// TODO: support idempotency token autofill
"Ec2ProtocolIdempotencyTokenAutoFill",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
@SmithyInternalApi
public final class RestXmlProtocolGenerator implements ProtocolGenerator {
private static final Set<String> TESTS_TO_SKIP = Set.of(
// TODO: support the request compression trait
// https://smithy.io/2.0/spec/behavior-traits.html#smithy-api-requestcompression-trait
"SDKAppliedContentEncoding_restXml",
"SDKAppendedGzipAfterProvidedEncoding_restXml",

// TODO: support idempotency token autofill
"QueryIdempotencyTokenAutoFill",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.python.codegen.integrations;

import java.util.List;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.codegen.core.Symbol;
import software.amazon.smithy.python.codegen.ConfigProperty;
import software.amazon.smithy.python.codegen.GenerationContext;
import software.amazon.smithy.utils.SmithyInternalApi;

/** Adds modeled request-compression controls to HTTP clients. */
@SmithyInternalApi
public final class RequestCompressionIntegration implements PythonIntegration {
@Override
public List<RuntimeClientPlugin> getClientPlugins(GenerationContext context) {
if (!context.applicationProtocol().isHttpProtocol()) {
return List.of();
}
return List.of(RuntimeClientPlugin.builder()
// AWS clients inherit the controls and resolvers from AsyncAwsConfig.
.servicePredicate((model, service) -> !service.hasTrait(ServiceTrait.class))
.addConfigProperty(ConfigProperty.builder()
.name("disable_request_compression")
.type(Symbol.builder().name("bool").build())
.nullable(false)
.documentation("Disable compression on operations that advertise support.")
.initialize(w -> w.write("self.disable_request_compression = "
+ "disable_request_compression if disable_request_compression is not None else False"))
.build())
.addConfigProperty(ConfigProperty.builder()
.name("request_min_compression_size_bytes")
.type(Symbol.builder().name("int").build())
.nullable(false)
.documentation("Minimum buffered request size for automatic compression; zero compresses all.")
.initialize(w -> w.write("self.request_min_compression_size_bytes = "
+ "request_min_compression_size_bytes if request_min_compression_size_bytes is not None "
+ "else 10240"))
.build())
.addConfigProperty(ConfigProperty.builder()
.name("request_compression_encoding")
.type(Symbol.builder().name("str").build())
.documentation("Explicitly request gzip regardless of size; unsupported operations fail.")
.build())
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ public class RestJsonProtocolGenerator implements ProtocolGenerator {
"RestJsonEndpointTraitWithHostLabel",
"RestJsonEndpointTrait",

// TODO: support the request compression trait
// https://smithy.io/2.0/spec/behavior-traits.html#smithy-api-requestcompression-trait
"SDKAppliedContentEncoding_restJson1",
"SDKAppendedGzipAfterProvidedEncoding_restJson1",

// TODO: These tests require a payload even when the httpPayload member is null. Should it?
"RestJsonHttpWithHeadersButNoPayload",
"RestJsonHttpWithEmptyStructurePayload",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ software.amazon.smithy.python.codegen.integrations.Rpcv2CborIntegration
software.amazon.smithy.python.codegen.integrations.HttpApiKeyAuth
software.amazon.smithy.python.codegen.integrations.UserAgentIntegration
software.amazon.smithy.python.codegen.integrations.EndpointBddIntegration

software.amazon.smithy.python.codegen.integrations.RequestCompressionIntegration
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "feature",
"description": "Resolve request compression settings from client overrides, environment variables, and shared AWS configuration, and apply compression across AWS HTTP protocols."
}
30 changes: 30 additions & 0 deletions packages/smithy-aws-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,33 @@

This is the core package that provides AWS specific interfaces for both client and server
tooling generated by Smithy.


## Request compression

HTTP operations with Smithy's `requestCompression` trait use gzip automatically
for buffered payloads of at least 10,240 bytes. Eligible streaming blobs are compressed
without checking their size. Event streams and streams requiring a known length
are excluded. Operations without the trait keep their existing behavior; enabling
compression requires support from the service endpoint.

Seekable input streams remain seekable after compression. SigV4 signing can hash
and rewind them without buffering the entire compressed payload; rewinding
recompresses data as needed. The aiohttp transport checks for an empty body with
a one-byte probe and restores the compressed position instead of scanning to EOF.
Inherently non-seekable streams remain non-seekable,
and the payload-hashing SigV4 signer may buffer them as it does without compression.

AWS client configuration accepts these settings:

- `disable_request_compression=True` disables automatic compression.
- `request_min_compression_size_bytes=0` compresses every eligible buffered request.
- `request_compression_encoding="gzip"` explicitly requests gzip regardless of size.
An unsupported operation raises an error, as does combining this option with
`disable_request_compression=True`.

The standard settings also resolve from `AWS_DISABLE_REQUEST_COMPRESSION` and
`AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES`, or the corresponding lowercase keys in
the shared AWS configuration file. Explicit client settings take precedence.
The explicit encoding option is a client setting only. Zstd is not currently an
encoding supported by Smithy's request-compression trait.
Loading