aws_smithy_http_server/schema/deserialize.rs
1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use aws_smithy_schema::serde::{SerdeError, ShapeDeserializer};
7
8use super::{HttpModeledError, RequestBodyCollectionError};
9use crate::rejection::MissingContentTypeReason;
10
11/// Why a request failed without reaching its operation handler.
12///
13/// This is the one protocol-independent failure enum on the schema path: every way a request can
14/// fail before its handler runs lands here — almost always a failure to become the operation
15/// input, plus the missing-handler [`InternalFailure`](Self::InternalFailure) — and each protocol
16/// turns the whole enum into its wire response through
17/// [`ServerProtocol::serialize_rejection`](super::ServerProtocol::serialize_rejection).
18///
19/// Variants preserve the failure category even when their rejection responses are identical.
20/// The `Content-Type` and `Accept` failures are not folded into [`Serde`](Self::Serde): protocols answer them
21/// differently (restJson1 keeps 415 and 406 distinct from the 400 that `Serde` produces), and
22/// the renderer matches on the variant to pick the response. They also originate in different
23/// layers: [`SerdeError`] belongs to the codec in `aws-smithy-schema` and describes failures
24/// reading the payload and bindings into the input shape, while the `Content-Type` and `Accept`
25/// checks run in the HTTP protocol layer before the codec — the codec never sees headers, and
26/// the transport-agnostic schema crate carries no HTTP semantics.
27#[derive(Debug)]
28#[non_exhaustive]
29pub enum DeserializeError {
30 /// The request could not be read as the input shape: a malformed document, a type mismatch,
31 /// an unparseable header, label or query value, or a request that could not be converted
32 /// into its canonical form.
33 Serde(SerdeError),
34 /// Reading an RPC initial-request event frame failed. Preserve the generated serde raw
35 /// validation rejection rather than treating a frame failure as a codec error.
36 InitialRequestFrame(String),
37 /// Reading the request body failed, exceeded its size limit, or timed out.
38 /// Preserves the typed failure while rendering the protocol's ordinary deserialization rejection.
39 BodyCollection(RequestBodyCollectionError<crate::Error>),
40 /// The request was well-formed but violates a modeled constraint.
41 ConstraintViolation(Box<dyn HttpModeledError>),
42 /// The request's `Content-Type` header does not match what the operation expects.
43 ///
44 /// Raised by the protocol layer's header check, never by the codec; carries the expected
45 /// and found mime types the 415 response is built from.
46 // Boxed to keep the enum small: the reason carries two parsed mime types.
47 UnsupportedMediaType(Box<MissingContentTypeReason>),
48 /// The request's `Accept` header cannot accept the operation's response content type.
49 ///
50 /// Raised by the protocol layer's header check, never by the codec.
51 NotAcceptable,
52 /// The service cannot run the selected operation.
53 ///
54 /// Raised by the missing-handler fallback when a service built with `build_unchecked` receives
55 /// a request for an operation without a registered handler, never by request parsing. Renders
56 /// as the protocol's 500 internal-failure response; the carried error is logged, not written
57 /// to the wire.
58 InternalFailure(crate::Error),
59}
60
61impl std::fmt::Display for DeserializeError {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Self::Serde(err) => write!(f, "failed to deserialize request: {err}"),
65 Self::InitialRequestFrame(err) => write!(f, "failed to read the initial-request frame: {err}"),
66 Self::BodyCollection(err) => write!(f, "failed to collect request body: {err}"),
67 Self::ConstraintViolation(err) => {
68 write!(f, "request does not adhere to modeled constraints: {err}")
69 }
70 Self::UnsupportedMediaType(reason) => {
71 write!(f, "request has an unsupported `Content-Type`: {reason}")
72 }
73 Self::NotAcceptable => write!(f, "request contains an invalid value for the `Accept` header"),
74 Self::InternalFailure(err) => write!(f, "internal server error: {err}"),
75 }
76 }
77}
78
79impl std::error::Error for DeserializeError {
80 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
81 match self {
82 Self::Serde(err) => Some(err),
83 Self::BodyCollection(err) => Some(err),
84 Self::ConstraintViolation(err) => Some(&**err),
85 Self::UnsupportedMediaType(reason) => Some(&**reason),
86 Self::NotAcceptable | Self::InitialRequestFrame(_) => None,
87 Self::InternalFailure(err) => Some(err),
88 }
89 }
90}
91
92impl From<SerdeError> for DeserializeError {
93 fn from(err: SerdeError) -> Self {
94 Self::Serde(err)
95 }
96}
97
98impl From<RequestBodyCollectionError<crate::Error>> for DeserializeError {
99 fn from(err: RequestBodyCollectionError<crate::Error>) -> Self {
100 Self::BodyCollection(err)
101 }
102}
103
104impl From<MissingContentTypeReason> for DeserializeError {
105 fn from(reason: MissingContentTypeReason) -> Self {
106 Self::UnsupportedMediaType(Box::new(reason))
107 }
108}
109
110/// An operation input that can be read from a [`ShapeDeserializer`].
111///
112/// The runtime deserializes inputs generically: [`DynUpgrade`](crate::operation::DynUpgrade) and
113/// its streaming counterpart are written once over `Op::Input: DeserializableShape`, so every
114/// generated input implements this trait. It returns [`DeserializeError`] so a builder's typed
115/// constraint violation reaches the protocol renderer as
116/// [`DeserializeError::ConstraintViolation`] and becomes the modeled validation response instead
117/// of collapsing into a generic parse failure.
118///
119/// Generated inputs walk their schema into the internal builder and then `build()`, so constraint
120/// validation keeps happening in one place.
121pub trait DeserializableShape: Sized {
122 /// Reads `Self` from `deserializer`.
123 fn deserialize(deserializer: &mut dyn ShapeDeserializer) -> Result<Self, DeserializeError>;
124}