pub trait ServerProtocol:
Send
+ Sync
+ Debug
+ 'static {
// Required methods
fn protocol_id(&self) -> &'static ShapeId<'static>;
fn deserialize_request<'a>(
&'a self,
input: &Schema<'_>,
request: &'a Request<Bytes>,
) -> Result<Box<dyn ShapeDeserializer + 'a>, DeserializeError>;
fn serialize_response(
&self,
output: &Schema<'_>,
value: &dyn SerializableStruct,
) -> Response;
fn serialize_streaming_response(
&self,
output: &Schema<'_>,
value: &dyn SerializableStruct,
streaming_member: &Schema<'_>,
body: Option<BoxBody>,
) -> Response;
fn serialize_error(&self, error: &dyn HttpModeledError) -> Response;
fn serialize_runtime_error(&self, error: RuntimeError) -> Response;
fn serialize_rejection(&self, err: DeserializeError) -> Response;
// Provided methods
fn validate_request_headers(
&self,
_operation: &OperationSchema<'_>,
_headers: &Headers,
) -> Result<(), DeserializeError> { ... }
fn request_body_requirement(
&self,
_operation: &OperationSchema<'_>,
) -> BodyDirective { ... }
fn serialize_routing_error(&self, err: &RoutingError) -> Response { ... }
}Expand description
Schema-driven serialization for one protocol, keyed on struct schemas.
The request side takes the operation’s input schema, the response side its output schema,
mirroring the client’s ClientProtocolInner with the two directions swapped. The trait is
object-safe: routing stores the protocol in a private request-extension carrier for
SelectedOperation. Everything after routing works
through that erased handle, so nothing downstream names a concrete protocol.
A response plan is precomputed per operation: Bindings, Media types, URI templates and status codes are cached.
Debug is a supertrait, as on the client’s protocol trait, so generated marshallers holding
the handle can derive it.
§Serializing from middleware
After routing, an HTTP plugin can read the selected protocol from the request extensions and
serialize any [SerializableStruct] whose schema it holds:
use aws_smithy_http_server::schema::SharedServerProtocol;
use aws_smithy_schema::serde::{SerdeError, SerializableStruct, ShapeSerializer};
use aws_smithy_schema::{shape_id, Schema, ShapeType};
static MESSAGE: Schema<'static> =
Schema::new_member(shape_id!("example", "Teapot", "message"), ShapeType::String, "message", 0);
static MEMBERS: [&Schema<'static>; 1] = [&MESSAGE];
static TEAPOT: Schema<'static> =
Schema::new_struct(shape_id!("example", "Teapot"), ShapeType::Structure, &MEMBERS)
.with_original_name("Teapot");
struct Teapot;
impl SerializableStruct for Teapot {
fn schema(&self) -> &Schema<'_> { &TEAPOT }
fn serialize_members(&self, serializer: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
serializer.write_string(&MESSAGE, "short and stout")
}
}
fn short_circuit(request: &http::Request<()>) -> Option<http::Response<aws_smithy_http_server::body::BoxBody>> {
let protocol = SharedServerProtocol::get_from_request(request)?;
let mut response = protocol.serialize_response(&TEAPOT, &Teapot);
*response.status_mut() = http::StatusCode::IM_A_TEAPOT;
Some(response)
}Required Methods§
Sourcefn protocol_id(&self) -> &'static ShapeId<'static>
fn protocol_id(&self) -> &'static ShapeId<'static>
The protocol trait’s shape ID, such as aws.protocols#restJson1.
Sourcefn deserialize_request<'a>(
&'a self,
input: &Schema<'_>,
request: &'a Request<Bytes>,
) -> Result<Box<dyn ShapeDeserializer + 'a>, DeserializeError>
fn deserialize_request<'a>( &'a self, input: &Schema<'_>, request: &'a Request<Bytes>, ) -> Result<Box<dyn ShapeDeserializer + 'a>, DeserializeError>
Presents request as a deserializer for input.
The Content-Type check happens here; the returned deserializer resolves @http bindings
from the request and hands body members to its internal codec. Synchronous over an
already collected body. The request retains HTTP metadata, including extensions.
Its body is empty when collection is skipped or the input is an event stream;
streaming glue receives the live body separately. Collected streaming blobs are
decoded from the document and exposed to handlers as buffered byte streams.
Sourcefn serialize_response(
&self,
output: &Schema<'_>,
value: &dyn SerializableStruct,
) -> Response
fn serialize_response( &self, output: &Schema<'_>, value: &dyn SerializableStruct, ) -> Response
Serializes value as a complete in-memory response for output.
The status is the @httpResponseCode member when bound and set, else the output schema’s
@http code, else 200; a caller wanting another status mutates the returned response. A
serialization failure is logged and answered with the protocol’s RuntimeError::Serialization
response, so this never fails outward.
Built-in protocols add the buffered body’s byte count as Content-Length only when
no modeled value is present. Matching generated serde responses, modeled values take
precedence without length-specific validation or normalization.
Sourcefn serialize_streaming_response(
&self,
output: &Schema<'_>,
value: &dyn SerializableStruct,
streaming_member: &Schema<'_>,
body: Option<BoxBody>,
) -> Response
fn serialize_streaming_response( &self, output: &Schema<'_>, value: &dyn SerializableStruct, streaming_member: &Schema<'_>, body: Option<BoxBody>, ) -> Response
Serializes a streaming response for output around a caller-supplied body.
The protocol interprets output and value to produce the response head and any
ordinary body members. Generated glue builds body from the streaming blob or
preframed event stream. CBOR frames blob chunks after the ordinary serialized members.
None means the modeled streaming member is absent. Some(body) means it is
present, even when the body yields no bytes. CBOR must distinguish these cases:
absence omits the blob member; a present empty blob encodes an explicit empty byte string.
REST and event-stream responses use an empty payload when the member is absent.
The body is passed separately because serialize_members omits streaming members
and the response must own their streams.
Built-in protocols do not infer a length or collect the stream. Matching generated serde
responses, modeled Content-Length values are preserved without length-specific validation
or normalization.
Sourcefn serialize_error(&self, error: &dyn HttpModeledError) -> Response
fn serialize_error(&self, error: &dyn HttpModeledError) -> Response
Serializes a modeled error with the protocol’s discriminator framing.
Sourcefn serialize_runtime_error(&self, error: RuntimeError) -> Response
fn serialize_runtime_error(&self, error: RuntimeError) -> Response
Frames a shared extractor runtime error using this protocol’s runtime-error contract.
Schema upgrades supply the protocol selected for the request. This is distinct from
modeled constraint validation and must preserve the protocol’s existing runtime error
status, headers, body, and response extensions. External protocols implement this
method alongside Self::serialize_rejection to define their own framing.
Sourcefn serialize_rejection(&self, err: DeserializeError) -> Response
fn serialize_rejection(&self, err: DeserializeError) -> Response
Converts a rejected request into the protocol’s response.
Each protocol answers with its RuntimeError responses, quirks included, such as awsJson
and rpcv2Cbor collapsing Accept and Content-Type failures into a plain 400, and
DeserializeError::InternalFailure becoming the 500 internal-failure response. These
responses are the protocol’s wire contract and must not change shape.
Provided Methods§
Sourcefn validate_request_headers(
&self,
_operation: &OperationSchema<'_>,
_headers: &Headers,
) -> Result<(), DeserializeError>
fn validate_request_headers( &self, _operation: &OperationSchema<'_>, _headers: &Headers, ) -> Result<(), DeserializeError>
Validates request headers for the selected operation before upgrade body collection
and Self::deserialize_request. Header failures therefore take precedence over
deserialization failures. The built-in protocols check Accept against the response
content type. The default accepts all headers.
Sourcefn request_body_requirement(
&self,
_operation: &OperationSchema<'_>,
) -> BodyDirective
fn request_body_requirement( &self, _operation: &OperationSchema<'_>, ) -> BodyDirective
Specifies whether deserialization needs the request body. The default collects it.
Returning BodyDirective::Skip leaves the deserialization request’s body empty.
Event-stream inputs bypass collection and receive the live body separately.
Streaming blob inputs honor Collect: the codec reads the blob and ordinary fields
before handler dispatch. With Skip, streaming glue receives the live body separately.
Sourcefn serialize_routing_error(&self, err: &RoutingError) -> Response
fn serialize_routing_error(&self, err: &RoutingError) -> Response
Converts a routing rejection into this protocol’s response.
The routing service classifies why routing failed
(RoutingError::kind); the protocol owns the
wire form. The default keeps the historical smithy-rs responses — the rejection is a
member-less modeled error (UnknownOperationException at 404,
MethodNotAllowedException at 405) framed by serialize_error.
A protocol whose clients expect different routing error responses overrides this;
returning a full [Response] permits any status, header — including
Connection: close — and body.