### Setup and Serve Websocket Application in Rust Source: https://github.com/basic-automation/onyums/blob/master/README.md Initializes an Axum router with a websocket route and starts the Onyums server. Requires `tokio` for async runtime and `axum` for web framework. ```rust use axum::routing::get; use axum::Router; use onyums::serve; #[tokio::main] async fn main() { // build our application with some routes let app = Router::new().route("/ws", get(ws_handler)); // start the serve // pass in the router and the server nickname (used to generate an onion address). serve(app, "my_onion").await.unwrap(); } ``` -------------------------------- ### Implement WebSocket Handler with Onyums Source: https://context7.com/basic-automation/onyums/llms.txt This example shows how to set up a WebSocket handler in Onyums, similar to Axum. It includes logic for identifying clients and echoing messages. ```rust use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade}; use axum::response::IntoResponse; use axum::routing::get; use axum::Router; use onyums::{serve, extract::ConnectInfo}; use std::net::{SocketAddr, IpAddr, Ipv4Addr}; async fn ws_handler( ws: WebSocketUpgrade, ConnectInfo(info): ConnectInfo ) -> impl IntoResponse { let default_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let addr = info.socket_addr.unwrap_or(default_addr).to_string(); let circuit = info.circuit_id.unwrap_or_default(); let client_id = if !circuit.is_empty() { circuit } else { addr }; println!("WebSocket connection from: {}", client_id); ws.on_upgrade(move |socket| handle_socket(socket, client_id)) } async fn handle_socket(mut socket: WebSocket, who: String) { // Send a ping to initiate the connection if socket.send(Message::Ping(vec![1, 2, 3])).await.is_ok() { println!("Pinged {}", who); } // Echo received messages back to the client while let Some(Ok(msg)) = socket.recv().await { match msg { Message::Text(text) => { println!("Received from {}: {}", who, text); if socket.send(Message::Text(format!("Echo: {}", text))).await.is_err() { break; } } Message::Close(_) => { println!("{} disconnected", who); break; } _ => {} } } } #[tokio::main] async fn main() { let app = Router::new().route("/ws", get(ws_handler)); serve(app, "websocket_service").await.unwrap(); } ``` -------------------------------- ### Configure Router and Routes with Onyums Source: https://context7.com/basic-automation/onyums/llms.txt Demonstrates setting up a router with multiple routes for GET and POST requests using Onyums, which re-exports Axum's routing capabilities. ```rust use onyums::{serve, routing::{get, post}, Router, Json}; use serde::{Deserialize, Serialize}; #[derive(Deserialize)] struct CreateUser { username: String, } #[derive(Serialize)] struct User { id: u64, username: String, } async fn list_users() -> Json> { Json(vec![ User { id: 1, username: "alice".to_string() }, User { id: 2, username: "bob".to_string() }, ]) } async fn create_user(Json(payload): Json) -> Json { Json(User { id: 3, username: payload.username, }) } async fn get_user(axum::extract::Path(id): axum::extract::Path) -> Json { Json(User { id, username: "example".to_string(), }) } #[tokio::main] async fn main() { let app = Router::new() .route("/users", get(list_users).post(create_user)) .route("/users/:id", get(get_user)); serve(app, "api_service").await.unwrap(); } ``` -------------------------------- ### Run a basic Axum server over Tor Source: https://github.com/basic-automation/onyums/blob/master/README.md This example demonstrates how to spawn an Onyums server on a new thread and retrieve its onion address. It includes a loop to wait until the server is ready and prints the onion address to the console. ```rust use onyums::{serve, routing::get, Router}; #[tokio::main] async fn main() { // spawn an onyums server on a new thread and return the onion address okio::spawn(async { let app = Router::new().route("/", axum_get(|| async { "Hello, World!" })); serve(app, "my_onion").await.unwrap(); }); // wait until `get_onion_name()` returns a non-empty String // then the server is ready let mut onion_name = String::new(); while onion_name.is_empty() { onion_name = get_onion_name(); // wait for 200 milliseconds before checking again tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; } println!("Onion Address: {onion_name}"); } ``` -------------------------------- ### Handle Websocket Upgrade Request in Rust Source: https://github.com/basic-automation/onyums/blob/master/README.md Handles the initial HTTP GET request for websocket negotiation. Extracts client connection info and user agent before upgrading the connection. ```rust use axum::extract::ws::WebSocketUpgrade; use axum::response::IntoResponse; use axum::extract::ConnectInfo; use axum_extra::TypedHeader; use std::net::{SocketAddr, IpAddr, Ipv4Addr}; use onyums::ConnectionInfo; async fn ws_handler(ws: WebSocketUpgrade, user_agent: Option>, ConnectInfo(connection_info): ConnectInfo) -> impl IntoResponse { let default_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let parsed_addr = connection_info.socket_addr.unwrap_or(default_addr).to_string(); let circuit_id = connection_info.circuit_id.unwrap_or_default(); let addr = if parsed_addr == default_addr.to_string() { circuit_id } else { parsed_addr }; let user_agent = if let Some(TypedHeader(user_agent)) = user_agent { user_agent.to_string() } else { String::from("Unknown browser") }; println!("`{user_agent}` at {addr} connected."); // finalize the upgrade process by returning upgrade callback. // we can customize the callback by sending additional info such as address. ws.on_upgrade(move |socket| handle_socket(socket, addr)) } ``` -------------------------------- ### Serve Axum App as Tor Onion Service Source: https://context7.com/basic-automation/onyums/llms.txt Use the `serve` function to launch an Axum application as a Tor onion service. It automatically handles Tor client setup and hidden service management. The service runs indefinitely. ```rust use onyums::{serve, routing::get, Router}; #[tokio::main] async fn main() { // Create an Axum router with routes let app = Router::new() .route("/", get(|| async { "Hello, World!" })) .route("/health", get(|| async { "OK" })); // Start the onion service with a nickname // The nickname is used to generate a persistent onion address serve(app, "my_service_nickname").await.unwrap(); } ``` -------------------------------- ### Get Onion Service Name Source: https://context7.com/basic-automation/onyums/llms.txt Retrieve the current onion address of a running Onyums service using `get_onion_name`. This is useful for logging or displaying the address. It returns an empty string if the service has not yet bootstrapped. ```rust use onyums::{serve, get_onion_name, routing::get, Router}; #[tokio::main] async fn main() { tokio::spawn(async { let app = Router::new().route("/", get(|| async { "Hello, World!" })); serve(app, "my_onion").await.unwrap(); }); // Poll until the onion name is available let mut onion_name = String::new(); while onion_name.is_empty() { onion_name = get_onion_name(); tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; } // Output: something like "abc123xyz.onion" println!("Service available at: {}", onion_name); } ``` -------------------------------- ### Handle Client Messages and Tasks in Tokio Source: https://github.com/basic-automation/onyums/blob/master/README.md This snippet shows how to manage concurrent send and receive tasks within a Tokio runtime. It uses `tokio::select!` to handle the completion of either task and aborts the other if one exits. This pattern is useful for managing bidirectional communication channels. ```rust // This second task will receive messages from client and print them on server console let mut recv_task = tokio::spawn(async move { let mut cnt = 0; while let Some(Ok(msg)) = receiver.next().await { cnt += 1; // print message and break if instructed to do so if process_message(msg, who).is_break() { break; } } cnt }); // If any one of the tasks exit, abort the other. tokio::select! { rv_a = (&mut send_task) => { match rv_a { Ok(a) => println!(ירת{a} messages sent to {who}) Err(a) => println!("Error sending messages {a:?}") } recv_task.abort(); }, rv_b = (&mut recv_task) => { match rv_b { Ok(b) => println!("Received {b} messages") Err(b) => println!("Error receiving messages {b:?}") } send_task.abort(); } } ``` -------------------------------- ### Custom ConnectionInfo Extractor for Tor/Direct Connections Source: https://context7.com/basic-automation/onyums/llms.txt Implement a custom Axum extractor to provide connection metadata. For Tor connections, it provides a circuit ID; for direct connections, it provides the socket address. This allows distinguishing connection types. ```rust use onyums::{serve, routing::get, Router, extract::ConnectInfo}; use std::net::{SocketAddr, IpAddr, Ipv4Addr}; #[derive(Clone, Debug, Default)] pub struct ConnectionInfo { pub circuit_id: Option, pub socket_addr: Option, } async fn handler( ConnectInfo(connection_info): ConnectInfo ) -> String { let default_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let addr = connection_info.socket_addr.unwrap_or(default_addr); let circuit = connection_info.circuit_id.unwrap_or_default(); // For Tor connections, circuit_id will be set // For direct connections, socket_addr will be set if !circuit.is_empty() { format!("Connected via Tor circuit: {}", circuit) } else { format!("Connected from: {}", addr) } } #[tokio::main] async fn main() { let app = Router::new().route("/whoami", get(handler)); serve(app, "my_onion").await.unwrap(); } ``` -------------------------------- ### Manage Individual Websocket Connection State in Rust Source: https://github.com/basic-automation/onyums/blob/master/README.md Manages the state machine for a single websocket connection. Sends a ping, receives a message, and sends a series of text messages. ```rust use axum::extract::ws::{Message, WebSocket}; use std::borrow::Cow; use std::ops::ControlFlow; async fn handle_socket(mut socket: WebSocket, who: String) { // send a ping (unsupported by some browsers) just to kick things off and get a response if socket.send(Message::Ping(vec![1, 2, 3])).await.is_ok() { println!("Pinged {who}..."); } else { println!("Could not send ping {who}!"); // no Error here since the only thing we can do is to close the connection. // If we can not send messages, there is no way to salvage the statemachine anyway. return; } // receive single message from a client (we can either receive or send with socket). // this will likely be the Pong for our Ping or a hello message from client. // waiting for message from a client will block this task, but will not block other client's // connections. if let Some(msg) = socket.recv().await { if let Ok(msg) = msg { if process_message(msg, who).is_break() { return; } } else { println!("client {who} abruptly disconnected"); return; } } // Since each client gets individual statemachine, we can pause handling // when necessary to wait for some external event (in this case illustrated by sleeping). // Waiting for this client to finish getting its greetings does not prevent other clients from // connecting to server and receiving their greetings. for i in 1..5 { if socket.send(Message::Text(format!("Hi {i} times!"))).await.is_err() { println!("client {who} abruptly disconnected"); return; } tokio::time::sleep(std::time::Duration::from_millis(100)).await; } // By splitting socket we can send and receive at the same time. In this example we will send // unsolicited messages to client based on some sort of server's internal event (i.e .timer). let (mut sender, mut receiver) = socket.split(); // Spawn a task that will push several messages to the client (does not matter what client does) let mut send_task = tokio::spawn(async move { let n_msg = 20; for i in 0..n_msg { // In case of any websocket error, we exit. if sender.send(Message::Text(format!("Server message {i} ..."))).await.is_err() { return i; } tokio::time::sleep(std::time::Duration::from_millis(300)).await; } println!("Sending close to {who}..."); if let Err(e) = sender.send(Message::Close(Some(CloseFrame {code: axum::extract::ws::close_code::NORMAL, reason: Cow::from("Goodbye"),}))).await { ``` -------------------------------- ### Process WebSocket Messages Source: https://github.com/basic-automation/onyums/blob/master/README.md A helper function to process incoming WebSocket messages. It handles different message types like Text, Binary, Close, Pong, and Ping, printing their contents to the console. It specifically checks for 'Close' messages to signal a break in processing. ```rust /// helper to print contents of messages to stdout. Has special treatment for Close. fn process_message(msg: Message, who: String) -> ControlFlow<(), ()> { match msg { Message::Text(t) => { println!(ירת>>> {who} sent str: {t:?}) } Message::Binary(d) => { println!(ירת>>> {} sent {} bytes: {:?}, who, d.len(), d) } Message::Close(c) => { if let Some(cf) = c { println!(ירת>>> {} sent close with code {} and reason `{}` , who, cf.code, cf.reason) } else { println!(ירת>>> {who} somehow sent close message without CloseFrame) } return ControlFlow::Break(()) } Message::Pong(v) => { println!(ירת>>> {who} sent pong with {:?}, v) } // You should never need to manually handle Message::Ping, as axum's websocket library // will do so for you automagically by replying with Pong and copying the v according to // spec. But if you need the contents of the pings you can see them here. Message::Ping(v) => { println!(ירת>>> {who} sent ping with {:?}, v) } } ControlFlow::Continue(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.