Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove PollError from server type parameters #2457

Merged
merged 9 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,13 @@ author = "david-perez"
references = ["smithy-rs#2676", "smithy-rs#2685"]
meta = { "breaking" = true, "tada" = false, "bug" = false }

[[smithy-rs]]
message = """Remove `PollError` from an operations `Service::Error`.

Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to
[`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service)
no longer requires `Service::Error = OperationError<Op::Error, PollError>`, instead requiring just `Service::Error = Op::Error`.
"""
references = ["smithy-rs#2457"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" }
author = "hlbarber"
13 changes: 5 additions & 8 deletions rust-runtime/aws-smithy-http-server/src/operation/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ use std::{
task::{Context, Poll},
};

use futures_util::{
future::{Map, MapErr},
FutureExt, TryFutureExt,
};
use futures_util::{future::Map, FutureExt};
use tower::Service;

use super::{OperationError, OperationShape};
use super::OperationShape;

/// A utility trait used to provide an even interface for all operation handlers.
///
Expand Down Expand Up @@ -143,14 +140,14 @@ where
H: Handler<Op, Exts>,
{
type Response = Op::Output;
type Error = OperationError<Op::Error, Infallible>;
type Future = MapErr<H::Future, fn(Op::Error) -> Self::Error>;
type Error = Op::Error;
type Future = H::Future;

fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}

fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future {
self.handler.call(input, exts).map_err(OperationError::Model)
self.handler.call(input, exts)
}
}
44 changes: 12 additions & 32 deletions rust-runtime/aws-smithy-http-server/src/operation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,13 @@
//! ## [`OperationService`]
//!
//! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with
//! `Response = Op::Output`, and `Error = OperationError<Op::Error, PollError>`.
//!
//! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom
//! [`Service::poll_ready`](tower::Service::poll_ready) implementation.
//! `Response = Op::Output`, and `Error = Op::Error`.
//!
//! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]:
//!
//! - `Service<CartIdentifier, Response = ShoppingCart, Error = OperationError<Infallible, Infallible>>`.
//! - `Service<(CartIdentifier, Extension<Context>), Response = ShoppingCart, Error = OperationError<GetShoppingCartError, Infallible>>`.
//! - `Service<(CartIdentifier, Extension<Context>, Extension<ExtraContext>), Response = ShoppingCart, Error = OperationError<GetShoppingCartError, PollError>)`.
//! - `Service<CartIdentifier, Response = ShoppingCart, Error = Infallible>`.
//! - `Service<(CartIdentifier, Extension<Context>), Response = ShoppingCart, Error = GetShoppingCartError>`.
//! - `Service<(CartIdentifier, Extension<Context>, Extension<ExtraContext>), Response = ShoppingCart, Error = GetShoppingCartError)`.
//!
//! Notice the parallels between [`OperationService`] and [`Handler`].
//!
Expand All @@ -116,7 +113,7 @@
//! # type Output = ShoppingCart;
//! # type Error = GetShoppingError;
//! # }
//! # type OpFuture = std::future::Ready<Result<ShoppingCart, OperationError<GetShoppingError, PollError>>>;
//! # type OpFuture = std::future::Ready<Result<ShoppingCart, GetShoppingError>>;
//! // Construction of an `Operation` from a `Handler`.
//!
//! async fn op_handler(input: CartIdentifier) -> Result<ShoppingCart, GetShoppingError> {
Expand All @@ -127,13 +124,11 @@
//!
//! // Construction of an `Operation` from a `Service`.
//!
//! pub struct PollError;
//!
//! pub struct OpService;
//!
//! impl Service<CartIdentifier> for OpService {
//! type Response = ShoppingCart;
//! type Error = OperationError<GetShoppingError, PollError>;
//! type Error = GetShoppingError;
//! type Future = OpFuture;
//!
//! fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
Expand All @@ -155,19 +150,13 @@
//!
//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is
//! constructed they are converted to a canonical form
//! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError<Op::Error, PollError>>`. The
//! `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The
//! [`UpgradeLayer`] acts upon such services by converting them to
//! `Service<http::Request, Response = http::Response, Error = PollError>`.
//! `Service<http::Request, Response = http::Response, Error = Infallible>`.
//!
//! Note that the `PollError` is still exposed, for two reasons:
//!
//! - Smithy is agnostic to `PollError` and therefore we have no prescribed way to serialize it to a [`http::Response`]
//! , unlike the operation errors.
//! - The intention of `PollError` is to signal that the underlying service is no longer able to take requests, so
//! should be discarded. See [`Service::poll_ready`](tower::Service::poll_ready).
//!
//! The [`UpgradeLayer`] and it's [`Layer::Service`](tower::Layer::Service) [`Upgrade`] are both parameterized by a
//! protocol. This allows for upgrading to `Service<http::Request, Response = http::Response, Error = PollError>` to be
//! protocol. This allows for upgrading to `Service<http::Request, Response = http::Response, Error = Infallible>` to be
//! protocol dependent.
//!
//! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`](tower::Layer) `L`. The
Expand Down Expand Up @@ -208,15 +197,15 @@ impl<S, L> Operation<S, L> {
}
}

