diff --git a/src/client.rs b/src/client.rs index 1741fa2..074c7ff 100644 --- a/src/client.rs +++ b/src/client.rs @@ -52,7 +52,7 @@ mod encryption { use std::net::TcpStream; use crate::{ - error::{Error, Result}, + error::{Error, UrlErrorType, Result}, stream::Mode, }; @@ -62,7 +62,7 @@ mod encryption { pub fn wrap_stream(stream: TcpStream, _domain: &str, mode: Mode) -> Result { match mode { Mode::Plain => Ok(stream), - Mode::Tls => Err(Error::Url("TLS support not compiled in.".into())), + Mode::Tls => Err(Error::Url(UrlErrorType::TlsFeatureNotEnabled)), } } } @@ -71,7 +71,7 @@ use self::encryption::wrap_stream; pub use self::encryption::AutoStream; use crate::{ - error::{Error, Result}, + error::{Error, UrlErrorType, Result}, handshake::{client::ClientHandshake, HandshakeError}, protocol::WebSocket, stream::{Mode, NoDelay}, @@ -104,7 +104,7 @@ pub fn connect_with_config( let uri = request.uri(); let mode = uri_mode(uri)?; let host = - request.uri().host().ok_or_else(|| Error::Url("No host name in the URL".into()))?; + request.uri().host().ok_or_else(|| Error::Url(UrlErrorType::NoHostName))?; let port = uri.port_u16().unwrap_or(match mode { Mode::Plain => 80, Mode::Tls => 443, @@ -166,7 +166,7 @@ pub fn connect(request: Req) -> Result<(WebSocket Result { - let domain = uri.host().ok_or_else(|| Error::Url("No host name in the URL".into()))?; + let domain = uri.host().ok_or_else(|| Error::Url(UrlErrorType::NoHostName))?; for addr in addrs { debug!("Trying to contact {} at {}...", uri, addr); if let Ok(raw_stream) = TcpStream::connect(addr) { @@ -175,7 +175,7 @@ fn connect_to_some(addrs: &[SocketAddr], uri: &Uri, mode: Mode) -> Result Result { match uri.scheme_str() { Some("ws") => Ok(Mode::Plain), Some("wss") => Ok(Mode::Tls), - _ => Err(Error::Url("URL scheme not supported".into())), + _ => Err(Error::Url(UrlErrorType::UnsupportedUrlScheme)), } } diff --git a/src/error.rs b/src/error.rs index c2becc7..f9891f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,7 +14,7 @@ pub mod tls { /// Result type of all Tungstenite library calls. pub type Result = result::Result; -/// Possible WebSocket errors +/// Possible WebSocket errors. #[derive(Debug)] pub enum Error { /// WebSocket connection closed normally. This informs you of the close. @@ -54,7 +54,7 @@ pub enum Error { /// UTF coding error Utf8, /// Invalid URL. - Url(Cow<'static, str>), + Url(UrlErrorType), /// HTTP error. Http(Response>), /// HTTP format error. @@ -151,3 +151,33 @@ impl From for Error { } } } + +/// Indicates the specific type/cause of URL error. +#[derive(Debug)] +pub enum UrlErrorType { + /// TLS is used despite not being compiled with the TLS feature enabled. + TlsFeatureNotEnabled, + /// The URL does not include a host name. + NoHostName, + /// Failed to connect with this URL. + UnableToConnect(String), + /// Unsupported URL scheme used (only `ws://` or `wss://` may be used). + UnsupportedUrlScheme, + /// The URL host name, though included, is empty. + EmptyHostName, + /// The URL does not include a path/query. + NoPathOrQuery +} + +impl fmt::Display for UrlErrorType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + UrlErrorType::TlsFeatureNotEnabled => write!(f, "TLS support not compiled in"), + UrlErrorType::NoHostName => write!(f, "No host name in the URL"), + UrlErrorType::UnableToConnect(uri) => write!(f, "Unable to connect to {}", uri), + UrlErrorType::UnsupportedUrlScheme => write!(f, "URL scheme not supported"), + UrlErrorType::EmptyHostName => write!(f, "URL contains empty host name"), + UrlErrorType::NoPathOrQuery => write!(f, "No path/query in URL") + } + } +} \ No newline at end of file diff --git a/src/handshake/client.rs b/src/handshake/client.rs index ea011fd..558eb85 100644 --- a/src/handshake/client.rs +++ b/src/handshake/client.rs @@ -16,7 +16,7 @@ use super::{ HandshakeRole, MidHandshake, ProcessingResult, }; use crate::{ - error::{Error, Result}, + error::{Error, UrlErrorType, Result}, protocol::{Role, WebSocket, WebSocketConfig}, }; @@ -98,7 +98,7 @@ fn generate_request(request: Request, key: &str) -> Result> { let uri = request.uri(); let authority = - uri.authority().ok_or_else(|| Error::Url("No host name in the URL".into()))?.as_str(); + uri.authority().ok_or_else(|| Error::Url(UrlErrorType::NoHostName))?.as_str(); let host = if let Some(idx) = authority.find('@') { // handle possible name:password@ authority.split_at(idx + 1).1 @@ -106,7 +106,7 @@ fn generate_request(request: Request, key: &str) -> Result> { authority }; if authority.is_empty() { - return Err(Error::Url("URL contains empty host name".into())); + return Err(Error::Url(UrlErrorType::EmptyHostName)); } write!( @@ -121,7 +121,7 @@ fn generate_request(request: Request, key: &str) -> Result> { version = request.version(), host = host, path = - uri.path_and_query().ok_or_else(|| Error::Url("No path/query in URL".into()))?.as_str(), + uri.path_and_query().ok_or_else(|| Error::Url(UrlErrorType::NoPathOrQuery))?.as_str(), key = key ) .unwrap();