use super::command_channel::*;
use super::timeout::*;
use crate::crypto::*;
use crate::error::{Error, Result};
use crate::util::*;
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use std::future::Future;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, ToSocketAddrs};
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::task::JoinHandle;
#[allow(clippy::type_complexity)]
#[must_use = "event callbacks do nothing unless you configure them for a client"]
pub struct ClientEventCallbacks<R>
where
R: DeserializeOwned + 'static,
{
receive: Option<Arc<dyn Fn(R) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>>,
disconnect: Option<Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>>,
}
impl<R> ClientEventCallbacks<R>
where
R: DeserializeOwned + 'static,
{
pub const fn new() -> Self {
Self {
receive: None,
disconnect: None,
}
}
pub fn on_receive<C, F>(mut self, callback: C) -> Self
where
C: Fn(R) -> F + Send + Sync + 'static,
F: Future<Output = ()> + Send + 'static,
{
self.receive = Some(Arc::new(move |data| Box::pin((callback)(data))));
self
}
pub fn on_disconnect<C, F>(mut self, callback: C) -> Self
where
C: Fn() -> F + Send + Sync + 'static,
F: Future<Output = ()> + Send + 'static,
{
self.disconnect = Some(Arc::new(move || Box::pin((callback)())));
self
}
}
impl<R> Default for ClientEventCallbacks<R>
where
R: DeserializeOwned + 'static,
{
fn default() -> Self {
Self::new()
}
}
pub trait ClientEventHandler<R>
where
Self: Send + Sync,
R: DeserializeOwned + 'static,
{
#[allow(unused_variables)]
fn on_receive(&self, data: R) -> impl Future<Output = ()> + Send {
async {}
}
fn on_disconnect(&self) -> impl Future<Output = ()> + Send {
async {}
}
}
pub struct ClientSendingUnknown;
pub struct ClientSending<S>(PhantomData<fn() -> S>)
where
S: Serialize + 'static;
trait ClientSendingConfig {}
impl ClientSendingConfig for ClientSendingUnknown {}
impl<S> ClientSendingConfig for ClientSending<S> where S: Serialize + 'static {}
pub struct ClientReceivingUnknown;
pub struct ClientReceiving<R>(PhantomData<fn() -> R>)
where
R: DeserializeOwned + 'static;
trait ClientReceivingConfig {}
impl ClientReceivingConfig for ClientReceivingUnknown {}
impl<R> ClientReceivingConfig for ClientReceiving<R> where R: DeserializeOwned + 'static {}
pub struct ClientEventReportingUnknown;
pub struct ClientEventReporting<E>(E);
pub struct ClientEventReportingCallbacks<R>(ClientEventCallbacks<R>)
where
R: DeserializeOwned + 'static;
pub struct ClientEventReportingHandler<R, H>
where
R: DeserializeOwned + 'static,
H: ClientEventHandler<R>,
{
handler: H,
phantom_receive: PhantomData<fn() -> R>,
}
pub struct ClientEventReportingChannel;
trait ClientEventReportingConfig {}
impl ClientEventReportingConfig for ClientEventReportingUnknown {}
impl<R> ClientEventReportingConfig for ClientEventReporting<ClientEventReportingCallbacks<R>> where
R: DeserializeOwned + 'static
{
}
impl<R, H> ClientEventReportingConfig for ClientEventReporting<ClientEventReportingHandler<R, H>>
where
R: DeserializeOwned + 'static,
H: ClientEventHandler<R>,
{
}
impl ClientEventReportingConfig for ClientEventReporting<ClientEventReportingChannel> {}
#[allow(private_bounds)]
#[must_use = "client builders do nothing unless `connect` is called"]
pub struct ClientBuilder<SC, RC, EC>
where
SC: ClientSendingConfig,
RC: ClientReceivingConfig,
EC: ClientEventReportingConfig,
{
marker: PhantomData<fn() -> (SC, RC)>,
event_reporting: EC,
}
impl ClientBuilder<ClientSendingUnknown, ClientReceivingUnknown, ClientEventReportingUnknown> {
pub const fn new() -> Self {
Self {
marker: PhantomData,
event_reporting: ClientEventReportingUnknown,
}
}
}
impl Default
for ClientBuilder<ClientSendingUnknown, ClientReceivingUnknown, ClientEventReportingUnknown>
{
fn default() -> Self {
Self::new()
}
}
#[allow(private_bounds)]
impl<RC, EC> ClientBuilder<ClientSendingUnknown, RC, EC>
where
RC: ClientReceivingConfig,
EC: ClientEventReportingConfig,
{
pub fn sending<S>(self) -> ClientBuilder<ClientSending<S>, RC, EC>
where
S: Serialize + 'static,
{
ClientBuilder {
marker: PhantomData,
event_reporting: self.event_reporting,
}
}
}
#[allow(private_bounds)]
impl<SC, EC> ClientBuilder<SC, ClientReceivingUnknown, EC>
where
SC: ClientSendingConfig,
EC: ClientEventReportingConfig,
{
pub fn receiving<R>(self) -> ClientBuilder<SC, ClientReceiving<R>, EC>
where
R: DeserializeOwned + 'static,
{
ClientBuilder {
marker: PhantomData,
event_reporting: self.event_reporting,
}
}
}
impl<S, R> ClientBuilder<ClientSending<S>, ClientReceiving<R>, ClientEventReportingUnknown>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
{
pub fn with_event_callbacks(
self,
callbacks: ClientEventCallbacks<R>,
) -> ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingCallbacks<R>>,
> {
ClientBuilder {
marker: PhantomData,
event_reporting: ClientEventReporting(ClientEventReportingCallbacks(callbacks)),
}
}
pub fn with_event_handler<H>(
self,
handler: H,
) -> ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingHandler<R, H>>,
>
where
H: ClientEventHandler<R>,
{
ClientBuilder {
marker: PhantomData,
event_reporting: ClientEventReporting(ClientEventReportingHandler {
handler,
phantom_receive: PhantomData,
}),
}
}
pub fn with_event_channel(
self,
) -> ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingChannel>,
> {
ClientBuilder {
marker: PhantomData,
event_reporting: ClientEventReporting(ClientEventReportingChannel),
}
}
}
impl<S, R>
ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingCallbacks<R>>,
>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
{
#[allow(clippy::future_not_send)]
pub async fn connect<A>(self, addr: A) -> Result<ClientHandle<S>>
where
A: ToSocketAddrs,
{
let (client, mut client_events) = Client::<S, R>::connect(addr).await?;
let callbacks = self.event_reporting.0 .0;
tokio::spawn(async move {
while let Ok(event) = client_events.next_raw().await {
match event {
ClientEventRawSafe::Receive { data } => {
if let Some(ref receive) = callbacks.receive {
let receive = Arc::clone(receive);
tokio::spawn(async move {
let data = data.deserialize();
(*receive)(data).await;
});
}
}
ClientEventRawSafe::Disconnect => {
if let Some(ref disconnect) = callbacks.disconnect {
let disconnect = Arc::clone(disconnect);
tokio::spawn(async move {
(*disconnect)().await;
});
}
}
}
}
});
Ok(client)
}
}
impl<S, R, H>
ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingHandler<R, H>>,
>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
H: ClientEventHandler<R> + 'static,
{
#[allow(clippy::future_not_send)]
pub async fn connect<A>(self, addr: A) -> Result<ClientHandle<S>>
where
A: ToSocketAddrs,
{
let (client, mut client_events) = Client::<S, R>::connect(addr).await?;
let handler = Arc::new(self.event_reporting.0.handler);
tokio::spawn(async move {
while let Ok(event) = client_events.next_raw().await {
match event {
ClientEventRawSafe::Receive { data } => {
let handler = Arc::clone(&handler);
tokio::spawn(async move {
let data = data.deserialize();
handler.on_receive(data).await;
});
}
ClientEventRawSafe::Disconnect => {
let handler = Arc::clone(&handler);
tokio::spawn(async move {
handler.on_disconnect().await;
});
}
}
}
});
Ok(client)
}
}
impl<S, R>
ClientBuilder<
ClientSending<S>,
ClientReceiving<R>,
ClientEventReporting<ClientEventReportingChannel>,
>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
{
#[allow(clippy::future_not_send)]
pub async fn connect<A>(self, addr: A) -> Result<(ClientHandle<S>, ClientEventStream<R>)>
where
A: ToSocketAddrs,
{
Client::<S, R>::connect(addr).await
}
}
pub enum ClientCommand {
Disconnect,
Send {
data: Vec<u8>,
},
GetAddr,
GetServerAddr,
}
pub enum ClientCommandReturn {
Disconnect(Result<()>),
Send(Result<()>),
GetAddr(Result<SocketAddr>),
GetServerAddr(Result<SocketAddr>),
}
#[derive(Debug, Clone)]
pub enum ClientEvent<R>
where
R: DeserializeOwned + 'static,
{
Receive {
data: R,
},
Disconnect,
}
enum ClientEventRaw {
Receive {
data: Vec<u8>,
},
Disconnect,
}
impl ClientEventRaw {
fn deserialize<R>(&self) -> Result<ClientEvent<R>>
where
R: DeserializeOwned + 'static,
{
match self {
Self::Receive { data } => {
Ok(serde_json::from_slice(data).map(|data| ClientEvent::Receive { data })?)
}
Self::Disconnect => Ok(ClientEvent::Disconnect),
}
}
}
#[derive(Debug, Clone)]
struct ClientEventRawSafeData<R>
where
R: DeserializeOwned + 'static,
{
data: Vec<u8>,
marker: PhantomData<fn() -> R>,
}
#[derive(Debug, Clone)]
enum ClientEventRawSafe<R>
where
R: DeserializeOwned + 'static,
{
Receive {
data: ClientEventRawSafeData<R>,
},
Disconnect,
}
impl<R> TryFrom<ClientEventRaw> for ClientEventRawSafe<R>
where
R: DeserializeOwned + 'static,
{
type Error = Error;
fn try_from(value: ClientEventRaw) -> std::result::Result<Self, Self::Error> {
value.deserialize::<R>()?;
Ok(match value {
ClientEventRaw::Receive { data } => Self::Receive {
data: ClientEventRawSafeData {
data,
marker: PhantomData,
},
},
ClientEventRaw::Disconnect => Self::Disconnect,
})
}
}
impl<R> ClientEventRawSafeData<R>
where
R: DeserializeOwned + 'static,
{
fn deserialize(&self) -> R {
serde_json::from_slice(&self.data).unwrap()
}
}
impl<R> ClientEventRawSafe<R>
where
R: DeserializeOwned + 'static,
{
#[allow(dead_code)]
fn deserialize(&self) -> ClientEvent<R> {
match self {
Self::Receive { data } => ClientEvent::Receive {
data: data.deserialize(),
},
Self::Disconnect => ClientEvent::Disconnect,
}
}
}
pub struct ClientEventStream<R>
where
R: DeserializeOwned + 'static,
{
event_receiver: Receiver<ClientEventRaw>,
marker: PhantomData<fn() -> R>,
}
impl<R> ClientEventStream<R>
where
R: DeserializeOwned + 'static,
{
pub async fn next(&mut self) -> Result<ClientEvent<R>> {
match self.event_receiver.recv().await {
Some(serialized_event) => serialized_event.deserialize(),
None => Err(Error::ConnectionClosed),
}
}
async fn next_raw(&mut self) -> Result<ClientEventRawSafe<R>> {
match self.event_receiver.recv().await {
Some(serialized_event) => serialized_event.try_into(),
None => Err(Error::ConnectionClosed),
}
}
}
pub struct ClientHandle<S>
where
S: Serialize + 'static,
{
client_command_sender: CommandChannelSender<ClientCommand, ClientCommandReturn>,
client_task_handle: JoinHandle<Result<()>>,
marker: PhantomData<fn() -> S>,
}
impl<S> ClientHandle<S>
where
S: Serialize + 'static,
{
#[allow(clippy::missing_panics_doc)]
pub async fn disconnect(mut self) -> Result<()> {
let value = self
.client_command_sender
.send_command(ClientCommand::Disconnect)
.await?;
self.client_task_handle.await.unwrap()?;
unwrap_enum!(value, ClientCommandReturn::Disconnect)
}
#[allow(clippy::future_not_send)]
pub async fn send(&mut self, data: S) -> Result<()> {
let data_serialized = serde_json::to_vec(&data)?;
let value = self
.client_command_sender
.send_command(ClientCommand::Send {
data: data_serialized,
})
.await?;
unwrap_enum!(value, ClientCommandReturn::Send)
}
pub async fn get_addr(&mut self) -> Result<SocketAddr> {
let value = self
.client_command_sender
.send_command(ClientCommand::GetAddr)
.await?;
unwrap_enum!(value, ClientCommandReturn::GetAddr)
}
pub async fn get_server_addr(&mut self) -> Result<SocketAddr> {
let value = self
.client_command_sender
.send_command(ClientCommand::GetServerAddr)
.await?;
unwrap_enum!(value, ClientCommandReturn::GetServerAddr)
}
}
pub struct Client<S, R>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
{
marker: PhantomData<fn() -> (S, R)>,
}
impl Client<(), ()> {
pub const fn builder(
) -> ClientBuilder<ClientSendingUnknown, ClientReceivingUnknown, ClientEventReportingUnknown>
{
ClientBuilder::new()
}
}
impl<S, R> Client<S, R>
where
S: Serialize + 'static,
R: DeserializeOwned + 'static,
{
#[allow(clippy::future_not_send)]
pub async fn connect<A>(addr: A) -> Result<(ClientHandle<S>, ClientEventStream<R>)>
where
A: ToSocketAddrs,
{
let mut stream = TcpStream::connect(addr).await?;
let (public_key, secret_key) = dh_key_pair().await;
stream.write_all(public_key.as_bytes()).await?;
stream.flush().await?;
let mut other_public_key = [0; PUBLIC_KEY_SIZE];
handshake_timeout! {
stream.read_exact(&mut other_public_key)
}??;
let aes_key = dh_shared_key(secret_key, other_public_key).await;
let (client_command_sender, client_command_receiver) = command_channel();
let (client_event_sender, client_event_receiver) = channel(CHANNEL_BUFFER_SIZE);
let client_task_handle = tokio::spawn(client_loop(
stream,
aes_key,
client_event_sender,
client_command_receiver,
));
let client_handle = ClientHandle {
client_command_sender,
client_task_handle,
marker: PhantomData,
};
let client_event_stream = ClientEventStream {
event_receiver: client_event_receiver,
marker: PhantomData,
};
Ok((client_handle, client_event_stream))
}
}
async fn client_loop(
mut stream: TcpStream,
aes_key: [u8; AES_KEY_SIZE],
client_event_sender: Sender<ClientEventRaw>,
mut client_command_receiver: CommandChannelReceiver<ClientCommand, ClientCommandReturn>,
) -> Result<()> {
let mut size_buffer = [0; LEN_SIZE];
loop {
tokio::select! {
read_value = stream.read(&mut size_buffer[..]) => {
let n_size = read_value?;
if n_size != LEN_SIZE {
stream.shutdown().await?;
break;
}
let encrypted_data_size = decode_message_size(&size_buffer);
let mut encrypted_data_buffer = vec![0; encrypted_data_size];
let n_data = data_read_timeout! {
stream.read_exact(&mut encrypted_data_buffer[..])
}??;
if n_data != encrypted_data_size {
stream.shutdown().await?;
break;
}
let data_serialized = aes_decrypt(aes_key, encrypted_data_buffer.into()).await?;
if let Err(_e) = client_event_sender.send(ClientEventRaw::Receive { data: data_serialized }).await {
stream.shutdown().await?;
break;
}
}
command_value = client_command_receiver.recv_command() => {
match command_value {
Ok(command) => {
match command {
ClientCommand::Disconnect => {
let value = stream.shutdown().await;
_ = client_command_receiver.command_return(ClientCommandReturn::Disconnect(value.map_err(Into::into))).await;
break;
},
ClientCommand::Send { data } => {
let value = 'val: {
let encrypted_data_buffer = break_on_err!(aes_encrypt(aes_key, data.into()).await, 'val);
let size_buffer = encode_message_size(encrypted_data_buffer.len());
let mut buffer = vec![];
buffer.extend_from_slice(&size_buffer);
buffer.extend(&encrypted_data_buffer);
break_on_err!(stream.write_all(&buffer).await, 'val);
break_on_err!(stream.flush().await, 'val);
Ok(())
};
let error_occurred = value.is_err();
if let Err(_e) = client_command_receiver.command_return(ClientCommandReturn::Send(value)).await {
stream.shutdown().await?;
break;
}
if error_occurred {
stream.shutdown().await?;
break;
}
},
ClientCommand::GetAddr => {
let addr = stream.local_addr();
if let Err(_e) = client_command_receiver.command_return(ClientCommandReturn::GetAddr(addr.map_err(Into::into))).await {
stream.shutdown().await?;
break;
}
},
ClientCommand::GetServerAddr => {
let addr = stream.peer_addr();
if let Err(_e) = client_command_receiver.command_return(ClientCommandReturn::GetServerAddr(addr.map_err(Into::into))).await {
stream.shutdown().await?;
break;
}
},
}
},
Err(_e) => {
stream.shutdown().await?;
break;
}
}
}
}
}
_ = client_event_sender.send(ClientEventRaw::Disconnect).await;
Ok(())
}