examples/website | ||
src | ||
.gitignore | ||
ARCHITECTURE.md | ||
Cargo.lock | ||
Cargo.toml | ||
README.md | ||
WASM_OPT_SOLUTION.md |
Framework WebSocket Connection Manager
A simplified WebSocket connection manager built on top of the robust circle_client_ws
library. This framework provides a clean builder pattern API for managing multiple self-managing WebSocket connections with authentication support and script execution capabilities.
Features
- 🔗 Multiple Self-Managing Connections: Each connection handles its own lifecycle automatically
- 🔐 secp256k1 Authentication: Built-in support for cryptographic authentication (native only)
- 📜 Rhai Script Execution: Execute Rhai scripts on connected servers via the
play
function - 🌐 Cross-Platform: Works in both WASM (browser) and native environments
- 🎯 Builder Pattern: Clean, fluent API for configuration
- ⚡ Async/Await: Modern async/await interface
- 🔄 Automatic Connection Management: Each client handles keep-alive and reconnection internally
- 🛠️ WASM-opt Compatible: Feature flags to avoid crypto-related wasm-opt issues
Simplified Architecture
graph TD
A[Framework Lib] --> B[WsManager]
B --> C[WsManagerBuilder]
B --> D[Connection Pool]
D --> E[Self-Managing CircleWsClient 1]
D --> F[Self-Managing CircleWsClient 2]
D --> G[Self-Managing CircleWsClient N]
E --> E1[Internal Keep-Alive]
E --> E2[Internal Reconnection]
E --> E3[Internal Auth]
F --> F1[Internal Keep-Alive]
F --> F2[Internal Reconnection]
F --> F3[Internal Auth]
G --> G1[Internal Keep-Alive]
G --> G2[Internal Reconnection]
G --> G3[Internal Auth]
H[Website Example] --> A
I[Other Applications] --> A
Key Architectural Changes
- Self-Managing Clients: Each
CircleWsClient
handles its own connection lifecycle - Simplified WsManager: Acts as a simple container and builder, not a complex orchestrator
- No External Keep-Alive: Keep-alive and reconnection logic moved into individual clients
- Builder Pattern: Clean API with
new()
,private_key()
,add_server_url()
, andbuild()
methods
Quick Start
Add to Your Project
Add the framework to your Cargo.toml
:
For Native Applications (with full crypto support)
[dependencies]
framework = { path = "path/to/framework", features = ["crypto"] }
serde = { version = "1.0", features = ["derive"] }
For WASM Applications (wasm-opt compatible)
[dependencies]
framework = { path = "path/to/framework", features = ["wasm-compatible"] }
serde = { version = "1.0", features = ["derive"] }
For Mixed Targets
[target.'cfg(target_arch = "wasm32")'.dependencies]
framework = { path = "path/to/framework", features = ["wasm-compatible"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
framework = { path = "path/to/framework", features = ["crypto"] }
[dependencies]
serde = { version = "1.0", features = ["derive"] }
Basic Usage (New Simplified API)
use framework::ws_manager;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a connection manager using the builder pattern
let manager = ws_manager()
.add_server_url("ws://localhost:8080".to_string())
.add_server_url("ws://localhost:8081".to_string())
.build();
// Connect to all configured servers
// Each client handles its own authentication, keep-alive, and reconnection
manager.connect().await?;
// Execute a Rhai script on a specific server
let script = r#"
let message = "Hello from WebSocket!";
let value = 42;
`{"message": "${message}", "value": ${value}}`
"#;
let result = manager.execute_script("ws://localhost:8080", script.to_string()).await?;
println!("Result: {:?}", result);
// Check connection status
println!("Connected URLs: {:?}", manager.get_connected_urls());
// Cleanup (optional - clients clean up automatically when dropped)
manager.disconnect_all().await;
Ok(())
}
With Authentication (Simplified)
use framework::ws_manager;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create manager with authentication using builder pattern
let manager = ws_manager()
.private_key("your_private_key_hex".to_string())
.add_server_url("wss://secure-server.com".to_string())
.build();
// Connect - authentication is handled automatically by each client
manager.connect().await?;
// Execute scripts on authenticated connections
let result = manager.execute_script("wss://secure-server.com", "your_script".to_string()).await?;
Ok(())
}
WASM/Yew Integration (Simplified)
use yew::prelude::*;
use framework::ws_manager;
#[function_component(WebSocketComponent)]
pub fn websocket_component() -> Html {
// Create manager with builder pattern
let manager = use_state(|| {
ws_manager()
.add_server_url("ws://localhost:8080".to_string())
.build()
});
let on_connect = {
let manager = manager.clone();
Callback::from(move |_| {
let manager = (*manager).clone();
wasm_bindgen_futures::spawn_local(async move {
// Simple connect - each client manages itself
if let Err(e) = manager.connect().await {
log::error!("Connection failed: {}", e);
} else {
log::info!("Connected successfully!");
// Clients automatically handle keep-alive and reconnection
}
});
})
};
let on_execute_script = {
let manager = manager.clone();
Callback::from(move |_| {
let manager = (*manager).clone();
wasm_bindgen_futures::spawn_local(async move {
let script = "\"Hello from WASM!\"".to_string();
match manager.execute_script("ws://localhost:8080", script).await {
Ok(result) => log::info!("Script result: {:?}", result),
Err(e) => log::error!("Script execution failed: {}", e),
}
});
})
};
html! {
<div>
<button onclick={on_connect}>{"Connect"}</button>
<button onclick={on_execute_script}>{"Execute Script"}</button>
// ... rest of your UI
</div>
}
}
API Reference
Core Types (Simplified API)
WsManagerBuilder
Builder for creating WebSocket connection managers with a fluent API.
Methods:
new() -> Self
- Create a new builderprivate_key(self, private_key: String) -> Self
- Set authentication private keyadd_server_url(self, url: String) -> Self
- Add a server URL to connect tobuild(self) -> WsManager
- Build the final manager
WsManager
The simplified connection manager that holds multiple self-managing WebSocket connections.
Methods:
builder() -> WsManagerBuilder
- Create a new builderconnect() -> Result<(), CircleWsClientError>
- Connect to all configured serversexecute_script(url: &str, script: String) -> Result<PlayResultClient, CircleWsClientError>
- Execute a Rhai scriptexecute_script_on_all(script: String) -> HashMap<String, Result<PlayResultClient, CircleWsClientError>>
- Execute script on all serversdisconnect(url: &str)
- Disconnect from a specific serverdisconnect_all()
- Disconnect from all serversget_connected_urls() -> Vec<String>
- Get list of connected URLsis_connected(url: &str) -> bool
- Check if connected to a URLconnection_count() -> usize
- Get number of connected serversget_connection_status(url: &str) -> String
- Get connection status for a URLget_all_connection_statuses() -> HashMap<String, String>
- Get all connection statusesget_server_urls() -> Vec<String>
- Get list of configured server URLs
Convenience Functions
ws_manager() -> WsManagerBuilder
- Create a new WsManager builder
Key Simplifications
- No Complex Configuration Objects: Simple builder pattern with direct methods
- Self-Managing Clients: Each connection handles its own lifecycle automatically
- No External Keep-Alive Management: Keep-alive logic is internal to each client
- Simplified Error Handling: Uses
CircleWsClientError
directly from the underlying library
Error Handling
The library uses CircleWsClientError
from the underlying client library for error handling:
match manager.connect().await {
Ok(_) => println!("Connected successfully to all configured servers"),
Err(CircleWsClientError::NotConnected) => println!("Failed to connect to any servers"),
Err(CircleWsClientError::Auth(auth_error)) => println!("Authentication error: {:?}", auth_error),
Err(e) => println!("Other error: {:?}", e),
}
// Execute script with error handling
match manager.execute_script("ws://localhost:8080", script).await {
Ok(result) => println!("Script result: {:?}", result),
Err(CircleWsClientError::NotConnected) => println!("Not connected to server"),
Err(e) => println!("Script execution error: {:?}", e),
}
Example Application
The examples/website
directory contains a complete Yew WASM application demonstrating the WebSocket connection manager:
- Interactive UI: Connect to multiple WebSocket servers
- Script Editor: Write and execute Rhai scripts
- Real-time Results: See script execution results in real-time
- Connection Management: Connect, disconnect, and monitor connection status
Running the Example
cd examples/website
## WASM-opt Compatibility
This framework solves the common issue where cryptographic dependencies cause wasm-opt parsing errors in WASM builds. The solution uses feature flags to conditionally enable crypto functionality.
### The Problem
When building WASM applications with aggressive optimizations, you might encounter:
[parse exception: invalid code after misc prefix: 17 (at 0:732852)] Fatal: error parsing wasm (try --debug for more info)
This is caused by cryptographic libraries (`secp256k1`, `sha3`) that are incompatible with wasm-opt's optimization passes.
### The Solution
Use feature flags to control crypto dependencies:
- **`crypto`**: Full secp256k1 authentication support (native applications)
- **`wasm-compatible`**: Basic WebSocket functionality without crypto (WASM applications)
### Usage Examples
#### WASM Application (Recommended)
```toml
[dependencies]
framework = { features = ["wasm-compatible"] }
Native Application with Authentication
[dependencies]
framework = { features = ["crypto"] }
Conditional Compilation
#[cfg(feature = "crypto")]
fn with_authentication() {
let auth = AuthConfig::new("private_key".to_string());
let manager = WsConnectionManager::<MyData>::with_auth(auth);
// ... authenticated operations
}
#[cfg(not(feature = "crypto"))]
fn without_authentication() {
let manager = WsConnectionManager::<MyData>::new();
// ... basic WebSocket operations
}
For detailed information about the solution, see WASM_OPT_SOLUTION.md
.
trunk serve
Then navigate to `http://localhost:8080/websocket` to see the demo.
## Dependencies
The framework builds on these excellent libraries:
- **[circle_client_ws](../circles/src/client_ws)**: Robust WebSocket client with authentication
- **[yew](https://yew.rs/)**: Modern Rust framework for web frontend (WASM only)
- **[tokio](https://tokio.rs/)**: Async runtime (native only)
- **[serde](https://serde.rs/)**: Serialization framework
## Development
### Building
```bash
# Check the library
cargo check
# Run tests
cargo test
# Build the example
cd examples/website
trunk build --release
Testing
The library includes comprehensive tests for all major functionality:
cargo test
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
This project is part of the larger framework and follows the same license terms.
Roadmap
- Connection pooling and load balancing
- Automatic reconnection with exponential backoff
- Metrics and monitoring integration
- Support for additional authentication methods
- WebSocket compression support
- Connection health checks and heartbeat
Support
For questions, issues, or contributions, please refer to the main project repository.