WIP
This commit is contained in:
Generated
+17
-8
@@ -17,21 +17,30 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hd-bus"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hd-client"
|
name = "hd-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hd-lib",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hd-lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hd-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"hd-lib",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
pub mod bus;
|
|
||||||
mod connection;
|
|
||||||
pub mod error;
|
|
||||||
mod thread_pool;
|
|
||||||
|
|
||||||
use tracing::{Level, event, info, span};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
bus::{EventBus, EventData},
|
|
||||||
connection::handle_event_bus_client,
|
|
||||||
error::HError,
|
|
||||||
thread_pool::ThreadPool,
|
|
||||||
};
|
|
||||||
use std::net::TcpListener;
|
|
||||||
|
|
||||||
pub struct TcpEventBus {
|
|
||||||
bus: EventBus,
|
|
||||||
pool: ThreadPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TcpEventBus {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let pool = ThreadPool::new(10);
|
|
||||||
Self {
|
|
||||||
bus: EventBus::new(),
|
|
||||||
pool,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn publish(&self, evt: EventData) -> Result<(), HError> {
|
|
||||||
self.bus.publish(evt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TcpEventBus {
|
|
||||||
pub fn start(&self, addr: &'static str) -> Result<(), HError> {
|
|
||||||
let listener = TcpListener::bind(addr).map_err(HError::TcpSocketBindError)?;
|
|
||||||
info!("Heimdall listening at {}", addr);
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
match stream {
|
|
||||||
Ok(stream) => handle_event_bus_client(&self.pool, self.bus.clone(), stream)?,
|
|
||||||
Err(e) => tracing::error!("TCP connection failed {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,3 +5,4 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
hd-lib = { path = "../hd-lib" }
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "hd-lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tracing = { workspace = true }
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
mod packet;
|
||||||
|
|
||||||
use tracing::{info, info_span};
|
use tracing::{info, info_span};
|
||||||
|
|
||||||
use crate::error::HError;
|
use crate::error::HError;
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
use std::{io::{Read, Write}, net::TcpStream};
|
||||||
|
|
||||||
|
use crate::bus::EventData;
|
||||||
|
|
||||||
|
enum PacketType {
|
||||||
|
Connect,
|
||||||
|
Unsubscribe,
|
||||||
|
Publish,
|
||||||
|
SendEvent,
|
||||||
|
Settle,
|
||||||
|
Ack,
|
||||||
|
Disconnect,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Packet {
|
||||||
|
Connect(ConnectPacket),
|
||||||
|
Unsubscribe,
|
||||||
|
Publish(PublishPacket),
|
||||||
|
SendEvent(SendEventPacket),
|
||||||
|
Settle(SettlePacket),
|
||||||
|
Ack(AckPacket),
|
||||||
|
Disconnect,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConnectPacket {
|
||||||
|
cursor: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PublishPacket {
|
||||||
|
event_type: u8,
|
||||||
|
data_utf8: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SendEventPacket {
|
||||||
|
event_id: u64,
|
||||||
|
event_type: u8,
|
||||||
|
data_utf8: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SettlePacket {
|
||||||
|
cursor: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AckPacket {
|
||||||
|
packet_type: PacketType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
fn get_packet_type(&self) -> PacketType {
|
||||||
|
match self {
|
||||||
|
Packet::Connect(_) => PacketType::Connect,
|
||||||
|
Packet::Unsubscribe => PacketType::Unsubscribe,
|
||||||
|
Packet::Publish(_) => PacketType::Publish,
|
||||||
|
Packet::SendEvent(_) => PacketType::SendEvent,
|
||||||
|
Packet::Settle(_) => PacketType::Settle,
|
||||||
|
Packet::Ack(_) => PacketType::Ack,
|
||||||
|
Packet::Disconnect => PacketType::Disconnect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_connect_packet(cursor: u64) -> Packet {
|
||||||
|
Packet::Connect(ConnectPacket { cursor })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_unsubscribe_packet() -> Packet {
|
||||||
|
Packet::Unsubscribe
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_publish_packet(evt: EventData) -> Packet {
|
||||||
|
Packet::Publish(PublishPacket {
|
||||||
|
event_type: evt.event_type,
|
||||||
|
data_utf8: evt.data_utf8,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_settle_packet(cursor: u64) -> Packet {
|
||||||
|
Packet::Settle(SettlePacket { cursor })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_ack_packet(packet_type: PacketType) -> Packet {
|
||||||
|
Packet::Ack(AckPacket { packet_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_disconnect_packet() -> Packet {
|
||||||
|
Packet::Disconnect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PacketFrame {
|
||||||
|
header: HeaderFrame,
|
||||||
|
data: DataFrame,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 3 Bytes
|
||||||
|
struct HeaderFrame {
|
||||||
|
/// 0 Connect
|
||||||
|
/// 1 Subscribe
|
||||||
|
/// 2 Unsubscribe
|
||||||
|
/// 3 Publish
|
||||||
|
/// 4 Settle
|
||||||
|
/// 5 Ack
|
||||||
|
/// 6 Disconnect
|
||||||
|
packet_type: u8,
|
||||||
|
/// Length of data frame, Little Endian, so 5 = [5 0], 256 = [255 1]
|
||||||
|
data_frame_length: [u8; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DataFrame {
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PacketError {
|
||||||
|
WrongPacketType(u8),
|
||||||
|
PacketProtocolError,
|
||||||
|
TcpStreamError(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_packet(stream: &mut TcpStream) -> Result<Packet, PacketError> {
|
||||||
|
let mut header_buf = vec![0; 3];
|
||||||
|
stream
|
||||||
|
.read_exact(&mut header_buf)
|
||||||
|
.map_err(PacketError::TcpStreamError)?;
|
||||||
|
|
||||||
|
let packet_type = get_packet_type_from_byte(header_buf[0])?;
|
||||||
|
|
||||||
|
match packet_type {
|
||||||
|
PacketType::Connect => {
|
||||||
|
let cursor = read_u64(stream)?;
|
||||||
|
Ok(Packet::Connect(ConnectPacket { cursor }))
|
||||||
|
}
|
||||||
|
PacketType::Unsubscribe => Ok(Packet::Unsubscribe),
|
||||||
|
PacketType::Publish => todo!(),
|
||||||
|
PacketType::SendEvent => todo!(),
|
||||||
|
PacketType::Settle => {
|
||||||
|
let cursor = read_u64(stream)?;
|
||||||
|
Ok(Packet::Settle(SettlePacket { cursor }))
|
||||||
|
}
|
||||||
|
PacketType::Ack => {
|
||||||
|
let packet_type = get_packet_type_from_byte(read_byte(stream)?)?;
|
||||||
|
Ok(Packet::Ack(AckPacket { packet_type }))
|
||||||
|
}
|
||||||
|
PacketType::Disconnect => Ok(Packet::Disconnect),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_packet(stream: &mut TcpStream, packet: &Packet) -> Result<(), PacketError> {
|
||||||
|
let packet_type = packet.get_packet_type();
|
||||||
|
let data_frame_length = calculate_data_frame_length(&packet);
|
||||||
|
|
||||||
|
let mut header_frame = [0; 3];
|
||||||
|
write_header_frame(&mut header_frame, packet_type, data_frame_length);
|
||||||
|
|
||||||
|
if data_frame_length > 0 {
|
||||||
|
let mut data_frame = vec![0; data_frame_length];
|
||||||
|
write_data_frame(&mut data_frame, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESUME FROM HERE
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_header_frame(header_frame: &mut [u8; 3], packet_type: PacketType, data_frame_length: usize) {
|
||||||
|
header_frame[0] = get_byte_from_packet_type(&packet_type);
|
||||||
|
if data_frame_length > 0 {
|
||||||
|
write_length(data_frame_length, &mut header_frame[1..3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_data_frame(data_frame: &mut [u8], packet: &Packet) {
|
||||||
|
match packet {
|
||||||
|
Packet::Connect(connect_packet) => {
|
||||||
|
write_u64(connect_packet.cursor, data_frame);
|
||||||
|
}
|
||||||
|
Packet::Publish(publish_packet) => {
|
||||||
|
write_event_data(publish_packet.event_type, &publish_packet.data_utf8, data_frame);
|
||||||
|
}
|
||||||
|
Packet::SendEvent(send_event_packet) => {
|
||||||
|
write_u64(send_event_packet.event_id, data_frame);
|
||||||
|
write_event_data(
|
||||||
|
send_event_packet.event_type,
|
||||||
|
&send_event_packet.data_utf8,
|
||||||
|
&mut data_frame[1..],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Packet::Settle(settle_packet) => {
|
||||||
|
write_u64(settle_packet.cursor, data_frame);
|
||||||
|
}
|
||||||
|
Packet::Ack(ack_packet) => {
|
||||||
|
data_frame[0] = get_byte_from_packet_type(&ack_packet.packet_type);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_event_data(event_type: u8, data_utf8: &[u8], buf: &mut [u8]) {
|
||||||
|
buf[0] = event_type;
|
||||||
|
write_length(data_utf8.len(), &mut buf[1..3]);
|
||||||
|
let available_length = buf.len() - 3;
|
||||||
|
buf[3..].copy_from_slice(&data_utf8[..available_length]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u64(v64: u64, data_frame: &mut [u8]) {
|
||||||
|
let bytes = v64.to_le_bytes();
|
||||||
|
data_frame[0..8].copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u64(stream: &mut TcpStream) -> Result<u64, PacketError> {
|
||||||
|
let mut le_bytes = [0u8; 8];
|
||||||
|
stream.read_exact(&mut le_bytes).map_err(PacketError::TcpStreamError)?;
|
||||||
|
Ok(u64::from_le_bytes(le_bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_byte(stream: &mut TcpStream) -> Result<u8, PacketError> {
|
||||||
|
let mut byte = [0u8; 1];
|
||||||
|
stream.read_exact(&mut byte).map_err(PacketError::TcpStreamError)?;
|
||||||
|
Ok(byte[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_byte_from_packet_type(packet_type: &PacketType) -> u8 {
|
||||||
|
match packet_type {
|
||||||
|
PacketType::Connect => 0,
|
||||||
|
PacketType::Unsubscribe => 1,
|
||||||
|
PacketType::Publish => 2,
|
||||||
|
PacketType::SendEvent => 3,
|
||||||
|
PacketType::Settle => 4,
|
||||||
|
PacketType::Ack => 5,
|
||||||
|
PacketType::Disconnect => 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_packet_type_from_byte(byte: u8) -> Result<PacketType, PacketError> {
|
||||||
|
let packet_type = match byte {
|
||||||
|
0 => Some(PacketType::Connect),
|
||||||
|
1 => Some(PacketType::Unsubscribe),
|
||||||
|
2 => Some(PacketType::Publish),
|
||||||
|
3 => Some(PacketType::SendEvent),
|
||||||
|
4 => Some(PacketType::Settle),
|
||||||
|
5 => Some(PacketType::Ack),
|
||||||
|
6 => Some(PacketType::Disconnect),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(packet_type) = packet_type {
|
||||||
|
return Ok(packet_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(PacketError::WrongPacketType(byte))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_length(mut length: usize, buf: &mut [u8]) {
|
||||||
|
if length > 0xffff {
|
||||||
|
length = 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = length.to_le_bytes();
|
||||||
|
buf[0..2].copy_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_data_frame_length(packet: &Packet) -> usize {
|
||||||
|
let mut ret = match packet {
|
||||||
|
Packet::Connect(_) => 8,
|
||||||
|
Packet::Unsubscribe => 0,
|
||||||
|
Packet::Publish(publish_packet) => 1 + 2 + publish_packet.data_utf8.len(),
|
||||||
|
Packet::SendEvent(send_event_packet) => 8 + 1 + 2 + send_event_packet.data_utf8.len(),
|
||||||
|
Packet::Settle(_) => 8,
|
||||||
|
Packet::Ack(_) => 1,
|
||||||
|
Packet::Disconnect => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize to 2 bytes
|
||||||
|
if ret > 0xffff {
|
||||||
|
ret = 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod thread_pool;
|
||||||
|
pub mod bus;
|
||||||
@@ -6,7 +6,7 @@ use std::{
|
|||||||
thread::{self, JoinHandle},
|
thread::{self, JoinHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::{Level, event, info, span};
|
use tracing::{Level, info, span};
|
||||||
|
|
||||||
use crate::error::HError;
|
use crate::error::HError;
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "hd-bus"
|
name = "hd-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||||
|
hd-lib = { path = "../hd-lib" }
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::{sync::Arc, thread, time::Duration};
|
use std::{sync::Arc, thread, time::Duration};
|
||||||
|
|
||||||
use hd_bus::{TcpEventBus, bus::EventData, error::HError};
|
use hd_lib::{bus::EventData, error::HError};
|
||||||
|
use hd_server::TcpEventBus;
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
|
|
||||||
fn main() -> Result<(), HError> {
|
fn main() -> Result<(), HError> {
|
||||||
@@ -5,13 +5,47 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use hd_lib::{
|
||||||
|
bus::{Event, EventBus, EventData}, error::HError, thread_pool::ThreadPool,
|
||||||
|
};
|
||||||
|
|
||||||
use tracing::{Span, info, info_span};
|
use tracing::{Span, info, info_span};
|
||||||
|
|
||||||
use crate::{
|
use std::net::TcpListener;
|
||||||
bus::{Event, EventBus},
|
|
||||||
error::HError,
|
pub struct TcpEventBus {
|
||||||
thread_pool::ThreadPool,
|
bus: EventBus,
|
||||||
};
|
pool: ThreadPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpEventBus {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let pool = ThreadPool::new(10);
|
||||||
|
Self {
|
||||||
|
bus: EventBus::new(),
|
||||||
|
pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn publish(&self, evt: EventData) -> Result<(), HError> {
|
||||||
|
self.bus.publish(evt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpEventBus {
|
||||||
|
pub fn start(&self, addr: &'static str) -> Result<(), HError> {
|
||||||
|
let listener = TcpListener::bind(addr).map_err(HError::TcpSocketBindError)?;
|
||||||
|
info!("Heimdall listening at {}", addr);
|
||||||
|
for stream in listener.incoming() {
|
||||||
|
match stream {
|
||||||
|
Ok(stream) => handle_event_bus_client(&self.pool, self.bus.clone(), stream)?,
|
||||||
|
Err(e) => tracing::error!("TCP connection failed {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
enum EventBusClientConnectionState {
|
enum EventBusClientConnectionState {
|
||||||
@@ -129,7 +163,10 @@ fn handle_state_send_event(client: &mut EventBusClientConnection) -> Result<Even
|
|||||||
buf[i] = evt.data.data_utf8[i - 17];
|
buf[i] = evt.data.data_utf8[i - 17];
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("SEND => ID: {} TYPE: {} LEN: {} <=", evt.id, evt.data.event_type, data_len);
|
info!(
|
||||||
|
"SEND => ID: {} TYPE: {} LEN: {} <=",
|
||||||
|
evt.id, evt.data.event_type, data_len
|
||||||
|
);
|
||||||
|
|
||||||
let stream = &mut client.stream;
|
let stream = &mut client.stream;
|
||||||
stream.write(&mut buf).map_err(HError::TcpPeerError)?;
|
stream.write(&mut buf).map_err(HError::TcpPeerError)?;
|
||||||
Reference in New Issue
Block a user