use std::fmt;
use std::net::TcpStream;
use tungstenite::protocol::WebSocket;
use tungstenite::stream::MaybeTlsStream;
use tungstenite::Message;
use crate::error::FrpError;
use crate::message::{FrpMessage, FrpProtocolMessage};
pub struct FrpClient {
socket: WebSocket<MaybeTlsStream<TcpStream>>,
version: String,
}
impl FrpClient {
pub fn connect(url: &str, name: &str, versions: &[&str]) -> Result<Self, FrpError> {
let (mut socket, _response) = tungstenite::connect(url)?;
let start = FrpProtocolMessage::Start {
version: versions.iter().map(|&s| s.to_owned()).collect(),
name: Some(name.to_owned()),
};
let json = serde_json::to_string(&start)?;
socket.send(Message::text(json))?;
let version = loop {
match socket.read()? {
Message::Text(text) => {
if let Ok(FrpMessage::Protocol(FrpProtocolMessage::Init { version })) =
FrpMessage::parse(&text)
{
break version;
}
if let Ok(FrpMessage::Protocol(FrpProtocolMessage::Alert {
severity: crate::Severity::Critical,
message,
})) = FrpMessage::parse(&text)
{
return Err(FrpError::Handshake(message));
}
}
Message::Close(_) => return Err(FrpError::Closed),
_ => {}
}
};
Ok(Self { socket, version })
}
#[must_use]
pub fn version(&self) -> &str {
&self.version
}
pub fn set_nonblocking(&self, nonblocking: bool) -> Result<(), FrpError> {
match self.socket.get_ref() {
MaybeTlsStream::Plain(tcp) => tcp
.set_nonblocking(nonblocking)
.map_err(|e| FrpError::WebSocket(Box::new(tungstenite::Error::Io(e)))),
_ => Ok(()),
}
}
pub fn recv(&mut self) -> Result<FrpMessage, FrpError> {
loop {
match self.socket.read()? {
Message::Text(text) => return FrpMessage::parse(&text),
Message::Close(_) => return Err(FrpError::Closed),
_ => {}
}
}
}
pub fn try_recv(&mut self) -> Result<Option<FrpMessage>, FrpError> {
loop {
match self.socket.read() {
Ok(Message::Text(text)) => return Ok(Some(FrpMessage::parse(&text)?)),
Ok(Message::Close(_)) => return Err(FrpError::Closed),
Ok(_) => {}
Err(tungstenite::Error::Io(ref e))
if e.kind() == std::io::ErrorKind::WouldBlock =>
{
return Ok(None);
}
Err(e) => return Err(e.into()),
}
}
}
pub fn send(&mut self, msg: &FrpMessage) -> Result<(), FrpError> {
let json = msg.to_json()?;
self.socket.send(Message::text(json))?;
Ok(())
}
pub fn send_protocol(&mut self, msg: &FrpProtocolMessage) -> Result<(), FrpError> {
let json = serde_json::to_string(msg)?;
self.socket.send(Message::text(json))?;
Ok(())
}
pub fn close(mut self) -> Result<(), FrpError> {
self.socket.close(None)?;
loop {
match self.socket.read() {
Ok(Message::Close(_)) | Err(tungstenite::Error::ConnectionClosed) => {
return Ok(());
}
Err(tungstenite::Error::AlreadyClosed) => return Ok(()),
Err(e) => return Err(e.into()),
_ => {}
}
}
}
}
impl fmt::Debug for FrpClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FrpClient")
.field("version", &self.version)
.finish_non_exhaustive()
}
}