use crate::{coding, serve, setup};
#[derive(thiserror::Error, Debug, Clone)]
pub enum SessionError {
#[error("webtransport error: {0}")]
WebTransport(#[from] web_transport::Error),
#[error("encode error: {0}")]
Encode(#[from] coding::EncodeError),
#[error("decode error: {0}")]
Decode(#[from] coding::DecodeError),
#[error("unsupported versions: client={0:?} server={1:?}")]
Version(setup::Versions, setup::Versions),
#[error("role violation")]
RoleViolation,
#[error("varint bounds exceeded")]
BoundsExceeded(#[from] coding::BoundsExceeded),
#[error("duplicate")]
Duplicate,
#[error("internal error")]
Internal,
#[error("serve error: {0}")]
Serve(#[from] serve::ServeError),
#[error("wrong size")]
WrongSize,
#[error("invalid connection path: {0}")]
InvalidPath(String),
}
impl SessionError {
pub fn code(&self) -> u64 {
match self {
Self::RoleViolation => 0x3,
Self::WebTransport(_) => 0x1,
Self::Encode(_) => 0x1,
Self::BoundsExceeded(_) => 0x1,
Self::Internal => 0x1,
Self::Version(..) => 0x15,
Self::Decode(_) => 0x3,
Self::WrongSize => 0x3,
Self::InvalidPath(_) => 0x3,
Self::Duplicate => 0x5,
Self::Serve(err) => err.code(),
}
}
pub fn unimplemented(feature: &str) -> Self {
Self::Serve(serve::ServeError::not_implemented_ctx(feature))
}
pub fn is_graceful_close(&self) -> bool {
match self {
Self::WebTransport(wt_err) => match wt_err {
web_transport::Error::Session(session_err) => {
is_session_error_graceful(session_err)
}
web_transport::Error::Read(read_err) => {
if let web_transport::quinn::ReadError::SessionError(session_err) = read_err {
return is_session_error_graceful(session_err);
}
false
}
web_transport::Error::Write(write_err) => {
if let web_transport::quinn::WriteError::SessionError(session_err) = write_err {
return is_session_error_graceful(session_err);
}
false
}
_ => false,
},
_ => false,
}
}
}
impl From<SessionError> for serve::ServeError {
fn from(err: SessionError) -> Self {
match err {
SessionError::Serve(err) => err,
_ => serve::ServeError::internal_ctx(format!("session error: {}", err)),
}
}
}
fn is_session_error_graceful(err: &web_transport::quinn::SessionError) -> bool {
use web_transport::quinn::{SessionError, WebTransportError};
match err {
SessionError::ConnectionError(conn_err) => is_connection_error_graceful(conn_err),
SessionError::WebTransportError(WebTransportError::Closed(0, _)) => true,
SessionError::WebTransportError(_) => false,
SessionError::SendDatagramError(_) => false,
}
}
fn is_connection_error_graceful(err: &web_transport::quinn::quinn::ConnectionError) -> bool {
use web_transport::quinn::quinn::ConnectionError;
match err {
ConnectionError::ApplicationClosed(close) => {
let code = close.error_code.into_inner();
if code == 0 {
return true;
}
if let Some(wt_code) = web_transport::quinn::proto::error_from_http3(code) {
return wt_code == 0;
}
false
}
ConnectionError::LocallyClosed => true,
_ => false,
}
}