use std::fmt;
use hotaru_core::connection::error::ConnectionError;
use hotaru_core::protocol::ProtocolError;
use crate::message::http_value::StatusCode;
#[derive(Debug)]
pub enum HttpError {
Io(std::io::Error),
Connection(ConnectionError),
ParseError(String),
InvalidHeader(String),
InvalidUri(String),
ChunkError(String),
PayloadTooLarge,
MethodNotAllowed,
UnsupportedMediaType,
HeaderTooLarge,
TooManyHeaders,
HeaderLineTooLong,
Status(StatusCode),
NoRoute(String),
Timeout,
VersionNotSupported,
ProtocolViolation(String),
Other(String),
}
impl fmt::Display for HttpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HttpError::Io(err) => write!(f, "I/O error: {}", err),
HttpError::Connection(err) => write!(f, "Connection error: {}", err),
HttpError::ParseError(msg) => write!(f, "Parse error: {}", msg),
HttpError::InvalidHeader(msg) => write!(f, "Invalid header: {}", msg),
HttpError::InvalidUri(uri) => write!(f, "Invalid URI: {}", uri),
HttpError::ChunkError(msg) => write!(f, "Chunked transfer error: {}", msg),
HttpError::PayloadTooLarge => write!(f, "Payload too large"),
HttpError::MethodNotAllowed => write!(f, "Method not allowed"),
HttpError::UnsupportedMediaType => write!(f, "Unsupported media type"),
HttpError::HeaderTooLarge => write!(f, "Header section too large"),
HttpError::TooManyHeaders => write!(f, "Too many headers"),
HttpError::HeaderLineTooLong => write!(f, "Header line too long"),
HttpError::Status(code) => write!(f, "HTTP status error: {:?}", code),
HttpError::NoRoute(path) => write!(f, "No route matched path: {}", path),
HttpError::Timeout => write!(f, "Request timed out"),
HttpError::VersionNotSupported => write!(f, "HTTP version not supported"),
HttpError::ProtocolViolation(msg) => write!(f, "Protocol violation: {}", msg),
HttpError::Other(msg) => write!(f, "HTTP error: {}", msg),
}
}
}
impl std::error::Error for HttpError {}
impl ProtocolError for HttpError {
fn can_continue(&self) -> bool {
matches!(
self,
HttpError::Status(_)
| HttpError::NoRoute(_)
| HttpError::PayloadTooLarge
| HttpError::MethodNotAllowed
| HttpError::UnsupportedMediaType
| HttpError::HeaderTooLarge
| HttpError::TooManyHeaders
| HttpError::HeaderLineTooLong
| HttpError::ParseError(_)
| HttpError::InvalidHeader(_)
| HttpError::InvalidUri(_)
| HttpError::ChunkError(_)
| HttpError::VersionNotSupported
| HttpError::ProtocolViolation(_)
| HttpError::Timeout
| HttpError::Other(_)
)
}
}
impl From<std::io::Error> for HttpError {
fn from(err: std::io::Error) -> Self {
HttpError::Io(err)
}
}
impl From<ConnectionError> for HttpError {
fn from(err: ConnectionError) -> Self {
HttpError::Connection(err)
}
}
impl From<StatusCode> for HttpError {
fn from(code: StatusCode) -> Self {
HttpError::Status(code)
}
}
impl From<&HttpError> for StatusCode {
fn from(err: &HttpError) -> Self {
match err {
HttpError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
HttpError::Connection(_) => StatusCode::BAD_GATEWAY,
HttpError::ParseError(_) => StatusCode::BAD_REQUEST,
HttpError::InvalidHeader(_) => StatusCode::BAD_REQUEST,
HttpError::InvalidUri(_) => StatusCode::BAD_REQUEST,
HttpError::ChunkError(_) => StatusCode::BAD_REQUEST,
HttpError::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
HttpError::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
HttpError::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
HttpError::HeaderTooLarge => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
HttpError::TooManyHeaders => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
HttpError::HeaderLineTooLong => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
HttpError::Status(code) => code.clone(),
HttpError::NoRoute(_) => StatusCode::NOT_FOUND,
HttpError::Timeout => StatusCode::REQUEST_TIMEOUT,
HttpError::VersionNotSupported => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
HttpError::ProtocolViolation(_) => StatusCode::BAD_REQUEST,
HttpError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}