commit f93d36911ce49cf8d29ea466d6bb2532e5664b10 Author: Emery Hemingway Date: Wed Sep 13 16:19:00 2023 +0200 Initial commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..d324c24 --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +source_env .. +use nix diff --git a/README.md b/README.md new file mode 100644 index 0000000..68e10ba --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# websocket_actor + +A [syndicated actor](https://syndicate-lang.org/) for communicating JSON to websocket servers. + +The actor observes `{dataspace: #!any url: string }` assertions and connects to a websocket endpoint. During the lifetime of the connection a `` assertion is made. Messages recieved from the server are sent to the dataspace wrapped in `` records and messages observed as `` are sent to the server. + +Unfortunately this utility is only as useful and reasonable as the websocket server it is connected to. + +## Example: + +### Syndicate server configuration: +``` +> + + +let ?exported-dataspace = dataspace + ?cap> [ + $cap { dataspace: $exported-dataspace url: "ws://127.0.0.1:5225/" } +] + + $exported-dataspace #f> +``` + +### Shell +``` +# Use the "syndump" utility to log '' records from the background. +syndump '' & + +# Use the "msg" utilite to send '' records. +# The message command wraps its arguments in a record with a label set to argv[0]. +cp $(which msg) send +send '{ type: "command" body: { type: "body" text: "fuck off webdevs"} }' +``` diff --git a/Tuprules.tup b/Tuprules.tup new file mode 100644 index 0000000..acc5cb1 --- /dev/null +++ b/Tuprules.tup @@ -0,0 +1,3 @@ +include ../syndicate-nim/depends.tup +NIM_FLAGS += --path:$(TUP_CWD)/../syndicate-nim/src +NIM_FLAGS += --path:$(TUP_CWD)/../nimble/ws/src diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..19e4f60 --- /dev/null +++ b/shell.nix @@ -0,0 +1,5 @@ +let + syndicate = builtins.getFlake "syndicate"; + pkgs = + import { overlays = builtins.attrValues syndicate.overlays; }; +in pkgs.nim2Packages.syndicate_utils diff --git a/src/Tupfile b/src/Tupfile new file mode 100644 index 0000000..ac617a1 --- /dev/null +++ b/src/Tupfile @@ -0,0 +1,3 @@ +include_rules +: websocket_actor.nim | $(SYNDICATE_PROTOCOL) ../ |> !nim_bin |> {bin} +: {bin} |> !assert_built |> diff --git a/src/websocket_actor.nim b/src/websocket_actor.nim new file mode 100644 index 0000000..791c44a --- /dev/null +++ b/src/websocket_actor.nim @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: ☭ Emery Hemingway +# SPDX-License-Identifier: Unlicense + +import std/[asyncdispatch, json] +import preserves, preserves/jsonhooks +import syndicate +import ws + +type + Args* {.preservesDictionary.} = object + dataspace: Cap + url: string + + SendJson* {.preservesRecord: "send".} = object + data: JsonNode + RecvJson* {.preservesRecord: "recv".} = object + data: JsonNode + +runActor("websocket-json-actor") do (root: Cap; turn: var Turn): + connectStdio(root, turn) + during(turn, root, ?Args) do (ds: Cap, url: string): + let facet = turn.facet + var + ws: WebSocket + connectedHandle: Handle + newWebSocket(url).addCallback(turn) do (turn: var Turn; sock: WebSocket): + ws = sock + let connectedHandle = publish(turn, ds, initRecord("connected", url.toPreserve)) + var fut: Future[(Opcode, string)] + proc recvMessage() {.gcsafe.} = + fut = receivePacket ws + addCallback(fut, facet) do (turn: var Turn): + let (opcode, data) = read fut + case opcode + of Text: + message(turn, ds, + RecvJson(data: data.parseJson)) + of Binary: + message(turn, ds, + initRecord("recv", cast[seq[byte]](data).toPreserve)) + of Ping: + asyncCheck(turn, ws.send(data, Pong)) + of Pong, Cont: + discard + of Close: + retract(turn, connectedHandle) + stderr.writeLine "closed connection with ", url + stop(turn) + return + recvMessage() + recvMessage() + onMessage(turn, ds, ?SendJson) do (data: JsonNode): + asyncCheck(turn, ws.send($data, Text)) + do: + close(ws) diff --git a/websocket_actor.nimble b/websocket_actor.nimble new file mode 100644 index 0000000..3c3e9d0 --- /dev/null +++ b/websocket_actor.nimble @@ -0,0 +1,6 @@ +bin = @["websocket_actor"] +license = "Unlicense" +srcDir = "src" +version = "20230913" + +requires: "nim", "syndicate", ws