use std::io;
use std::net::TcpStream;
use std::sync::Arc;
use std::time::Duration;
use rsurl::net::{Client, Connector, NetStream};
use rsurl::{WsMessage, WsReader, WsWriter};
use crate::error::{Error, Result};
use crate::resolver::lookup_host;
#[derive(Debug)]
struct GdnsConnector;
impl Connector for GdnsConnector {
fn connect(
&self,
host: &str,
port: u16,
timeout: Option<Duration>,
) -> rsurl::Result<Box<dyn NetStream>> {
let addrs = lookup_host(host, port).map_err(|e| match e {
Error::Io(io) => rsurl::Error::Io(io),
other => rsurl::Error::Io(io::Error::other(other.to_string())),
})?;
let mut last: Option<io::Error> = None;
for addr in addrs {
let res = match timeout {
Some(t) => TcpStream::connect_timeout(&addr, t),
None => TcpStream::connect(addr),
};
match res {
Ok(s) => return Ok(Box::new(s)),
Err(e) => last = Some(e),
}
}
Err(rsurl::Error::Io(last.unwrap_or_else(|| {
io::Error::other(format!("no addresses found for {host}"))
})))
}
fn is_direct(&self) -> bool {
true
}
}
pub enum Incoming {
Packet(Vec<u8>),
Closed,
Timeout,
}
pub fn connect(host: &str, path: &str, connect_timeout: Duration) -> Result<(WsReader, WsWriter)> {
let client = Client::new()
.connector(Arc::new(GdnsConnector))
.verify_tls(true)
.connect_timeout(Some(connect_timeout))
.read_timeout(None);
let url = format!("wss://{host}{path}");
let ws = client
.websocket(&url)
.map_err(|e| Error::Ws(e.to_string()))?;
Ok(ws.split())
}
pub fn recv_packet(reader: &mut WsReader) -> Result<Incoming> {
loop {
match reader.recv() {
Ok(Some(WsMessage::Binary(data))) => return Ok(Incoming::Packet(data)),
Ok(Some(WsMessage::Text(_))) => continue, Ok(None) => return Ok(Incoming::Closed),
Err(rsurl::Error::Io(e))
if matches!(e.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut) =>
{
return Ok(Incoming::Timeout)
}
Err(e) => return Err(Error::Ws(e.to_string())),
}
}
}
pub fn send(writer: &std::sync::Mutex<WsWriter>, bytes: &[u8]) -> Result<()> {
writer
.lock()
.unwrap()
.send_binary(bytes)
.map_err(|e| Error::Ws(e.to_string()))
}
pub fn close(writer: &std::sync::Mutex<WsWriter>) {
let _ = writer.lock().unwrap().close();
}