first commit
This commit is contained in:
389
README.md
Normal file
389
README.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# 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.
|
Reference in New Issue
Block a user