edgy-s
A minimalist WebSocket/HTTP bidirectional RPC framework for building complex microservice applications with elegant, function-based routing.
Features
- Minimalist API - Bind functions as routes with a single call
- Bidirectional RPC - Both client and server can initiate remote calls
- HTTP Support - Full HTTP request/response handling with streaming support
- Type Safe - Strict type constraints and full serde-based serialization
- Automatic Path Derivation - Routes are auto-generated from function names
- Zero Boilerplate - No macros, no complex configuration
- Feature Flags - Include only what you need (client/server)
- Auto Reconnection - WebSocket client with configurable retry logic
- Builder Pattern - Flexible client/service configuration
- Shared State - Built-in state management with
Arc<RwLock<S>>for concurrent access - Multiple Serialization Backends - Support for postcard (default), CBOR, and JSON
Installation
[]
= { = "1.3", = ["server", "client"] }
Quick Start
Server Example
use ;
use stream;
use ;
use ;
use ;
async
// WebSocket handler - bidirectional RPC
async
async
async
// HTTP handler - simple response
async
// HTTP handler - streaming response
async
Client Example
use ;
async
// WebSocket handler - receives calls from server
async
// HTTP request configurator
async
async
async
API Reference
Client Configuration
let client = builder?
.workers // Number of async worker threads
.max_retries // Max WebSocket reconnection attempts
.retry_interval_ms // Milliseconds between retries
.retry_interval // Or use Duration
.build?;
Server Configuration
let service = builder
.workers
.build
.await?;
HTTP Methods
// GET request
let : = .get.await?;
// POST request
let : = "body".post.await?;
// PUT request
let : = "body".put.await?;
// PATCH request
let : = "body".patch.await?;
// DELETE request
let : = .delete.await?;
// HEAD request
let accessor: HttpAccessor = .head.await?;
Accessor Methods
Server-side (WsAccessor / HttpAccessor)
| Method | Description |
|---|---|
get_addr() |
Get client socket address |
get_argument(name) |
Get URL query parameter (decoded) |
get_arguments(name) |
Get all values for a parameter |
get_all_arguments() |
Get all query parameters as HashMap |
get_header(name) |
Get request header |
get_headers() |
Get all request headers |
set_header(name, value) |
Set response header |
add_header(name, value) |
Append response header |
set_status(status) |
Set HTTP status code (HttpAccessor only) |
get_other_conns() |
Get other connections to same path (WsAccessor only) |
find_conn(target, predicate) |
Find a WebSocket connection to a specific path matching predicate (WsAccessor only) |
find_ws_conn(target, predicate) |
Find a WebSocket connection to a specific path matching predicate (HttpAccessor only) |
get_all_ws_conns(target) |
Get all WebSocket connections to a specific path (HttpAccessor only) |
Client-side (WsAccessor / RequestAccessor)
| Method | Description |
|---|---|
path() |
Get the request path |
status() |
Get HTTP status code (WsAccessor after connection) |
get_header(name) |
Get response header |
get_headers() |
Get all response headers |
set_header(name, value) |
Set request header (RequestAccessor) |
add_header(name, value) |
Append request header (RequestAccessor) |
set_argument(name, value) |
Set URL query parameter (RequestAccessor) |
Breaking Changes in 1.0
API Renames
| 0.x | 1.0 |
|---|---|
AsyncFun |
WsAsyncFn |
ServiceAccessor |
WsAccessor (server) |
ClientAccessor |
WsAccessor (client) |
ClientCaller / ServiceCaller |
WsCaller |
Constructor Changes
// 0.x
let client = new?;
let service = new.await?;
// 1.0
let client = builder?
.workers
.max_retries
.build?;
let service = builder
.workers
.build
.await?;
New HTTP Support
1.0 adds comprehensive HTTP support for both client and server:
- Server:
bind_as_response(),bind_by_path_as_response() - Client:
bind_as_request(),bind_by_path_as_request() - HTTP methods:
get(),post(),put(),patch(),delete(),head() - Streaming request/response bodies
Lifecycle Hooks
// 0.x - no lifecycle hooks
// 1.0 - chain lifecycle handlers
binding
.on_open
.await
.on_close
.await
Request ID Configuration
Choose the appropriate request ID width based on your concurrency needs:
| Feature | Type | Max Concurrent Requests |
|---|---|---|
req_id_u8 (default) |
u8 | 256 |
req_id_u16 |
u16 | 65,536 |
req_id_u32 |
u32 | ~4.2 billion |
req_id_u64 |
u64 | Virtually unlimited |
[]
= { = "1.3", = ["server", "client", "req_id_u32"] }
Serialization Backend Configuration
Choose one serialization backend based on your needs. The priority is: postcard > cbor4 > serde_json.
| Feature | Library | Format | Size | Use Case |
|---|---|---|---|---|
postcard (default) |
postcard | Custom binary | Compact | Maximum efficiency, embedded systems |
cbor4 |
cbor4ii | CBOR | Compact | Wide data type support, standard format |
serde_json |
serde_json | JSON | Larger | Human-readable, debugging, web APIs |
# Default: postcard (most compact)
[]
= { = "1.3", = ["server", "client"] }
# Use CBOR for standard binary format
[]
= { = "1.3", = ["server", "client", "cbor4"] }
# Use JSON for human-readable output
[]
= { = "1.3", = ["server", "client", "serde_json"] }
Note: If multiple serialization features are enabled, the highest priority one will be used (postcard > cbor4 > serde_json). At least one serialization backend must be enabled.
Shared State Management
EdgyService and EdgyClient support shared state for managing application data across connections.
Server with State
use ;
// Define your state type
async
// Access state in WebSocket handler
async
// on_open receives HttpAccessor (before WebSocket upgrade)
async
Client with State
use ;
async
async
State Access Methods
| Method | Description |
|---|---|
borrow().await |
Get read guard (Ref<S>) - multiple concurrent readers |
borrow_mut().await |
Get write guard (RefMut<S>) - exclusive access |
Cross-Protocol Communication
HTTP handlers can communicate with WebSocket connections using find_ws_conn():
use ;
// HTTP endpoint that broadcasts to WebSocket connections
async
async
Feature Flags
| Flag | Description |
|---|---|
client |
Enable client functionality (WebSocket + HTTP) |
server |
Enable server functionality (WebSocket + HTTP) |
req_id_u8 |
Use u8 for request IDs (default) |
req_id_u16 |
Use u16 for request IDs |
req_id_u32 |
Use u32 for request IDs |
req_id_u64 |
Use u64 for request IDs |
License
Licensed under Apache-2.0.