Skip to main content

ServerProtocol

Trait ServerProtocol 

Source
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§

Source

fn protocol_id(&self) -> &'static ShapeId<'static>

The protocol trait’s shape ID, such as aws.protocols#restJson1.

Source

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.

Source

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.

Source

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.

Source

fn serialize_error(&self, error: &dyn HttpModeledError) -> Response

Serializes a modeled error with the protocol’s discriminator framing.

Source

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.

Source

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§

Source

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.

Source

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.

Source

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.

Implementors§