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(String),
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    /// WebSocket over the requested transport is unsupported.
100    #[error("WebSocket unsupported: {0}")]
101    WebSocketUnsupported(String),
102
103    /// WebSocket opening handshake failed.
104    #[error("WebSocket handshake failed with HTTP status {status}")]
105    WebSocketHandshake {
106        status: u16,
107        headers: crate::headers::Headers,
108    },
109}
110
111impl Error {
112    /// Create an HTTP status error.
113    #[cold]
114    pub fn http_status(status: u16, message: impl Into<String>) -> Self {
115        Self::HttpStatus {
116            status,
117            message: message.into(),
118        }
119    }
120
121    /// Create a missing field error.
122    #[cold]
123    pub fn missing(field: impl Into<String>) -> Self {
124        Self::Missing(field.into())
125    }
126
127    /// Create an IO error with custom message.
128    #[cold]
129    pub fn io(message: impl Into<String>) -> Self {
130        Self::Io(io::Error::other(message.into()))
131    }
132
133    /// Create an HTTP protocol error.
134    #[cold]
135    pub fn http_protocol(message: impl Into<String>) -> Self {
136        Self::HttpProtocol(message.into())
137    }
138
139    /// Create a connection error.
140    #[cold]
141    pub fn connection(message: impl Into<String>) -> Self {
142        Self::Connection(message.into())
143    }
144
145    /// Create a timeout error.
146    #[cold]
147    pub fn timeout(message: impl Into<String>) -> Self {
148        Self::Timeout(message.into())
149    }
150
151    /// Create a TLS error.
152    #[cold]
153    pub fn tls(message: impl Into<String>) -> Self {
154        Self::Tls(message.into())
155    }
156
157    /// Create a QUIC error.
158    #[cold]
159    pub fn quic(message: impl Into<String>) -> Self {
160        Self::Quic(message.into())
161    }
162}
163
164impl From<crate::url::ParseError> for Error {
165    fn from(err: crate::url::ParseError) -> Self {
166        Self::UrlParse(err.to_string())
167    }
168}