aerosocket_server/
error.rs

1//! Error handling and logging for the WebSocket server
2//!
3//! This module provides comprehensive error handling and logging capabilities
4//! for the WebSocket server implementation.
5
6use aerosocket_core::error::Error;
7use std::fmt;
8use std::io;
9
10/// Server-specific errors
11#[derive(Debug, thiserror::Error)]
12pub enum ServerError {
13    /// Configuration error
14    #[error("Configuration error: {0}")]
15    Config(#[from] ConfigError),
16
17    /// Connection error
18    #[error("Connection error: {0}")]
19    Connection(#[from] ConnectionError),
20
21    /// Handshake error
22    #[error("Handshake error: {0}")]
23    Handshake(#[from] HandshakeError),
24
25    /// Protocol error
26    #[error("Protocol error: {0}")]
27    Protocol(#[from] ProtocolError),
28
29    /// Transport error
30    #[error("Transport error: {0}")]
31    Transport(#[from] TransportError),
32
33    /// Handler error
34    #[error("Handler error: {0}")]
35    Handler(#[from] HandlerError),
36
37    /// Manager error
38    #[error("Manager error: {0}")]
39    Manager(#[from] ManagerError),
40
41    /// I/O error
42    #[error("I/O error: {0}")]
43    Io(#[from] io::Error),
44
45    /// Core error
46    #[error("Core error: {0}")]
47    Core(#[from] Error),
48
49    /// Timeout error
50    #[error("Operation timed out after {duration:?}")]
51    Timeout { duration: std::time::Duration },
52
53    /// Capacity error
54    #[error("Capacity exceeded: {0}")]
55    Capacity(String),
56
57    /// Authentication error
58    #[error("Authentication failed: {0}")]
59    Authentication(String),
60
61    /// Authorization error
62    #[error("Access denied: {0}")]
63    Authorization(String),
64
65    /// Rate limit error
66    #[error("Rate limit exceeded: {0}")]
67    RateLimit(String),
68
69    /// Internal error
70    #[error("Internal server error: {0}")]
71    Internal(String),
72}
73
74/// Configuration errors
75#[derive(Debug, thiserror::Error)]
76pub enum ConfigError {
77    /// Invalid bind address
78    #[error("Invalid bind address: {0}")]
79    InvalidBindAddress(String),
80
81    /// Invalid timeout value
82    #[error("Invalid timeout value: {0}")]
83    InvalidTimeout(String),
84
85    /// Invalid buffer size
86    #[error("Invalid buffer size: {0}")]
87    InvalidBufferSize(String),
88
89    /// Missing required configuration
90    #[error("Missing required configuration: {0}")]
91    MissingRequired(String),
92
93    /// Invalid TLS configuration
94    #[error("Invalid TLS configuration: {0}")]
95    InvalidTlsConfig(String),
96
97    /// Invalid compression configuration
98    #[error("Invalid compression configuration: {0}")]
99    InvalidCompressionConfig(String),
100}
101
102/// Connection errors
103#[derive(Debug, thiserror::Error)]
104pub enum ConnectionError {
105    /// Connection closed
106    #[error("Connection closed")]
107    Closed,
108
109    /// Connection timed out
110    #[error("Connection timed out")]
111    TimedOut,
112
113    /// Connection reset
114    #[error("Connection reset by peer")]
115    Reset,
116
117    /// Connection refused
118    #[error("Connection refused")]
119    Refused,
120
121    /// Connection limit exceeded
122    #[error("Connection limit exceeded: {current}/{max}")]
123    LimitExceeded { current: usize, max: usize },
124
125    /// Invalid connection state
126    #[error("Invalid connection state: {state}")]
127    InvalidState { state: String },
128
129    /// Connection not found
130    #[error("Connection not found: {id}")]
131    NotFound { id: u64 },
132
133    /// Connection already exists
134    #[error("Connection already exists: {id}")]
135    AlreadyExists { id: u64 },
136}
137
138/// Handshake errors
139#[derive(Debug, thiserror::Error)]
140pub enum HandshakeError {
141    /// Invalid request method
142    #[error("Invalid HTTP method: {method}")]
143    InvalidMethod { method: String },
144
145    /// Invalid WebSocket version
146    #[error("Invalid WebSocket version: {version}")]
147    InvalidVersion { version: String },
148
149    /// Missing required header
150    #[error("Missing required header: {header}")]
151    MissingHeader { header: String },
152
153    /// Invalid header value
154    #[error("Invalid header value for '{header}': {value}")]
155    InvalidHeaderValue { header: String, value: String },
156
157    /// Unsupported subprotocol
158    #[error("Unsupported subprotocol: {protocol}")]
159    UnsupportedSubprotocol { protocol: String },
160
161    /// Unsupported extension
162    #[error("Unsupported extension: {extension}")]
163    UnsupportedExtension { extension: String },
164
165    /// Invalid origin
166    #[error("Invalid origin: {origin}")]
167    InvalidOrigin { origin: String },
168
169    /// Key mismatch
170    #[error("WebSocket key mismatch")]
171    KeyMismatch,
172
173    /// Authentication failed
174    #[error("Handshake authentication failed: {reason}")]
175    AuthenticationFailed { reason: String },
176}
177
178/// Protocol errors
179#[derive(Debug, thiserror::Error)]
180pub enum ProtocolError {
181    /// Invalid opcode
182    #[error("Invalid WebSocket opcode: {opcode}")]
183    InvalidOpcode { opcode: u8 },
184
185    /// Invalid frame format
186    #[error("Invalid frame format: {reason}")]
187    InvalidFrame { reason: String },
188
189    /// Frame too large
190    #[error("Frame too large: {size}/{max_size}")]
191    FrameTooLarge { size: usize, max_size: usize },
192
193    /// Message too large
194    #[error("Message too large: {size}/{max_size}")]
195    MessageTooLarge { size: usize, max_size: usize },
196
197    /// Invalid UTF-8
198    #[error("Invalid UTF-8 sequence in text frame")]
199    InvalidUtf8,
200
201    /// Control frame fragmented
202    #[error("Control frame fragmented")]
203    FragmentedControlFrame,
204
205    /// Invalid continuation
206    #[error("Invalid continuation frame")]
207    InvalidContinuation,
208
209    /// Masking required
210    #[error("Client frames must be masked")]
211    MaskingRequired,
212
213    /// Masking forbidden
214    #[error("Server frames must not be masked")]
215    MaskingForbidden,
216
217    /// Reserved bits set
218    #[error("Reserved bits are set")]
219    ReservedBitsSet,
220}
221
222/// Transport errors
223#[derive(Debug, thiserror::Error)]
224pub enum TransportError {
225    /// Accept failed
226    #[error("Failed to accept connection: {0}")]
227    AcceptFailed(String),
228
229    /// Bind failed
230    #[error("Failed to bind to address: {0}")]
231    BindFailed(String),
232
233    /// Read failed
234    #[error("Read failed: {0}")]
235    ReadFailed(String),
236
237    /// Write failed
238    #[error("Write failed: {0}")]
239    WriteFailed(String),
240
241    /// Flush failed
242    #[error("Flush failed: {0}")]
243    FlushFailed(String),
244
245    /// Close failed
246    #[error("Close failed: {0}")]
247    CloseFailed(String),
248
249    /// TLS error
250    #[error("TLS error: {0}")]
251    Tls(String),
252}
253
254/// Handler errors
255#[derive(Debug, thiserror::Error)]
256pub enum HandlerError {
257    /// Handler panicked
258    #[error("Handler panicked: {0}")]
259    Panicked(String),
260
261    /// Handler returned error
262    #[error("Handler returned error: {0}")]
263    ReturnedError(String),
264
265    /// Handler timeout
266    #[error("Handler timed out after {duration:?}")]
267    Timeout { duration: std::time::Duration },
268
269    /// Handler not found
270    #[error("Handler not found for path: {path}")]
271    NotFound { path: String },
272}
273
274/// Manager errors
275#[derive(Debug, thiserror::Error)]
276pub enum ManagerError {
277    /// Manager not initialized
278    #[error("Connection manager not initialized")]
279    NotInitialized,
280
281    /// Manager shutdown
282    #[error("Connection manager is shutting down")]
283    Shutdown,
284
285    /// Invalid connection ID
286    #[error("Invalid connection ID: {id}")]
287    InvalidId { id: u64 },
288
289    /// Operation failed
290    #[error("Operation failed: {0}")]
291    OperationFailed(String),
292}
293
294impl From<ServerError> for Error {
295    fn from(err: ServerError) -> Self {
296        Error::Other(err.to_string())
297    }
298}
299
300/// Error context for better error reporting
301#[derive(Debug, Clone)]
302pub struct ErrorContext {
303    /// Connection ID
304    pub connection_id: Option<u64>,
305    /// Remote address
306    pub remote_addr: Option<std::net::SocketAddr>,
307    /// Operation being performed
308    pub operation: Option<String>,
309    /// Additional context
310    pub context: std::collections::HashMap<String, String>,
311}
312
313impl ErrorContext {
314    /// Create a new error context
315    pub fn new() -> Self {
316        Self {
317            connection_id: None,
318            remote_addr: None,
319            operation: None,
320            context: std::collections::HashMap::new(),
321        }
322    }
323
324    /// Set connection ID
325    pub fn with_connection_id(mut self, id: u64) -> Self {
326        self.connection_id = Some(id);
327        self
328    }
329
330    /// Set remote address
331    pub fn with_remote_addr(mut self, addr: std::net::SocketAddr) -> Self {
332        self.remote_addr = Some(addr);
333        self
334    }
335
336    /// Set operation
337    pub fn with_operation(mut self, op: impl Into<String>) -> Self {
338        self.operation = Some(op.into());
339        self
340    }
341
342    /// Add context key-value pair
343    pub fn with_context(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
344        self.context.insert(key.into(), value.into());
345        self
346    }
347}
348
349impl Default for ErrorContext {
350    fn default() -> Self {
351        Self::new()
352    }
353}
354
355/// Result type with error context
356pub type ContextResult<T> = Result<T, ContextError>;
357
358/// Error with context
359#[derive(Debug)]
360pub struct ContextError {
361    /// The underlying error
362    pub error: ServerError,
363    /// Error context
364    pub context: ErrorContext,
365}
366
367impl fmt::Display for ContextError {
368    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369        write!(f, "{}", self.error)?;
370
371        if let Some(id) = self.context.connection_id {
372            write!(f, " (connection: {})", id)?;
373        }
374
375        if let Some(addr) = self.context.remote_addr {
376            write!(f, " (remote: {})", addr)?;
377        }
378
379        if let Some(op) = &self.context.operation {
380            write!(f, " (operation: {})", op)?;
381        }
382
383        if !self.context.context.is_empty() {
384            write!(f, " (context: {:?})", self.context.context)?;
385        }
386
387        Ok(())
388    }
389}
390
391impl std::error::Error for ContextError {
392    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
393        Some(&self.error)
394    }
395}
396
397/// Logging utilities
398#[cfg(feature = "logging")]
399pub mod logging {
400    use super::*;
401    use tracing::{debug, error, info, instrument, trace, warn, Level};
402
403    /// Initialize logging with default configuration
404    pub fn init_default() {
405        tracing_subscriber::fmt().with_max_level(Level::INFO).init();
406    }
407
408    /// Initialize logging with custom level
409    pub fn init_with_level(level: Level) {
410        tracing_subscriber::fmt().with_max_level(level).init();
411    }
412
413    /// Log server startup
414    #[instrument]
415    pub fn log_server_start(config: &crate::config::ServerConfig) {
416        info!(
417            bind_address = %config.bind_address,
418            max_connections = config.max_connections,
419            max_frame_size = config.max_frame_size,
420            max_message_size = config.max_message_size,
421            handshake_timeout = ?config.handshake_timeout,
422            idle_timeout = ?config.idle_timeout,
423            "WebSocket server starting"
424        );
425    }
426
427    /// Log server shutdown
428    #[instrument]
429    pub fn log_server_shutdown() {
430        info!("WebSocket server shutting down");
431    }
432
433    /// Log connection established
434    #[instrument]
435    pub fn log_connection_established(id: u64, remote_addr: std::net::SocketAddr) {
436        info!(
437            connection_id = id,
438            remote_addr = %remote_addr,
439            "WebSocket connection established"
440        );
441    }
442
443    /// Log connection closed
444    #[instrument]
445    pub fn log_connection_closed(id: u64, reason: &str) {
446        info!(
447            connection_id = id,
448            reason = reason,
449            "WebSocket connection closed"
450        );
451    }
452
453    /// Log message received
454    #[instrument]
455    pub fn log_message_received(id: u64, message_type: &str, size: usize) {
456        trace!(
457            connection_id = id,
458            message_type = message_type,
459            size = size,
460            "Message received"
461        );
462    }
463
464    /// Log message sent
465    #[instrument]
466    pub fn log_message_sent(id: u64, message_type: &str, size: usize) {
467        trace!(
468            connection_id = id,
469            message_type = message_type,
470            size = size,
471            "Message sent"
472        );
473    }
474
475    /// Log error with context
476    #[instrument]
477    pub fn log_error(error: &ContextError) {
478        error!(
479            error = %error,
480            connection_id = ?error.context.connection_id,
481            remote_addr = ?error.context.remote_addr,
482            operation = ?error.context.operation,
483            "Server error occurred"
484        );
485    }
486
487    /// Log warning with context
488    #[instrument]
489    pub fn log_warning(message: &str, context: &ErrorContext) {
490        warn!(
491            message = message,
492            connection_id = ?context.connection_id,
493            remote_addr = ?context.remote_addr,
494            operation = ?context.operation,
495            "Server warning"
496        );
497    }
498
499    /// Log performance metrics
500    #[instrument]
501    pub fn log_performance_metrics(stats: &crate::manager::ManagerStats) {
502        info!(
503            active_connections = stats.active_connections,
504            total_connections = stats.total_connections,
505            timeout_closures = stats.timeout_closures,
506            error_closures = stats.error_closures,
507            normal_closures = stats.normal_closures,
508            peak_connections = stats.peak_connections,
509            "Performance metrics"
510        );
511    }
512}
513
514/// Fallback logging when logging feature is disabled
515#[cfg(not(feature = "logging"))]
516pub mod logging {
517    use super::*;
518
519    /// No-op logging initialization
520    pub fn init_default() {
521        // No-op
522    }
523
524    /// No-op logging initialization with level
525    pub fn init_with_level(_level: log::Level) {
526        // No-op
527    }
528
529    /// No-op server start logging
530    pub fn log_server_start(_config: &crate::config::ServerConfig) {
531        // No-op
532    }
533
534    /// No-op server shutdown logging
535    pub fn log_server_shutdown() {
536        // No-op
537    }
538
539    /// No-op connection established logging
540    pub fn log_connection_established(_id: u64, _remote_addr: std::net::SocketAddr) {
541        // No-op
542    }
543
544    /// No-op connection closed logging
545    pub fn log_connection_closed(_id: u64, _reason: &str) {
546        // No-op
547    }
548
549    /// No-op message received logging
550    pub fn log_message_received(_id: u64, _message_type: &str, _size: usize) {
551        // No-op
552    }
553
554    /// No-op message sent logging
555    pub fn log_message_sent(_id: u64, _message_type: &str, _size: usize) {
556        // No-op
557    }
558
559    /// No-op error logging
560    pub fn log_error(_error: &ContextError) {
561        // No-op
562    }
563
564    /// No-op warning logging
565    pub fn log_warning(_message: &str, _context: &ErrorContext) {
566        // No-op
567    }
568
569    /// No-op performance metrics logging
570    pub fn log_performance_metrics(_stats: &crate::manager::ManagerStats) {
571        // No-op
572    }
573}