### Automated Benchmark Setup and Run Source: https://github.com/infinitefield/yawc/blob/master/benches/README.md Clones and builds all WebSocket libraries, then runs the benchmark suite. Requires Deno to be installed. This is a convenient way to execute the full benchmark process. ```bash cd benches # Option 1: Build everything and run benchmarks (requires Deno) make setup-all # Clones and builds all libraries make run # Runs the benchmark suite # Option 2: Just build the load_test tool make # Builds only load_test (for manual testing) ``` -------------------------------- ### Start the server Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Run this command in your terminal to start the WebSocket broadcasting server. ```bash cargo run ``` -------------------------------- ### Start Server on Custom Port and Path Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Start a WebSocket server listening on a custom network address and port, with a specified URI path. ```bash yawcc s --listen 0.0.0.0:8080 --path /ws ``` -------------------------------- ### Start Basic Echo Server Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Start a basic WebSocket echo server on localhost:9090. It echoes back all received messages. ```bash yawcc s ``` -------------------------------- ### Install yawc CLI Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Install the yawc command-line interface tool using cargo. ```bash cargo install yawcc ``` -------------------------------- ### Connect Client to Local Server Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Demonstrates connecting the yawc client to a locally running yawc server. First, start the server, then connect the client to its address and path. ```bash # In terminal 1, start the server yawcc s --listen 127.0.0.1:9090 --path /ws # In terminal 2, connect with the client yawcc c ws://127.0.0.1:9090/ws # Now you can send messages that will be echoed back ``` -------------------------------- ### Simple Echo Server Migration (Before 0.3.x) Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md Example of a simple echo server using yawc 0.1.x, where frame opcode was accessed directly. ```rust use yawc::{WebSocket, frame::FrameView}; use futures::StreamExt; while let Some(frame) = ws.next().await { match frame.opcode { OpCode::Text | OpCode::Binary => { ws.send(frame).await?; } _ => {} } } ``` -------------------------------- ### Simple Echo Server Migration (After 0.3.x) Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md Example of a simple echo server using yawc 0.3.x, where frame opcode is accessed via a method call. ```rust use yawc::{WebSocket, frame::Frame}; use futures::StreamExt; while let Some(frame) = ws.next().await { match frame.opcode() { OpCode::Text | OpCode::Binary => { ws.send(frame).await?; } _ => {} } } ``` -------------------------------- ### Run Autobahn Client Tests Source: https://github.com/infinitefield/yawc/blob/master/README.md Execute the Autobahn client tests using Deno. Ensure Docker is running and Deno is installed. Reports will be generated at ./autobahn/reports/client/index.html. ```bash deno -A ./autobahn/client-test.js ``` -------------------------------- ### Yawc Server Example Source: https://github.com/infinitefield/yawc/blob/master/README.md Set up a WebSocket server that upgrades incoming HTTP requests and echoes received frames back to the client. Handles connection upgrades and message echoing. ```rust use hyper::{Request, Response, body::Incoming}; use futures::StreamExt; use futures::SinkExt; use bytes::Bytes; use http_body_util::Empty; use yawc::{WebSocket, Result}; async fn handle_upgrade(req: Request) -> Result>> { // Upgrade the connection let (response, upfn) = WebSocket::upgrade(req)?; // Handle the WebSocket connection in a separate task tokio::spawn(async move { let mut ws = upfn.await.expect("upgrade"); while let Some(frame) = ws.next().await { // Echo the received frames back to the client let _ = ws.send(frame).await; } }); Ok(response) } #[tokio::main] async fn main() { // configure the server } ``` -------------------------------- ### Install OpenSSL on macOS Source: https://github.com/infinitefield/yawc/blob/master/benches/README.md Installs and links OpenSSL version 3 on macOS using Homebrew. Ensure OpenSSL is correctly linked before proceeding with builds that depend on it. ```bash brew install openssl@3 # Ensure it's linked brew link --force openssl@3 ``` -------------------------------- ### Axum WebSocket Server Example Source: https://github.com/infinitefield/yawc/blob/master/README.md Demonstrates how to integrate Yawc with the Axum web framework to handle WebSocket connections. Requires the 'axum' feature flag. ```rust use axum:: routing::get, Router, }; use futures::StreamExt; use yawc::{IncomingUpgrade, Options, CompressionLevel}; async fn websocket_handler(ws: IncomingUpgrade) -> axum::response::Response { let options = Options::default() .with_compression_level(CompressionLevel::default()) .with_utf8(); let (response, ws_future) = ws.upgrade(options).unwrap(); // Handle the WebSocket connection in a separate task tokio::spawn(async move { if let Ok(mut ws) = ws_future.await { while let Some(frame) = ws.next().await { // Echo the received frames back to the client let _ = ws.send(frame).await; } } }); response } #[tokio::main] async fn main() { let app = Router::new() .route("/ws", get(websocket_handler)); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); axum::serve(listener, app).await.unwrap(); } ``` ```toml [dependencies] yawc = { version = "0.3", features = ["axum"] } axum = "0.7" tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } utures = { version = "0.3", default-features = false, features = ["std"] } ``` -------------------------------- ### Multi-Runtime Support with Smol Adapter Example Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md Yawc 0.3 supports runtimes other than tokio. This example shows a basic adapter for `smol::net::TcpStream` to implement tokio's `AsyncRead` and `AsyncWrite` traits. ```rust // See examples/client_smol.rs for a complete example use smol::net::TcpStream; // Implement a simple adapter struct SmolStream(TcpStream); impl tokio::io::AsyncRead for SmolStream { // Bridge implementation } impl tokio::io::AsyncWrite for SmolStream { // Bridge implementation } ``` -------------------------------- ### Yawc Client Dependencies Source: https://github.com/infinitefield/yawc/blob/master/README.md Dependencies required for the yawc client example, including futures and tokio runtimes. ```toml [dependencies] yawc = { version = "0.3" } utures = { version = "0.3", default-features = false, features = ["std"] } tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } ``` -------------------------------- ### Codec Layer Frame Decoding Example Source: https://github.com/infinitefield/yawc/blob/master/README.md Shows how the Tokio Codec Layer decodes individual frames, including details about compression flags and frame types. ```text Frame 1: `OpCode::Text, RSV1=1 (compressed), FIN=0` → Returns individual frame Frame 2: `OpCode::Continuation, RSV1=0, FIN=0` → Returns individual frame Frame 3: `OpCode::Continuation, RSV1=0, FIN=1` → Returns individual frame ``` -------------------------------- ### Yawc Server Dependencies Source: https://github.com/infinitefield/yawc/blob/master/README.md Dependencies required for the yawc server example, including futures, tokio, hyper, http-body-util, and bytes. ```toml [dependencies] yawc = "0.3" utures = { version = "0.3", default-features = false, features = ["std"] } tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } hyper = { version = "1", features = ["http1", "server"] } http-body-util = "0.1" bytes = "1" ``` -------------------------------- ### Run Autobahn Server Tests Source: https://github.com/infinitefield/yawc/blob/master/README.md Execute the Autobahn server tests using Deno. Ensure Docker is running and Deno is installed. Expect numerous client connection and disconnection logs, which indicate the fuzzing client is operating correctly. Reports will be generated at ./autobahn/reports/servers/index.html. ```bash deno -A ./autobahn/server-test.js ``` -------------------------------- ### Configure Advanced Compression Options Source: https://github.com/infinitefield/yawc/blob/master/README.md Configure memory-optimized compression for long-lived connections by disabling context takeover on both server and client sides. This example uses default compression levels and pure Rust deflate. ```rust let options = Options::default() .with_compression_level(CompressionLevel::fast()) .server_no_context_takeover() // Reset context after each message .client_no_context_takeover(); // Prevent client-side memory growth ``` -------------------------------- ### Yawc Streaming API Example Source: https://github.com/infinitefield/yawc/blob/master/README.md Illustrates the low-level Streaming API for manual control over WebSocket frame fragmentation. Useful for large messages or custom protocols. ```rust use yawc::{WebSocket, Frame, OpCode}; use futures::{SinkExt, StreamExt}; // Convert WebSocket to Streaming for manual fragment control let ws = WebSocket::connect("wss://example.com".parse()?) .await?; let mut streaming = ws.into_streaming(); // Send a large message as multiple fragments manually streaming.send(Frame::text("First part").with_fin(false)).await?; streaming.send(Frame::continuation(" second part").with_fin(false)).await?; streaming.send(Frame::continuation(" final part")).await?; // Receive frames without automatic reassembly while let Some(frame) = streaming.next().await { match frame.opcode() { OpCode::Text => println!("Text fragment: FIN={}", frame.is_fin()), OpCode::Continuation => println!("Continuation: FIN={}", frame.is_fin()), _ => {} } } ``` -------------------------------- ### Interactive Client Commands with Comments Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Example of sending messages in the interactive client, including inline comments using '//'. Comments are saved in history but not sent. ```text > {"type": "ping"} // Heartbeat message > {"command": "subscribe", "channel": "updates"} // Subscribe to updates ``` -------------------------------- ### Yawc Client Example Source: https://github.com/infinitefield/yawc/blob/master/README.md Connect to a WebSocket server, send a text message, and process incoming frames. Handles text and binary frames, and automatically manages control frames. ```rust use futures::SinkExt; use futures::StreamExt; use yawc::{frame::Frame, frame::OpCode, Options, Result, WebSocket}; #[tokio::main] async fn main() -> Result<()> { // Connect with default options let mut ws = WebSocket::connect("wss://echo.websocket.org".parse()?) .await?; // Send and receive messages ws.send(Frame::text("Hello WebSocket!")).await?; while let Some(frame) = ws.next().await { match frame.opcode() { OpCode::Text => println!("Received: {}", frame.as_str()), OpCode::Binary => println!("Received binary: {} bytes", frame.payload().len()), _ => {} // Handle control frames automatically } } Ok(()) } ``` -------------------------------- ### Yawc Frame API Examples Source: https://context7.com/infinitefield/yawc/llms.txt Demonstrates creating and inspecting various WebSocket frame types using the Yawc Frame API. Supports text, binary, ping, pong, close, and fragmented messages. ```rust use yawc::{Frame, close::CloseCode}; use bytes::Bytes; // Create different frame types let text_frame = Frame::text("Hello, WebSocket!"); let binary_frame = Frame::binary(vec![0x01, 0x02, 0x03, 0x04]); let ping_frame = Frame::ping("heartbeat"); let pong_frame = Frame::pong("heartbeat"); let close_frame = Frame::close(CloseCode::Normal, b"Goodbye"); // Access frame properties assert_eq!(text_frame.opcode(), yawc::OpCode::Text); assert!(text_frame.is_fin()); assert_eq!(text_frame.as_str(), "Hello, WebSocket!"); assert_eq!(text_frame.payload().len(), 17); // Destructure frame into parts let (opcode, is_fin, payload) = text_frame.into_parts(); // Close frame details let close = Frame::close(CloseCode::Normal, b"Session ended"); assert_eq!(close.close_code(), Some(CloseCode::Normal)); assert_eq!(close.close_reason().unwrap(), Some("Session ended")); // Create fragmented messages let first_fragment = Frame::text("Hello, ").with_fin(false); let continuation = Frame::continuation("World!"); assert!(!first_fragment.is_fin()); assert!(continuation.is_fin()); ``` -------------------------------- ### Run Manual Load Test Source: https://github.com/infinitefield/yawc/blob/master/benches/README.md Executes the load test tool manually with specified parameters. Use this for targeted testing or when automated setup is not desired. Ensure the server is running on the target host and port. ```bash # ./load_test ./load_test 100 0.0.0.0 8080 0 0 1024 ``` -------------------------------- ### Handle Custom WebSocket Frame Opcodes Source: https://github.com/infinitefield/yawc/blob/master/README.md Manually process different WebSocket frame opcodes like Ping and Close. This example shows how to automatically respond to Pings and handle close frame payloads. ```rust match frame.opcode() { OpCode::Ping => { // Automatic pong responses println!("Received ping"); } OpCode::Close => { // Handle close frames let code = u16::from_be_bytes(frame.payload[0..2].try_into()?); println!("Connection closing with code: {}", code); } _ => { /* Handle data frames */ } } ``` -------------------------------- ### Streaming API for Manual Fragment Control Source: https://context7.com/infinitefield/yawc/llms.txt Utilize the Streaming API for low-level control over WebSocket frames, enabling custom fragmentation strategies and memory-efficient processing of large messages. This example demonstrates sending a large file in chunks and receiving fragments directly to disk. ```rust use yawc::{WebSocket, Frame, OpCode, Options}; use futures::{SinkExt, StreamExt}; use std::fs::File; use std::io::Write; #[tokio::main] async fn main() -> yawc::Result<()> { // Connect and convert to streaming mode let ws = WebSocket::connect("ws://example.com/upload".parse()?) .with_options(Options::default().with_high_compression()) .await?; let mut streaming = ws.into_streaming(); // Send large file as fragments without loading entirely in memory let file_data = std::fs::read("large_file.bin")?; let chunk_size = 64 * 1024; // 64 KiB chunks for (i, chunk) in file_data.chunks(chunk_size).enumerate() { let is_first = i == 0; let is_last = (i + 1) * chunk_size >= file_data.len(); let frame = if is_first { Frame::binary(chunk.to_vec()).with_fin(is_last) } else if is_last { Frame::continuation(chunk.to_vec()) } else { Frame::continuation(chunk.to_vec()).with_fin(false) }; streaming.send(frame).await?; } // Receive fragments and write directly to disk let mut output_file = File::create("received_file.bin")?; while let Some(frame) = streaming.next().await { let (opcode, is_fin, payload) = frame.into_parts(); match opcode { OpCode::Text | OpCode::Binary | OpCode::Continuation => { output_file.write_all(&payload)?; if is_fin { break; // Message complete } } _ => {} } } output_file.flush()?; Ok(()) } ``` -------------------------------- ### Manual Cleanup of MPSC Senders in Rust Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Shows the manual process required in an mpsc setup to retain only active senders, contrasting with the automatic resource management of broadcast receivers. ```rust // vs mpsc where you need manual cleanup senders.retain(|tx| !tx.is_closed()); ``` -------------------------------- ### Build yawc from Source Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Instructions for cloning the yawc repository, navigating to the yawcc directory, and building the release version using cargo. ```bash git clone https://github.com/infinitefield/yawc cd yawc/yawcc cargo build --release ``` -------------------------------- ### Connect with Basic Compression Source: https://github.com/infinitefield/yawc/blob/master/README.md Connect to a WebSocket server with default compression options. The `fast()` compression level balances compression ratio and CPU usage. ```rust let mut client = WebSocket::connect("wss://my-websocket-server.com".parse().unwrap()) .with_options(Options::default().with_compression_level(CompressionLevel::fast())) .await; ``` -------------------------------- ### Connect with WebSocket client Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Use this command to connect to the running server with a WebSocket client utility. You can then send subscription messages. ```bash yawcc c ws://localhost:3001/ws --input-as-json > {"method":"subscribe","symbol":"BTCUSDT","topic":"orderbook","levels":50} ``` -------------------------------- ### Fix Compiler Error: Cannot move out of `frame.payload` Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md The `payload()` method returns a reference, not owned data. Clone the payload or use `into_parts()` to get ownership. ```rust // Option 1: Clone let payload = frame.payload().clone(); // Option 2: Use into_parts() for ownership let (opcode, is_fin, payload) = frame.into_parts(); ``` -------------------------------- ### Build Dependencies with Makefile Source: https://github.com/infinitefield/yawc/blob/master/benches/README.md Builds all project dependencies, including the load_test tool, using the provided Makefile. Navigate to the benches directory before running. ```bash cd benches make ``` -------------------------------- ### Server Options Help Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Displays the available command-line options for the yawcc server mode, including listen address/port and URI path. ```text Usage: yawcc server [OPTIONS] Options: -l, --listen Network address and port to listen on [default: 127.0.0.1:9090] -p, --path URI path to serve the WebSocket endpoint [default: /] -h, --help Print help (see more with '--help') ``` -------------------------------- ### Connect to WebSocket Server (Basic) Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Connect to a WebSocket server using the client mode. Supports ws:// and wss:// URLs. ```bash yawcc c wss://fstream.binance.com/ws/btcusdt@aggTrade ``` -------------------------------- ### Server Initialization Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Initializes the WebSocket server, sets up routing, and spawns a task for upstream connection management. Uses `unbounded_channel` for upstream communication and `Arc` for shared state. ```rust async fn server() -> io::Result<()> { let (tx, rx) = unbounded_channel(); let state = Arc::new(AppState::new(tx)); let router = Router::new() .route("/ws", get(on_websocket)) .with_state(Arc::clone(&state)); tokio::spawn(async move { connect_upstream(rx, state).await }); let listener = TcpListener::bind("0.0.0.0:3001").await?; axum::serve(listener, router).await } ``` -------------------------------- ### WebSocket Close Codes Source: https://context7.com/infinitefield/yawc/llms.txt Demonstrates the use of the `CloseCode` enum for sending and interpreting WebSocket close frames. Includes examples of common codes and conversion to/from u16. ```rust use yawc::{Frame, close::CloseCode}; // Common close codes let normal_close = Frame::close(CloseCode::Normal, b"Session complete"); let going_away = Frame::close(CloseCode::Away, b"Server shutting down"); let protocol_error = Frame::close(CloseCode::Protocol, b"Invalid frame received"); let unsupported = Frame::close(CloseCode::Unsupported, b"Binary not supported"); let too_large = Frame::close(CloseCode::Size, b"Message exceeded limit"); let server_error = Frame::close(CloseCode::Error, b"Internal error"); // Check if a close code is valid for sending let code = CloseCode::Normal; assert!(code.is_allowed()); // Convert between u16 and CloseCode let code_from_u16 = CloseCode::from(1000_u16); assert_eq!(code_from_u16, CloseCode::Normal); let code_to_u16: u16 = CloseCode::Normal.into(); assert_eq!(code_to_u16, 1000); ``` -------------------------------- ### Client Options Help Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Displays the available command-line options for the yawcc client mode, including URL, timeout, headers, JSON validation, and TCP host settings. ```text Usage: yawcc client [OPTIONS] Arguments: The WebSocket URL to connect to (ws:// or wss://) Options: -t, --timeout Maximum duration to wait when establishing the connection. Accepts human-readable formats like "5s", "1m", "500ms" [default: 5s] --include-time Includes the timestamp for each message -H, --header Custom headers to send to the server in "Key: Value" format For example: --header "Authorization: Bearer token123" --input-as-json When enabled, validates and pretty-prints received messages as JSON. Invalid JSON messages will result in an error --tcp-host Connect directly to a TCP host instead of using WebSocket URL. Format: host:port (e.g., "127.0.0.1:8080") -h, --help Print help (see more with '--help') ``` -------------------------------- ### Configure WebSocket Compression Options Source: https://github.com/infinitefield/yawc/blob/master/README.md Set custom compression levels and context takeover behavior for WebSocket connections. Use `server_no_context_takeover` and `client_no_context_takeover` for memory optimization in constrained environments or with diverse message content. ```rust use yawc::{WebSocket, Options, CompressionLevel}; let ws = WebSocket::connect("wss://example.com".parse()?) .with_options( Options::default() .with_compression_level(CompressionLevel::default()) .server_no_context_takeover() // Reset compression context after each message .client_no_context_takeover() // Optimize memory for client side .with_client_max_window_bits(11) // Control compression window (requires zlib feature) ) .await?; ``` ```rust // Example: Memory-optimized compression for long-lived connections let options = Options::default() .with_compression_level(CompressionLevel::fast()) .server_no_context_takeover() .client_no_context_takeover(); ``` -------------------------------- ### Update Frame Pattern Matching Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md Adapt frame pattern matching to use method calls instead of direct field access. For example, change `match frame.opcode` to `match frame.opcode()`. ```rust // Before match frame.opcode { OpCode::Text => { /* ... */ } OpCode::Binary => { /* ... */ } _ => {} } // After match frame.opcode() { OpCode::Text => { /* ... */ } OpCode::Binary => { /* ... */ } _ => {} } ``` -------------------------------- ### MPSC Unbounded Channel Initialization in Rust Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Demonstrates setting up multiple unbounded channels using mpsc for a scenario with multiple subscribers. This is presented as an alternative to the broadcast channel. ```rust // vs mpsc approach let mut senders = Vec::new(); for _ in 0..n_subscribers { let (tx, rx) = mpsc::unbounded_channel(); senders.push(tx); } ``` -------------------------------- ### Message Creation and Handling with tokio-tungstenite Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Demonstrates creating various `Message` types (Text, Binary, Ping, Pong, Close) and handling incoming messages using a match statement in tokio-tungstenite. Note that Ping messages are auto-responded. ```rust use tokio_tungstenite::tungstenite::Message; // Creating messages let text = Message::Text("Hello".to_string()); let binary = Message::Binary(vec![1, 2, 3]); let ping = Message::Ping(vec![]); let pong = Message::Pong(vec![]); let close = Message::Close(Some(CloseFrame { code: CloseCode::Normal, reason: "Goodbye".into(), })); // Handling messages match msg { Message::Text(text) => { /* ... */ } Message::Binary(data) => { /* ... */ } Message::Ping(_) => { /* auto-responded */ } Message::Pong(_) => { /* ... */ } Message::Close(_) => { /* ... */ } Message::Frame(_) => { /* raw frame */ } } ``` -------------------------------- ### Broadcast Sender Initialization in Rust Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Illustrates the creation of a broadcast sender with a buffer size of 1024 and subscribing two receivers to it. This is compared against an alternative mpsc approach. ```rust // With broadcast let tx = broadcast::Sender::new(1024); let rx1 = tx.subscribe(); let rx2 = tx.subscribe(); ``` -------------------------------- ### Configure yawc Options Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Set memory limits, compression level, and UTF-8 validation for yawc connections. Disables Nagle's algorithm for reduced latency. ```rust use yawc::{Options, CompressionLevel}; let options = Options::default() .with_max_payload_read(64 * 1024 * 1024) .with_max_read_buffer(128 * 1024 * 1024) .with_compression_level(CompressionLevel::fast()) .with_utf8() // Validate UTF-8 .with_no_delay(); // Disable Nagle's algorithm ``` -------------------------------- ### Handling Subscription Updates in Rust Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Illustrates the current implementation of handling user subscriptions, where a new broadcast sender is created and added to the topics map. This approach may cause new subscribers to miss the initial state. ```rust fn on_subscription( state: &AppState, streams: &mut StreamMap, BroadcastStream>, sub: UserSubscribe, ) -> Result<(), String> { // Current implementation only handles update stream let tx = broadcast::Sender::new(1024); topics.insert(topic.clone(), tx.clone()); // New subscribers might miss initial state } ``` -------------------------------- ### Establish WebSocket Client Connection Source: https://context7.com/infinitefield/yawc/llms.txt Connects to a WebSocket server using ws:// or wss://. Supports configurable options for compression, payload limits, and TCP settings. Ensure necessary imports are included. ```rust use futures::{SinkExt, StreamExt}; use yawc::{Frame, OpCode, Options, WebSocket}; #[tokio::main] async fn main() -> yawc::Result<()> { // Connect with compression enabled let mut client = WebSocket::connect("wss://echo.websocket.org".parse()?) .with_options( Options::default() .with_compression_level(yawc::CompressionLevel::fast()) .with_utf8() // Enable UTF-8 validation .with_no_delay() // Disable Nagle's algorithm for lower latency ) .await?; // Send a text message client.send(Frame::text("Hello WebSocket!")).await?; // Receive and process messages while let Some(frame) = client.next().await { match frame.opcode() { OpCode::Text => { println!("Received: {}", frame.as_str()); } OpCode::Binary => { println!("Received {} bytes of binary data", frame.payload().len()); } OpCode::Pong => { println!("Pong received"); } OpCode::Close => { println!("Connection closed with code: {:?}", frame.close_code()); break; } _ => {} } } Ok(()) } ``` -------------------------------- ### Create and Handle WebSocket Frames with yawc Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Demonstrates creating various WebSocket frame types (text, binary, ping, pong, close) and handling incoming frames using pattern matching on opcode or by consuming the frame into parts. ```rust use yawc::{Frame, OpCode}; use yawc::close::CloseCode; // Creating frames let text = Frame::text("Hello"); let binary = Frame::binary(vec![1, 2, 3]); let ping = Frame::ping(""); let pong = Frame::pong(""); let close = Frame::close(CloseCode::Normal, b"Goodbye"); ``` ```rust // Handling frames - Option 1: Using accessors match frame.opcode() { OpCode::Text => { let text = frame.as_str(); // Direct &str access } OpCode::Binary => { let data = frame.payload(); // Bytes reference } OpCode::Ping => { // Pong automatically sent, but ping frame is still returned // so you can observe/log it if needed } OpCode::Pong => { /* ... */ } OpCode::Close => { let code = frame.close_code(); let reason = frame.close_reason()?; } _ => {} } ``` ```rust // Handling frames - Option 2: Using into_parts() for ownership let (opcode, _is_fin, payload) = frame.into_parts(); match opcode { OpCode::Text => { let text = std::str::from_utf8(&payload)?; } OpCode::Binary => { // payload is now owned Bytes } _ => {} } ``` -------------------------------- ### Basic Client Connection with tokio-tungstenite Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Establishes an asynchronous WebSocket connection using tokio-tungstenite and handles sending and receiving text messages. Requires `tokio-tungstenite` and `futures` crates. ```rust use tokio_tungstenite::{connect_async, tungstenite::Message}; use futures::{StreamExt, SinkExt}; let (ws_stream, _) = connect_async("wss://echo.websocket.org").await?; let (mut write, mut read) = ws_stream.split(); write.send(Message::Text("Hello".to_string())).await?; while let Some(msg) = read.next().await { let msg = msg?; match msg { Message::Text(text) => println!("Received: {}", text), Message::Binary(data) => println!("Binary: {} bytes", data.len()), _ => {} } } ``` -------------------------------- ### Handle Client Subscription with Snapshot Retrieval Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md An asynchronous function to handle client subscriptions by first retrieving the current snapshot state and then listening for subsequent real-time updates. Sends the snapshot to the client before processing live updates. ```rust /// Enhanced client handler async fn handle_client_subscription( state: &ImprovedAppState, topic: Topic<'static>, ) -> Result<(), Error> { let TopicSubscription { mut updates, mut snapshot } = state.subscribe(&topic)?; // First, get current state if let Some(current_state) = *snapshot.borrow() { // Send snapshot to client send_to_client(current_state).await?; } // Then listen for updates while let Ok(update) = updates.recv().await { send_to_client(update).await?; } Ok(()) } ``` -------------------------------- ### Create WebSocket Server with Hyper Source: https://context7.com/infinitefield/yawc/llms.txt Upgrades HTTP connections to WebSocket using hyper. Handles the WebSocket handshake and provides a future for WebSocket connection management. Configure options like compression level and max payload size. ```rust use bytes::Bytes; use futures::{SinkExt, StreamExt}; use http_body_util::Empty; use hyper::{ body::Incoming, server::conn::http1, service::service_fn, Request, Response, }; use tokio::net::TcpListener; use yawc::{Frame, OpCode, Options, WebSocket}; async fn handle_websocket(fut: yawc::UpgradeFut) -> yawc::Result<()> { let mut ws = fut.await?; println!("Client connected"); while let Some(frame) = ws.next().await { match frame.opcode() { OpCode::Text | OpCode::Binary => { // Echo the message back ws.send(frame).await?; } OpCode::Close => break, _ => {} } } Ok(()) } async fn upgrade_handler( mut req: Request, ) -> yawc::Result>> { let options = Options::default() .with_compression_level(yawc::CompressionLevel::default()) .with_max_payload_read(2 * 1024 * 1024); // 2 MiB limit let (response, upgrade_future) = WebSocket::upgrade_with_options(&mut req, options)?; tokio::spawn(async move { if let Err(e) = handle_websocket(upgrade_future).await { eprintln!("WebSocket error: {}", e); } }); Ok(response) } #[tokio::main] async fn main() -> anyhow::Result<()> { let listener = TcpListener::bind("0.0.0.0:8080").await?; println!("WebSocket server listening on port 8080"); loop { let (stream, _) = listener.accept().await?; tokio::spawn(async move { let io = hyper_util::rt::TokioIo::new(stream); let _ = http1::Builder::new() .serve_connection(io, service_fn(upgrade_handler)) .with_upgrades() .await; }); } } ``` -------------------------------- ### Create Custom Frames: yawc 0.3.x vs 0.1.x Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md In 0.3.x, `Frame::text()` and `Frame::binary()` are used directly for creating frames, replacing `FrameView` from 0.1.x. ```rust let mut frames = vec![ FrameView::text("Message 1"), FrameView::text("Message 2"), FrameView::binary(vec![1, 2, 3]), ]; for frame in frames { ws.send(frame).await?; } ``` ```rust let frames = vec![ Frame::text("Message 1"), Frame::text("Message 2"), Frame::binary(vec![1, 2, 3]), ]; for frame in frames { ws.send(frame).await?; } ``` -------------------------------- ### Subscribe to Topic with Snapshot in ImprovedAppState Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Implements a method to subscribe to a topic, retrieving both real-time updates and the latest snapshot. Requires the topic to exist and returns a `TopicSubscription` containing update and snapshot channels. ```rust impl ImprovedAppState { fn subscribe(&self, topic: &Topic<'static>) -> Result { let topics = self.topics.read().unwrap(); let snapshots = self.snapshots.read().unwrap(); let updates = topics.get(topic) .ok_or(Error::TopicNotFound)? .subscribe(); let snapshot = snapshots.get(topic) .ok_or(Error::TopicNotFound)? .subscribe(); Ok(TopicSubscription { updates, snapshot }) } fn update_snapshot(&self, topic: &Topic<'static>, data: Bytes) -> Result<(), Error> { let snapshots = self.snapshots.write().unwrap(); if let Some(sender) = snapshots.get(topic) { sender.send(Some(data))?; } Ok(()) } } ``` -------------------------------- ### Axum Integration with yawc Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Shows how to integrate yawc with Axum using the IncomingUpgrade extractor. This approach requires explicit configuration of upgrade options and manual spawning of a task to handle the WebSocket connection, offering more control over the upgrade process. ```rust use yawc::{IncomingUpgrade, Options}; use axum::response::Response; async fn ws_handler(ws: IncomingUpgrade) -> Response { let (response, upgrade) = ws .upgrade(Options::default()) .unwrap(); tokio::spawn(async move { let mut ws = upgrade.await.unwrap(); while let Some(frame) = ws.next().await { let _ = ws.send(frame).await; } }); response } ``` -------------------------------- ### Direct TCP Connection Source: https://github.com/infinitefield/yawc/blob/master/yawcc/README.md Establish a direct TCP connection to a specified host and port instead of using a WebSocket URL. ```bash yawcc c https://echo.websocket.org --tcp-host 127.0.0.1:8080 ``` -------------------------------- ### Synchronized Subscription with Snapshot Buffering Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Handles subscriptions by synchronizing real-time updates with an initial snapshot. It buffers updates that occur after the snapshot is retrieved but before the client is fully synchronized, ensuring data consistency. ```rust struct MarketDataSequence { sequence: u64, data: Bytes, } async fn synchronized_subscription( state: &ImprovedAppState, topic: Topic<'static>, ) -> Result<(), Error> { let TopicSubscription { mut updates, mut snapshot } = state.subscribe(&topic)?; // Get initial sequence from snapshot let initial_seq = snapshot.borrow().as_ref() .and_then(|data| parse_sequence(data)) .ok_or(Error::NoSnapshot)?; // Buffer updates while processing snapshot let mut buffer = Vec::new(); loop { match updates.recv().await? { update if parse_sequence(&update) <= initial_seq => { // Discard updates older than snapshot continue; } update => { buffer.push(update); break; } } } // Process buffered updates in sequence for update in buffer { send_to_client(update).await?; } // Continue with real-time updates while let Ok(update) = updates.recv().await { send_to_client(update).await?; } Ok(()) } ``` -------------------------------- ### Custom WebSocket Handshake with Headers Source: https://context7.com/infinitefield/yawc/llms.txt Establishes a WebSocket connection with custom HTTP headers for authentication or metadata. Requires manual TCP stream establishment and building a custom HttpRequest. ```rust use tokio::net::TcpStream; use yawc::{WebSocket, Options, HttpRequest}; #[tokio::main] async fn main() -> yawc::Result<()> { // Establish TCP connection manually let stream = TcpStream::connect("example.com:443").await?; let url = "wss://example.com/ws".parse()?; // Create custom HTTP request with auth headers let request = HttpRequest::builder() .header("Authorization", "Bearer my-secret-token") .header("X-API-Key", "api-key-12345") .header("X-Client-Version", "1.0.0"); // Perform handshake with custom headers let ws = WebSocket::handshake_with_request( url, stream, Options::default().with_compression_level(yawc::CompressionLevel::default()), request, ).await?; // Use the WebSocket connection Ok(()) } ``` -------------------------------- ### Configure WebSocket Connection with tokio-tungstenite Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Sets up a WebSocket configuration for tokio-tungstenite, specifying maximum message and frame sizes. Note that tokio-tungstenite itself does not directly support compression; it requires the 'tungstenite' crate with compression features enabled. ```rust use tokio_tungstenite::{ tungstenite::protocol::WebSocketConfig, connect_async_with_config, }; let config = WebSocketConfig { max_message_size: Some(64 << 20), max_frame_size: Some(16 << 20), ..Default::default() }; let (ws, _) = connect_async_with_config(url, Some(config), false).await?; ``` -------------------------------- ### Basic Server Connection with tokio-tungstenite Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Accepts an incoming WebSocket connection using tokio-tungstenite and echoes received messages back to the client. Requires `tokio-tungstenite` and `futures` crates. ```rust use tokio_tungstenite::accept_async; use futures::{StreamExt, SinkExt}; let stream = tokio::net::TcpStream::connect("localhost:8080").await?; let ws_stream = accept_async(stream).await?; while let Some(msg) = ws_stream.next().await { let msg = msg?; ws_stream.send(msg).await?; } ``` -------------------------------- ### Basic Client Connection with yawc Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Establishes an asynchronous WebSocket connection using yawc and handles sending and receiving text frames. Requires `yawc` and `futures` crates. Note the use of `Frame` and `OpCode`. ```rust use yawc::{WebSocket, Frame, OpCode}; use futures::{StreamExt, SinkExt}; let mut ws = WebSocket::connect("wss://echo.websocket.org".parse()?) .await?; ws.send(Frame::text("Hello")).await?; while let Some(frame) = ws.next().await { let (opcode, _is_fin, payload) = frame.into_parts(); match opcode { OpCode::Text => { let text = std::str::from_utf8(&payload)?; println!("Received: {}", text); } OpCode::Binary => println!("Binary: {} bytes", payload.len()), _ => {} } } ``` -------------------------------- ### Basic Server Connection with yawc and hyper Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Handles WebSocket upgrades within a hyper server using yawc. It returns a response and an upgrade future, requiring the response to be sent before awaiting the upgrade. Requires `yawc`, `hyper`, and `futures` crates. ```rust use yawc::WebSocket; use hyper::{Request, Response, body::Incoming}; use futures::{StreamExt, SinkExt}; async fn handle_upgrade(req: Request) -> Result> { let (response, upfn) = WebSocket::upgrade(req)?; tokio::spawn(async move { let mut ws = upfn.await.expect("upgrade"); while let Some(frame) = ws.next().await { let _ = ws.send(frame).await; } }); Ok(response) } ``` -------------------------------- ### Application State with Broadcast Sender in Rust Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Defines the application state using a RwLock to manage a HashMap of topics, where each topic maps to a broadcast sender for distributing messages. This is the current implementation. ```rust struct AppState { // Current implementation topics: RwLock, broadcast::Sender>>, // Alternative approach with mpsc // topics: RwLock, Vec>>>, } ``` -------------------------------- ### Handle WebSocket Client Connections Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Manages incoming WebSocket connections from clients, multiplexing broadcast streams and handling client messages concurrently using tokio::select!. ```rust async fn on_websocket_client(state: Arc, fut: UpgradeFut) -> yawc::Result<()> { let mut ws = fut.await?; let mut streams = StreamMap::new(); loop { tokio::select! { Some((_, res)) = streams.next() => { match res { Ok(input) => { let _ = ws.send(FrameView::text(input)).await; } Err(_) => { // Stream error handling } } } maybe_frame = ws.next() => { // Handle client messages } } } } ``` -------------------------------- ### Update Frame Construction Methods Source: https://github.com/infinitefield/yawc/blob/master/UPGRADE_GUIDE.md Replace `FrameView` constructors with the corresponding `Frame` constructors. This includes methods for text, binary, ping, and close frames. ```rust // Before let frame = FrameView::text("Hello"); let frame = FrameView::binary(vec![1, 2, 3]); let frame = FrameView::ping(""); let frame = FrameView::close(CloseCode::Normal, b"Goodbye"); // After let frame = Frame::text("Hello"); let frame = Frame::binary(vec![1, 2, 3]); let frame = Frame::ping(""); let frame = Frame::close(CloseCode::Normal, b"Goodbye"); ``` -------------------------------- ### Yawc Architecture Layers Source: https://github.com/infinitefield/yawc/blob/master/README.md Illustrates the layered architecture of the yawc WebSocket library, from the Tokio Codec Layer up to the Application Layer. ```text ┌─────────────────────────────────────────────────────────────┐ │ Application Layer │ │ (Your WebSocket Application) │ └──────────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ WebSocket Layer │ │ • Decompression (permessage-deflate RFC 7692) │ │ • UTF-8 validation for text frames │ │ • Protocol control (Ping/Pong, Close) │ └──────────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ ReadHalf Layer │ │ • Fragment assembly (RFC 6455 fragmentation) │ │ • Fragment timeout management │ │ • Maximum message size enforcement │ └──────────────────────────┬──────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Tokio Codec Layer │ │ • Frame decoding from raw bytes │ │ • Frame encoding to raw bytes │ │ • Masking/unmasking │ │ • Header parsing (FIN, RSV, OpCode) │ └──────────────────────────┬──────────────────────────────────┘ │ Network (TCP/TLS) ``` -------------------------------- ### Add tokio-tungstenite Dependency Source: https://github.com/infinitefield/yawc/blob/master/MIGRATION.md Add the tokio-tungstenite crate to your Cargo.toml with TLS support. ```toml [dependencies] tokio-tungstenite = { version = "0.20", features = [ "native-tls", # or "rustls-tls-native-roots" ] } ``` -------------------------------- ### Configure WebSocket Fragmentation and Backpressure Source: https://github.com/infinitefield/yawc/blob/master/README.md Set maximum fragment size, backpressure boundary, and fragment timeout for handling large messages and controlling memory usage. Useful for streaming large data or preventing memory exhaustion. ```rust use yawc::{WebSocket, Options}; use std::time::Duration; let ws = WebSocket::connect("wss://example.com".parse()?) .with_options( Options::default() .with_max_fragment_size(64 * 1024) // Auto-fragment messages > 64 KiB .with_backpressure_boundary(128 * 1024) // Apply backpressure at 128 KiB .with_fragment_timeout(Duration::from_secs(30)) // Timeout incomplete fragments ) .await?; ``` -------------------------------- ### Topic Parsing Implementation Source: https://github.com/infinitefield/yawc/blob/master/examples/axum_proxy/README.md Implements the `TryFrom` trait for the Topic struct to parse topic strings. It handles different formats for orderbook and other subscriptions, preserving zero-copy string handling. ```rust impl<'a> TryFrom<&'a str> for Topic<'a> { type Error = (); fn try_from(s: &'a str) -> Result { let mut parts = s.split('.'); let name = parts.next().ok_or(())?; let second = parts.next().ok_or(())?; if name == "orderbook" { let levels = second.parse().map_err(|_| ())?; let symbol = parts.next().ok_or(())?; Ok(Topic { symbol: Cow::from(symbol), name: Cow::from(name), levels: Some(levels), }) } else { Ok(Topic { symbol: Cow::from(second), name: Cow::from(name), levels: None, }) } } } ```