use bytes::Bytes;
use monocoque::rt::{self, LocalRuntime};
use monocoque::zmq::proxy::proxy_steerable;
use monocoque::zmq::{DealerSocket, ReqSocket, RouterSocket};
use monocoque_zmtp::pair::PairSocket;
use std::time::Duration;
use tracing::{error, info};
#[allow(clippy::future_not_send)]
async fn worker(id: u32) -> std::io::Result<()> {
info!("[Worker-{}] Starting", id);
rt::sleep(Duration::from_millis(500)).await;
let mut socket = DealerSocket::connect("127.0.0.1:5556").await?;
loop {
if let Ok(Some(mut msg)) = socket.recv().await {
if !msg.is_empty() && msg[0].is_empty() {
msg.remove(0);
}
if let Some(request) = msg.last() {
info!(
"[Worker-{}] Processing: {}",
id,
String::from_utf8_lossy(request)
);
}
rt::sleep(Duration::from_millis(100)).await;
let reply = format!("Processed by worker-{id}");
let mut response = vec![Bytes::new()];
response.extend(msg[..msg.len().saturating_sub(1)].to_vec());
response.push(Bytes::from(reply));
socket.send(response).await?;
}
rt::sleep(Duration::from_millis(10)).await;
}
}
#[allow(clippy::future_not_send)]
async fn client(id: u32, requests: u32) -> std::io::Result<()> {
info!("[Client-{}] Starting", id);
rt::sleep(Duration::from_secs(1)).await;
let mut socket = ReqSocket::connect("127.0.0.1:5555").await?;
for i in 1..=requests {
let request = format!("Request {i} from client-{id}");
info!("[Client-{}] Sending: {}", id, request);
socket.send(vec![Bytes::from(request)]).await?;
if let Ok(Some(reply)) = socket.recv().await
&& let Some(data) = reply.first()
{
info!(
"[Client-{}] Received: {}",
id,
String::from_utf8_lossy(data)
);
}
rt::sleep(Duration::from_millis(500)).await;
}
info!("[Client-{}] Done", id);
Ok(())
}
#[allow(clippy::future_not_send)]
async fn broker() -> std::io::Result<()> {
info!("🚀 Starting Steerable Broker");
let (_, mut frontend) = RouterSocket::bind("127.0.0.1:5555").await?;
info!("📡 Frontend (clients): 127.0.0.1:5555");
let (_, mut backend) = DealerSocket::bind("127.0.0.1:5556").await?;
info!("📡 Backend (workers): 127.0.0.1:5556");
let (_, mut control) = PairSocket::bind("127.0.0.1:5557").await?;
info!("🎮 Control socket: 127.0.0.1:5557");
info!(" Send commands: PAUSE, RESUME, TERMINATE, STATISTICS\n");
proxy_steerable(
&mut frontend,
&mut backend,
Option::<&mut DealerSocket>::None,
&mut control,
)
.await?;
Ok(())
}
#[allow(clippy::future_not_send)]
async fn controller() -> std::io::Result<()> {
info!("[Controller] Starting");
rt::sleep(Duration::from_millis(800)).await;
let mut control = PairSocket::connect("127.0.0.1:5557").await?;
rt::sleep(Duration::from_secs(3)).await;
info!("\n[Controller] 🛑 Sending PAUSE command\n");
control.send(vec![Bytes::from("PAUSE")]).await?;
rt::sleep(Duration::from_secs(2)).await;
info!("\n[Controller] ▶️ Sending RESUME command\n");
control.send(vec![Bytes::from("RESUME")]).await?;
rt::sleep(Duration::from_secs(3)).await;
info!("\n[Controller] 📊 Sending STATISTICS command\n");
control.send(vec![Bytes::from("STATISTICS")]).await?;
rt::sleep(Duration::from_secs(1)).await;
info!("\n[Controller] 🛑 Sending TERMINATE command\n");
control.send(vec![Bytes::from("TERMINATE")]).await?;
Ok(())
}
fn main() -> std::io::Result<()> {
LocalRuntime::new()?.block_on(async_main())
}
async fn async_main() -> std::io::Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.init();
info!("🎬 Steerable Proxy Demo");
info!("========================");
info!("Demonstrates:");
info!(" • Steerable proxy with control socket");
info!(" • PAUSE/RESUME/TERMINATE commands");
info!(" • STATISTICS reporting");
info!("========================\n");
rt::spawn_detached(async {
if let Err(e) = broker().await {
error!("Broker: {}", e);
}
});
rt::sleep(Duration::from_millis(500)).await;
rt::spawn_detached(async {
let _ = worker(1).await;
});
rt::spawn_detached(async {
let _ = worker(2).await;
});
rt::sleep(Duration::from_millis(500)).await;
rt::spawn_detached(async {
let _ = client(1, 10).await;
});
let controller_task = rt::spawn(async { controller().await });
let _ = rt::join(controller_task).await;
rt::sleep(Duration::from_secs(1)).await;
info!("\n✅ Demo Complete!");
info!("\nKey Points:");
info!(" • Proxy can be controlled via control socket");
info!(" • PAUSE stops forwarding (messages dropped)");
info!(" • RESUME restarts forwarding");
info!(" • TERMINATE gracefully stops proxy");
info!(" • STATISTICS reports message count");
Ok(())
}