aws_smithy_http_server/schema/routing/
route_errors.rs1use aws_smithy_schema::serde::{SerdeError, SerializableStruct, ShapeSerializer};
19use aws_smithy_schema::{shape_id, Schema, ShapeType, StringTrait, TraitMap};
20
21use crate::schema::HttpModeledError;
22
23static ERROR_CLIENT_TRAITS: std::sync::LazyLock<TraitMap> = std::sync::LazyLock::new(|| {
24 let mut map = TraitMap::new();
25 map.insert(Box::new(StringTrait::new(shape_id!("smithy.api", "error"), "client")));
26 map
27});
28
29pub static UNKNOWN_OPERATION: Schema<'static> = Schema::new_struct(
31 shape_id!("smithy.framework", "UnknownOperationException"),
32 ShapeType::Structure,
33 &[],
34)
35.with_traits(&ERROR_CLIENT_TRAITS);
36
37pub static METHOD_NOT_ALLOWED: Schema<'static> = Schema::new_struct(
39 shape_id!("smithy.framework", "MethodNotAllowedException"),
40 ShapeType::Structure,
41 &[],
42)
43.with_traits(&ERROR_CLIENT_TRAITS);
44
45#[non_exhaustive]
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum RoutingErrorKind {
54 UnknownOperation,
57 MethodNotAllowed,
63 MalformedRequest,
66 UnsupportedOperation,
68 MissingContentType,
71}
72
73#[derive(Debug)]
76pub struct RoutingError {
77 kind: RoutingErrorKind,
78 source: Option<Box<dyn std::error::Error + Send + Sync>>,
79 expected_content_type: Option<&'static str>,
83}
84
85impl RoutingError {
86 pub fn unknown_operation() -> Self {
88 Self {
89 kind: RoutingErrorKind::UnknownOperation,
90 source: None,
91 expected_content_type: None,
92 }
93 }
94
95 pub fn method_not_allowed() -> Self {
97 Self {
98 kind: RoutingErrorKind::MethodNotAllowed,
99 source: None,
100 expected_content_type: None,
101 }
102 }
103
104 pub fn malformed(source: impl std::error::Error + Send + Sync + 'static) -> Self {
107 Self {
108 kind: RoutingErrorKind::MalformedRequest,
109 source: Some(Box::new(source)),
110 expected_content_type: None,
111 }
112 }
113
114 pub fn unsupported_operation() -> Self {
116 Self {
117 kind: RoutingErrorKind::UnsupportedOperation,
118 source: None,
119 expected_content_type: None,
120 }
121 }
122
123 pub fn missing_content_type(expected: &'static str) -> Self {
125 Self {
126 kind: RoutingErrorKind::MissingContentType,
127 source: Some(Box::new(
128 crate::rejection::MissingContentTypeReason::UnexpectedMimeType {
129 expected_mime: expected.parse().ok(),
130 found_mime: None,
131 },
132 )),
133 expected_content_type: Some(expected),
134 }
135 }
136
137 pub fn expected_content_type(&self) -> Option<&'static str> {
139 self.expected_content_type
140 }
141
142 pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
144 self.source = Some(Box::new(source));
145 self
146 }
147
148 pub fn kind(&self) -> RoutingErrorKind {
150 self.kind
151 }
152}
153
154impl std::fmt::Display for RoutingError {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 match self.kind {
157 RoutingErrorKind::UnknownOperation => f.write_str("unknown operation"),
158 RoutingErrorKind::MethodNotAllowed => f.write_str("method not allowed"),
159 RoutingErrorKind::MalformedRequest => f.write_str("malformed request"),
160 RoutingErrorKind::MissingContentType => f.write_str("missing content type"),
161 RoutingErrorKind::UnsupportedOperation => f.write_str("unsupported operation"),
162 }
163 }
164}
165
166impl std::error::Error for RoutingError {
167 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
168 self.source.as_deref().map(|err| err as _)
169 }
170}
171
172impl SerializableStruct for RoutingError {
176 fn schema(&self) -> &Schema<'_> {
177 match self.kind {
178 RoutingErrorKind::MethodNotAllowed => &METHOD_NOT_ALLOWED,
179 _ => &UNKNOWN_OPERATION,
180 }
181 }
182
183 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
184 Ok(())
185 }
186}
187
188impl HttpModeledError for RoutingError {
189 fn status_code(&self) -> u16 {
190 match self.kind {
191 RoutingErrorKind::MethodNotAllowed => 405,
192 _ => 404,
193 }
194 }
195}
196
197impl From<crate::protocol::rest::router::Error> for RoutingError {
198 fn from(err: crate::protocol::rest::router::Error) -> Self {
199 use crate::protocol::rest::router::Error;
200 match err {
201 Error::NotFound => Self::unknown_operation(),
202 Error::MethodNotAllowed => Self::method_not_allowed(),
203 }
204 }
205}
206
207impl From<crate::protocol::aws_json::router::Error> for RoutingError {
208 fn from(err: crate::protocol::aws_json::router::Error) -> Self {
209 use crate::protocol::aws_json::router::Error;
210 match err {
211 Error::NotFound | Error::NotRootUrl | Error::MissingHeader => Self::unknown_operation(),
214 Error::MethodNotAllowed => Self::method_not_allowed(),
215 Error::InvalidHeader(_) => Self::malformed(err),
216 }
217 }
218}
219
220impl From<crate::protocol::rpc_v2_cbor::router::Error> for RoutingError {
221 fn from(err: crate::protocol::rpc_v2_cbor::router::Error) -> Self {
222 use crate::protocol::rpc_v2_cbor::router::{Error, WireFormatError};
223 match err {
224 Error::NotFound => Self::unknown_operation(),
225 Error::MethodNotAllowed => Self::method_not_allowed(),
226 Error::ForbiddenHeaders => Self::malformed(err),
227 Error::InvalidWireFormatHeader(
230 WireFormatError::HeaderNotFound | WireFormatError::WireFormatNotSupported(_),
231 ) => Self::unknown_operation().with_source(err),
232 Error::InvalidWireFormatHeader(_) => Self::malformed(err),
235 }
236 }
237}