nautilus_network/http/
error.rs1use std::error::Error;
19
20#[derive(thiserror::Error, Debug)]
24pub enum HttpClientError {
25 #[error("HTTP error occurred: {0}")]
26 Error(String),
27
28 #[error("HTTP transport error: {0}")]
29 TransportError(String),
30
31 #[error("HTTP request timed out: {0}")]
32 TimeoutError(String),
33
34 #[error("Invalid proxy URL: {0}")]
35 InvalidProxy(String),
36
37 #[error("Failed to build HTTP client: {0}")]
38 ClientBuildError(String),
39}
40
41impl From<String> for HttpClientError {
42 fn from(value: String) -> Self {
43 Self::Error(value)
44 }
45}
46
47pub(super) fn transport_error(e: &(dyn Error + 'static)) -> HttpClientError {
48 let mut message = String::new();
49 let mut cause = Some(e);
50 let mut timed_out = false;
51
52 while let Some(e) = cause {
53 if !message.is_empty() {
54 message.push_str(": ");
55 }
56 message.push_str(&e.to_string());
57
58 timed_out |= e
59 .downcast_ref::<hyper::Error>()
60 .is_some_and(hyper::Error::is_timeout)
61 || e.downcast_ref::<std::io::Error>()
62 .is_some_and(|e| e.kind() == std::io::ErrorKind::TimedOut);
63 cause = e.source();
64 }
65
66 if timed_out {
67 HttpClientError::TimeoutError(message)
68 } else {
69 HttpClientError::TransportError(message)
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use std::io;
76
77 use rstest::rstest;
78
79 use super::*;
80
81 #[rstest]
82 #[case::timeout(io::ErrorKind::TimedOut, true)]
83 #[case::refused(io::ErrorKind::ConnectionRefused, false)]
84 fn socket_errors_preserve_classification(#[case] kind: io::ErrorKind, #[case] timeout: bool) {
85 let error = transport_error(&io::Error::new(kind, "socket failure"));
86 match (error, timeout) {
87 (HttpClientError::TimeoutError(message), true)
88 | (HttpClientError::TransportError(message), false) => {
89 assert_eq!(message, "socket failure");
90 }
91 (error, _) => panic!("unexpected classification: {error}"),
92 }
93 }
94}