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 242c72edd4..07ef6f1e99 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 @@ -439,17 +439,18 @@ class ServerHttpBoundProtocolTraitImplGenerator( Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{http}::Response::builder();", *codegenScope) serverRenderResponseHeaders(operationShape) - // Fallback to the default code of `@http`, 200. + // Fallback to the default code of `@http`, which should be 200. val httpTraitDefaultStatusCode = HttpTrait .builder().method("GET").uri(UriPattern.parse("/")) /* Required to build */ .build() .code + check(httpTraitDefaultStatusCode == 200) val httpTraitStatusCode = operationShape.getTrait()?.code ?: httpTraitDefaultStatusCode bindings.find { it.location == HttpLocation.RESPONSE_CODE } ?.let { serverRenderResponseCodeBinding(it, httpTraitStatusCode)(this) } - // no binding, use http's + // No binding, use `@http`. ?: serverRenderHttpResponseCode(httpTraitStatusCode)(this) operationShape.outputShape(model).findStreamingMember(model)?.let { @@ -555,46 +556,42 @@ class ServerHttpBoundProtocolTraitImplGenerator( ) } - private fun serverRenderHttpResponseCode( - defaultCode: Int, - ): Writable { - return writable { - rustTemplate( - """ - let status = $defaultCode; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; - builder = builder.status(http_status); - """.trimIndent(), - *codegenScope, - ) + private fun serverRenderHttpResponseCode(defaultCode: Int) = writable { + check(defaultCode in 100..999) { + """ + Smithy library lied to us. According to https://smithy.io/2.0/spec/http-bindings.html#http-trait, + "The provided value SHOULD be between 100 and 599, and it MUST be between 100 and 999". + """.replace("\n", "").trimIndent() } + rustTemplate( + """ + let http_status: u16 = $defaultCode; + builder = builder.status(http_status); + """, + *codegenScope, + ) } private fun serverRenderResponseCodeBinding( binding: HttpBindingDescriptor, + /** This is the status code to fall back on if the member shape bound with `@httpResponseCode` is not + * `@required` and the user did not provide a value for it at runtime. **/ fallbackStatusCode: Int, ): Writable { check(binding.location == HttpLocation.RESPONSE_CODE) return writable { val memberName = symbolProvider.toMemberName(binding.member) - rust("let status = output.$memberName") - if (symbolProvider.toSymbol(binding.member).isOptional()) { - rustTemplate( - """ - .unwrap_or($fallbackStatusCode) - """.trimIndent(), - *codegenScope, - ) + withBlock("let status = output.$memberName", ";") { + if (symbolProvider.toSymbol(binding.member).isOptional()) { + rust(".unwrap_or($fallbackStatusCode)") + } } rustTemplate( """ - ; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; + let http_status: u16 = status.try_into().map_err(#{ResponseRejection}::InvalidHttpStatusCode)?; builder = builder.status(http_status); - """.trimIndent(), + """, *codegenScope, ) } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs index c47057f661..1a74168f4a 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs @@ -9,7 +9,6 @@ use crate::rejection::MissingContentTypeReason; #[derive(Debug, Display)] pub enum ResponseRejection { - InvalidHttpStatusCode, Serialization(crate::Error), Http(crate::Error), } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs index 7a670ae1d1..2d365cbcba 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs @@ -47,6 +47,8 @@ //! //! Consult `crate::proto::$protocolName::rejection` for rejection types for other protocols. +use std::num::TryFromIntError; + use strum_macros::Display; use crate::rejection::MissingContentTypeReason; @@ -58,7 +60,7 @@ pub enum ResponseRejection { /// Used when the service implementer provides an integer outside the 100-999 range for a /// member targeted by `httpResponseCode`. /// See . - InvalidHttpStatusCode, + InvalidHttpStatusCode(TryFromIntError), /// Used when an invalid HTTP header value (a value that cannot be parsed as an /// `[http::header::HeaderValue]`) is provided for a shape member bound to an HTTP header with diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs index e3e54baa92..bbcf398985 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs @@ -7,11 +7,13 @@ //! [`crate::proto::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for //! [`RequestRejection::XmlDeserialize`]. +use std::num::TryFromIntError; + use strum_macros::Display; #[derive(Debug, Display)] pub enum ResponseRejection { - InvalidHttpStatusCode, + InvalidHttpStatusCode(TryFromIntError), Build(crate::Error), Serialization(crate::Error), Http(crate::Error),