1use std::{error::Error as StdError, fmt};
3
4pub type Result<T, E = Error> = std::result::Result<T, E>;
6
7pub type BoxError = Box<dyn StdError + Send + Sync>;
8
9type Cause = BoxError;
10
11pub struct Error {
33 inner: Box<ErrorImpl>,
34}
35
36struct ErrorImpl {
37 kind: Kind,
38 cause: Option<Cause>,
39}
40
41#[derive(Debug)]
42pub(super) enum Kind {
43 Parse(Parse),
44 User(User),
45 IncompleteMessage,
47 UnexpectedMessage,
49 Canceled,
51 ChannelClosed,
53 Io,
55 Body,
57 BodyWrite,
59 Shutdown,
61 Http2,
63}
64
65#[derive(Debug)]
66pub(crate) enum Parse {
67 Method,
68 Version,
69 VersionH2,
70 Uri,
71 Header(Header),
72 TooLarge,
73 Status,
74 Internal,
75}
76
77#[derive(Debug)]
78pub(crate) enum Header {
79 Token,
80 ContentLengthInvalid,
81 TransferEncodingUnexpected,
82}
83
84#[derive(Debug)]
85pub(super) enum User {
86 Body,
88 BodyWriteAborted,
90 InvalidConnectWithBody,
92 Service,
94 NoUpgrade,
96 ManualUpgrade,
98 DispatchGone,
100}
101
102#[derive(Debug)]
104pub(super) struct TimedOut;
105
106impl Error {
107 #[inline]
109 pub fn is_parse(&self) -> bool {
110 matches!(self.inner.kind, Kind::Parse(_))
111 }
112
113 #[inline]
116 pub fn is_parse_status(&self) -> bool {
117 matches!(self.inner.kind, Kind::Parse(Parse::Status))
118 }
119
120 #[inline]
122 pub fn is_user(&self) -> bool {
123 matches!(self.inner.kind, Kind::User(_))
124 }
125
126 #[inline]
128 pub fn is_canceled(&self) -> bool {
129 matches!(self.inner.kind, Kind::Canceled)
130 }
131
132 #[inline]
134 pub fn is_closed(&self) -> bool {
135 matches!(self.inner.kind, Kind::ChannelClosed)
136 }
137
138 #[inline]
140 pub fn is_incomplete_message(&self) -> bool {
141 matches!(self.inner.kind, Kind::IncompleteMessage)
142 }
143
144 #[inline]
146 pub fn is_body_write_aborted(&self) -> bool {
147 matches!(self.inner.kind, Kind::User(User::BodyWriteAborted))
148 }
149
150 #[inline]
152 pub fn is_timeout(&self) -> bool {
153 self.find_source::<TimedOut>().is_some()
154 }
155
156 #[inline]
157 pub(super) fn new(kind: Kind) -> Error {
158 Error {
159 inner: Box::new(ErrorImpl { kind, cause: None }),
160 }
161 }
162
163 #[inline]
164 pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
165 self.inner.cause = Some(cause.into());
166 self
167 }
168
169 pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
170 let mut cause = self.source();
171 while let Some(err) = cause {
172 if let Some(typed) = err.downcast_ref() {
173 return Some(typed);
174 }
175 cause = err.source();
176 }
177
178 None
180 }
181
182 pub(super) fn h2_reason(&self) -> http2::Reason {
183 self.find_source::<http2::Error>()
186 .and_then(|h2_err| h2_err.reason())
187 .unwrap_or(http2::Reason::INTERNAL_ERROR)
188 }
189
190 #[inline]
191 pub(super) fn new_canceled() -> Error {
192 Error::new(Kind::Canceled)
193 }
194
195 #[inline]
196 pub(super) fn new_incomplete() -> Error {
197 Error::new(Kind::IncompleteMessage)
198 }
199
200 #[inline]
201 pub(super) fn new_too_large() -> Error {
202 Error::new(Kind::Parse(Parse::TooLarge))
203 }
204
205 #[inline]
206 pub(super) fn new_version_h2() -> Error {
207 Error::new(Kind::Parse(Parse::VersionH2))
208 }
209
210 #[inline]
211 pub(super) fn new_unexpected_message() -> Error {
212 Error::new(Kind::UnexpectedMessage)
213 }
214
215 #[inline]
216 pub(super) fn new_io(cause: std::io::Error) -> Error {
217 Error::new(Kind::Io).with(cause)
218 }
219
220 #[inline]
221 pub(super) fn new_closed() -> Error {
222 Error::new(Kind::ChannelClosed)
223 }
224
225 #[inline]
226 pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
227 Error::new(Kind::Body).with(cause)
228 }
229
230 #[inline]
231 pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
232 Error::new(Kind::BodyWrite).with(cause)
233 }
234
235 #[inline]
236 pub(super) fn new_body_write_aborted() -> Error {
237 Error::new(Kind::User(User::BodyWriteAborted))
238 }
239
240 #[inline]
241 fn new_user(user: User) -> Error {
242 Error::new(Kind::User(user))
243 }
244
245 #[inline]
246 pub(super) fn new_user_no_upgrade() -> Error {
247 Error::new_user(User::NoUpgrade)
248 }
249
250 #[inline]
251 pub(super) fn new_user_manual_upgrade() -> Error {
252 Error::new_user(User::ManualUpgrade)
253 }
254
255 #[inline]
256 pub(super) fn new_user_service<E: Into<Cause>>(cause: E) -> Error {
257 Error::new_user(User::Service).with(cause)
258 }
259
260 #[inline]
261 pub(super) fn new_user_body<E: Into<Cause>>(cause: E) -> Error {
262 Error::new_user(User::Body).with(cause)
263 }
264
265 #[inline]
266 pub(super) fn new_user_invalid_connect() -> Error {
267 Error::new_user(User::InvalidConnectWithBody)
268 }
269
270 #[inline]
271 pub(super) fn new_shutdown(cause: std::io::Error) -> Error {
272 Error::new(Kind::Shutdown).with(cause)
273 }
274
275 #[inline]
276 pub(super) fn new_user_dispatch_gone() -> Error {
277 Error::new(Kind::User(User::DispatchGone))
278 }
279
280 pub(super) fn new_h2(cause: ::http2::Error) -> Error {
281 if cause.is_io() {
282 Error::new_io(cause.into_io().expect("http2::Error::is_io"))
283 } else {
284 Error::new(Kind::Http2).with(cause)
285 }
286 }
287
288 fn description(&self) -> &str {
289 match self.inner.kind {
290 Kind::Parse(Parse::Method) => "invalid HTTP method parsed",
291 Kind::Parse(Parse::Version) => "invalid HTTP version parsed",
292 Kind::Parse(Parse::VersionH2) => "invalid HTTP version parsed (found HTTP2 preface)",
293 Kind::Parse(Parse::Uri) => "invalid URI",
294 Kind::Parse(Parse::Header(Header::Token)) => "invalid HTTP header parsed",
295 Kind::Parse(Parse::Header(Header::ContentLengthInvalid)) => {
296 "invalid content-length parsed"
297 }
298 Kind::Parse(Parse::Header(Header::TransferEncodingUnexpected)) => {
299 "unexpected transfer-encoding parsed"
300 }
301 Kind::Parse(Parse::TooLarge) => "message head is too large",
302 Kind::Parse(Parse::Status) => "invalid HTTP status-code parsed",
303 Kind::Parse(Parse::Internal) => {
304 "internal error inside wreq and/or its dependencies, please report"
305 }
306
307 Kind::IncompleteMessage => "connection closed before message completed",
308 Kind::UnexpectedMessage => "received unexpected message from connection",
309 Kind::ChannelClosed => "channel closed",
310 Kind::Canceled => "operation was canceled",
311 Kind::Body => "error reading a body from connection",
312 Kind::BodyWrite => "error writing a body to connection",
313 Kind::Shutdown => "error shutting down connection",
314 Kind::Http2 => "http2 error",
315 Kind::Io => "connection error",
316
317 Kind::User(User::Body) => "error from user's Body stream",
318 Kind::User(User::BodyWriteAborted) => "user body write aborted",
319 Kind::User(User::InvalidConnectWithBody) => {
320 "user sent CONNECT request with non-zero body"
321 }
322 Kind::User(User::Service) => "error from user's Service",
323 Kind::User(User::NoUpgrade) => "no upgrade available",
324 Kind::User(User::ManualUpgrade) => "upgrade expected but low level API in use",
325 Kind::User(User::DispatchGone) => "dispatch task is gone",
326 }
327 }
328}
329
330impl fmt::Debug for Error {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 let mut f = f.debug_tuple("wreq_proto::Error");
333 f.field(&self.inner.kind);
334 if let Some(ref cause) = self.inner.cause {
335 f.field(cause);
336 }
337 f.finish()
338 }
339}
340
341impl fmt::Display for Error {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 f.write_str(self.description())
344 }
345}
346
347impl StdError for Error {
348 fn source(&self) -> Option<&(dyn StdError + 'static)> {
349 self.inner
350 .cause
351 .as_ref()
352 .map(|cause| &**cause as &(dyn StdError + 'static))
353 }
354}
355
356#[doc(hidden)]
357impl From<Parse> for Error {
358 fn from(err: Parse) -> Error {
359 Error::new(Kind::Parse(err))
360 }
361}
362
363impl Parse {
364 #[inline]
365 pub(crate) fn content_length_invalid() -> Self {
366 Parse::Header(Header::ContentLengthInvalid)
367 }
368
369 #[inline]
370 pub(crate) fn transfer_encoding_unexpected() -> Self {
371 Parse::Header(Header::TransferEncodingUnexpected)
372 }
373}
374
375impl From<httparse::Error> for Parse {
376 fn from(err: httparse::Error) -> Parse {
377 match err {
378 httparse::Error::HeaderName
379 | httparse::Error::HeaderValue
380 | httparse::Error::NewLine
381 | httparse::Error::Token => Parse::Header(Header::Token),
382 httparse::Error::Status => Parse::Status,
383 httparse::Error::TooManyHeaders => Parse::TooLarge,
384 httparse::Error::Version => Parse::Version,
385 }
386 }
387}
388
389impl From<http::method::InvalidMethod> for Parse {
390 fn from(_: http::method::InvalidMethod) -> Parse {
391 Parse::Method
392 }
393}
394
395impl From<http::status::InvalidStatusCode> for Parse {
396 fn from(_: http::status::InvalidStatusCode) -> Parse {
397 Parse::Status
398 }
399}
400
401impl From<http::uri::InvalidUri> for Parse {
402 fn from(_: http::uri::InvalidUri) -> Parse {
403 Parse::Uri
404 }
405}
406
407impl From<http::uri::InvalidUriParts> for Parse {
408 fn from(_: http::uri::InvalidUriParts) -> Parse {
409 Parse::Uri
410 }
411}
412
413impl fmt::Display for TimedOut {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 f.write_str("operation timed out")
418 }
419}
420
421impl StdError for TimedOut {}
422
423#[cfg(test)]
424mod tests {
425 use std::mem;
426
427 use super::*;
428
429 fn assert_send_sync<T: Send + Sync + 'static>() {}
430
431 #[test]
432 fn error_satisfies_send_sync() {
433 assert_send_sync::<Error>()
434 }
435
436 #[test]
437 fn error_size_of() {
438 assert_eq!(mem::size_of::<Error>(), mem::size_of::<usize>());
439 }
440
441 #[test]
442 fn h2_reason_unknown() {
443 let closed = Error::new_closed();
444 assert_eq!(closed.h2_reason(), http2::Reason::INTERNAL_ERROR);
445 }
446
447 #[test]
448 fn h2_reason_one_level() {
449 let body_err = Error::new_user_body(http2::Error::from(http2::Reason::ENHANCE_YOUR_CALM));
450 assert_eq!(body_err.h2_reason(), http2::Reason::ENHANCE_YOUR_CALM);
451 }
452
453 #[test]
454 fn h2_reason_nested() {
455 let recvd = Error::new_h2(http2::Error::from(http2::Reason::HTTP_1_1_REQUIRED));
456 let svc_err = Error::new_user_service(recvd);
458 assert_eq!(svc_err.h2_reason(), http2::Reason::HTTP_1_1_REQUIRED);
459 }
460}