Skip to main content

specter/
error.rs

1//! Error types for specter crate.
2
3use std::io;
4
5/// Result type alias using our Error type.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Errors that can occur during HTTP operations.
9#[derive(Debug, thiserror::Error)]
10pub enum Error {
11    /// HTTP protocol error.
12    #[error("HTTP protocol error: {0}")]
13    HttpProtocol(String),
14
15    /// Invalid HTTP status code.
16    #[error("HTTP {status}: {message}")]
17    HttpStatus { status: u16, message: String },
18
19    /// Redirect limit exceeded.
20    #[error("Redirect limit exceeded ({count} redirects)")]
21    RedirectLimit { count: u32 },
22
23    /// Invalid redirect URL.
24    #[error("Invalid redirect URL: {0}")]
25    InvalidRedirectUrl(String),
26
27    /// Cookie parsing error.
28    #[error("Cookie parse error: {0}")]
29    CookieParse(String),
30
31    /// Decompression error.
32    #[error("Decompression error: {0}")]
33    Decompression(String),
34
35    /// URL parsing error.
36    #[error("URL parse error: {0}")]
37    UrlParse(#[from] url::ParseError),
38
39    /// JSON serialization/deserialization error.
40    #[error("JSON error: {0}")]
41    Json(#[from] serde_json::Error),
42
43    /// URL encoding error.
44    #[error("URL encoding error: {0}")]
45    UrlEncode(#[from] serde_urlencoded::ser::Error),
46
47    /// IO error.
48    #[error("IO error: {0}")]
49    Io(#[from] io::Error),
50
51    /// Missing required field or data.
52    #[error("Missing required: {0}")]
53    Missing(String),
54
55    /// Generic timeout error.
56    #[error("Operation timed out: {0}")]
57    Timeout(String),
58
59    /// Connect timeout (TCP + TLS handshake).
60    #[error("Connect timeout after {0:?}")]
61    ConnectTimeout(std::time::Duration),
62
63    /// TTFB (time-to-first-byte) timeout.
64    #[error("TTFB timeout after {0:?} - server did not respond with headers")]
65    TtfbTimeout(std::time::Duration),
66
67    /// Read idle timeout (no data received within duration).
68    #[error("Read idle timeout after {0:?} - stream may be hung")]
69    ReadIdleTimeout(std::time::Duration),
70
71    /// Write idle timeout (could not send data within duration).
72    #[error("Write idle timeout after {0:?}")]
73    WriteIdleTimeout(std::time::Duration),
74
75    /// Total request deadline exceeded.
76    #[error("Total request deadline exceeded after {0:?}")]
77    TotalTimeout(std::time::Duration),
78
79    /// Pool acquire timeout (no connection available).
80    #[error("Pool acquire timeout after {0:?} - no connections available")]
81    PoolAcquireTimeout(std::time::Duration),
82
83    /// Connection error.
84    #[error("Connection error: {0}")]
85    Connection(String),
86
87    /// TLS/SSL error.
88    #[error("TLS error: {0}")]
89    Tls(String),
90
91    /// QUIC/HTTP3 error.
92    #[error("QUIC error: {0}")]
93    Quic(String),
94
95    /// HTTP/2 SETTINGS_TIMEOUT error (RFC 9113 Section 7).
96    #[error("SETTINGS_TIMEOUT (0x04): No SETTINGS frame received within {0:?}")]
97    SettingsTimeout(std::time::Duration),
98}
99
100impl Error {
101    /// Create an HTTP status error.
102    pub fn http_status(status: u16, message: impl Into<String>) -> Self {
103        Self::HttpStatus {
104            status,
105            message: message.into(),
106        }
107    }
108
109    /// Create a missing field error.
110    pub fn missing(field: impl Into<String>) -> Self {
111        Self::Missing(field.into())
112    }
113
114    /// Create an IO error with custom message.
115    pub fn io(message: impl Into<String>) -> Self {
116        Self::Io(io::Error::other(message.into()))
117    }
118
119    /// Create an HTTP protocol error.
120    pub fn http_protocol(message: impl Into<String>) -> Self {
121        Self::HttpProtocol(message.into())
122    }
123
124    /// Create a connection error.
125    pub fn connection(message: impl Into<String>) -> Self {
126        Self::Connection(message.into())
127    }
128
129    /// Create a timeout error.
130    pub fn timeout(message: impl Into<String>) -> Self {
131        Self::Timeout(message.into())
132    }
133
134    /// Create a TLS error.
135    pub fn tls(message: impl Into<String>) -> Self {
136        Self::Tls(message.into())
137    }
138
139    /// Create a QUIC error.
140    pub fn quic(message: impl Into<String>) -> Self {
141        Self::Quic(message.into())
142    }
143}