use product_os_server::ProductOSServer;
#[cfg(not(feature = "ws"))]
use product_os_server::{StatusCode, Response, Body};
use product_os_server::ServerConfig;
use product_os_async_executor::TokioExecutor;
#[cfg(feature = "ws")]
use axum::{
extract::ws::{WebSocketUpgrade, WebSocket, Message},
response::IntoResponse,
};
#[cfg(feature = "ws")]
use futures::{sink::SinkExt, stream::{StreamExt, SplitSink, SplitStream}};
#[cfg(feature = "ws")]
async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket)
}
#[cfg(feature = "ws")]
async fn handle_socket(mut socket: WebSocket) {
while let Some(msg) = socket.recv().await {
let msg = if let Ok(msg) = msg {
msg
} else {
return;
};
if socket.send(msg).await.is_err() {
return;
}
}
}
#[cfg(feature = "ws")]
async fn ws_concurrent_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
ws.on_upgrade(handle_socket_concurrent)
}
#[cfg(feature = "ws")]
async fn handle_socket_concurrent(socket: WebSocket) {
let (sender, receiver) = socket.split();
tokio::spawn(write_messages(sender));
tokio::spawn(read_messages(receiver));
}
#[cfg(feature = "ws")]
async fn read_messages(mut receiver: SplitStream<WebSocket>) {
while let Some(Ok(msg)) = receiver.next().await {
println!("Received message: {:?}", msg);
}
}
#[cfg(feature = "ws")]
async fn write_messages(mut sender: SplitSink<WebSocket, Message>) {
use tokio::time::{interval, Duration};
let mut interval = interval(Duration::from_secs(1));
for i in 0..10 {
interval.tick().await;
let msg = Message::Text(format!("Server message {}", i).into());
if sender.send(msg).await.is_err() {
break;
}
}
}
#[cfg(not(feature = "ws"))]
async fn fallback_handler() -> Result<Response<Body>, StatusCode> {
Ok(Response::builder()
.status(StatusCode::NOT_IMPLEMENTED)
.body(Body::from("WebSocket feature not enabled"))
.unwrap())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let mut config = ServerConfig::new();
config.network.port = 3000;
println!("Creating WebSocket server...");
let mut server: ProductOSServer<(), TokioExecutor, _> =
ProductOSServer::new_with_config(config);
#[cfg(feature = "ws")]
{
server.add_ws_handler("/ws", ws_handler);
server.add_ws_handler("/ws/concurrent", ws_concurrent_handler);
println!("WebSocket Server listening on http://localhost:3000");
println!("Try connecting to:");
println!(" ws://localhost:3000/ws");
println!(" ws://localhost:3000/ws/concurrent");
println!("");
println!("Example with websocat:");
println!(" websocat ws://localhost:3000/ws");
}
#[cfg(not(feature = "ws"))]
{
server.add_get("/", fallback_handler);
println!("WebSocket feature not enabled. Rebuild with --features ws");
}
server.start(false).await.map_err(|e| format!("Server error: {}", e))?;
loop {
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
}
}