use std::fmt;
#[derive(Debug)]
pub enum SessionError {
ClientDisconnect(std::io::Error),
Backend(anyhow::Error),
}
impl fmt::Display for SessionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ClientDisconnect(e) => write!(f, "client disconnected: {e}"),
Self::Backend(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for SessionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ClientDisconnect(e) => Some(e),
Self::Backend(e) => e.source(),
}
}
}
impl From<anyhow::Error> for SessionError {
fn from(e: anyhow::Error) -> Self {
use crate::connection_error::{ConnectionError, is_disconnect_kind};
match e.downcast::<std::io::Error>() {
Ok(io_err) if is_disconnect_kind(io_err.kind()) => Self::ClientDisconnect(io_err),
Ok(io_err) => Self::Backend(io_err.into()),
Err(e) => {
match e.downcast::<ConnectionError>() {
Ok(ConnectionError::IoError(io_err)) if is_disconnect_kind(io_err.kind()) => {
Self::ClientDisconnect(io_err)
}
Ok(conn_err) => Self::Backend(conn_err.into()),
Err(e) => Self::Backend(e),
}
}
}
}
}
impl From<crate::session::response_transfer::ResponseTransferError> for SessionError {
fn from(e: crate::session::response_transfer::ResponseTransferError) -> Self {
match e {
crate::session::response_transfer::ResponseTransferError::ClientDisconnect(io_err) => {
Self::ClientDisconnect(io_err)
}
other => Self::Backend(other.into_anyhow()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::ErrorKind;
#[test]
fn client_disconnect_from_broken_pipe_io_error() {
let io_err = std::io::Error::from(ErrorKind::BrokenPipe);
let e = SessionError::from(anyhow::Error::from(io_err));
assert!(matches!(e, SessionError::ClientDisconnect(_)));
}
#[test]
fn client_disconnect_from_connection_reset_io_error() {
let io_err = std::io::Error::from(ErrorKind::ConnectionReset);
let e = SessionError::from(anyhow::Error::from(io_err));
assert!(matches!(e, SessionError::ClientDisconnect(_)));
}
#[test]
fn backend_from_non_disconnect_io_error() {
let io_err = std::io::Error::from(ErrorKind::TimedOut);
let e = SessionError::from(anyhow::Error::from(io_err));
assert!(matches!(e, SessionError::Backend(_)));
}
#[test]
fn backend_from_non_io_error() {
let e = SessionError::from(anyhow::anyhow!("generic error"));
assert!(matches!(e, SessionError::Backend(_)));
}
#[test]
fn client_disconnect_from_response_transfer_error() {
let io_err = std::io::Error::from(ErrorKind::BrokenPipe);
let response_transfer_err =
crate::session::response_transfer::ResponseTransferError::ClientDisconnect(io_err);
let e = SessionError::from(response_transfer_err);
assert!(matches!(e, SessionError::ClientDisconnect(_)));
}
#[test]
fn backend_from_response_transfer_backend_eof() {
let response_transfer_err =
crate::session::response_transfer::ResponseTransferError::BackendEof {
backend_id: crate::types::BackendId::from_index(0),
bytes_received: 100,
};
let e = SessionError::from(response_transfer_err);
assert!(matches!(e, SessionError::Backend(_)));
}
}