aws_smithy_http_server/operation/shape.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use std::future::Future;
7use std::marker::PhantomData;
8use std::pin::Pin;
9
10use super::{Handler, IntoService, Normalize, OperationService};
11use crate::body::RequestBody;
12use crate::response::Response;
13use crate::schema::OperationSchema;
14use crate::schema::{DeserializeError, SharedServerProtocol};
15use crate::shape_id::ShapeId;
16use aws_smithy_runtime_api::http::Request;
17
18/// Models the [Smithy Operation shape].
19///
20/// [Smithy Operation shape]: https://smithy.io/2.0/spec/service-types.html#operation
21pub trait OperationShape {
22 /// The ID of the operation.
23 const ID: ShapeId;
24
25 /// The operation input.
26 type Input;
27 /// The operation output.
28 type Output;
29 /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error
30 /// exists.
31 type Error;
32}
33
34/// Associates a generated operation marker with its schema descriptor.
35pub trait SchemaOperationShape: OperationShape {
36 const SCHEMA: &'static OperationSchema<'static>;
37}
38
39/// The future returned by [`StreamingOperationShape::deserialize_streaming_input`].
40pub type StreamingInputFuture<I> = Pin<Box<dyn Future<Output = Result<I, DeserializeError>> + Send>>;
41
42/// The generated glue between the HTTP bodies and the streaming members of an operation with an
43/// event stream or streaming blob on either side.
44///
45/// Both halves work through the erased protocol handle selected by routing: the generated
46/// marshallers and unmarshallers ask it for the payload codec and the event media type, and
47/// [`EventStreamFraming::initial_messages_in_frames`](crate::schema::EventStreamFraming::initial_messages_in_frames) decides at runtime whether the non-stream
48/// members travel in `initial-request` and `initial-response` frames.
49///
50/// An operation that streams on one side only implements the other half in terms of the
51/// collected path: [`DeserializableShape`](crate::schema::DeserializableShape) for the input,
52/// [`ServerProtocol::serialize_response`](crate::schema::ServerProtocol::serialize_response) for the output.
53pub trait StreamingOperationShape: SchemaOperationShape {
54 /// Reads the input from an owned request, including its routing extensions.
55 ///
56 /// Generated glue prepares HTTP bindings and the live stream inside the returned future.
57 /// Request-body collection limits come from the routed operation's request extension.
58 fn deserialize_streaming_input(
59 request: Request<RequestBody>,
60 protocol: SharedServerProtocol,
61 ) -> StreamingInputFuture<Self::Input>;
62
63 /// Serializes the output: the streaming member becomes the body, the rest the response head.
64 ///
65 /// Response construction is synchronous; the body streams asynchronously, including document-bound blobs.
66 fn serialize_streaming_output(output: Self::Output, protocol: &SharedServerProtocol) -> Response;
67}
68
69/// An extension trait over [`OperationShape`].
70pub trait OperationShapeExt: OperationShape {
71 /// Creates a new [`Service`](tower::Service), [`IntoService`], for well-formed [`Handler`]s.
72 fn from_handler<H, Exts>(handler: H) -> IntoService<Self, H>
73 where
74 H: Handler<Self, Exts>,
75 Self: Sized,
76 {
77 IntoService {
78 handler,
79 _operation: PhantomData,
80 }
81 }
82
83 /// Creates a new normalized [`Service`](tower::Service), [`Normalize`], for well-formed
84 /// [`Service`](tower::Service)s.
85 fn from_service<S, Exts>(svc: S) -> Normalize<Self, S>
86 where
87 S: OperationService<Self, Exts>,
88 Self: Sized,
89 {
90 Normalize {
91 inner: svc,
92 _operation: PhantomData,
93 }
94 }
95}
96
97impl<S> OperationShapeExt for S where S: OperationShape {}