-
Notifications
You must be signed in to change notification settings - Fork 206
/
Copy pathruntime_error.rs
122 lines (111 loc) · 5.33 KB
/
runtime_error.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
//! Runtime error type.
//!
//! This module contains the [`RuntimeError`] type.
//!
//! As opposed to rejection types (see [`crate::proto::rest_json_1::rejection`]), which are an internal detail about
//! the framework, `RuntimeError` is surfaced to clients in HTTP responses: indeed, it implements
//! [`RuntimeError::into_response`]. Rejections can be "grouped" and converted into a
//! specific `RuntimeError` kind: for example, all request rejections due to serialization issues
//! can be conflated under the [`RuntimeError::Serialization`] enum variant.
//!
//! The HTTP response representation of the specific `RuntimeError` is protocol-specific: for
//! example, the runtime error in the [`crate::proto::rest_json_1`] protocol sets the `X-Amzn-Errortype` header.
//!
//! Generated code works always works with [`crate::rejection`] types when deserializing requests
//! and serializing response. Just before a response needs to be sent, the generated code looks up
//! and converts into the corresponding `RuntimeError`, and then it uses the its
//! [`RuntimeError::into_response`] method to render and send a response.
//!
//! This module hosts the `RuntimeError` type _specific_ to the [`crate::proto::rest_json_1`] protocol, but
//! the paragraphs above apply to _all_ protocol-specific rejection types.
//!
//! Similarly, `RuntimeError` variants are exhaustively documented solely in this module if they have
//! direct counterparts in other protocols. This is to avoid documentation getting out of date.
//!
//! Consult `crate::proto::$protocolName::runtime_error` for the `RuntimeError` type for other protocols.
use super::rejection::RequestRejection;
use super::rejection::ResponseRejection;
use super::RestJson1;
use crate::extension::RuntimeErrorExtension;
use crate::response::IntoResponse;
use crate::runtime_error::InternalFailureException;
use crate::runtime_error::INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE;
use http::StatusCode;
#[derive(Debug)]
pub enum RuntimeError {
/// Request failed to deserialize or response failed to serialize.
Serialization(crate::Error),
/// As of writing, this variant can only occur upon failure to extract an
/// [`crate::extension::Extension`] from the request.
InternalFailure(crate::Error),
/// Request contained an `Accept` header with a MIME type, and the server cannot return a response
/// body adhering to that MIME type.
NotAcceptable,
/// The request does not contain the expected `Content-Type` header value.
UnsupportedMediaType,
/// Operation input contains data that does not adhere to the modeled [constraint traits].
/// [constraint traits]: <https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html>
Validation(String),
}
impl RuntimeError {
/// String representation of the `RuntimeError` kind.
/// Used as the value passed to construct an [`crate::extension::RuntimeErrorExtension`].
/// Used as the value of the `X-Amzn-Errortype` header.
pub fn name(&self) -> &'static str {
match self {
Self::Serialization(_) => "SerializationException",
Self::InternalFailure(_) => "InternalFailureException",
Self::NotAcceptable => "NotAcceptableException",
Self::UnsupportedMediaType => "UnsupportedMediaTypeException",
Self::Validation(_) => "ValidationException",
}
}
pub fn status_code(&self) -> StatusCode {
match self {
Self::Serialization(_) => StatusCode::BAD_REQUEST,
Self::InternalFailure(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::NotAcceptable => StatusCode::NOT_ACCEPTABLE,
Self::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
Self::Validation(_) => StatusCode::BAD_REQUEST,
}
}
}
impl IntoResponse<RestJson1> for InternalFailureException {
fn into_response(self) -> http::Response<crate::body::BoxBody> {
IntoResponse::<RestJson1>::into_response(RuntimeError::InternalFailure(crate::Error::new(String::new())))
}
}
impl IntoResponse<RestJson1> for RuntimeError {
fn into_response(self) -> http::Response<crate::body::BoxBody> {
let res = http::Response::builder()
.status(self.status_code())
.header("Content-Type", "application/json")
.header("X-Amzn-Errortype", self.name())
.extension(RuntimeErrorExtension::new(self.name().to_string()));
let body = match self {
RuntimeError::Validation(reason) => crate::body::to_boxed(reason),
_ => crate::body::to_boxed("{}"),
};
res.body(body)
.expect(INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE)
}
}
impl From<ResponseRejection> for RuntimeError {
fn from(err: ResponseRejection) -> Self {
Self::Serialization(crate::Error::new(err))
}
}
impl From<RequestRejection> for RuntimeError {
fn from(err: RequestRejection) -> Self {
match err {
RequestRejection::MissingContentType(_reason) => Self::UnsupportedMediaType,
RequestRejection::ConstraintViolation(reason) => Self::Validation(reason),
RequestRejection::NotAcceptable(_reason) => Self::NotAcceptable,
_ => Self::Serialization(crate::Error::new(err)),
}
}
}