diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index d95ec84fd6c59ee8667339c592a8107fccf35ef9..6e094abe1ff8a343afa29a8a91b49cd191ad1727 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -66,6 +66,9 @@ resource Service { params: { id: "1", name: "TestService" }, body: "{\"id\":\"1\",\"name\":\"TestService\"}", code: 200, + headers: { + "Content-Length": "31" + } } ]) operation RegisterService { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 5af80140abde34823deb70407c6ba1edffc0097c..56359d59cd6888a83735028fc71e9cf358cb7aea 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -473,6 +473,9 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val status = variantShape.getTrait()?.let { trait -> trait.code } ?: errorTrait.defaultHttpStatusCode + + serverRenderContentLengthHeader() + rustTemplate( """ builder.status($status).body(#{SmithyHttpServer}::body::to_boxed(payload))? @@ -508,19 +511,22 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val memberName = symbolProvider.toMemberName(it) rustTemplate( """ - let body = #{SmithyHttpServer}::body::to_boxed(#{SmithyHttpServer}::body::Body::wrap_stream(output.$memberName)); + let payload = #{SmithyHttpServer}::body::Body::wrap_stream(output.$memberName); """, *codegenScope, ) } ?: run { val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE) - withBlockTemplate("let body = #{SmithyHttpServer}::body::to_boxed(", ");", *codegenScope) { + withBlockTemplate("let payload = ", ";") { payloadGenerator.generatePayload(this, "output", operationShape) } + + serverRenderContentLengthHeader() } rustTemplate( """ + let body = #{SmithyHttpServer}::body::to_boxed(payload); builder.body(body)? """, *codegenScope, @@ -585,6 +591,22 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } + /** + * Adds the `Content-Length` header. + * + * Unlike the headers added in `serverRenderResponseHeaders` the `Content-Length` depends on + * the payload post-serialization. + */ + private fun RustWriter.serverRenderContentLengthHeader() { + rustTemplate( + """ + let content_length = payload.len(); + builder = #{header_util}::set_response_header_if_absent(builder, #{http}::header::CONTENT_LENGTH, content_length); + """, + *codegenScope + ) + } + private fun serverRenderHttpResponseCode( defaultCode: Int ): Writable { diff --git a/codegen-test/model/misc.smithy b/codegen-test/model/misc.smithy index f32b892a15bcfdc5c0c409ea1b0650b66e6767c0..d29744bf76cbf32bb6793e02aa843857ddd566ea 100644 --- a/codegen-test/model/misc.smithy +++ b/codegen-test/model/misc.smithy @@ -161,6 +161,9 @@ operation ResponseCodeDefaultOperation { id: "ResponseCodeHttpFallbackOperation", protocol: "aws.protocols#restJson1", code: 418, + headers: { + "Content-Length": "2" + } } ]) @http(method: "GET", uri: "/responseCodeHttpFallbackOperation", code: 418) @@ -178,7 +181,10 @@ structure EmptyStructure {} id: "ResponseCodeRequiredOperation", protocol: "aws.protocols#restJson1", code: 201, - params: {"responseCode": 201} + params: {"responseCode": 201}, + headers: { + "Content-Length": "2" + } } ]) @http(method: "GET", uri: "/responseCodeRequiredOperation", code: 200)