Unverified Commit 12b4943e authored by Harry Barber's avatar Harry Barber Committed by GitHub
Browse files

Remove FromRequest and IntoResponse trait (#1527)

`async fn` are currently not supported by Rust. To allow for such syntax today, the [async_trait](https://docs.rs/async-trait/latest/async_trait/) macro eagerly `Box::pin`s the `async` block. This incurs an unnecessary allocation.

One instance where `async_trait` is used is on the [FromRequest](https://docs.rs/axum/latest/axum/extract/trait.FromRequest.html) method (inherited from [axum](https://docs.rs/axum/latest/axum/)). The use case for `FromRequest` in `axum` does not exist in our case, it is therefore a candidate for removal.

While `IntoResponse` does not involve the same boxing as `FromRequest`, it also does not need to be a trait - and so we remove it for symmetry. 

* Remove `FromRequest` and replace it with a static method

* Remove `IntoResponse` and replace it with a static method
parent 3a7c60c3
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -105,8 +105,6 @@ open class ServerOperationHandlerGenerator(
                    type Sealed = #{ServerOperationHandler}::sealed::Hidden;
                    async fn call(self, req: #{http}::Request<B>) -> #{http}::Response<#{SmithyHttpServer}::body::BoxBody> {
                        let mut req = #{SmithyHttpServer}::request::RequestParts::new(req);
                        use #{SmithyHttpServer}::request::FromRequest;
                        use #{SmithyHttpServer}::response::IntoResponse;
                        let input_wrapper = match $inputWrapperName::from_request(&mut req).await {
                            Ok(v) => v,
                            Err(runtime_error) => {
+0 −4
Original line number Diff line number Diff line
@@ -295,7 +295,6 @@ class ServerProtocolTestGenerator(
        rustTemplate(
            """
            let output = super::$operationImpl;
            use #{SmithyHttpServer}::response::IntoResponse;
            let http_response = output.into_response();
            """,
            *codegenScope,
@@ -317,10 +316,8 @@ class ServerProtocolTestGenerator(
        val operationName = "${operationSymbol.name}${ServerHttpBoundProtocolGenerator.OPERATION_INPUT_WRAPPER_SUFFIX}"
        rustTemplate(
            """
            use #{SmithyHttpServer}::request::FromRequest;
            let mut http_request = #{SmithyHttpServer}::request::RequestParts::new(http_request);
            let rejection = super::$operationName::from_request(&mut http_request).await.expect_err("request was accepted but we expected it to be rejected");
            use #{SmithyHttpServer}::response::IntoResponse;
            let http_response = rejection.into_response();
            """,
            *codegenScope,
@@ -382,7 +379,6 @@ class ServerProtocolTestGenerator(
        val operationName = "${operationSymbol.name}${ServerHttpBoundProtocolGenerator.OPERATION_INPUT_WRAPPER_SUFFIX}"
        rustWriter.rustTemplate(
            """
            use #{SmithyHttpServer}::request::FromRequest;
            let mut http_request = #{SmithyHttpServer}::request::RequestParts::new(http_request);
            let parsed = super::$operationName::from_request(&mut http_request).await.expect("failed to parse request").0;
            """,
+17 −16
Original line number Diff line number Diff line
@@ -143,7 +143,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
    }

    /*
     * Generation of `FromRequest` and `IntoResponse`.
     * Generation of `from_request` and `into_response`.
     * For non-streaming request bodies, that is, models without streaming traits
     * (https://awslabs.github.io/smithy/1.0/spec/core/stream-traits.html)
     * we require the HTTP body to be fully read in memory before parsing or deserialization.
@@ -166,7 +166,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
                    if let Some(headers) = req.headers() {
                        if let Some(accept) = headers.get(#{http}::header::ACCEPT) {
                            if accept != "$contentType" {
                                return Err(Self::Rejection {
                                return Err(#{RuntimeError} {
                                    protocol: #{SmithyHttpServer}::protocols::Protocol::${codegenContext.protocol.name.toPascalCase()},
                                    kind: #{SmithyHttpServer}::runtime_error::RuntimeErrorKind::NotAcceptable,
                                })
@@ -179,20 +179,19 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
            }
        }

        // Implement `FromRequest` trait for input types.
        // Implement `from_request` trait for input types.
        rustTemplate(
            """
            ##[derive(Debug)]
            pub(crate) struct $inputName(#{I});
            ##[#{AsyncTrait}::async_trait]
            impl<B> #{SmithyHttpServer}::request::FromRequest<B> for $inputName
            impl $inputName
            {
                pub async fn from_request<B>(req: &mut #{SmithyHttpServer}::request::RequestParts<B>) -> Result<Self, #{RuntimeError}>
                where
                    B: #{SmithyHttpServer}::body::HttpBody + Send, ${streamingBodyTraitBounds(operationShape)}
                    B::Data: Send,
                    #{RequestRejection} : From<<B as #{SmithyHttpServer}::body::HttpBody>::Error>
                {
                type Rejection = #{RuntimeError};
                async fn from_request(req: &mut #{SmithyHttpServer}::request::RequestParts<B>) -> Result<Self, Self::Rejection> {
                    #{verify_response_content_type:W}
                    #{parse_request}(req)
                        .await
@@ -212,7 +211,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
            "verify_response_content_type" to verifyResponseContentType,
        )

        // Implement `IntoResponse` for output types.
        // Implement `into_response` for output types.

        val outputName = "${operationName}${ServerHttpBoundProtocolGenerator.OPERATION_OUTPUT_WRAPPER_SUFFIX}"
        val errorSymbol = operationShape.errorSymbol(symbolProvider)
@@ -257,8 +256,9 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
                    Output(#{O}),
                    Error(#{E})
                }
                impl #{SmithyHttpServer}::response::IntoResponse for $outputName {
                    fn into_response(self) -> #{SmithyHttpServer}::response::Response {

                impl $outputName {
                    pub fn into_response(self) -> #{SmithyHttpServer}::response::Response {
                        $intoResponseImpl
                    }
                }
@@ -288,8 +288,9 @@ private class ServerHttpBoundProtocolTraitImplGenerator(
            rustTemplate(
                """
                pub(crate) struct $outputName(#{O});
                impl #{SmithyHttpServer}::response::IntoResponse for $outputName {
                    fn into_response(self) -> #{SmithyHttpServer}::response::Response {

                impl $outputName {
                    pub fn into_response(self) -> #{SmithyHttpServer}::response::Response {
                        $intoResponseImpl
                    }
                }
+1 −1
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ open class ProtocolGenerator(
    }

    /**
     * The server implementation uses this method to generate implementations of the `FromRequest` and `IntoResponse`
     * The server implementation uses this method to generate implementations of the `from_request` and `into_response`
     * traits for operation input and output shapes, respectively.
     */
    fun serverRenderOperation(
+0 −16
Original line number Diff line number Diff line
@@ -32,24 +32,8 @@
 * DEALINGS IN THE SOFTWARE.
 */

use async_trait::async_trait;
use http::{Extensions, HeaderMap, Request, Uri};

use crate::response::IntoResponse;

/// Trait for extracting information from requests.
///
/// A type implementing the `FromRequest` trait is used to handle and extract information from an async handler taking in a `RequestParts` as input.
#[async_trait]
pub trait FromRequest<B>: Sized {
    /// If the extractor fails it'll use this "rejection" type. A rejection is
    /// a kind of error that can be converted into a response.
    type Rejection: IntoResponse;

    /// Perform the extraction.
    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection>;
}

#[doc(hidden)]
#[derive(Debug)]
pub struct RequestParts<B> {
Loading