diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index 3a5af6cb50..2da3c0fcde 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -57,10 +57,6 @@ class ServerOperationGenerator( impl #{SmithyHttpServer}::operation::OperationShape for $operationName { const ID: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); - - type Input = crate::input::${operationName}Input; - type Output = crate::output::${operationName}Output; - type Error = #{Error:W}; } impl #{SmithyHttpServer}::instrumentation::sensitivity::Sensitivity for $operationName { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 2b4e2ee2e9..7d3f796d5d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -39,6 +39,7 @@ class ServerServiceGenerator( private val codegenScope = arrayOf( "Bytes" to RuntimeType.Bytes, + "FuturesUtil" to ServerCargoDependency.FuturesUtil.toType(), "Http" to RuntimeType.Http, "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), "HttpBody" to RuntimeType.HttpBody, @@ -129,19 +130,20 @@ class ServerServiceGenerator( /// /* Set other handlers */ /// .build() /// .unwrap(); - /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; + /// ## fn check>>(_: S) {} + /// ## check(app); /// ``` /// - pub fn $fieldName(self, handler: HandlerType) -> Self + pub fn $fieldName(self, handler: HandlerType) -> Self where - HandlerType: #{SmithyHttpServer}::operation::Handler, + HandlerType: #{SmithyHttpServer}::operation::Handler, ModelPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - #{SmithyHttpServer}::operation::IntoService + #{SmithyHttpServer}::operation::FixInput> >, - #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + #{SmithyHttpServer}::operation::UpgradePlugin: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, ModelPl::Output @@ -150,7 +152,7 @@ class ServerServiceGenerator( $serviceName, crate::operation_shape::$structName, < - #{SmithyHttpServer}::operation::UpgradePlugin:: + #{SmithyHttpServer}::operation::UpgradePlugin as #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, @@ -163,13 +165,8 @@ class ServerServiceGenerator( >>::Future: Send + 'static, { - use #{SmithyHttpServer}::operation::OperationShapeExt; - use #{SmithyHttpServer}::plugin::Plugin; - let svc = crate::operation_shape::$structName::from_handler(handler); - let svc = self.model_plugin.apply(svc); - let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); - let svc = self.http_plugin.apply(svc); - self.${fieldName}_custom(svc) + let svc = #{SmithyHttpServer}::operation::IntoService::new(handler); + self.${fieldName}_service(svc) } /// Sets the [`$structName`](crate::operation_shape::$structName) operation. @@ -192,19 +189,18 @@ class ServerServiceGenerator( /// /* Set other handlers */ /// .build() /// .unwrap(); - /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; + /// ## fn check>>(_: S) {} + /// ## check(app); /// ``` /// - pub fn ${fieldName}_service(self, service: S) -> Self + pub fn ${fieldName}_service(self, service: S) -> Self where - S: #{SmithyHttpServer}::operation::OperationService, - ModelPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - #{SmithyHttpServer}::operation::Normalize + #{SmithyHttpServer}::operation::FixInput >, - #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + #{SmithyHttpServer}::operation::UpgradePlugin: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, ModelPl::Output @@ -213,7 +209,7 @@ class ServerServiceGenerator( $serviceName, crate::operation_shape::$structName, < - #{SmithyHttpServer}::operation::UpgradePlugin:: + #{SmithyHttpServer}::operation::UpgradePlugin as #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, @@ -226,11 +222,10 @@ class ServerServiceGenerator( >>::Future: Send + 'static, { - use #{SmithyHttpServer}::operation::OperationShapeExt; use #{SmithyHttpServer}::plugin::Plugin; - let svc = crate::operation_shape::$structName::from_service(service); + let svc = #{SmithyHttpServer}::operation::FixInput::new(service); let svc = self.model_plugin.apply(svc); - let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); + let svc = #{SmithyHttpServer}::operation::UpgradePlugin::new().apply(svc); let svc = self.http_plugin.apply(svc); self.${fieldName}_custom(svc) } @@ -295,7 +290,13 @@ class ServerServiceGenerator( /// /// Check out [`$builderName::build_unchecked`] if you'd prefer the service to return status code 500 when an /// unspecified route requested. - pub fn build(self) -> Result<$serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>>, MissingOperationsError> + pub fn build(self) -> Result<$serviceName< + #{SmithyHttpServer}::routing::RoutingService< + #{Router} < + #{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName> + > + > + >, MissingOperationsError> { let router = { use #{SmithyHttpServer}::operation::OperationShape; @@ -313,7 +314,7 @@ class ServerServiceGenerator( #{Router}::from_iter([#{RoutesArrayElements:W}]) }; Ok($serviceName { - router: #{SmithyHttpServer}::routing::RoutingService::new(router), + inner: #{SmithyHttpServer}::routing::RoutingService::new(router), }) } """, @@ -359,7 +360,7 @@ class ServerServiceGenerator( ( $requestSpecsModuleName::$specBuilderFunctionName(), self.$fieldName.unwrap_or_else(|| { - let svc = #{SmithyHttpServer}::operation::MissingFailure::<#{Protocol}>::default(); + let svc = #{SmithyHttpServer}::operation::MissingFailure::<$serviceName, #{Protocol}>::default(); #{SmithyHttpServer}::routing::Route::new(svc) }) ), @@ -376,13 +377,19 @@ class ServerServiceGenerator( /// /// Check out [`$builderName::build`] if you'd prefer the builder to fail if one or more operations do /// not have a registered handler. - pub fn build_unchecked(self) -> $serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>> + pub fn build_unchecked(self) -> $serviceName< + #{SmithyHttpServer}::routing::RoutingService< + #{Router} < + #{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName> + > + > + > where $builderBodyGenericTypeName: Send + 'static { let router = #{Router}::from_iter([#{Pairs:W}]); $serviceName { - router: #{SmithyHttpServer}::routing::RoutingService::new(router), + inner: #{SmithyHttpServer}::routing::RoutingService::new(router), } } """, @@ -463,8 +470,8 @@ class ServerServiceGenerator( /// /// See the [root](crate) documentation for more information. ##[derive(Clone)] - pub struct $serviceName { - router: #{SmithyHttpServer}::routing::RoutingService<#{Router}, #{Protocol}>, + pub struct $serviceName { + inner: S, } impl $serviceName<()> { @@ -498,7 +505,6 @@ class ServerServiceGenerator( #{SmithyHttpServer}::routing::IntoMakeService::new(self) } - /// Converts [`$serviceName`] into a [`MakeService`](tower::make::MakeService) with [`ConnectInfo`](#{SmithyHttpServer}::request::connect_info::ConnectInfo). pub fn into_make_service_with_connect_info(self) -> #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo { #{SmithyHttpServer}::routing::IntoMakeServiceWithConnectInfo::new(self) @@ -510,7 +516,7 @@ class ServerServiceGenerator( L: #{Tower}::Layer { $serviceName { - router: self.router.map(|s| s.layer(layer)) + inner: layer.layer(self.inner) } } @@ -530,22 +536,26 @@ class ServerServiceGenerator( } } - impl #{Tower}::Service<#{Http}::Request> for $serviceName + impl #{Tower}::Service<#{Http}::Request> for $serviceName where - S: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response> + Clone, - RespB: #{HttpBody}::Body + Send + 'static, - RespB::Error: Into> + S: #{Tower}::Service< + #{Http}::Request, + Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody> + > + Clone, + S::Error: #{SmithyHttpServer}::response::IntoResponseUniform<<$serviceName as #{SmithyHttpServer}::service::ServiceShape>::Protocol> { - type Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>; + type Response = S::Response; type Error = S::Error; - type Future = #{SmithyHttpServer}::routing::RoutingFuture; + type Future = #{FuturesUtil}::future::Map) -> Result>; fn poll_ready(&mut self, cx: &mut std::task::Context) -> std::task::Poll> { - self.router.poll_ready(cx) + self.inner.poll_ready(cx) } fn call(&mut self, request: #{Http}::Request) -> Self::Future { - self.router.call(request) + use #{FuturesUtil}::FutureExt; + use #{SmithyHttpServer}::response::IntoResponseUniform; + self.inner.call(request).map(|result| match result { Ok(ok) => Ok(ok), Err(err) => Ok(err.into_response()) }) } } """, 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 6e22828a1c..cf174fd7a5 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 @@ -71,6 +71,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.outputShape +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape @@ -157,6 +158,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver private val protocolFunctions = ProtocolFunctions(codegenContext) + private val serviceName = codegenContext.serviceShape.id.name.toPascalCase() private val codegenScope = arrayOf( "AsyncTrait" to ServerCargoDependency.AsyncTrait.toType(), @@ -277,7 +279,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( } } - impl #{SmithyHttpServer}::request::FromRequest<#{Marker}, B> for #{I} + impl #{SmithyHttpServer}::request::FromRequest for #{I} where B: #{SmithyHttpServer}::body::HttpBody + Send, B: 'static, @@ -309,7 +311,6 @@ class ServerHttpBoundProtocolTraitImplGenerator( """, *codegenScope, "I" to inputSymbol, - "Marker" to protocol.markerStruct(), "parse_request" to serverParseRequest(operationShape), "verifyAcceptHeader" to verifyAcceptHeader, "verifyAcceptHeaderStaticContentTypeInit" to verifyAcceptHeaderStaticContentTypeInit, @@ -323,13 +324,13 @@ class ServerHttpBoundProtocolTraitImplGenerator( // to let them know. rustTemplate( """ - impl #{SmithyHttpServer}::response::IntoResponse<#{Marker}> for #{O} { + impl #{SmithyHttpServer}::response::IntoResponse for #{O} { fn into_response(self) -> #{SmithyHttpServer}::response::Response { match #{serialize_response}(self) { Ok(response) => response, Err(e) => { #{Tracing}::error!(error = %e, "failed to serialize response"); - #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + #{SmithyHttpServer}::response::IntoResponse::::into_response(#{RuntimeError}::from(e)) } } } @@ -337,14 +338,13 @@ class ServerHttpBoundProtocolTraitImplGenerator( """, *codegenScope, "O" to outputSymbol, - "Marker" to protocol.markerStruct(), "serialize_response" to serverSerializeResponse(operationShape), ) if (operationShape.operationErrors(model).isNotEmpty()) { rustTemplate( """ - impl #{SmithyHttpServer}::response::IntoResponse<#{Marker}> for #{E} { + impl #{SmithyHttpServer}::response::IntoResponse for #{E} { fn into_response(self) -> #{SmithyHttpServer}::response::Response { match #{serialize_error}(&self) { Ok(mut response) => { @@ -353,7 +353,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( }, Err(e) => { #{Tracing}::error!(error = %e, "failed to serialize response"); - #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + #{SmithyHttpServer}::response::IntoResponse::::into_response(#{RuntimeError}::from(e)) } } } @@ -361,7 +361,6 @@ class ServerHttpBoundProtocolTraitImplGenerator( """.trimIndent(), *codegenScope, "E" to errorSymbol, - "Marker" to protocol.markerStruct(), "serialize_error" to serverSerializeError(operationShape), ) } diff --git a/rust-runtime/aws-smithy-http-server-python/src/error.rs b/rust-runtime/aws-smithy-http-server-python/src/error.rs index 2cc308fea5..01c4984e1a 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/error.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/error.rs @@ -10,7 +10,7 @@ use aws_smithy_http_server::{ protocol::{ aws_json_10::AwsJson1_0, aws_json_11::AwsJson1_1, rest_json_1::RestJson1, rest_xml::RestXml, }, - response::IntoResponse, + response::IntoResponseUniform, }; use aws_smithy_types::date_time::{ConversionError, DateTimeParseError}; use pyo3::{create_exception, exceptions::PyException as BasePyException, prelude::*, PyErr}; @@ -79,7 +79,7 @@ impl From for PyMiddlewareException { } } -impl IntoResponse for PyMiddlewareException { +impl IntoResponseUniform for PyMiddlewareException { fn into_response(self) -> http::Response { http::Response::builder() .status(self.status_code) @@ -90,7 +90,7 @@ impl IntoResponse for PyMiddlewareException { } } -impl IntoResponse for PyMiddlewareException { +impl IntoResponseUniform for PyMiddlewareException { fn into_response(self) -> http::Response { http::Response::builder() .status(self.status_code) @@ -100,7 +100,7 @@ impl IntoResponse for PyMiddlewareException { } } -impl IntoResponse for PyMiddlewareException { +impl IntoResponseUniform for PyMiddlewareException { fn into_response(self) -> http::Response { http::Response::builder() .status(self.status_code) @@ -111,7 +111,7 @@ impl IntoResponse for PyMiddlewareException { } } -impl IntoResponse for PyMiddlewareException { +impl IntoResponseUniform for PyMiddlewareException { fn into_response(self) -> http::Response { http::Response::builder() .status(self.status_code) diff --git a/rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs b/rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs index a658e1bb06..7cabcfaf49 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/middleware/layer.rs @@ -14,7 +14,7 @@ use std::{ use aws_smithy_http_server::{ body::{Body, BoxBody}, - response::IntoResponse, + response::IntoResponseUniform, }; use futures::{future::BoxFuture, TryFutureExt}; use http::{Request, Response}; @@ -47,7 +47,7 @@ impl

PyMiddlewareLayer

{ impl Layer for PyMiddlewareLayer

where - PyMiddlewareException: IntoResponse

, + PyMiddlewareException: IntoResponseUniform

, { type Service = PyMiddlewareService; diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 17ff01e961..7695c2a1d2 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -216,10 +216,6 @@ mod tests { "com.amazonaws.ebs", "CompleteSnapshot", ); - - type Input = (); - type Output = (); - type Error = (); } // Apply `Plugin`. diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index 28ad3fd743..169950cfc0 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -6,148 +6,75 @@ use std::{ convert::Infallible, future::Future, - marker::PhantomData, task::{Context, Poll}, }; use futures_util::{future::Map, FutureExt}; use tower::Service; -use super::OperationShape; - /// A utility trait used to provide an even interface for all operation handlers. /// /// See [`operation`](crate::operation) documentation for more info. -pub trait Handler -where - Op: OperationShape, -{ - type Future: Future>; - - fn call(&mut self, input: Op::Input, exts: Exts) -> Self::Future; -} - -/// A utility trait used to provide an even interface over return types `Result`/`Ok`. -trait IntoResult { - fn into_result(self) -> Result; -} - -// We can convert from `Result` to `Result`. -impl IntoResult for Result { - fn into_result(self) -> Result { - self - } -} +pub trait Handler: Clone + Send + 'static { + type Future: Future; -// We can convert from `T` to `Result`. -impl IntoResult for Ok { - fn into_result(self) -> Result { - Ok(self) - } + fn call(self, request: Input) -> Self::Future; } -// fn(Input) -> Output -impl Handler for F -where - Op: OperationShape, - F: Fn(Op::Input) -> Fut, - Fut: Future, - Fut::Output: IntoResult, -{ - type Future = Map Result>; - - fn call(&mut self, input: Op::Input, _exts: ()) -> Self::Future { - (self)(input).map(IntoResult::into_result) - } -} - -// fn(Input, Ext_i) -> Output macro_rules! impl_handler { - ($($var:ident),+) => ( - impl Handler for F + ($($ty: ident),*) => { + impl<$($ty,)* F, Fut> Handler<($($ty,)*)> for F where - Op: OperationShape, - F: Fn(Op::Input, $($var,)*) -> Fut, + F: Fn($($ty),*) -> Fut, + F: Clone + Send + 'static, Fut: Future, - Fut::Output: IntoResult, { - type Future = Map Result>; + type Future = Fut; - fn call(&mut self, input: Op::Input, exts: ($($var,)*)) -> Self::Future { - #[allow(non_snake_case)] - let ($($var,)*) = exts; - (self)(input, $($var,)*).map(IntoResult::into_result) + #[allow(non_snake_case)] + fn call(self, ($($ty,)*): ($($ty,)*)) -> Self::Future { + self($($ty),*) } } - ) -} - -impl_handler!(Exts0); -impl_handler!(Exts0, Exts1); -impl_handler!(Exts0, Exts1, Exts2); -impl_handler!(Exts0, Exts1, Exts2, Exts3); -impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4); -impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5); -impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6); -impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6, Exts7); -impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6, Exts7, Exts8); - -/// An extension trait for [`Handler`]. -pub trait HandlerExt: Handler -where - Op: OperationShape, -{ - /// Convert the [`Handler`] into a [`Service`]. - fn into_service(self) -> IntoService - where - Self: Sized, - { - IntoService { - handler: self, - _operation: PhantomData, - } - } + }; } -impl HandlerExt for H -where - Op: OperationShape, - H: Handler, -{ -} +impl_handler!(T0); +impl_handler!(T0, T2); +impl_handler!(T0, T2, T3); +impl_handler!(T0, T2, T3, T4); +impl_handler!(T0, T2, T3, T4, T5); +impl_handler!(T0, T2, T3, T4, T5, T6); +impl_handler!(T0, T2, T3, T4, T5, T6, T7); +impl_handler!(T0, T2, T3, T4, T5, T6, T7, T8); +impl_handler!(T0, T2, T3, T4, T5, T6, T7, T8, T9); /// A [`Service`] provided for every [`Handler`]. -pub struct IntoService { - pub(crate) handler: H, - pub(crate) _operation: PhantomData, +#[derive(Debug, Clone)] +pub struct IntoService { + handler: H, } -impl Clone for IntoService +impl Service for IntoService where - H: Clone, + H: Handler, + H::Future: Future, { - fn clone(&self) -> Self { - Self { - handler: self.handler.clone(), - _operation: PhantomData, - } - } -} - -impl Service<(Op::Input, Exts)> for IntoService -where - Op: OperationShape, - H: Handler, -{ - type Response = Op::Output; - type Error = Op::Error; - type Future = H::Future; + type Response = ::Output; + type Error = Infallible; + type Future = Map Result>; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { - self.handler.call(input, exts) + fn call(&mut self, request: Input) -> Self::Future { + self.handler.clone().call(request).map(Ok) + } +} + +impl IntoService { + pub fn new(handler: H) -> Self { + Self { handler } } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 8450da8366..5873648686 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -33,10 +33,6 @@ //! //! impl OperationShape for GetShopping { //! const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); -//! -//! type Input = CartIdentifier; -//! type Output = ShoppingCart; -//! type Error = GetShoppingError; //! } //! ``` //! @@ -110,10 +106,6 @@ //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { //! # const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); -//! # -//! # type Input = CartIdentifier; -//! # type Output = ShoppingCart; -//! # type Error = GetShoppingError; //! # } //! # type OpFuture = std::future::Ready>; //! // Construction of an `Operation` from a `Handler`. @@ -122,8 +114,6 @@ //! todo!() //! } //! -//! let operation = GetShopping::from_handler(op_handler); -//! //! // Construction of an `Operation` from a `Service`. //! //! pub struct OpService; @@ -142,8 +132,6 @@ //! } //! } //! -//! let operation = GetShopping::from_service(OpService); -//! //! ``` //! //! ## Upgrading Smithy services to HTTP services @@ -161,11 +149,70 @@ //! [Smithy operation]: https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation mod handler; -mod operation_service; mod shape; mod upgrade; +use std::{ + marker::PhantomData, + task::{Context, Poll}, +}; + pub use handler::*; -pub use operation_service::*; pub use shape::*; +use tower::Service; pub use upgrade::*; + +pub trait FixedService: Service { + type FixedInput; +} + +#[derive(Debug)] +pub struct FixInput { + inner: S, + _input: PhantomData, +} + +impl Clone for FixInput +where + S: Clone, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + _input: PhantomData, + } + } +} + +impl FixInput { + pub fn new(inner: S) -> Self { + Self { + inner, + _input: PhantomData, + } + } +} + +impl Service for FixInput +where + S: Service, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Input) -> Self::Future { + self.inner.call(req) + } +} + +impl FixedService for FixInput +where + S: Service, +{ + type FixedInput = Input; +} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs deleted file mode 100644 index b9a87aa6e9..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::{ - marker::PhantomData, - task::{Context, Poll}, -}; - -use tower::Service; - -use super::OperationShape; - -/// A utility trait used to provide an even interface for all operation services. -/// -/// This serves to take [`Service`]s of the form `Service<(Op::Input, Ext0, Ext1, ...)>` to the canonical -/// representation of `Service<(Input, (Ext0, Ext1, ...))>` inline with -/// [`IntoService`](super::IntoService). -/// -/// See [`operation`](crate::operation) documentation for more info. -pub trait OperationService: Service -where - Op: OperationShape, -{ - type Normalized; - - // Normalize the request type. - fn normalize(input: Op::Input, exts: Exts) -> Self::Normalized; -} - -// `Service` -impl OperationService for S -where - Op: OperationShape, - S: Service, -{ - type Normalized = Op::Input; - - fn normalize(input: Op::Input, _exts: ()) -> Self::Normalized { - input - } -} - -// `Service<(Op::Input, Ext0)>` -impl OperationService for S -where - Op: OperationShape, - S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>, -{ - type Normalized = (Op::Input, Ext0); - - fn normalize(input: Op::Input, exts: (Ext0,)) -> Self::Normalized { - (input, exts.0) - } -} - -// `Service<(Op::Input, Ext0, Ext1)>` -impl OperationService for S -where - Op: OperationShape, - S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>, -{ - type Normalized = (Op::Input, Ext0, Ext1); - - fn normalize(input: Op::Input, exts: (Ext0, Ext1)) -> Self::Normalized { - (input, exts.0, exts.1) - } -} - -/// An extension trait of [`OperationService`]. -pub trait OperationServiceExt: OperationService -where - Op: OperationShape, -{ - /// Convert the [`OperationService`] into a canonicalized [`Service`]. - fn normalize(self) -> Normalize - where - Self: Sized, - { - Normalize { - inner: self, - _operation: PhantomData, - } - } -} - -impl OperationServiceExt for F -where - Op: OperationShape, - F: OperationService, -{ -} - -/// A [`Service`] normalizing the request type of a [`OperationService`]. -#[derive(Debug)] -pub struct Normalize { - pub(crate) inner: S, - pub(crate) _operation: PhantomData, -} - -impl Clone for Normalize -where - S: Clone, -{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - _operation: PhantomData, - } - } -} - -impl Service<(Op::Input, Exts)> for Normalize -where - Op: OperationShape, - S: OperationService, -{ - type Response = S::Response; - type Error = S::Error; - type Future = >::Future; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx) - } - - fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { - let req = S::normalize(input, exts); - self.inner.call(req) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 6d3d774868..4d926eb987 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -3,9 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::marker::PhantomData; - -use super::{Handler, IntoService, Normalize, OperationService}; use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. @@ -14,42 +11,4 @@ use crate::shape_id::ShapeId; pub trait OperationShape { /// The ID of the operation. const ID: ShapeId; - - /// The operation input. - type Input; - /// The operation output. - type Output; - /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error - /// exists. - type Error; -} - -/// An extension trait over [`OperationShape`]. -pub trait OperationShapeExt: OperationShape { - /// Creates a new [`Service`](tower::Service), [`IntoService`], for well-formed [`Handler`]s. - fn from_handler(handler: H) -> IntoService - where - H: Handler, - Self: Sized, - { - IntoService { - handler, - _operation: PhantomData, - } - } - - /// Creates a new normalized [`Service`](tower::Service), [`Normalize`], for well-formed - /// [`Service`](tower::Service)s. - fn from_service(svc: S) -> Normalize - where - S: OperationService, - Self: Sized, - { - Normalize { - inner: svc, - _operation: PhantomData, - } - } } - -impl OperationShapeExt for S where S: OperationShape {} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index 15e87ebc19..7cd458389f 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -21,43 +21,33 @@ use crate::{ runtime_error::InternalFailureException, service::ServiceShape, }; -use super::OperationShape; +use super::{FixedService, OperationShape}; /// A [`Plugin`] responsible for taking an operation [`Service`], accepting and returning Smithy /// types and converting it into a [`Service`] taking and returning [`http`] types. /// /// See [`Upgrade`]. -#[derive(Debug, Clone)] -pub struct UpgradePlugin { - _extractors: PhantomData, -} - -impl Default for UpgradePlugin { - fn default() -> Self { - Self { - _extractors: PhantomData, - } - } -} +#[derive(Debug, Default, Clone)] +pub struct UpgradePlugin; -impl UpgradePlugin { +impl UpgradePlugin { /// Creates a new [`UpgradePlugin`]. pub fn new() -> Self { Self::default() } } -impl Plugin for UpgradePlugin +impl Plugin for UpgradePlugin where Ser: ServiceShape, Op: OperationShape, { - type Output = Upgrade; + type Output = Upgrade; fn apply(&self, inner: T) -> Self::Output { Upgrade { - _protocol: PhantomData, - _input: PhantomData, + _service: PhantomData, + _operation: PhantomData, inner, } } @@ -65,20 +55,20 @@ where /// A [`Service`] responsible for wrapping an operation [`Service`] accepting and returning Smithy /// types, and converting it into a [`Service`] accepting and returning [`http`] types. -pub struct Upgrade { - _protocol: PhantomData, - _input: PhantomData, +pub struct Upgrade { + _service: PhantomData, + _operation: PhantomData, inner: S, } -impl Clone for Upgrade +impl Clone for Upgrade where S: Clone, { fn clone(&self) -> Self { Self { - _protocol: PhantomData, - _input: PhantomData, + _service: PhantomData, + _operation: PhantomData, inner: self.inner.clone(), } } @@ -99,27 +89,27 @@ pin_project! { } } -type InnerAlias = Inner<>::Future, Oneshot>; +type InnerAlias = Inner<>::Future, Oneshot>; pin_project! { /// The [`Service::Future`] of [`Upgrade`]. - pub struct UpgradeFuture + pub struct UpgradeFuture where - Input: FromRequest, - S: Service, + S: FixedService, + S::FixedInput: FromRequest, { service: Option, #[pin] - inner: InnerAlias + inner: InnerAlias } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where - Input: FromRequest, - S: Service, - S::Response: IntoResponse

, - S::Error: IntoResponse

, + S: FixedService, + S::FixedInput: FromRequest, + S::Response: IntoResponse, + S::Error: IntoResponse, { type Output = Result, Infallible>; @@ -155,16 +145,16 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where - Input: FromRequest, - S: Service + Clone, - S::Response: IntoResponse

, - S::Error: IntoResponse

, + S: FixedService + Clone, + S::FixedInput: FromRequest, + S::Response: IntoResponse, + S::Error: IntoResponse, { type Response = http::Response; type Error = Infallible; - type Future = UpgradeFuture; + type Future = UpgradeFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -176,7 +166,7 @@ where UpgradeFuture { service: Some(service), inner: Inner::FromRequest { - inner: >::from_request(req), + inner: >::from_request(req), }, } } @@ -184,25 +174,25 @@ where /// A [`Service`] which always returns an internal failure message and logs an error. #[derive(Copy)] -pub struct MissingFailure

{ - _protocol: PhantomData, +pub struct MissingFailure { + _protocol: PhantomData, } -impl

Default for MissingFailure

{ +impl Default for MissingFailure { fn default() -> Self { Self { _protocol: PhantomData } } } -impl

Clone for MissingFailure

{ +impl Clone for MissingFailure { fn clone(&self) -> Self { MissingFailure { _protocol: PhantomData } } } -impl Service for MissingFailure

+impl Service for MissingFailure where - InternalFailureException: IntoResponse

, + InternalFailureException: IntoResponse, { type Response = http::Response; type Error = Infallible; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index d78ba4c937..e217703610 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -41,8 +41,8 @@ where /// # type Protocol = (); /// # type Operations = Operation; /// # } -/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } -/// # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); } +/// # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); } /// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } /// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } /// use aws_smithy_http_server::plugin::plugin_from_operation_fn; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index f29fac8e50..79932b1194 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -62,7 +62,7 @@ impl ModelMarker for FilterByOperation where Inner: ModelMar /// # impl ServiceShape for PokemonService { const VERSION: Option<&'static str> = None; const ID: ShapeId = ShapeId::new("", "", ""); type Operations = Operation; type Protocol = (); } /// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); } /// # impl Plugin for Pl { type Output = (); fn apply(&self, input: ()) -> Self::Output { input }} /// # let plugin = Pl; /// # let svc = (); diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 5bbeda3eba..7340863a5b 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -79,8 +79,8 @@ //! # type Protocol = (); //! # type Operations = Operation; //! # } -//! # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } -//! # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +//! # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); } +//! # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); } //! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } //! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } //! use aws_smithy_http_server::plugin::plugin_from_operation_fn; @@ -293,9 +293,6 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// # "com.amazonaws.simple", /// # "CheckHealth", /// # ); -/// # type Input = CheckHealthInput; -/// # type Output = CheckHealthOutput; -/// # type Error = std::convert::Infallible; /// # } /// /// /// A model plugin that can only be applied to the `CheckHealth` operation. @@ -328,9 +325,9 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// _exts: PhantomData, /// } /// -/// impl Service<(::Input, Exts)> for CheckHealthService +/// impl Service<(CheckHealthInput, Exts)> for CheckHealthService /// where -/// S: Service<(::Input, Exts)>, +/// S: Service<(CheckHealthInput, Exts)>, /// { /// type Response = S::Response; /// type Error = S::Error; @@ -340,7 +337,7 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// self.inner.poll_ready(cx) /// } /// -/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// fn call(&mut self, req: (CheckHealthInput, Exts)) -> Self::Future { /// let (input, _exts) = &req; /// /// // We have access to `CheckHealth`'s modeled operation input! @@ -394,9 +391,6 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// # "com.amazonaws.simple", /// # "CheckHealth", /// # ); -/// # type Input = CheckHealthInput; -/// # type Output = CheckHealthOutput; -/// # type Error = std::convert::Infallible; /// # } /// /// #[derive(Clone)] @@ -405,9 +399,9 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// _exts: PhantomData, /// } /// -/// impl Service<(::Input, Exts)> for CheckHealthService +/// impl Service<(CheckHealthInput, Exts)> for CheckHealthService /// where -/// S: Service<(::Input, Exts)>, +/// S: Service<(CheckHealthInput, Exts)>, /// { /// type Response = S::Response; /// type Error = S::Error; @@ -417,7 +411,7 @@ impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} /// self.inner.poll_ready(cx) /// } /// -/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// fn call(&mut self, req: (CheckHealthInput, Exts)) -> Self::Future { /// let (input, _exts) = &req; /// /// // We have access to `CheckHealth`'s modeled operation input! diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs index 2cd48f8e61..0f3e0f7240 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs @@ -3,14 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::convert::Infallible; - -use tower::Layer; use tower::Service; -use crate::body::BoxBody; use crate::routing::tiny_map::TinyMap; -use crate::routing::Route; use crate::routing::Router; use http::header::ToStrError; @@ -50,37 +45,9 @@ pub struct AwsJsonRouter { routes: TinyMap, } -impl AwsJsonRouter { - /// Applies a [`Layer`] uniformly to all routes. - pub fn layer(self, layer: L) -> AwsJsonRouter - where - L: Layer, - { - AwsJsonRouter { - routes: self - .routes - .into_iter() - .map(|(key, route)| (key, layer.layer(route))) - .collect(), - } - } - - /// Applies type erasure to the inner route using [`Route::new`]. - pub fn boxed(self) -> AwsJsonRouter> - where - S: Service, Response = http::Response, Error = Infallible>, - S: Send + Clone + 'static, - S::Future: Send + 'static, - { - AwsJsonRouter { - routes: self.routes.into_iter().map(|(key, s)| (key, Route::new(s))).collect(), - } - } -} - impl Router for AwsJsonRouter where - S: Clone, + S: Service, Error = std::convert::Infallible> + Clone, { type Service = S; type Error = Error; @@ -125,6 +92,7 @@ mod tests { use http::{HeaderMap, HeaderValue, Method}; use pretty_assertions::assert_eq; + use tower::service_fn; #[tokio::test] async fn simple_routing() { @@ -132,7 +100,12 @@ mod tests { let router: AwsJsonRouter<_> = routes .clone() .into_iter() - .map(|operation| (operation.to_string(), ())) + .map(|operation| { + ( + operation.to_string(), + service_fn(move |_| async move { Ok(http::Request::new(operation)) }), + ) + }) .collect(); let mut headers = HeaderMap::new(); diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs index 8970d16f30..5ecfabf967 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs @@ -4,7 +4,7 @@ */ use crate::protocol::aws_json_11::AwsJson1_1; -use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::runtime_error::{InternalFailureException, INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; use crate::{extension::RuntimeErrorExtension, protocol::aws_json_10::AwsJson1_0}; use http::StatusCode; @@ -42,19 +42,15 @@ impl RuntimeError { } } -impl IntoResponse for InternalFailureException { +impl IntoResponseUniform for InternalFailureException { fn into_response(self) -> http::Response { - IntoResponse::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) + IntoResponseUniform::::into_response(RuntimeError::InternalFailure( + crate::Error::new(String::new()), + )) } } -impl IntoResponse for InternalFailureException { - fn into_response(self) -> http::Response { - IntoResponse::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) - } -} - -impl IntoResponse for RuntimeError { +impl IntoResponseUniform for RuntimeError { fn into_response(self) -> http::Response { let res = http::Response::builder() .status(self.status_code()) @@ -72,7 +68,7 @@ impl IntoResponse for RuntimeError { } } -impl IntoResponse for RuntimeError { +impl IntoResponseUniform for RuntimeError { fn into_response(self) -> http::Response { let res = http::Response::builder() .status(self.status_code()) diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs index f3093685e2..0a3491efe2 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs @@ -5,14 +5,14 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_0; pub use crate::protocol::aws_json::router::*; -impl IntoResponse for Error { +impl IntoResponseUniform for Error { fn into_response(self) -> http::Response { match self { Error::MethodNotAllowed => method_disallowed(), diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs index 898d8c29af..a34ed787f7 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs @@ -5,14 +5,14 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_1; pub use crate::protocol::aws_json::router::*; -impl IntoResponse for Error { +impl IntoResponseUniform for Error { fn into_response(self) -> http::Response { match self { Error::MethodNotAllowed => method_disallowed(), diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs index fbbb98ee81..18cd4ed805 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs @@ -3,14 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::convert::Infallible; - -use crate::body::BoxBody; use crate::routing::request_spec::Match; use crate::routing::request_spec::RequestSpec; -use crate::routing::Route; use crate::routing::Router; -use tower::Layer; use tower::Service; use thiserror::Error; @@ -35,37 +30,9 @@ pub struct RestRouter { routes: Vec<(RequestSpec, S)>, } -impl RestRouter { - /// Applies a [`Layer`] uniformly to all routes. - pub fn layer(self, layer: L) -> RestRouter - where - L: Layer, - { - RestRouter { - routes: self - .routes - .into_iter() - .map(|(request_spec, route)| (request_spec, layer.layer(route))) - .collect(), - } - } - - /// Applies type erasure to the inner route using [`Route::new`]. - pub fn boxed(self) -> RestRouter> - where - S: Service, Response = http::Response, Error = Infallible>, - S: Send + Clone + 'static, - S::Future: Send + 'static, - { - RestRouter { - routes: self.routes.into_iter().map(|(spec, s)| (spec, Route::new(s))).collect(), - } - } -} - impl Router for RestRouter where - S: Clone, + S: Service, Error = std::convert::Infallible> + Clone, { type Service = S; type Error = Error; @@ -115,11 +82,12 @@ mod tests { use crate::{protocol::test_helpers::req, routing::request_spec::*}; use http::Method; + use tower::service_fn; // This test is a rewrite of `mux.spec.ts`. // https://github.com/awslabs/smithy-typescript/blob/fbf97a9bf4c1d8cf7f285ea7c24e1f0ef280142a/smithy-typescript-ssdk-libs/server-common/src/httpbinding/mux.spec.ts - #[test] - fn simple_routing() { + #[tokio::test] + async fn simple_routing() { let request_specs: Vec<(RequestSpec, &'static str)> = vec![ ( RequestSpec::from_parts( @@ -169,7 +137,10 @@ mod tests { // Test both RestJson1 and RestXml routers. let router: RestRouter<_> = request_specs .into_iter() - .map(|(spec, svc_name)| (spec, svc_name)) + .map(|(spec, svc_name)| { + let svc = service_fn(move |_| async move { Ok(http::Request::new(svc_name)) }); + (spec, svc) + }) .collect(); let hits = vec![ @@ -186,7 +157,14 @@ mod tests { ("QueryKeyOnly", Method::POST, "/query_key_only?foo=&"), ]; for (svc_name, method, uri) in &hits { - assert_eq!(router.match_route(&req(method, uri, None)).unwrap(), *svc_name); + let name = router + .match_route(&req(method, uri, None)) + .unwrap() + .call(http::Request::new(())) + .await + .unwrap() + .into_body(); + assert_eq!(name, *svc_name); } for (_, _, uri) in hits { @@ -257,7 +235,10 @@ mod tests { let router: RestRouter<_> = request_specs .into_iter() - .map(|(spec, svc_name)| (spec, svc_name)) + .map(|(spec, svc_name)| { + let svc = service_fn(move |_| async move { Ok(http::Request::new(svc_name)) }); + (spec, svc) + }) .collect(); let hits = vec![ @@ -266,8 +247,15 @@ mod tests { ("B1", Method::GET, "/b/foo/bar/baz"), ("B2", Method::GET, "/b/foo?q=baz"), ]; - for (svc_name, method, uri) in hits { - assert_eq!(router.match_route(&req(&method, uri, None)).unwrap(), svc_name); + for (svc_name, method, uri) in hits.into_iter() { + let name = router + .match_route(&req(&method, uri, None)) + .unwrap() + .call(http::Request::new(())) + .await + .unwrap() + .into_body(); + assert_eq!(name, svc_name); } } } diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs index 76d3d69277..737a91148b 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs @@ -5,14 +5,14 @@ use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestJson1; pub use crate::protocol::rest::router::*; -impl IntoResponse for Error { +impl IntoResponseUniform for Error { fn into_response(self) -> http::Response { match self { Error::NotFound => http::Response::builder() diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs index 5faf3ea8cf..792c3072dc 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs @@ -34,8 +34,10 @@ use super::rejection::ResponseRejection; use super::RestJson1; use crate::extension::RuntimeErrorExtension; use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::runtime_error::InternalFailureException; use crate::runtime_error::INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE; +use crate::service::ServiceShape; use http::StatusCode; #[derive(Debug)] @@ -80,13 +82,25 @@ impl RuntimeError { } } -impl IntoResponse for InternalFailureException { +impl IntoResponse for InternalFailureException { fn into_response(self) -> http::Response { - IntoResponse::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) + IntoResponseUniform::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) } } -impl IntoResponse for RuntimeError { +impl IntoResponse for RuntimeError +where + Ser: ServiceShape, + Self: IntoResponseUniform, +{ + fn into_response(self) -> http::Response { + IntoResponseUniform::::into_response(RuntimeError::InternalFailure(crate::Error::new( + String::new(), + ))) + } +} + +impl IntoResponseUniform for RuntimeError { fn into_response(self) -> http::Response { let res = http::Response::builder() .status(self.status_code()) diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs index d8a0905a7f..1ded62867f 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs @@ -6,7 +6,7 @@ use crate::body::empty; use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::response::IntoResponse; +use crate::response::IntoResponseUniform; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestXml; @@ -14,7 +14,7 @@ use super::RestXml; pub use crate::protocol::rest::router::*; /// An AWS REST routing error. -impl IntoResponse for Error { +impl IntoResponseUniform for Error { fn into_response(self) -> http::Response { match self { Error::NotFound => http::Response::builder() diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs index d4ecb5c218..de73b062f8 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs @@ -4,8 +4,9 @@ */ use crate::protocol::rest_xml::RestXml; -use crate::response::IntoResponse; +use crate::response::{IntoResponse, IntoResponseUniform}; use crate::runtime_error::InternalFailureException; +use crate::service::ServiceShape; use crate::{extension::RuntimeErrorExtension, runtime_error::INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; use http::StatusCode; @@ -42,13 +43,25 @@ impl RuntimeError { } } -impl IntoResponse for InternalFailureException { +impl IntoResponseUniform for InternalFailureException { fn into_response(self) -> http::Response { - IntoResponse::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) + IntoResponseUniform::::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new()))) } } -impl IntoResponse for RuntimeError { +impl IntoResponse for RuntimeError +where + Ser: ServiceShape, + Self: IntoResponseUniform, +{ + fn into_response(self) -> http::Response { + IntoResponseUniform::::into_response(RuntimeError::InternalFailure(crate::Error::new( + String::new(), + ))) + } +} + +impl IntoResponseUniform for RuntimeError { fn into_response(self) -> http::Response { let res = http::Response::builder() .status(self.status_code()) diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 1f1e247435..491c53d322 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -36,9 +36,9 @@ pub mod any_rejections { $($var ($var),)* } - impl IntoResponse

for $name<$($var),*> + impl IntoResponse for $name<$($var),*> where - $($var: IntoResponse

,)* + $($var: IntoResponse,)* { #[allow(non_snake_case)] fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs b/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs index 7a48e70421..8351ec408b 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/connect_info.rs @@ -28,7 +28,7 @@ use super::{internal_server_error, FromParts}; )] pub struct MissingConnectInfo; -impl IntoResponse for MissingConnectInfo { +impl IntoResponse for MissingConnectInfo { fn into_response(self) -> http::Response { internal_server_error() } @@ -45,7 +45,7 @@ pub struct ConnectInfo( pub T, ); -impl FromParts

for ConnectInfo +impl FromParts for ConnectInfo where T: Send + Sync + 'static, { diff --git a/rust-runtime/aws-smithy-http-server/src/request/extension.rs b/rust-runtime/aws-smithy-http-server/src/request/extension.rs index b7f10683eb..3b32aacd9a 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/extension.rs @@ -81,13 +81,13 @@ impl Deref for Extension { #[error("the `Extension` is not present in the `http::Request`")] pub struct MissingExtension; -impl IntoResponse for MissingExtension { +impl IntoResponse for MissingExtension { fn into_response(self) -> http::Response { internal_server_error() } } -impl FromParts for Extension +impl FromParts for Extension where T: Send + Sync + 'static, { diff --git a/rust-runtime/aws-smithy-http-server/src/request/lambda.rs b/rust-runtime/aws-smithy-http-server/src/request/lambda.rs index 6088ee8f94..4321c2ba33 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/lambda.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/lambda.rs @@ -25,13 +25,13 @@ use crate::{body::BoxBody, response::IntoResponse}; #[error("`Context` is not present in the `http::Request` extensions - consider using `aws_smithy_http_server::routing::LambdaHandler`")] pub struct MissingContext; -impl IntoResponse for MissingContext { +impl IntoResponse for MissingContext { fn into_response(self) -> http::Response { internal_server_error() } } -impl

FromParts

for Context { +impl FromParts for Context { type Rejection = MissingContext; fn from_parts(parts: &mut http::request::Parts) -> Result { @@ -56,13 +56,13 @@ pub struct MissingGatewayContextV1 { inner: MissingGatewayContextTypeV1, } -impl IntoResponse for MissingGatewayContextV1 { +impl IntoResponse for MissingGatewayContextV1 { fn into_response(self) -> http::Response { internal_server_error() } } -impl

FromParts

for ApiGatewayProxyRequestContext { +impl FromParts for ApiGatewayProxyRequestContext { type Rejection = MissingGatewayContextV1; fn from_parts(parts: &mut http::request::Parts) -> Result { @@ -96,13 +96,13 @@ pub struct MissingGatewayContextV2 { inner: MissingGatewayContextTypeV2, } -impl IntoResponse for MissingGatewayContextV2 { +impl IntoResponse for MissingGatewayContextV2 { fn into_response(self) -> http::Response { internal_server_error() } } -impl

FromParts

for ApiGatewayV2httpRequestContext { +impl FromParts for ApiGatewayV2httpRequestContext { type Rejection = MissingGatewayContextV2; fn from_parts(parts: &mut http::request::Parts) -> Result { diff --git a/rust-runtime/aws-smithy-http-server/src/request/mod.rs b/rust-runtime/aws-smithy-http-server/src/request/mod.rs index be507b804f..7bd95ff903 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/mod.rs @@ -79,15 +79,15 @@ fn internal_server_error() -> http::Response { /// Provides a protocol aware extraction from a [`Request`]. This borrows the [`Parts`], in contrast to /// [`FromRequest`] which consumes the entire [`http::Request`] including the body. -pub trait FromParts: Sized { +pub trait FromParts: Sized { /// The type of the extraction failures. - type Rejection: IntoResponse; + type Rejection: IntoResponse; /// Extracts `self` from a [`Parts`] synchronously. fn from_parts(parts: &mut Parts) -> Result; } -impl

FromParts

for () { +impl FromParts for () { type Rejection = Infallible; fn from_parts(_parts: &mut Parts) -> Result { @@ -95,9 +95,9 @@ impl

FromParts

for () { } } -impl FromParts

for (T,) +impl FromParts for (T,) where - T: FromParts

, + T: FromParts, { type Rejection = T::Rejection; @@ -108,9 +108,9 @@ where macro_rules! impl_from_parts { ($error_name:ident, $($var:ident),+) => ( - impl FromParts

for ($($var),*) + impl FromParts for ($($var),*) where - $($var: FromParts

,)* + $($var: FromParts,)* { type Rejection = any_rejections::$error_name<$($var::Rejection),*>; @@ -137,9 +137,9 @@ impl_from_parts!(Eight, A, B, C, D, E, F, G, H); /// /// This should not be implemented by hand. Code generation should implement this for your operations input. To extract /// items from a HTTP request [`FromParts`] should be used. -pub trait FromRequest: Sized { +pub trait FromRequest: Sized { /// The type of the extraction failures. - type Rejection: IntoResponse; + type Rejection: IntoResponse; /// The type of the extraction [`Future`]. type Future: Future>; @@ -147,39 +147,64 @@ pub trait FromRequest: Sized { fn from_request(request: Request) -> Self::Future; } -impl FromRequest for (T1,) -where - T1: FromRequest, -{ - type Rejection = T1::Rejection; - type Future = MapOk (T1,)>; - - fn from_request(request: Request) -> Self::Future { - T1::from_request(request).map_ok(|t1| (t1,)) - } -} - -impl FromRequest for (T1, T2) -where - T1: FromRequest, - T2: FromParts

, -{ - type Rejection = any_rejections::Two; - type Future = TryJoin Self::Rejection>, Ready>>; - - fn from_request(request: Request) -> Self::Future { - let (mut parts, body) = request.into_parts(); - let t2_result = T2::from_parts(&mut parts).map_err(any_rejections::Two::B); - try_join( - T1::from_request(Request::from_parts(parts, body)).map_err(any_rejections::Two::A), - ready(t2_result), - ) - } +macro_rules! impl_from_request { + ($($var:ident),*) => ( + #[allow(unused_parens)] + impl FromRequest for (T, $($var),*) + where + T: FromRequest, + $( + $var: FromParts + ),* + { + type Rejection = any_rejections::Two< + T::Rejection, + <($($var),*) as FromParts>::Rejection + >; + + type Future = MapOk< + TryJoin< + MapErr Self::Rejection>, + Ready> + >, + fn((T, ($($var),*))) -> Self + >; + + #[allow(non_snake_case)] + fn from_request(request: Request) -> Self::Future { + let (mut parts, body) = request.into_parts(); + let vars = <($($var),*) as FromParts>::from_parts(&mut parts).map_err(any_rejections::Two::B); + + let request = Request::from_parts(parts, body); + // This is required to coerce the closure + let map_err: fn(_) -> _ = |err| any_rejections::Two::A(err); + let t = T::from_request(request).map_err(map_err); + let closure: fn((T, ($($var),*))) -> (T, $($var),*) = |x| { + let (t, ($($var),*)) = x; + (t, $($var),*) + }; + try_join( + t, + ready(vars) + ).map_ok(closure) + } + } + ) } -impl FromParts

for Option +impl_from_request!(); +impl_from_request!(T0); +impl_from_request!(T0, T1); +impl_from_request!(T0, T1, T2); +impl_from_request!(T0, T1, T2, T3); +impl_from_request!(T0, T1, T2, T3, T4); +impl_from_request!(T0, T1, T2, T3, T4, T5); +impl_from_request!(T0, T1, T2, T3, T4, T5, T6); +impl_from_request!(T0, T1, T2, T3, T4, T5, T6, T7); + +impl FromParts for Option where - T: FromParts

, + T: FromParts, { type Rejection = Infallible; @@ -188,9 +213,9 @@ where } } -impl FromParts

for Result +impl FromParts for Result where - T: FromParts

, + T: FromParts, { type Rejection = Infallible; diff --git a/rust-runtime/aws-smithy-http-server/src/request/request_id.rs b/rust-runtime/aws-smithy-http-server/src/request/request_id.rs index 7d6ee70ab7..979f7ac3f3 100644 --- a/rust-runtime/aws-smithy-http-server/src/request/request_id.rs +++ b/rust-runtime/aws-smithy-http-server/src/request/request_id.rs @@ -92,7 +92,7 @@ impl Display for ServerRequestId { } } -impl

FromParts

for ServerRequestId { +impl FromParts for ServerRequestId { type Rejection = MissingServerRequestId; fn from_parts(parts: &mut Parts) -> Result { @@ -188,7 +188,7 @@ where } } -impl IntoResponse for MissingServerRequestId { +impl IntoResponse for MissingServerRequestId { fn into_response(self) -> http::Response { internal_server_error() } diff --git a/rust-runtime/aws-smithy-http-server/src/response.rs b/rust-runtime/aws-smithy-http-server/src/response.rs index 75a5be9759..938e562b66 100644 --- a/rust-runtime/aws-smithy-http-server/src/response.rs +++ b/rust-runtime/aws-smithy-http-server/src/response.rs @@ -37,13 +37,31 @@ use crate::body::BoxBody; pub type Response = http::Response; /// A protocol aware function taking `self` to [`http::Response`]. -pub trait IntoResponse { +pub trait IntoResponse { /// Performs a conversion into a [`http::Response`]. fn into_response(self) -> http::Response; } -impl

IntoResponse

for std::convert::Infallible { +impl IntoResponse for std::convert::Infallible { fn into_response(self) -> http::Response { match self {} } } + +impl IntoResponse for Result +where + T: IntoResponse, + E: IntoResponse, +{ + fn into_response(self) -> http::Response { + match self { + Ok(ok) => ok.into_response(), + Err(err) => err.into_response(), + } + } +} + +pub trait IntoResponseUniform { + /// Performs a conversion into a [`http::Response`]. + fn into_response(self) -> http::Response; +} diff --git a/rust-runtime/aws-smithy-http-server/src/routing/mod.rs b/rust-runtime/aws-smithy-http-server/src/routing/mod.rs index 4f0cb8c0fa..cafe4ab68d 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/mod.rs @@ -21,29 +21,18 @@ mod route; pub(crate) mod tiny_map; use std::{ + convert::Infallible, error::Error, fmt, - future::{ready, Future, Ready}, - marker::PhantomData, + future::Future, pin::Pin, task::{Context, Poll}, }; -use bytes::Bytes; -use futures_util::{ - future::{Either, MapOk}, - TryFutureExt, -}; -use http::Response; -use http_body::Body as HttpBody; use tower::{util::Oneshot, Service, ServiceExt}; use tracing::debug; -use crate::{ - body::{boxed, BoxBody}, - error::BoxError, - response::IntoResponse, -}; +use crate::body::BoxBody; #[cfg(feature = "aws-lambda")] #[cfg_attr(docsrs, doc(cfg(feature = "aws-lambda")))] @@ -67,7 +56,7 @@ pub(crate) fn method_disallowed() -> http::Response { /// An interface for retrieving an inner [`Service`] given a [`http::Request`]. pub trait Router { - type Service; + type Service: Service, Error = Infallible>; type Error; /// Matches a [`http::Request`] to a target [`Service`]. @@ -77,114 +66,103 @@ pub trait Router { /// A [`Service`] using the [`Router`] `R` to redirect messages to specific routes. /// /// The `Protocol` parameter is used to determine the serialization of errors. -pub struct RoutingService { +pub struct RoutingService { router: R, - _protocol: PhantomData, } -impl fmt::Debug for RoutingService +impl fmt::Debug for RoutingService where R: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RoutingService") - .field("router", &self.router) - .field("_protocol", &self._protocol) - .finish() + f.debug_struct("RoutingService").field("router", &self.router).finish() } } -impl Clone for RoutingService +impl Clone for RoutingService where R: Clone, { fn clone(&self) -> Self { Self { router: self.router.clone(), - _protocol: PhantomData, } } } -impl RoutingService { +impl RoutingService { /// Creates a [`RoutingService`] from a [`Router`]. pub fn new(router: R) -> Self { - Self { - router, - _protocol: PhantomData, - } + Self { router } } +} - /// Maps a [`Router`] using a closure. - pub fn map(self, f: F) -> RoutingService - where - F: FnOnce(R) -> RNew, - { - RoutingService { - router: f(self.router), - _protocol: PhantomData, - } +pin_project_lite::pin_project! { + #[project = RoutingFutureInnerProj] + enum RoutingFutureInner where R: Router { + Oneshot { + #[pin] + oneshot: Oneshot> + }, + Error { error: Option } } } -type EitherOneshotReady = Either< - MapOk>, fn(>>::Response) -> http::Response>, - Ready, >>::Error>>, ->; - pin_project_lite::pin_project! { - pub struct RoutingFuture where S: Service> { + pub struct RoutingFuture where R: Router { #[pin] - inner: EitherOneshotReady + inner: RoutingFutureInner } } -impl RoutingFuture +impl RoutingFuture where - S: Service>, + R: Router, { /// Creates a [`RoutingFuture`] from [`ServiceExt::oneshot`]. - pub(super) fn from_oneshot(future: Oneshot>) -> Self - where - S: Service, Response = http::Response>, - RespB: HttpBody + Send + 'static, - RespB::Error: Into, - { + pub(super) fn from_oneshot(oneshot: Oneshot>) -> Self { Self { - inner: Either::Left(future.map_ok(|x| x.map(boxed))), + inner: RoutingFutureInner::Oneshot { oneshot }, } } /// Creates a [`RoutingFuture`] from [`Service::Response`]. - pub(super) fn from_response(response: http::Response) -> Self { + pub(super) fn from_error(error: R::Error) -> Self { Self { - inner: Either::Right(ready(Ok(response))), + inner: RoutingFutureInner::Error { error: Some(error) }, } } } -impl Future for RoutingFuture +impl Future for RoutingFuture where - S: Service>, + R: Router, { - type Output = Result, S::Error>; + type Output = Result<>>::Response, R::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.project().inner.poll(cx) + let this = self.project(); + let inner = this.inner.project(); + use RoutingFutureInnerProj::*; + match inner { + Oneshot { oneshot } => oneshot.poll(cx).map_err(|err| match err {}), + Error { error } => { + let error = error.take().expect("futures should not be polled after completion"); + Poll::Ready(Err(error)) + } + } } } -impl Service> for RoutingService +impl Service> for RoutingService where R: Router, - R::Service: Service, Response = http::Response> + Clone, - R::Error: IntoResponse

+ Error, - RespB: HttpBody + Send + 'static, - RespB::Error: Into, + R::Service: Clone, + R::Error: Error, { - type Response = Response; - type Error = >>::Error; - type Future = RoutingFuture; + type Response = >>::Response; + type Error = R::Error; + type Future = RoutingFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -197,7 +175,7 @@ where // Failed to route, use the `R::Error`s `IntoResponse

`. Err(error) => { debug!(%error, "failed to route"); - RoutingFuture::from_response(error.into_response()) + RoutingFuture::from_error(error) } } }