use std::io;
use crate::http::error::{DecodeError, EncodeError, HttpError, ResponseError};
use crate::http::{Response, StatusCode, header::ALLOW, header::HeaderValue};
use crate::{connect::ConnectError, util::Either, util::clone_io_error};
use super::OpCode;
#[derive(Debug, thiserror::Error)]
pub enum WsError<E> {
#[error("Service error")]
Service(E),
#[error("Keep-alive error")]
KeepAlive,
#[error("Frame read timeout")]
ReadTimeout,
#[error("Ws protocol level error")]
Protocol(ProtocolError),
#[error("Peer has been disconnected: {0:?}")]
Disconnected(Option<io::Error>),
}
#[derive(Copy, Clone, Debug, thiserror::Error)]
pub enum ProtocolError {
#[error("Received an unmasked frame from client")]
UnmaskedFrame,
#[error("Received a masked frame from server")]
MaskedFrame,
#[error("Invalid opcode: {0}")]
InvalidOpcode(u8),
#[error("Invalid control frame length: {0}")]
InvalidLength(usize),
#[error("Bad web socket op code")]
BadOpCode,
#[error("A payload reached size limit.")]
Overflow,
#[error("Continuation is not started.")]
ContinuationNotStarted,
#[error("Received new continuation but it is already started")]
ContinuationStarted,
#[error("Unknown continuation fragment {0}")]
ContinuationFragment(OpCode),
}
#[derive(Clone, Debug, thiserror::Error)]
pub enum WsClientBuilderError<E> {
#[error("Cannot create connector {0}")]
Connector(E),
#[error("Missing url scheme")]
MissingScheme,
#[error("Unknown url scheme")]
UnknownScheme,
#[error("Missing host name")]
MissingHost,
#[error("Url parse error: {0}")]
Http(#[from] HttpError),
}
#[derive(Debug, thiserror::Error)]
pub enum WsClientError {
#[error("Invalid request")]
InvalidRequest(#[from] EncodeError),
#[error("Invalid response")]
InvalidResponse(#[from] DecodeError),
#[error("Invalid response status")]
InvalidResponseStatus(StatusCode),
#[error("Invalid upgrade header")]
InvalidUpgradeHeader,
#[error("Invalid connection header")]
InvalidConnectionHeader(HeaderValue),
#[error("Missing CONNECTION header")]
MissingConnectionHeader,
#[error("Missing SEC-WEBSOCKET-ACCEPT header")]
MissingWebSocketAcceptHeader,
#[error("Invalid challenge response")]
InvalidChallengeResponse(String, HeaderValue),
#[error("{0}")]
Protocol(#[from] ProtocolError),
#[error("Timeout out while waiting for response")]
Timeout,
#[error("Failed to connect to host: {0}")]
Connect(#[from] ConnectError),
#[error("Connector has been disconnected: {0:?}")]
Disconnected(Option<io::Error>),
}
impl From<Either<DecodeError, io::Error>> for WsClientError {
fn from(err: Either<DecodeError, io::Error>) -> Self {
match err {
Either::Left(err) => WsClientError::InvalidResponse(err),
Either::Right(err) => WsClientError::Disconnected(Some(err)),
}
}
}
impl From<Either<EncodeError, io::Error>> for WsClientError {
fn from(err: Either<EncodeError, io::Error>) -> Self {
match err {
Either::Left(err) => WsClientError::InvalidRequest(err),
Either::Right(err) => WsClientError::Disconnected(Some(err)),
}
}
}
impl Clone for WsClientError {
fn clone(&self) -> Self {
match self {
WsClientError::InvalidRequest(err) => {
WsClientError::InvalidRequest(err.clone())
}
WsClientError::InvalidResponse(err) => WsClientError::InvalidResponse(*err),
WsClientError::InvalidResponseStatus(err) => {
WsClientError::InvalidResponseStatus(*err)
}
WsClientError::InvalidUpgradeHeader => WsClientError::InvalidUpgradeHeader,
WsClientError::InvalidConnectionHeader(err) => {
WsClientError::InvalidConnectionHeader(err.clone())
}
WsClientError::MissingConnectionHeader => {
WsClientError::MissingConnectionHeader
}
WsClientError::MissingWebSocketAcceptHeader => {
WsClientError::MissingWebSocketAcceptHeader
}
WsClientError::InvalidChallengeResponse(n, val) => {
WsClientError::InvalidChallengeResponse(n.clone(), val.clone())
}
WsClientError::Protocol(err) => WsClientError::Protocol(*err),
WsClientError::Timeout => WsClientError::Timeout,
WsClientError::Connect(err) => WsClientError::Connect(err.clone()),
WsClientError::Disconnected(err) => {
WsClientError::Disconnected(err.as_ref().map(clone_io_error))
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, thiserror::Error)]
pub enum HandshakeError {
#[error("Method not allowed")]
GetMethodRequired,
#[error("Websocket upgrade is expected")]
NoWebsocketUpgrade,
#[error("Connection upgrade is expected")]
NoConnectionUpgrade,
#[error("Websocket version header is required")]
NoVersionHeader,
#[error("Unsupported version")]
UnsupportedVersion,
#[error("Unknown websocket key")]
BadWebsocketKey,
}
impl ResponseError for HandshakeError {
fn error_response(&self) -> Response {
match *self {
HandshakeError::GetMethodRequired => {
Response::MethodNotAllowed().header(ALLOW, "GET").finish()
}
HandshakeError::NoWebsocketUpgrade => Response::BadRequest()
.reason("No WebSocket UPGRADE header found")
.finish(),
HandshakeError::NoConnectionUpgrade => Response::BadRequest()
.reason("No CONNECTION upgrade")
.finish(),
HandshakeError::NoVersionHeader => Response::BadRequest()
.reason("Websocket version header is required")
.finish(),
HandshakeError::UnsupportedVersion => Response::BadRequest()
.reason("Unsupported version")
.finish(),
HandshakeError::BadWebsocketKey => {
Response::BadRequest().reason("Handshake error").finish()
}
}
}
}
impl ResponseError for ProtocolError {}