use websocket::{
result::WebSocketError,
server::{sync::Server, NoTlsAcceptor},
ClientBuilder,
};
use crate::transport::{ConnectError, Connection, ListenError, Listener, Transport};
use super::connection::WsClientConnection;
use super::listener::WsListener;
const PROTOCOL_PREFIX: &str = "ws://";
#[derive(Default)]
pub struct WsTransport {}
impl Transport for WsTransport {
fn accepts(&self, address: &str) -> bool {
address.starts_with(PROTOCOL_PREFIX)
}
fn connect(&mut self, endpoint: &str) -> Result<Box<dyn Connection>, ConnectError> {
if !self.accepts(endpoint) {
return Err(ConnectError::ProtocolError(format!(
"Invalid protocol \"{}\"",
endpoint
)));
}
let client = ClientBuilder::new(endpoint)?.connect_insecure()?;
client.set_nonblocking(true)?;
let remote_endpoint = format!("ws://{}", client.peer_addr()?);
let local_endpoint = format!("ws://{}", client.local_addr()?);
Ok(Box::new(WsClientConnection::new(
client,
remote_endpoint,
local_endpoint,
)))
}
fn listen(&mut self, bind: &str) -> Result<Box<dyn Listener>, ListenError> {
if !self.accepts(bind) {
return Err(ListenError::ProtocolError(format!(
"Invalid protocol \"{}\"",
bind
)));
}
let address = if bind.starts_with(PROTOCOL_PREFIX) {
&bind[PROTOCOL_PREFIX.len()..]
} else {
bind
};
let server: Server<NoTlsAcceptor> = Server::bind(address)
.map_err(|err| ListenError::IoError(format!("Failed to bind to {}", address), err))?;
let local_endpoint = format!(
"ws://{}",
server.local_addr().map_err(|err| {
ListenError::IoError("Failed to get local address".into(), err)
})?
);
Ok(Box::new(WsListener::new(server, local_endpoint)))
}
}
impl From<WebSocketError> for ConnectError {
fn from(err: WebSocketError) -> Self {
match err {
WebSocketError::IoError(e) => ConnectError::from(e),
_ => ConnectError::ProtocolError(format!("WebSocketError: {}", err.to_string())),
}
}
}