Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gdvalle/webchannel
A simple HTTP API for publishing messages to WebSocket clients.
https://github.com/gdvalle/webchannel
Last synced: about 1 month ago
JSON representation
A simple HTTP API for publishing messages to WebSocket clients.
- Host: GitHub
- URL: https://github.com/gdvalle/webchannel
- Owner: gdvalle
- License: mit
- Created: 2021-06-26T18:39:37.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2021-06-26T22:42:50.000Z (over 3 years ago)
- Last Synced: 2024-11-07T19:49:34.502Z (3 months ago)
- Language: Rust
- Size: 32.2 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# webchannel
A simple HTTP API for publishing messages to WebSocket clients.
Basically an authed, unidirectional pipe connecting an HTTP publisher to a WebSocket subscriber, with no message persistence.
## What is this for?
Take a scenario where a server doing processing needs to notify a browser client when the task completes.
The client starts by requesting some work to be done, e.x. `POST /update-inventory`.
The server receives the request, and starts by using an API key to create a new `channel`.
Note the body is optional. If `channelId` is not provided a random URL-safe string will be generated.```bash
curl --request POST --header "x-api-key: secret" --data '{"channelId": "user:1"}' http://localhost:8080/webchannel/v1/channels
```It gets a response with a JWT:
```json
{
"channelId": "user:1",
"token":""
}
```The server then issues a task to do the work. Minimally, it could just include the `token`, and the processing server could parse the channel ID out of the JWT claims. E.g. in JS: `JSON.parse(atob(token.split(".")[1])).cid`. Note the token is only valid for the channel it was generated for.
Now that the task has started, the server replies to the browser with the `token`, and perhaps the `channelId`.
On the client, you can use the token to setup a subscriber:
```javascript
var ws = new WebSocket("ws://localhost:8080/webchannel/v1/channels/user:1?access_token=")
ws.onmessage = m => m.data.text().then(JSON.parse).then(console.log)
```Note `access_token` is passed as a URL parameter. That may not be so safe depending on your logging setup. The endpoint also accepts an `Authorization` header, but accepts the query param because the WebSocket browser API doesn't support that.
Back on the server, you can publish status messages:
```bash
curl --request POST --data '{"message": "Inventory update starting...", "percent": 0}' --header "Authorization: Bearer " http://localhost:8080/webchannel/v1/channels/user:1curl --request POST --data '{"message": "Added 14 banana breads", "percent": 50}' --header "Authorization: Bearer " http://localhost:8080/webchannel/v1/channels/user:1
curl --request POST --data '{"message": "Inventory updated!", "percent": 100}' --header "Authorization: Bearer " http://localhost:8080/webchannel/v1/channels/user:1
```## What kind of data can I send over this thing?
_Any_ binary data is valid. The example here uses JSON, but this is essentially a raw pipe between an HTTP server, a Redis Pub/Sub channel, and a WebSocket client, and each simply relay that data without modification.
## Configuration
For options available, it's probably easiest to just look at `struct Settings` in [settings.rs](src/settings.rs).
Configuration can be managed through a TOML file or environment variables.
To specify a config file, use the CLI arg `--config-file` or `-c` with a path to a TOML file.
For environment vars, use the prefix `WC_`, and a double underscore to separate config areas from their keys. For example, to set `channel.ttl`, use `WC_CHANNEL__TTL=60`. This doesn't work for array types, so set those using a config file.
An example config file:
```toml
[server]
listen_address = "0.0.0.0:5000"
# Set CORS domains, or allow any.
cors_origins = ["https://example.com", "https://app.example.com"]
# cors_allow_any_origin = false[channel]
# The secret used for JWT signing.
secret_key = "change-please"
# API keys used for creating channels, or publishing messages.
api_keys = ["foo", "bar"]
# TTL, in seconds, of the auth tokens generated for clients.
ttl = 86400[metrics]
auth_enabled = true
auth_username = "chip"
auth_password = "munk"
```## Metrics, Logging
Prometheus metrics are available at `/metrics`. Basic auth can be configured if needed.
Log levels are managed through the `RUST_LOG` environment variable.
To modify the server's log level try `RUST_LOG=webchannel=trace` to see _all_ server logs.
Use `RUST_LOG=debug` for debug across all modules.
See [env_logger](https://docs.rs/env_logger/) for more detail.## Disclaimer
This is basically a weekend project that seemed simple enough to implement in Rust for fun. I decided to write a README to perhaps convince myself this has some kind of use. I can't guarantee any support, help, or maintenance, but it is MIT licensed, so feel free to do whatever with it.