nntp_proxy/session/
session_error.rs1use std::fmt;
8
9#[derive(Debug)]
15pub enum SessionError {
16 ClientDisconnect(std::io::Error),
20
21 Backend(anyhow::Error),
23}
24
25impl fmt::Display for SessionError {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 Self::ClientDisconnect(e) => write!(f, "client disconnected: {e}"),
29 Self::Backend(e) => write!(f, "{e}"),
30 }
31 }
32}
33
34impl std::error::Error for SessionError {
35 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
36 match self {
37 Self::ClientDisconnect(e) => Some(e),
38 Self::Backend(e) => e.source(),
39 }
40 }
41}
42
43impl From<anyhow::Error> for SessionError {
49 fn from(e: anyhow::Error) -> Self {
50 use crate::connection_error::{ConnectionError, is_disconnect_kind};
51
52 match e.downcast::<std::io::Error>() {
54 Ok(io_err) if is_disconnect_kind(io_err.kind()) => Self::ClientDisconnect(io_err),
55 Ok(io_err) => Self::Backend(io_err.into()),
56 Err(e) => {
57 match e.downcast::<ConnectionError>() {
59 Ok(ConnectionError::IoError(io_err)) if is_disconnect_kind(io_err.kind()) => {
60 Self::ClientDisconnect(io_err)
61 }
62 Ok(conn_err) => Self::Backend(conn_err.into()),
63 Err(e) => Self::Backend(e),
64 }
65 }
66 }
67 }
68}
69
70impl From<crate::session::response_transfer::ResponseTransferError> for SessionError {
72 fn from(e: crate::session::response_transfer::ResponseTransferError) -> Self {
73 match e {
74 crate::session::response_transfer::ResponseTransferError::ClientDisconnect(io_err) => {
75 Self::ClientDisconnect(io_err)
76 }
77 other => Self::Backend(other.into_anyhow()),
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use std::io::ErrorKind;
86
87 #[test]
88 fn client_disconnect_from_broken_pipe_io_error() {
89 let io_err = std::io::Error::from(ErrorKind::BrokenPipe);
90 let e = SessionError::from(anyhow::Error::from(io_err));
91 assert!(matches!(e, SessionError::ClientDisconnect(_)));
92 }
93
94 #[test]
95 fn client_disconnect_from_connection_reset_io_error() {
96 let io_err = std::io::Error::from(ErrorKind::ConnectionReset);
97 let e = SessionError::from(anyhow::Error::from(io_err));
98 assert!(matches!(e, SessionError::ClientDisconnect(_)));
99 }
100
101 #[test]
102 fn backend_from_non_disconnect_io_error() {
103 let io_err = std::io::Error::from(ErrorKind::TimedOut);
104 let e = SessionError::from(anyhow::Error::from(io_err));
105 assert!(matches!(e, SessionError::Backend(_)));
106 }
107
108 #[test]
109 fn backend_from_non_io_error() {
110 let e = SessionError::from(anyhow::anyhow!("generic error"));
111 assert!(matches!(e, SessionError::Backend(_)));
112 }
113
114 #[test]
115 fn client_disconnect_from_response_transfer_error() {
116 let io_err = std::io::Error::from(ErrorKind::BrokenPipe);
117 let response_transfer_err =
118 crate::session::response_transfer::ResponseTransferError::ClientDisconnect(io_err);
119 let e = SessionError::from(response_transfer_err);
120 assert!(matches!(e, SessionError::ClientDisconnect(_)));
121 }
122
123 #[test]
124 fn backend_from_response_transfer_backend_eof() {
125 let response_transfer_err =
126 crate::session::response_transfer::ResponseTransferError::BackendEof {
127 backend_id: crate::types::BackendId::from_index(0),
128 bytes_received: 100,
129 };
130 let e = SessionError::from(response_transfer_err);
131 assert!(matches!(e, SessionError::Backend(_)));
132 }
133}