framework/README.md
2025-07-21 00:17:46 +02:00

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.