impl<Op, S, PollError> Operation<Normalize<Op, S, PollError>> {
impl<Op, S> Operation<Normalize<Op, S>> {
/// Creates an [`Operation`] from a [`Service`](tower::Service).
pub fn from_service<Exts>(inner: S) -> Self
where
Op: OperationShape,
S: OperationService<Op, Exts, PollError>,
S: OperationService<Op, Exts>,
{
Self {
inner: inner.canonicalize(),
inner: inner.normalize(),
layer: Identity::new(),
}
}
Expand All @@ -235,12 +224,3 @@ impl<Op, H> Operation<IntoService<Op, H>> {
}
}
}

/// The operation [`Service`](tower::Service) has two classes of failure modes - those specified by the Smithy model
/// and those associated with [`Service::poll_ready`](tower::Service::poll_ready).
pub enum OperationError<ModelError, PollError> {
/// An error modelled by the Smithy model occurred.
Model(ModelError),
/// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred.
PollReady(PollError),
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{

use tower::Service;

use super::{OperationError, OperationShape};
use super::OperationShape;

/// A utility trait used to provide an even interface for all operation services.
///
Expand All @@ -19,8 +19,7 @@ use super::{OperationError, OperationShape};
/// [`IntoService`](super::IntoService).
///
/// See [`operation`](crate::operation) documentation for more info.
pub trait OperationService<Op, Exts, PollError>:
Service<Self::Normalized, Response = Op::Output, Error = OperationError<Op::Error, PollError>>
pub trait OperationService<Op, Exts>: Service<Self::Normalized, Response = Op::Output, Error = Op::Error>
where
Op: OperationShape,
{
Expand All @@ -31,10 +30,10 @@ where
}

// `Service<Op::Input>`
impl<Op, S, PollError> OperationService<Op, (), PollError> for S
impl<Op, S> OperationService<Op, ()> for S
where
Op: OperationShape,
S: Service<Op::Input, Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
S: Service<Op::Input, Response = Op::Output, Error = Op::Error>,
{
type Normalized = Op::Input;

Expand All @@ -44,10 +43,10 @@ where
}

// `Service<(Op::Input, Ext0)>`
impl<Op, Ext0, S, PollError> OperationService<Op, (Ext0,), PollError> for S
impl<Op, Ext0, S> OperationService<Op, (Ext0,)> for S
where
Op: OperationShape,
S: Service<(Op::Input, Ext0), Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>,
{
type Normalized = (Op::Input, Ext0);

Expand All @@ -57,10 +56,10 @@ where
}

// `Service<(Op::Input, Ext0, Ext1)>`
impl<Op, Ext0, Ext1, S, PollError> OperationService<Op, (Ext0, Ext1), PollError> for S
impl<Op, Ext0, Ext1, S> OperationService<Op, (Ext0, Ext1)> for S
where
Op: OperationShape,
S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = OperationError<Op::Error, PollError>>,
S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>,
{
type Normalized = (Op::Input, Ext0, Ext1);

Expand All @@ -70,55 +69,52 @@ where
}

/// An extension trait of [`OperationService`].
pub trait OperationServiceExt<Op, Exts, PollError>: OperationService<Op, Exts, PollError>
pub trait OperationServiceExt<Op, Exts>: OperationService<Op, Exts>
where
Op: OperationShape,
{
/// Convert the [`OperationService`] into a canonicalized [`Service`].
fn canonicalize(self) -> Normalize<Op, Self, PollError>
fn normalize(self) -> Normalize<Op, Self>
where
Self: Sized,
{
Normalize {
inner: self,
_operation: PhantomData,
_poll_error: PhantomData,
}
}
}

impl<F, Op, Exts, PollError> OperationServiceExt<Op, Exts, PollError> for F
impl<F, Op, Exts> OperationServiceExt<Op, Exts> for F
where
Op: OperationShape,
F: OperationService<Op, Exts, PollError>,
F: OperationService<Op, Exts>,
{
}

/// A [`Service`] normalizing the request type of a [`OperationService`].
#[derive(Debug)]
pub struct Normalize<Op, S, PollError> {
pub struct Normalize<Op, S> {
inner: S,
_operation: PhantomData<Op>,
_poll_error: PhantomData<PollError>,
}

impl<Op, S, PollError> Clone for Normalize<Op, S, PollError>
impl<Op, S> Clone for Normalize<Op, S>
where
S: Clone,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_operation: PhantomData,
_poll_error: PhantomData,
}
}
}

impl<Op, S, Exts, PollError> Service<(Op::Input, Exts)> for Normalize<Op, S, PollError>
impl<Op, S, Exts> Service<(Op::Input, Exts)> for Normalize<Op, S>
where
Op: OperationShape,
S: OperationService<Op, Exts, PollError>,
S: OperationService<Op, Exts>,
{
type Response = S::Response;
type Error = S::Error;
Expand Down
4 changes: 2 additions & 2 deletions rust-runtime/aws-smithy-http-server/src/operation/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ pub trait OperationShapeExt: OperationShape {
}

/// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s.
fn from_service<S, Exts, PollError>(svc: S) -> Operation<Normalize<Self, S, PollError>>
fn from_service<S, Exts>(svc: S) -> Operation<Normalize<Self, S>>
where
S: OperationService<Self, Exts, PollError>,
S: OperationService<Self, Exts>,
Self: Sized,
{
Operation::from_service(svc)
Expand Down
Loading