Unverified Commit 33cd698f authored by ysaito1001's avatar ysaito1001 Committed by GitHub
Browse files

Remove `futures_core::stream::Stream` from `aws_smithy_http::byte_stream::ByteStream` (#2983)

## Motivation and Context
Removes `futures_core::stream::Stream` from
`aws_smithy_http::byte_stream::ByteStream`

## Description
This PR is part of our ongoing effort,
https://github.com/awslabs/smithy-rs/issues/2413. We remove the
`futures_core::stream::Stream` trait from
`aws_smithy_http::byte_stream::ByteStream`. To continue support existing
places that relied upon `ByteStream` implementing the `Stream` trait, we
provide explicit `.next`, `.try_next`, and `.size_hints` methods on that
type. As part of it, a doc-hidden type `FuturesStreamCompatByteStream`,
which implements `Stream`, has been added to `aws_smithy_http` to cater
to those who need a type implementing `Stream` (see
[discussion](https://github.com/awslabs/smithy-rs/pull/2910#discussion_r1317112233)
why doc-hidden).

Another place we need to update is codegen responsible for rendering
stream payload serializer, and this affects the server. The regular
server and the python server have slightly different rendering
requirements, since the former uses
`aws_smithy_http::byte_stream::ByteStream` (that does _not_ implement
the `Stream` trait) and latter uses its own
[ByteStream](https://github.com/awslabs/smithy-rs/blob/cb79a68e3c38d1e62d3980d5e7baedc1144bacc7/rust-runtime/aws-smithy-http-server-python/src/types.rs#L343)
(that does implement `Stream`). We use
`ServerHttpBoundProtocolCustomization` to handle the difference:
`StreamPayloadSerializerCustomization` and
`PythonServerStreamPayloadSerializerCustomization`.

## Testing
No new behavior has been added, relied on the existing tests in CI.

## Checklist
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS
SDK, generated SDK code, or SDK runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent bc418284
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -229,3 +229,15 @@ message = "Add support for Sigv4A request signing. Sigv4a signing will be used a
references = ["smithy-rs#1797"]
meta = { "breaking" = true, "tada" = true, "bug" = false }
author = "Velfi"

[[aws-sdk-rust]]
message = "The `futures_core::stream::Stream` trait has been removed from [`ByteStream`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/byte_stream/struct.ByteStream.html). The methods mentioned in the [doc](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/byte_stream/struct.ByteStream.html#getting-data-out-of-a-bytestream) will continue to be supported. Other stream operations that were previously available through the trait or its extension traits can be added later in a backward compatible manner."
references = ["smithy-rs#2983"]
meta = { "breaking" = true, "tada" = false, "bug" = false }
author = "ysaito1001"

[[smithy-rs]]
message = "The `futures_core::stream::Stream` trait has been removed from [`ByteStream`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/byte_stream/struct.ByteStream.html). The methods mentioned in the [doc](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/byte_stream/struct.ByteStream.html#getting-data-out-of-a-bytestream) will continue to be supported. Other stream operations that were previously available through the trait or its extension traits can be added later in a backward compatible manner."
references = ["smithy-rs#2983"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "ysaito1001"
+0 −1
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ use bytes::Buf;
use bytes_utils::SegmentedBuf;
use http::header::HeaderName;
use ring::digest::{Context, Digest, SHA256};
use tokio_stream::StreamExt;

const TREE_HASH_HEADER: &str = "x-amz-sha256-tree-hash";
const X_AMZ_CONTENT_SHA256: &str = "x-amz-content-sha256";
+2 −1
Original line number Diff line number Diff line
@@ -407,9 +407,10 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
        fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind")
        fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType =
            smithyHttp(runtimeConfig).resolve("event_stream::Receiver")

        fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType =
            smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender")
        fun futuresStreamCompatByteStream(runtimeConfig: RuntimeConfig): RuntimeType =
            smithyHttp(runtimeConfig).resolve("futures_stream_adapter::FuturesStreamCompatByteStream")

        fun errorMetadata(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata")
        fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) =
+21 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ class PythonServerAfterDeserializedMemberServerHttpBoundCustomization() :
        is ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember -> writable {
            rust(".into()")
        }

        else -> emptySection
    }
}
@@ -78,6 +79,23 @@ class PythonServerAfterDeserializedMemberHttpBindingCustomization(private val ru
    }
}

/**
 * Customization class used to determine how serialized stream payload should be rendered for the Python server.
 *
 * In this customization, we do not need to wrap the payload in a new-type wrapper to enable the
 * `futures_core::stream::Stream` trait since the payload in question has a type
 * `aws_smithy_http_server_python::types::ByteStream` which already implements the `Stream` trait.
 */
class PythonServerStreamPayloadSerializerCustomization() : ServerHttpBoundProtocolCustomization() {
    override fun section(section: ServerHttpBoundProtocolSection): Writable = when (section) {
        is ServerHttpBoundProtocolSection.WrapStreamPayload -> writable {
            section.params.payloadGenerator.generatePayload(this, section.params.shapeName, section.params.shape)
        }

        else -> emptySection
    }
}

class PythonServerProtocolLoader(
    private val supportedProtocols: ProtocolMap<ServerProtocolGenerator, ServerCodegenContext>,
) : ProtocolLoader<ServerProtocolGenerator, ServerCodegenContext>(supportedProtocols) {
@@ -91,6 +109,7 @@ class PythonServerProtocolLoader(
                    ),
                    additionalServerHttpBoundProtocolCustomizations = listOf(
                        PythonServerAfterDeserializedMemberServerHttpBoundCustomization(),
                        PythonServerStreamPayloadSerializerCustomization(),
                    ),
                    additionalHttpBindingCustomizations = listOf(
                        PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig),
@@ -103,6 +122,7 @@ class PythonServerProtocolLoader(
                    ),
                    additionalServerHttpBoundProtocolCustomizations = listOf(
                        PythonServerAfterDeserializedMemberServerHttpBoundCustomization(),
                        PythonServerStreamPayloadSerializerCustomization(),
                    ),
                    additionalHttpBindingCustomizations = listOf(
                        PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig),
@@ -115,6 +135,7 @@ class PythonServerProtocolLoader(
                    ),
                    additionalServerHttpBoundProtocolCustomizations = listOf(
                        PythonServerAfterDeserializedMemberServerHttpBoundCustomization(),
                        PythonServerStreamPayloadSerializerCustomization(),
                    ),
                    additionalHttpBindingCustomizations = listOf(
                        PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig),
+30 −2
Original line number Diff line number Diff line
@@ -81,11 +81,28 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser
import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol
import java.util.logging.Logger

data class StreamPayloadSerializerParams(
    val codegenContext: ServerCodegenContext,
    val payloadGenerator: ServerHttpBoundProtocolPayloadGenerator,
    val shapeName: String,
    val shape: OperationShape,
)

/**
 * Class describing a ServerHttpBoundProtocol section that can be used in a customization.
 */
sealed class ServerHttpBoundProtocolSection(name: String) : Section(name) {
    data class AfterTimestampDeserializedMember(val shape: MemberShape) : ServerHttpBoundProtocolSection("AfterTimestampDeserializedMember")

    /**
     * Represent a section for rendering the serialized stream payload.
     *
     * If the payload does not implement the `futures_core::stream::Stream`, which is the case for
     * `aws_smithy_http::byte_stream::ByteStream`, the section needs to be overridden and renders a new-type wrapper
     * around the payload to enable the `Stream` trait.
     */
    data class WrapStreamPayload(val params: StreamPayloadSerializerParams) :
        ServerHttpBoundProtocolSection("WrapStreamPayload")
}

/**
@@ -540,7 +557,18 @@ class ServerHttpBoundProtocolTraitImplGenerator(
        operationShape.outputShape(model).findStreamingMember(model)?.let {
            val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol)
            withBlockTemplate("let body = #{SmithyHttpServer}::body::boxed(#{SmithyHttpServer}::body::Body::wrap_stream(", "));", *codegenScope) {
                payloadGenerator.generatePayload(this, "output", operationShape)
                for (customization in customizations) {
                    customization.section(
                        ServerHttpBoundProtocolSection.WrapStreamPayload(
                            StreamPayloadSerializerParams(
                                codegenContext,
                                payloadGenerator,
                                "output",
                                operationShape,
                            ),
                        ),
                    )(this)
                }
            }
        } ?: run {
            val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol)
Loading