Jamsocket
Jamsocket is a lightweight framework for building services that are accessed through WebSocket connections.
Services can either be native Rust code that runs in the server process, or be compiled into WebAssembly modules and loaded dynamically.
Concepts
Services
A Jamsocket server hosts one or more services. Services determine how the server responds to various types of messages.
Clients
External connections into a Jamsocket server are called clients. Usually, clients have a 1:1 correspondence with users, so the term user is (informally) interchangable with client.
Rooms
When clients connect to a Jamsocket server, they connect to a particular room. Each room has a 1:1 correspondence with an instance of your service. This means that all service state is scoped to a room. Client identifiers are also scoped to a room, and when a service broadcasts a message it is delivered only to clients in that service instance's room.
You can think of rooms as being isolated and independent of each other, analogous to rooms in a chat service.
Usage
To create a Jamsocket service, implement the SimpleJamsocketService
trait. There's only one function that you must implement, the constructor new
.
Let's implement a simple shared counter. Any connected client will be able to increment or decrement it by sending increment
or decrement
messages (other messages will be ignored). Whenever the value is changed, we'll broadcast it to every connected client.
impl SimpleJamsocketService for SharedCounterServer {
fn new(_: &str,
_: &impl JamsocketContext) -> Self {
SharedCounterServer(0)
}
fn message(&mut self, _: ClientId,
message: &str,
ctx: &impl JamsocketContext) {
match message {
"increment" => self.0 += 1,
"decrement" => self.0 -= 1,
_ => (),
}
ctx.send_message(
MessageRecipient::Broadcast,
&format!("new value: {}", self.0));
}
}
To serve this service, we will compile it into a WebAssembly module. We import the #[jamsocket_wasm]
annotation macro and apply it to the existing SharedCounterServer
declaration.
use jamsocket_wasm::jamsocket_wasm;
#[jamsocket_wasm]
struct SharedCounterServer(i32);
Then, install the jamsocket
command-line tool and the wasm32-wasi
target, and run jamsocket dev
:
$ cargo install jamsocket-cli
$ rustup target add wasm32-wasi
$ jamsocket dev
jamsocket dev
will build your app and serve it on port :8080
. Then, open http://localhost:8080/status
in your browser -- if all went well, you should see the status message ok
. Open up developer tools in your browser and type:
let ws = new WebSocket('ws://localhost:8080/ws/1');
ws.onmessage = (c) => console.log(c.data);
This connects to your service, creating a new room with the id 1
if one doesn't exist (under default server settings, any string is a vaild room ID and connecting to a non-existant room will create it).
Now, you can increment the counter by sending the increment
message using the ws
handle:
ws.send('increment')
If everything is set up correctly, the result will be printed out:
new value: 1
If multiple clients are connected, each one will receive this message. Just like that, we have a mechanism for sharing some (very basic) application state between clients.
Using without WebAssembly
If you don't want to compile your service to WebAssembly (for example, if you want to use capabilities that are not exposed by WASI), you can use jamsocket-server
.
use jamsocket_server::*;
fn main() -> std::io::Result<()> {
serve::
()?;
Ok(())
}
Modules
Jamsocket has a modular architecture. If all you want to do is generate a Jamsocket service to be served with an existing Jamsocket WebAssembly server, the main crates you will interact with will probably be jamsocket-cli
, which provides a command-line tool, and jamsocket-wasm
, the main Cargo dependency for building services.
jamsocket
is the core, minimal implementation of the service interface.jamsocket-cli
is a command-line interface for interacting with WebAssembly-compiled Jamsocket services.jamsocket-server
provides Actix actors to facilitate serving Jamsocket services in a WebSocket server.jamsocket-wasm
provides a macro for generating WebAssembly modules from Jamsocket services.jamsocket-wasm-host
provides a way to import Jamsocket services from WebAssembly modules.
See Also
Aper is a state synchronization library which works with Jamsocket.