389 lines
12 KiB
Markdown
389 lines
12 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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()`, and `build()` methods
|
|
|
|
## Quick Start
|
|
|
|
### Add to Your Project
|
|
|
|
Add the framework to your `Cargo.toml`:
|
|
|
|
#### For Native Applications (with full crypto support)
|
|
```toml
|
|
[dependencies]
|
|
framework = { path = "path/to/framework", features = ["crypto"] }
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
```
|
|
|
|
#### For WASM Applications (wasm-opt compatible)
|
|
```toml
|
|
[dependencies]
|
|
framework = { path = "path/to/framework", features = ["wasm-compatible"] }
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
```
|
|
|
|
#### For Mixed Targets
|
|
```toml
|
|
[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)
|
|
|
|
```rust
|
|
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)
|
|
|
|
```rust
|
|
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)
|
|
|
|
```rust
|
|
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 builder
|
|
- `private_key(self, private_key: String) -> Self` - Set authentication private key
|
|
- `add_server_url(self, url: String) -> Self` - Add a server URL to connect to
|
|
- `build(self) -> WsManager` - Build the final manager
|
|
|
|
#### `WsManager`
|
|
|
|
The simplified connection manager that holds multiple self-managing WebSocket connections.
|
|
|
|
**Methods:**
|
|
- `builder() -> WsManagerBuilder` - Create a new builder
|
|
- `connect() -> Result<(), CircleWsClientError>` - Connect to all configured servers
|
|
- `execute_script(url: &str, script: String) -> Result<PlayResultClient, CircleWsClientError>` - Execute a Rhai script
|
|
- `execute_script_on_all(script: String) -> HashMap<String, Result<PlayResultClient, CircleWsClientError>>` - Execute script on all servers
|
|
- `disconnect(url: &str)` - Disconnect from a specific server
|
|
- `disconnect_all()` - Disconnect from all servers
|
|
- `get_connected_urls() -> Vec<String>` - Get list of connected URLs
|
|
- `is_connected(url: &str) -> bool` - Check if connected to a URL
|
|
- `connection_count() -> usize` - Get number of connected servers
|
|
- `get_connection_status(url: &str) -> String` - Get connection status for a URL
|
|
- `get_all_connection_statuses() -> HashMap<String, String>` - Get all connection statuses
|
|
- `get_server_urls() -> Vec<String>` - Get list of configured server URLs
|
|
|
|
#### Convenience Functions
|
|
|
|
- `ws_manager() -> WsManagerBuilder` - Create a new WsManager builder
|
|
|
|
### Key Simplifications
|
|
|
|
1. **No Complex Configuration Objects**: Simple builder pattern with direct methods
|
|
2. **Self-Managing Clients**: Each connection handles its own lifecycle automatically
|
|
3. **No External Keep-Alive Management**: Keep-alive logic is internal to each client
|
|
4. **Simplified Error Handling**: Uses `CircleWsClientError` directly from the underlying library
|
|
|
|
### Error Handling
|
|
|
|
The library uses `CircleWsClientError` from the underlying client library for error handling:
|
|
|
|
```rust
|
|
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
|
|
|
|
```bash
|
|
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
|
|
```toml
|
|
[dependencies]
|
|
framework = { features = ["crypto"] }
|
|
```
|
|
|
|
#### Conditional Compilation
|
|
```rust
|
|
#[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`](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:
|
|
|
|
```bash
|
|
cargo test
|
|
```
|
|
|
|
### Contributing
|
|
|
|
1. Fork the repository
|
|
2. Create a feature branch
|
|
3. Add tests for new functionality
|
|
4. Ensure all tests pass
|
|
5. 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. |