1use crate::{
2 http_context::HttpContext,
3 responder::{DynResponder, Responder},
4 response::Builder,
5};
6use http::{
7 header::{InvalidHeaderValue, ToStrError},
8 Error as HttpCrateError,
9};
10use hyper::Error as HyperError;
11use std::{
12 error::Error as StdError,
13 fmt::{Debug, Formatter},
14 io::Error as IoError,
15};
16use thiserror::Error;
17
18#[derive(Error, Debug)]
21pub enum InternalError {
22 #[error("Http: {0}")]
23 Http(HttpCrateError),
24 #[error("Hyper: {0}")]
25 Hyper(HyperError),
26 #[error("ToStr: {0}")]
27 ToStr(ToStrError),
28 #[error("Stack")]
29 Stack,
30}
31
32#[derive(Error)]
34pub enum SaphirError {
35 #[error("Internal: {0}")]
37 Internal(#[from] InternalError),
38 #[error("Io: {0}")]
40 Io(#[from] IoError),
41 #[error("Body already taken")]
43 BodyAlreadyTaken,
44 #[error("Request moved before handler")]
47 RequestMovedBeforeHandler,
48 #[error("Response moved")]
50 ResponseMoved,
51 #[error("Custom: {0}")]
53 Custom(Box<dyn StdError + Send + Sync + 'static>),
54 #[error("Responder")]
56 Responder(Box<dyn DynResponder + Send + Sync + 'static>),
57 #[error("Other: {0}")]
59 Other(String),
60 #[cfg(feature = "json")]
62 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
63 #[error("SerdeJson: {0}")]
64 SerdeJson(#[from] serde_json::error::Error),
65 #[cfg(feature = "form")]
67 #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
68 #[error("SerdeUrlDe: {0}")]
69 SerdeUrlDe(#[from] serde_urlencoded::de::Error),
70 #[cfg(feature = "form")]
72 #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
73 #[error("SerdeUrlSer: {0}")]
74 SerdeUrlSer(#[from] serde_urlencoded::ser::Error),
75 #[error("Missing parameter `{0}` (is_query: {1})")]
77 MissingParameter(String, bool),
78 #[error("Invalid parameter `{0}` (is_query: {1})")]
80 InvalidParameter(String, bool),
81 #[error("Request timed out")]
83 RequestTimeout,
84 #[error("Stack alrealy initialized")]
86 StackAlreadyInitialized,
87 #[error("Too many requests")]
89 TooManyRequests,
90 #[cfg(feature = "validate-requests")]
92 #[cfg_attr(docsrs, doc(cfg(feature = "validate-requests")))]
93 #[error("ValidationErrors: {0}")]
94 ValidationErrors(#[from] validator::ValidationErrors),
95}
96
97impl Debug for SaphirError {
98 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99 match self {
100 SaphirError::Internal(d) => std::fmt::Debug::fmt(d, f),
101 SaphirError::Io(d) => std::fmt::Debug::fmt(d, f),
102 SaphirError::BodyAlreadyTaken => f.write_str("BodyAlreadyTaken"),
103 SaphirError::RequestMovedBeforeHandler => f.write_str("RequestMovedBeforeHandler"),
104 SaphirError::ResponseMoved => f.write_str("ResponseMoved"),
105 SaphirError::Custom(d) => std::fmt::Debug::fmt(d, f),
106 SaphirError::Responder(_) => f.write_str("Responder"),
107 SaphirError::Other(d) => std::fmt::Debug::fmt(d, f),
108 #[cfg(feature = "json")]
109 SaphirError::SerdeJson(d) => std::fmt::Debug::fmt(d, f),
110 #[cfg(feature = "form")]
111 SaphirError::SerdeUrlDe(d) => std::fmt::Debug::fmt(d, f),
112 #[cfg(feature = "form")]
113 SaphirError::SerdeUrlSer(d) => std::fmt::Debug::fmt(d, f),
114 SaphirError::MissingParameter(d, _) => std::fmt::Debug::fmt(d, f),
115 SaphirError::InvalidParameter(d, _) => std::fmt::Debug::fmt(d, f),
116 SaphirError::RequestTimeout => f.write_str("RequestTimeout"),
117 SaphirError::StackAlreadyInitialized => f.write_str("StackAlreadyInitialized"),
118 SaphirError::TooManyRequests => f.write_str("TooManyRequests"),
119 #[cfg(feature = "validate-requests")]
120 SaphirError::ValidationErrors(d) => std::fmt::Debug::fmt(d, f),
121 }
122 }
123}
124
125impl SaphirError {
126 pub fn responder<T: Responder + Send + Sync + 'static>(e: T) -> Self {
127 SaphirError::Responder(Box::new(Some(e)))
128 }
129
130 pub(crate) fn response_builder(self, builder: Builder, ctx: &HttpContext) -> Builder {
131 match self {
132 SaphirError::Internal(_) => builder.status(500),
133 SaphirError::Io(_) => builder.status(500),
134 SaphirError::BodyAlreadyTaken => builder.status(500),
135 SaphirError::Custom(_) => builder.status(500),
136 SaphirError::Other(_) => builder.status(500),
137 #[cfg(feature = "json")]
138 SaphirError::SerdeJson(_) => builder.status(400),
139 #[cfg(feature = "form")]
140 SaphirError::SerdeUrlDe(_) => builder.status(400),
141 #[cfg(feature = "form")]
142 SaphirError::SerdeUrlSer(_) => builder.status(400),
143 SaphirError::MissingParameter(..) => builder.status(400),
144 SaphirError::InvalidParameter(..) => builder.status(400),
145 SaphirError::RequestMovedBeforeHandler => builder.status(500),
146 SaphirError::ResponseMoved => builder.status(500),
147 SaphirError::Responder(mut r) => r.dyn_respond(builder, ctx),
148 SaphirError::RequestTimeout => builder.status(408),
149 SaphirError::StackAlreadyInitialized => builder.status(500),
150 SaphirError::TooManyRequests => builder.status(429),
151 #[cfg(feature = "validate-requests")]
152 SaphirError::ValidationErrors(_) => builder.status(400),
153 }
154 }
155
156 #[allow(unused_variables)]
157 pub(crate) fn log(&self, ctx: &HttpContext) {
158 let op_id = {
159 #[cfg(not(feature = "operation"))]
160 {
161 String::new()
162 }
163
164 #[cfg(feature = "operation")]
165 {
166 format!("[Operation id: {}] ", ctx.operation_id)
167 }
168 };
169
170 match self {
171 SaphirError::Internal(e) => {
172 warn!("{}Saphir encountered an internal error that was returned as a responder: {:?}", op_id, e);
173 }
174 SaphirError::Io(e) => {
175 warn!("{}Saphir encountered an Io error that was returned as a responder: {:?}", op_id, e);
176 }
177 SaphirError::BodyAlreadyTaken => {
178 warn!("{}A controller handler attempted to take the request body more thant one time", op_id);
179 }
180 SaphirError::Custom(e) => {
181 warn!("{}A custom error was returned as a responder: {:?}", op_id, e);
182 }
183 SaphirError::Other(e) => {
184 warn!("{}Saphir encountered an Unknown error that was returned as a responder: {:?}", op_id, e);
185 }
186 #[cfg(feature = "json")]
187 SaphirError::SerdeJson(e) => {
188 debug!("{}Unable to de/serialize json type: {:?}", op_id, e);
189 }
190 #[cfg(feature = "form")]
191 SaphirError::SerdeUrlDe(e) => {
192 debug!("{}Unable to deserialize form type: {:?}", op_id, e);
193 }
194 #[cfg(feature = "form")]
195 SaphirError::SerdeUrlSer(e) => {
196 debug!("{}Unable to serialize form type: {:?}", op_id, e);
197 }
198 SaphirError::MissingParameter(name, is_query) => {
199 if *is_query {
200 debug!("{}Missing query parameter {}", op_id, name);
201 } else {
202 debug!("{}Missing path parameter {}", op_id, name);
203 }
204 }
205 SaphirError::InvalidParameter(name, is_query) => {
206 if *is_query {
207 debug!("{}Unable to parse query parameter {}", op_id, name);
208 } else {
209 debug!("{}Unable to parse path parameter {}", op_id, name);
210 }
211 }
212 SaphirError::RequestMovedBeforeHandler => {
213 warn!(
214 "{}A request was moved out of its context by a middleware, but the middleware did not stop request processing",
215 op_id
216 );
217 }
218 SaphirError::ResponseMoved => {
219 warn!("{}A response was moved before being sent to the client", op_id);
220 }
221 SaphirError::RequestTimeout => {
222 warn!("{}Request timed out", op_id);
223 }
224 SaphirError::Responder(_) => {}
225 SaphirError::StackAlreadyInitialized => {
226 warn!("{}Attempted to initialize stack twice", op_id);
227 }
228 SaphirError::TooManyRequests => {
229 warn!("{}Made too many requests", op_id);
230 }
231 #[cfg(feature = "validate-requests")]
232 SaphirError::ValidationErrors(e) => {
233 debug!("{}Validation error: {:?}", op_id, e);
234 }
235 }
236 }
237}
238
239impl From<HttpCrateError> for SaphirError {
240 fn from(e: HttpCrateError) -> Self {
241 SaphirError::Internal(InternalError::Http(e))
242 }
243}
244
245impl From<InvalidHeaderValue> for SaphirError {
246 fn from(e: InvalidHeaderValue) -> Self {
247 SaphirError::Internal(InternalError::Http(HttpCrateError::from(e)))
248 }
249}
250
251impl From<HyperError> for SaphirError {
252 fn from(e: HyperError) -> Self {
253 SaphirError::Internal(InternalError::Hyper(e))
254 }
255}
256
257impl From<ToStrError> for SaphirError {
258 fn from(e: ToStrError) -> Self {
259 SaphirError::Internal(InternalError::ToStr(e))
260 }
261}
262
263impl Responder for SaphirError {
264 #[allow(unused_variables)]
265 fn respond_with_builder(self, builder: Builder, ctx: &HttpContext) -> Builder {
266 self.log(ctx);
267 self.response_builder(builder, ctx)
268 }
269}