use ahash::{HashMap, HashMapExt, HashSet, HashSetExt};
use arrayvec::ArrayVec;
use driver::NetDriverFactory;
use parking_lot::{Mutex, RwLock};
use smol::future;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::net::{IpAddr, SocketAddr};
use std::num::{NonZeroU32, NonZeroU64};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use tracing::{debug, trace, warn};
use crate::event::{EventBus, EventBusRegistry, EventCancellable};
use crate::net::driver::{
NetDriver,
NetDriverBroadcast,
NetDriverCloseConnection,
NetDriverConnect,
NetDriverListen,
NetDriverSend,
NetDriverSendTo,
};
use crate::net::message::dispatch::{InterceptMessageHandler, MessageDispatch};
use crate::net::message::{
Message,
MessageDispatchError,
MessageHandler,
MessageHeader,
MessageRecv,
MessageRepliable,
MessageReplyFuture,
MessageSend,
};
pub mod driver;
pub mod gns;
pub mod message;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Connection(Option<NonZeroU32>);
impl Connection {
pub const INVALID: Self = Connection(None);
}
impl Display for Connection {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.0 {
Some(id) => write!(f, "connection-{id}"),
None => write!(f, "connection-INVALID"),
}
}
}
#[derive(Debug, Clone)]
pub struct ConnectionInfo {
connection: Connection,
remote_addr: IpAddr,
}
impl ConnectionInfo {
#[inline]
pub fn connection(&self) -> Connection {
self.connection
}
#[inline]
pub fn remote_addr(&self) -> IpAddr {
self.remote_addr
}
}
#[derive(Debug)]
pub struct NetworkingConnectEvent {
connection: Connection,
connection_info: ConnectionInfo,
}
impl EventCancellable for NetworkingConnectEvent {}
impl NetworkingConnectEvent {
#[inline]
pub fn new(connection: Connection, connection_info: ConnectionInfo) -> Self {
Self {
connection,
connection_info,
}
}
#[inline]
pub fn connection(&self) -> Connection {
self.connection
}
#[inline]
pub fn connection_info(&self) -> &ConnectionInfo {
&self.connection_info
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DisconnectReason {
ClosedLocally,
ClosedByPeer,
Unexpected,
}
impl Display for DisconnectReason {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::ClosedLocally => write!(f, "Connection closed gracefully"),
Self::ClosedByPeer => write!(f, "Connection closed by peer"),
Self::Unexpected => write!(f, "Unexpected disconnect occurred"),
}
}
}
#[derive(Debug)]
pub struct NetworkingDisconnectEvent {
connection: Connection,
connection_info: ConnectionInfo,
reason: DisconnectReason,
}
impl NetworkingDisconnectEvent {
#[inline]
pub fn new(
connection: Connection,
connection_info: ConnectionInfo,
reason: DisconnectReason,
) -> Self {
Self {
connection,
connection_info,
reason,
}
}
#[inline]
pub fn connection(&self) -> Connection {
self.connection
}
#[inline]
pub fn connection_info(&self) -> &ConnectionInfo {
&self.connection_info
}
#[inline]
pub fn reason(&self) -> DisconnectReason {
self.reason
}
}
#[derive(Debug, thiserror::Error)]
pub enum NetworkingError {
#[error("Internal Error: {0}")]
InternalError(String),
#[error("Invalid socket address: {0}")]
InvalidAddress(SocketAddr),
#[error("Invalid connection: {0}")]
InvalidConnection(Connection),
#[error("The operation was cancelled by the user.")]
Cancelled,
#[error("Malformed message; {0}")]
MalformedMessage(#[source] Box<dyn Error + Send + Sync>),
#[error("Socket has been closed.")]
Closed,
}
impl NetworkingError {
#[inline]
pub fn internal_error(msg: impl Into<String>) -> Self {
Self::InternalError(msg.into())
}
}
impl From<ump::Error<NetworkingError>> for NetworkingError {
#[inline]
fn from(value: ump::Error<NetworkingError>) -> Self {
match value {
ump::Error::App(err) => err,
ump::Error::ClientsDisappeared | ump::Error::ServerDisappeared => Self::Closed,
ump::Error::NoReply => Self::internal_error("No response from internal system"),
}
}
}
pub struct NetworkingApi {
event_bus: EventBus,
msg_dispatch: Arc<MessageDispatch>,
next_connect_id: AtomicU32,
connection_infos: RwLock<HashMap<Connection, ConnectionInfo>>,
}
impl NetworkingApi {
fn new() -> Arc<Self> {
let event_bus = EventBus::builder()
.event_type::<NetworkingConnectEvent>()
.event_type::<NetworkingDisconnectEvent>()
.build();
let msg_dispatch = Arc::new(MessageDispatch::new());
let next_connect_id = AtomicU32::new(1);
let connection_infos = RwLock::new(HashMap::new());
Arc::new(Self {
event_bus,
msg_dispatch,
next_connect_id,
connection_infos,
})
}
#[inline]
pub fn try_init_connect(&self, remote_addr: IpAddr) -> Option<Connection> {
let connection = Connection(Some(self.next_connect_id.fetch_add(1, Ordering::SeqCst)
.try_into().unwrap()));
let connection_info = ConnectionInfo {
connection,
remote_addr,
};
let event = self.event_bus.fire(NetworkingConnectEvent::new(
connection,
connection_info.clone(),
));
if !event.is_cancelled() {
self.connection_infos.write().insert(connection, connection_info);
Some(connection)
} else {
None
}
}
#[inline]
pub fn disconnect(&self, connection: Connection, reason: DisconnectReason) {
if let Some(connection_info) = self.connection_infos.write().remove(&connection) {
self.event_bus.fire(NetworkingDisconnectEvent::new(connection, connection_info, reason));
} else {
warn!(?connection, ?reason, "Attempted to disconnect an already disconnected connection");
}
}
#[inline]
pub fn dispatch_message(
&self,
connection: Connection,
msg: &[u8],
) -> Result<(), MessageDispatchError> {
self.msg_dispatch.dispatch(connection, msg)
}
}
pub struct Networking<D: NetDriver> {
driver: Arc<D>,
api: Arc<NetworkingApi>,
}
impl<D: NetDriver> Networking<D> {
pub fn new(driver_factory: impl NetDriverFactory<D>) -> Self {
let api = NetworkingApi::new();
let driver = driver_factory.create(api.clone());
Self {
driver,
api,
}
}
#[inline]
pub fn event_bus(&self) -> &EventBusRegistry {
self.api.event_bus.registry()
}
#[inline]
pub fn insert_msg_handler<M, E>(
&self,
handler: impl MessageHandler<M, E>,
)
where
M: MessageRecv,
E: Error + Send + Sync + 'static,
{
self.api.msg_dispatch.insert_handler(handler);
}
#[inline]
pub fn connection_info(&self, connection: &Connection) -> Option<ConnectionInfo> {
self.api.connection_infos.read().get(connection).cloned()
}
#[inline]
pub async fn close(&self) {
self.driver.close().await;
}
#[inline]
pub fn close_blocking(&self) {
future::block_on(self.close());
}
}
impl<D: NetDriver> Drop for Networking<D> {
#[inline]
fn drop(&mut self) {
self.close_blocking();
}
}
const CONNECT_BUFFER_SIZE: usize = 64;
#[derive(Debug)]
struct ConnectBuffer {
connection: Connection,
buffer: Mutex<ArrayVec<(MessageHeader, Box<[u8]>), CONNECT_BUFFER_SIZE>>,
active_replies: Mutex<HashSet<NonZeroU64>>,
msg_dispatch: Arc<MessageDispatch>,
}
impl ConnectBuffer {
fn new(connection: Connection, msg_dispatch: Arc<MessageDispatch>) -> Self {
Self {
connection,
buffer: Mutex::new(ArrayVec::new()),
active_replies: Mutex::new(HashSet::new()),
msg_dispatch,
}
}
}
impl InterceptMessageHandler for ConnectBuffer {
fn accept(
&self,
connection: Connection,
msg_header: &MessageHeader,
msg_body: &[u8],
) -> Result<bool, MessageDispatchError> {
if connection == self.connection {
if let Some(reply_num) = msg_header.reply_num() {
if self.active_replies.lock().remove(&reply_num) {
return Ok(false)
}
}
let entry = (
msg_header.clone(),
Box::from(msg_body),
);
match self.buffer.lock().try_push(entry) {
Ok(_) => Ok(true),
Err(_) => Err(MessageDispatchError::DispatchFailed(Box::from(
"ConnectBuffer is full".to_owned()
))),
}
} else {
Ok(false)
}
}
fn register_reply(&self, msg_header: &MessageHeader) -> bool {
let msg_num = msg_header.msg_num()
.expect("msg_num should already be set");
self.active_replies.lock().insert(msg_num);
true
}
}
impl Drop for ConnectBuffer {
fn drop(&mut self) {
let mut buffer = self.buffer.lock();
debug!(msg_count = buffer.len(), "Dispatching buffered ConnectContext messages");
let range = 0..buffer.len();
for (msg_header, msg_body) in buffer.drain(range) {
match self.msg_dispatch.dispatch_parsed(self.connection, msg_header, &*msg_body) {
Ok(_) => {},
Err(error) =>
warn!(?error, "Failed to dispatch buffered ConnectContext message"),
}
}
}
}
#[derive(Debug)]
pub struct ClientConnectContext{
connection: Connection,
#[allow(unused)]
buffer: Arc<ConnectBuffer>,
}
impl ClientConnectContext {
#[inline]
pub fn connection(&self) -> Connection {
self.connection
}
#[inline]
pub fn into_connection(self) -> Connection {
self.connection
}
}
impl<D: NetDriverConnect> Networking<D> {
pub async fn connect(
&self,
socket_addr: SocketAddr,
) -> Result<ClientConnectContext, NetworkingError> {
if let Some(connection) = self.api.try_init_connect(socket_addr.ip()) {
let buffer = Arc::new(ConnectBuffer::new(
connection,
self.api.msg_dispatch.clone(),
));
self.api.msg_dispatch.insert_intercept_handler(&buffer);
let connect_ctx = ClientConnectContext{
connection,
buffer,
};
self.driver.connect(connection, socket_addr).await?;
Ok(connect_ctx)
} else {
Err(NetworkingError::Cancelled)
}
}
#[inline]
pub fn connect_sync(
&self,
socket_addr: SocketAddr,
) -> Result<ClientConnectContext, NetworkingError> {
future::block_on(self.connect(socket_addr))
}
}
impl<D: NetDriverCloseConnection> Networking<D> {
pub async fn close_connection(
&self,
connection: Connection,
) {
self.driver.close_connection(connection).await
}
}
impl<D: NetDriverListen> Networking<D> {
pub async fn listen(
&self,
socket_addr: SocketAddr,
) -> Result<(), NetworkingError> {
self.driver.listen(socket_addr).await
}
}
impl<D: NetDriverSend> Networking<D> {
pub fn send<M>(
&self,
msg: impl Into<Message<M>>,
) -> Result<(), NetworkingError>
where
M: MessageSend,
{
let mut msg = msg.into();
trace!(msg_key = %msg.header().key(), "Sending message");
msg.set_msg_num(self.api.msg_dispatch.gen_msg_num());
let bytes = msg.encode()?;
self.driver.send(bytes, M::flags())
}
pub fn send_recv<M>(
&self,
msg: impl Into<Message<M>>,
) -> Result<MessageReplyFuture<M::Reply>, NetworkingError>
where
M: MessageSend + MessageRepliable,
M::Reply: MessageRecv + Send + 'static,
{
let mut msg = msg.into();
trace!(msg_key = %msg.header().key(), "Sending repliable message");
msg.set_msg_num(self.api.msg_dispatch.gen_msg_num());
let bytes = msg.encode()?;
let fut = self.api.msg_dispatch.register_reply(&msg);
self.driver.send(bytes, M::flags())?;
Ok(fut)
}
}
impl<D: NetDriverSendTo> Networking<D> {
pub fn send_to<M>(
&self,
connection: Connection,
msg: impl Into<Message<M>>,
) -> Result<(), NetworkingError>
where
M: MessageSend,
{
let mut msg = msg.into();
trace!(to = %connection, msg_key = %msg.header().key(), "Sending message to");
msg.set_msg_num(self.api.msg_dispatch.gen_msg_num());
let bytes = msg.encode()?;
self.driver.send_to(connection, bytes, M::flags())
}
pub fn send_recv_to<M>(
&self,
connection: Connection,
msg: impl Into<Message<M>>,
) -> Result<MessageReplyFuture<M::Reply>, NetworkingError>
where
M: MessageSend + MessageRepliable,
M::Reply: MessageRecv + Send + 'static,
{
let mut msg = msg.into();
trace!(to = %connection, msg_key = %msg.header().key(), "Sending repliable message to");
msg.set_msg_num(self.api.msg_dispatch.gen_msg_num());
let bytes = msg.encode()?;
let fut = self.api.msg_dispatch.register_reply(&msg);
self.driver.send_to(connection, bytes, M::flags())?;
Ok(fut)
}
}
impl<D: NetDriverBroadcast> Networking<D> {
pub fn broadcast<M>(
&self,
msg: impl Into<Message<M>>,
) -> Result<(), NetworkingError>
where
M: MessageSend,
{
let mut msg = msg.into();
trace!(msg_key = %msg.header().key(), "Broadcasting message");
msg.set_msg_num(self.api.msg_dispatch.gen_msg_num());
let bytes = msg.encode()?;
self.driver.broadcast(bytes, M::flags())
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use std::sync::atomic::AtomicU64;
use std::sync::Weak;
use async_trait::async_trait;
use flagset::FlagSet;
use crate::net::message::MessageFlags;
use crate::net::driver::tests::{
ClientServerStackFactory,
test_net_driver_client_server_core,
test_net_driver_client_server_send,
test_net_driver_client_server_send_to,
test_net_driver_client_server_broadcast,
};
use super::*;
struct TestClient {
weak: Weak<Self>,
subsystem: Arc<TestSubsystem>,
api: Arc<NetworkingApi>,
server: RwLock<(Connection, Weak<TestServer>)>,
}
impl TestClient {
fn new(subsystem: Arc<TestSubsystem>, api: Arc<NetworkingApi>) -> Arc<Self> {
Arc::new_cyclic(|weak| Self {
weak: weak.clone(),
subsystem,
api,
server: RwLock::new((Connection::INVALID, Weak::new()))
})
}
}
#[async_trait]
impl NetDriver for TestClient {
async fn close(&self) {
let mut lock = self.server.write();
let (serverside_connection, ref server) = *lock;
if let Some(server) = server.upgrade() {
if let Some(clients) = &mut *server.clients.write() {
if let Some((clientside_connection, _)) = clients.remove(&serverside_connection) {
self.api.disconnect(clientside_connection, DisconnectReason::ClosedLocally);
}
}
server.api.disconnect(serverside_connection, DisconnectReason::ClosedByPeer);
}
*lock = (Connection::INVALID, Weak::new());
}
}
#[async_trait]
impl NetDriverConnect for TestClient {
async fn connect(&self, connection: Connection, socket_addr: SocketAddr) -> Result<(), NetworkingError> {
self.close().await;
let client = self.weak.upgrade().unwrap();
self.subsystem.connect(connection, socket_addr, &client)
}
}
impl NetDriverSend for TestClient {
fn send(&self, msg: Vec<u8>, _flags: FlagSet<MessageFlags>) -> Result<(), NetworkingError> {
let lock = self.server.read();
let (connection, ref server) = *lock;
if let Some(server) = server.upgrade() {
server.api.dispatch_message(connection, &msg)
.expect("Message should dispatch");
Ok(())
} else {
Err(NetworkingError::Closed)
}
}
}
struct TestServer {
weak: Weak<Self>,
id: u64,
subsystem: Arc<TestSubsystem>,
api: Arc<NetworkingApi>,
clients: RwLock<Option<HashMap<Connection, (Connection, Weak<TestClient>)>>>,
}
impl TestServer {
fn new(id: u64, subsystem: Arc<TestSubsystem>, api: Arc<NetworkingApi>) -> Arc<Self> {
Arc::new_cyclic(|weak| Self {
weak: weak.clone(),
id,
subsystem,
api,
clients: RwLock::new(None),
})
}
fn add_client(
&self,
clientside_connection: Connection,
client: &Arc<TestClient>,
) -> Result<(), NetworkingError> {
let ip = Ipv4Addr::LOCALHOST.into();
if let Some(clients) = &mut *self.clients.write() {
if let Some(serverside_connection) = self.api.try_init_connect(ip) {
clients.insert(
serverside_connection,
(clientside_connection, Arc::downgrade(&client)),
);
*client.server.write() = (serverside_connection, self.weak.clone());
Ok(())
} else {
Err(NetworkingError::Cancelled)
}
} else {
Err(NetworkingError::Closed)
}
}
}
#[async_trait]
impl NetDriver for TestServer {
async fn close(&self) {
let mut clients = self.clients.write();
self.subsystem.close_server(self);
if let Some(clients) = &mut *clients {
clients.drain().for_each(|(serverside_connection, (clientside_connection, client))| {
if let Some(client) = client.upgrade() {
*client.server.write() = (Connection::INVALID, Weak::new());
client.api.disconnect(clientside_connection, DisconnectReason::ClosedByPeer);
}
self.api.disconnect(serverside_connection, DisconnectReason::ClosedLocally);
})
}
*clients = None;
}
}
#[async_trait]
impl NetDriverListen for TestServer {
async fn listen(&self, socket_addr: SocketAddr) -> Result<(), NetworkingError> {
self.close().await;
let server = self.weak.upgrade().unwrap();
self.subsystem.listen(socket_addr, server)?;
*self.clients.write() = Some(HashMap::new());
Ok(())
}
}
impl NetDriverSendTo for TestServer {
fn send_to(&self, connection: Connection, msg: Vec<u8>, _flags: FlagSet<MessageFlags>) -> Result<(), NetworkingError> {
if let Some(clients) = &*self.clients.read() {
if let Some((connection, client)) = clients.get(&connection) {
if let Some(client) = client.upgrade() {
client.api.dispatch_message(*connection, &msg)
.expect("Message should dispatch");
Ok(())
} else {
Err(NetworkingError::Closed)
}
} else {
Err(NetworkingError::InvalidConnection(connection))
}
} else {
Err(NetworkingError::Closed)
}
}
}
impl NetDriverBroadcast for TestServer {
fn broadcast(&self, msg: Vec<u8>, _flags: FlagSet<MessageFlags>) -> Result<(), NetworkingError> {
if let Some(clients) = &*self.clients.read() {
for (connection, client) in clients.values() {
if let Some(client) = client.upgrade() {
client.api.dispatch_message(*connection, &msg)
.expect("Message should dispatch");
}
}
Ok(())
} else {
Err(NetworkingError::Closed)
}
}
}
struct TestSubsystem {
next_server_id: AtomicU64,
servers: RwLock<HashMap<SocketAddr, Arc<TestServer>>>,
}
impl TestSubsystem {
fn new() -> Arc<Self> {
Arc::new(Self {
next_server_id: AtomicU64::new(1),
servers: RwLock::new(HashMap::new()),
})
}
fn connect(
&self,
connection: Connection,
socket_addr: SocketAddr,
client: &Arc<TestClient>,
) -> Result<(), NetworkingError> {
let servers = self.servers.read();
if let Some(server) = servers.get(&socket_addr) {
server.add_client(connection, client)?;
Ok(())
} else {
Err(NetworkingError::InvalidAddress(socket_addr))
}
}
fn listen(&self, socket_addr: SocketAddr, server: Arc<TestServer>) -> Result<(), NetworkingError> {
let mut servers = self.servers.write();
if servers.contains_key(&socket_addr) {
Err(NetworkingError::InvalidAddress(socket_addr))
} else {
servers.insert(socket_addr, server);
Ok(())
}
}
fn close_server(&self, server: &TestServer) {
let mut servers = self.servers.write();
servers.retain(|_, entry| {
entry.id != server.id
});
}
}
impl NetDriverFactory<TestClient> for Arc<TestSubsystem> {
#[inline]
fn create(&self, api: Arc<NetworkingApi>) -> Arc<TestClient> {
TestClient::new(self.clone(), api)
}
}
impl NetDriverFactory<TestServer> for Arc<TestSubsystem> {
#[inline]
fn create(&self, api: Arc<NetworkingApi>) -> Arc<TestServer> {
let id = self.next_server_id.fetch_add(1, Ordering::SeqCst);
TestServer::new(id, self.clone(), api)
}
}
struct TestStackFactory {
subsystem: Arc<TestSubsystem>,
}
impl Default for TestStackFactory {
#[inline]
fn default() -> Self {
Self {
subsystem: TestSubsystem::new(),
}
}
}
impl ClientServerStackFactory for TestStackFactory {
type ClientDriver = TestClient;
type ClientDriverFactory = Arc<TestSubsystem>;
type ServerDriver = TestServer;
type ServerDriverFactory = Arc<TestSubsystem>;
#[inline]
fn client_factory(&self) -> Arc<TestSubsystem> {
self.subsystem.clone()
}
#[inline]
fn server_factory(&self) -> Arc<TestSubsystem> {
self.subsystem.clone()
}
}
test_net_driver_client_server_core! {
name: networking,
stack_factory: TestStackFactory,
}
test_net_driver_client_server_send! {
name: networking,
stack_factory: TestStackFactory,
}
test_net_driver_client_server_send_to! {
name: networking,
stack_factory: TestStackFactory,
}
test_net_driver_client_server_broadcast! {
name: networking,
stack_factory: TestStackFactory,
}
}