syndicate-nim/src/syndicate/relays.nim

577 lines
19 KiB
Nim
Raw Normal View History

# SPDX-FileCopyrightText: ☭ Emery Hemingway
2021-09-24 19:25:47 +00:00
# SPDX-License-Identifier: Unlicense
2024-03-01 14:02:27 +00:00
import std/[options, tables]
from std/os import getEnv, `/`
2024-03-04 18:20:29 +00:00
import pkg/sys/ioqueue
2022-03-19 00:09:19 +00:00
import preserves
import ../syndicate, /capabilities, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
2021-09-24 19:25:47 +00:00
2021-11-03 18:22:42 +00:00
when defined(traceSyndicate):
when defined(posix):
template trace(args: varargs[untyped]): untyped = stderr.writeLine(args)
else:
template trace(args: varargs[untyped]): untyped = echo(args)
2021-11-03 18:22:42 +00:00
else:
template trace(args: varargs[untyped]): untyped = discard
export `$`
export Stdio, Tcp, WebSocket, Unix
2021-09-24 19:25:47 +00:00
type
2023-12-31 17:15:06 +00:00
Assertion = Value
2024-03-04 18:20:29 +00:00
Event = protocol.Event
2024-01-14 10:09:49 +00:00
Handle = actors.Handle
2024-03-04 18:20:29 +00:00
Oid = sturdy.Oid
Turn = syndicate.Turn
WireRef = sturdy.WireRef
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
PacketWriter = proc (turn: var Turn; buf: seq[byte]) {.closure.}
RelaySetup = proc (turn: var Turn; relay: Relay) {.closure.}
2021-09-24 19:25:47 +00:00
2024-01-18 22:08:51 +00:00
Relay* = ref object
2021-09-24 19:25:47 +00:00
facet: Facet
inboundAssertions: Table[Handle,
tuple[localHandle: Handle, imported: seq[WireSymbol]]]
outboundAssertions: Table[Handle, seq[WireSymbol]]
exported: Membrane
imported: Membrane
nextLocalOid: Oid
2024-01-16 17:59:34 +00:00
wireBuf: BufferedDecoder
2024-01-18 22:08:51 +00:00
packetWriter: PacketWriter
peer: Cap
2021-09-24 19:25:47 +00:00
SyncPeerEntity = ref object of Entity
relay: Relay
2023-07-24 15:13:36 +00:00
peer: Cap
2021-09-24 19:25:47 +00:00
handleMap: Table[Handle, Handle]
e: WireSymbol
RelayEntity = ref object of Entity
2023-05-18 10:20:44 +00:00
## https://synit.org/book/protocol.html#relay-entities
2021-09-24 19:25:47 +00:00
label: string
relay: Relay
2023-07-24 15:13:36 +00:00
proc releaseCapOut(r: Relay; e: WireSymbol) =
2021-09-24 19:25:47 +00:00
r.exported.drop e
2024-03-08 15:40:07 +00:00
method publish(spe: SyncPeerEntity; t: var Turn; a: AssertionRef; h: Handle) =
spe.handleMap[h] = publish(t, spe.peer, a.value)
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
method retract(se: SyncPeerEntity; t: var Turn; h: Handle) =
2021-09-24 19:25:47 +00:00
var other: Handle
if se.handleMap.pop(h, other):
retract(t, other)
2024-03-08 15:40:07 +00:00
method message(se: SyncPeerEntity; t: var Turn; a: AssertionRef) =
2021-09-24 19:25:47 +00:00
if not se.e.isNil:
2023-07-24 15:13:36 +00:00
se.relay.releaseCapOut(se.e)
message(t, se.peer, a.value)
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
method sync(se: SyncPeerEntity; t: var Turn; peer: Cap) =
2021-09-24 19:25:47 +00:00
sync(t, se.peer, peer)
2023-07-24 15:13:36 +00:00
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
SyncPeerEntity(relay: r, peer: p)
2021-09-24 19:25:47 +00:00
2023-07-24 15:13:36 +00:00
proc rewriteCapOut(relay: Relay; cap: Cap; exported: var seq[WireSymbol]): WireRef =
if cap.target of RelayEntity and cap.target.RelayEntity.relay == relay and cap.attenuation.len == 0:
2024-01-06 12:31:17 +00:00
result = WireRef(orKind: WireRefKind.yours, yours: WireRefYours(oid: cap.target.oid))
2022-03-07 19:15:32 +00:00
else:
2023-07-24 15:13:36 +00:00
var ws = grab(relay.exported, cap)
2022-03-07 19:15:32 +00:00
if ws.isNil:
2023-07-24 15:13:36 +00:00
ws = newWireSymbol(relay.exported, relay.nextLocalOid, cap)
2022-03-16 15:34:29 +00:00
inc relay.nextLocalOid
2022-03-07 19:15:32 +00:00
exported.add ws
2024-01-06 12:31:17 +00:00
result = WireRef(
2022-03-16 15:34:29 +00:00
orKind: WireRefKind.mine,
mine: WireRefMine(oid: ws.oid))
2021-09-24 19:25:47 +00:00
2023-05-18 10:20:44 +00:00
proc rewriteOut(relay: Relay; v: Assertion):
2024-03-04 18:20:29 +00:00
tuple[rewritten: Value, exported: seq[WireSymbol]] =
2021-09-24 19:25:47 +00:00
var exported: seq[WireSymbol]
2024-01-07 22:11:59 +00:00
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
let o = pr.unembed(Cap); if o.isSome:
rewriteCapOut(relay, o.get, exported).toPreserves
else: pr
2022-03-16 15:34:29 +00:00
result.exported = exported
2023-05-18 10:20:44 +00:00
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
result = rewriteOut(relay, v)
relay.outboundAssertions[h] = result.exported
2021-09-24 19:25:47 +00:00
proc deregister(relay: Relay; h: Handle) =
var outbound: seq[WireSymbol]
if relay.outboundAssertions.pop(h, outbound):
2023-07-24 15:13:36 +00:00
for e in outbound: releaseCapOut(relay, e)
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
proc send(relay: Relay; turn: var Turn; rOid: protocol.Oid; m: Event) =
2024-03-04 18:20:29 +00:00
# TODO: don't send right away.
var pendingTurn: protocol.Turn
pendingTurn.add TurnEvent(oid: rOid, event: m)
2024-03-08 15:40:07 +00:00
relay.facet.run do (turn: var Turn):
2024-03-04 18:20:29 +00:00
var pkt = Packet(
orKind: PacketKind.Turn,
turn: pendingTurn)
trace "C: ", pkt
relay.packetWriter(turn, encode pkt)
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
proc send(re: RelayEntity; turn: var Turn; ev: Event) =
send(re.relay, turn, protocol.Oid re.oid, ev)
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
method publish(re: RelayEntity; t: var Turn; a: AssertionRef; h: Handle) =
re.send(t, Event(
2021-10-28 16:57:09 +00:00
orKind: EventKind.Assert,
2022-12-08 08:15:01 +00:00
`assert`: protocol.Assert(
2023-05-18 10:20:44 +00:00
assertion: re.relay.register(a.value, h).rewritten,
handle: h)))
2021-10-28 16:57:09 +00:00
2024-03-08 15:40:07 +00:00
method retract(re: RelayEntity; t: var Turn; h: Handle) =
2021-09-24 19:25:47 +00:00
re.relay.deregister h
re.send(t, Event(
2021-09-24 19:25:47 +00:00
orKind: EventKind.Retract,
retract: Retract(handle: h)))
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
method message(re: RelayEntity; turn: var Turn; msg: AssertionRef) =
2023-05-18 10:20:44 +00:00
var (value, exported) = rewriteOut(re.relay, msg.value)
assert(len(exported) == 0, "cannot send a reference in a message")
if len(exported) == 0:
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
method sync(re: RelayEntity; turn: var Turn; peer: Cap) =
2021-09-24 19:25:47 +00:00
var
2021-10-28 16:57:09 +00:00
peerEntity = newSyncPeerEntity(re.relay, peer)
2021-09-24 19:25:47 +00:00
exported: seq[WireSymbol]
2024-01-14 10:09:49 +00:00
wr = rewriteCapOut(re.relay, turn.newCap(peerEntity), exported)
2021-09-24 19:25:47 +00:00
peerEntity.e = exported[0]
2024-01-14 10:09:49 +00:00
var ev = Event(orKind: EventKind.Sync)
ev.sync.peer = wr.toPreserves.embed
re.send(turn, ev)
2021-09-24 19:25:47 +00:00
2021-10-28 16:57:09 +00:00
proc newRelayEntity(label: string; r: Relay; o: Oid): RelayEntity =
RelayEntity(label: label, relay: r, oid: o)
2021-10-28 16:57:09 +00:00
2021-09-24 19:25:47 +00:00
using
relay: Relay
facet: Facet
2023-07-24 15:13:36 +00:00
proc lookupLocal(relay; oid: Oid): Cap =
2022-03-07 19:15:32 +00:00
let sym = relay.exported.grab oid
2024-03-08 16:11:02 +00:00
if not sym.isNil:
result = sym.cap
2021-09-24 19:25:47 +00:00
2023-07-24 15:13:36 +00:00
proc rewriteCapIn(relay; facet; n: WireRef, imported: var seq[WireSymbol]): Cap =
2021-09-24 19:25:47 +00:00
case n.orKind
of WireRefKind.mine:
2022-03-07 19:15:32 +00:00
var e = relay.imported.grab(n.mine.oid)
2024-01-14 10:09:49 +00:00
if e.isNil:
e = newWireSymbol(
relay.imported,
n.mine.oid,
newCap(facet, newRelayEntity("rewriteCapIn", relay, n.mine.oid)),
)
2021-09-24 19:25:47 +00:00
imported.add e
2023-07-24 15:13:36 +00:00
result = e.cap
2021-09-24 19:25:47 +00:00
of WireRefKind.yours:
2024-03-08 16:11:02 +00:00
result = relay.lookupLocal(n.yours.oid)
if result.isNil:
result = newInertCap()
elif n.yours.attenuation.len > 0:
result = attenuate(result, n.yours.attenuation)
2021-09-24 19:25:47 +00:00
2022-12-08 08:15:01 +00:00
proc rewriteIn(relay; facet; v: Value):
tuple[rewritten: Assertion; imported: seq[WireSymbol]] =
var imported: seq[WireSymbol]
2024-01-06 12:31:17 +00:00
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
2024-01-07 22:11:59 +00:00
let wr = pr.preservesTo WireRef; if wr.isSome:
result = rewriteCapIn(relay, facet, wr.get, imported).embed
else:
result = pr
result.imported = imported
2021-09-24 19:25:47 +00:00
proc close(r: Relay) = discard
2024-03-08 15:40:07 +00:00
proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) =
2021-09-24 19:25:47 +00:00
case event.orKind
of EventKind.Assert:
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
2023-07-24 15:13:36 +00:00
relay.inboundAssertions[event.assert.handle] = (publish(turn, cap, a), imported,)
2021-09-24 19:25:47 +00:00
of EventKind.Retract:
let remoteHandle = event.retract.handle
var outbound: tuple[localHandle: Handle, imported: seq[WireSymbol]]
if relay.inboundAssertions.pop(remoteHandle, outbound):
for e in outbound.imported: relay.imported.drop e
turn.retract(outbound.localHandle)
of EventKind.Message:
let (a, imported) = rewriteIn(relay, turn.facet, event.message.body)
2021-09-24 19:25:47 +00:00
assert imported.len == 0, "Cannot receive transient reference"
2023-07-24 15:13:36 +00:00
turn.message(cap, a)
2021-09-24 19:25:47 +00:00
of EventKind.Sync:
2024-03-08 15:40:07 +00:00
turn.sync(cap) do (turn: var Turn):
2024-03-08 16:11:02 +00:00
var
(v, imported) = rewriteIn(relay, turn.facet, event.sync.peer)
peer = unembed(v, Cap)
if peer.isSome:
turn.message(get peer, true)
for e in imported: relay.imported.drop e
2021-09-24 19:25:47 +00:00
proc dispatch(relay: Relay; v: Value) =
2022-03-12 16:12:21 +00:00
trace "S: ", v
2024-03-08 15:40:07 +00:00
run(relay.facet) do (t: var Turn):
2021-09-24 19:25:47 +00:00
var pkt: Packet
2023-12-31 17:15:06 +00:00
if pkt.fromPreserves(v):
2021-09-24 19:25:47 +00:00
case pkt.orKind
of PacketKind.Turn:
2023-05-18 10:20:44 +00:00
# https://synit.org/book/protocol.html#turn-packets
2021-09-24 19:25:47 +00:00
for te in pkt.turn:
2023-05-18 10:20:44 +00:00
let r = lookupLocal(relay, te.oid.Oid)
2024-03-08 16:11:02 +00:00
if not r.isNil:
2023-05-18 10:20:44 +00:00
dispatch(relay, t, r, te.event)
2021-09-24 19:25:47 +00:00
of PacketKind.Error:
2023-05-18 10:20:44 +00:00
# https://synit.org/book/protocol.html#error-packets
2022-12-08 08:15:01 +00:00
when defined(posix):
stderr.writeLine("Error from server: ", pkt.error.message, " (detail: ", pkt.error.detail, ")")
2021-09-24 19:25:47 +00:00
close relay
2022-03-14 15:09:29 +00:00
of PacketKind.Extension:
2023-05-18 10:20:44 +00:00
# https://synit.org/book/protocol.html#extension-packets
2022-03-14 15:09:29 +00:00
discard
else:
2022-12-08 08:15:01 +00:00
when defined(posix):
stderr.writeLine("discarding undecoded packet ", v)
2021-09-24 19:25:47 +00:00
2024-03-04 18:20:29 +00:00
proc recv(relay: Relay; buf: openarray[byte]; slice: Slice[int]) =
feed(relay.wireBuf, buf, slice)
2024-01-16 17:59:34 +00:00
var pr = decode(relay.wireBuf)
2024-01-18 22:08:51 +00:00
if pr.isSome: dispatch(relay, pr.get)
2024-01-16 17:59:34 +00:00
2021-09-24 19:25:47 +00:00
type
RelayOptions* = object of RootObj
2024-01-18 22:08:51 +00:00
packetWriter*: PacketWriter
RelayActorOptions* = object of RelayOptions
initialOid*: Option[Oid]
2023-07-24 15:13:36 +00:00
initialCap*: Cap
nextLocalOid*: Option[Oid]
2021-09-24 19:25:47 +00:00
2024-03-08 15:40:07 +00:00
proc spawnRelay(name: string; turn: var Turn; opts: RelayActorOptions; setup: RelaySetup) =
2024-03-19 15:37:25 +00:00
linkActor(turn, name) do (turn: var Turn):
2024-03-12 12:32:50 +00:00
turn.preventInertCheck()
2024-01-18 22:08:51 +00:00
let relay = Relay(
facet: turn.facet,
packetWriter: opts.packetWriter,
wireBuf: newBufferedDecoder(0),
)
2023-07-24 15:13:36 +00:00
if not opts.initialCap.isNil:
2021-09-24 19:25:47 +00:00
var exported: seq[WireSymbol]
2023-07-24 15:13:36 +00:00
discard rewriteCapOut(relay, opts.initialCap, exported)
2021-09-24 19:25:47 +00:00
opts.nextLocalOid.map do (oid: Oid):
relay.nextLocalOid =
if oid == 0.Oid: 1.Oid
else: oid
2024-01-18 22:08:51 +00:00
assert opts.initialOid.isSome
if opts.initialOid.isSome:
var
imported: seq[WireSymbol]
wr = WireRef(
orKind: WireRefKind.mine,
mine: WireRefMine(oid: opts.initialOid.get))
2024-01-18 22:08:51 +00:00
relay.peer = rewriteCapIn(relay, turn.facet, wr, imported)
assert not relay.peer.isNil
setup(turn, relay)
2021-09-24 19:25:47 +00:00
2024-01-18 22:08:51 +00:00
proc rejected(detail: Value): Resolved =
result = Resolved(orKind: ResolvedKind.Rejected)
result.rejected.detail = detail
2021-09-24 19:25:47 +00:00
2024-01-18 22:08:51 +00:00
proc accepted(cap: Cap): Resolved =
result = Resolved(orKind: ResolvedKind.accepted)
result.accepted.responderSession = cap
2021-09-24 19:25:47 +00:00
when defined(posix):
2024-01-18 22:08:51 +00:00
2024-03-04 18:20:29 +00:00
import std/[oserrors, posix]
import pkg/sys/[files, handles, sockets]
export transportAddress.Unix
2023-05-18 10:20:44 +00:00
2024-03-08 12:01:20 +00:00
type StdioEntity = ref object of Entity
2024-03-04 18:20:29 +00:00
relay: Relay
2024-01-18 22:08:51 +00:00
stdin: AsyncFile
2024-03-08 12:01:20 +00:00
alive: bool
2024-01-18 22:08:51 +00:00
2024-03-08 15:40:07 +00:00
method message(entity: StdioEntity; turn: var Turn; ass: AssertionRef) =
2024-01-18 22:08:51 +00:00
if ass.value.preservesTo(ForceDisconnect).isSome:
2024-03-08 12:01:20 +00:00
entity.alive = false
2024-03-04 18:20:29 +00:00
2024-03-08 12:01:20 +00:00
proc loop(entity: StdioEntity) {.asyncio.} =
let buf = new seq[byte]
entity.alive = true
while entity.alive:
buf[].setLen(0x1000)
let n = read(entity.stdin, buf)
2024-03-04 18:20:29 +00:00
if n == 0:
2024-03-08 12:01:20 +00:00
stopActor(entity.facet)
2024-03-04 18:20:29 +00:00
else:
2024-03-08 12:01:20 +00:00
entity.relay.recv(buf[], 0..<n)
2024-01-18 22:08:51 +00:00
2024-03-08 15:40:07 +00:00
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Stdio) =
2024-01-18 22:08:51 +00:00
## Connect to an external dataspace over stdio.
let localDataspace = newDataspace(turn)
2024-03-08 15:40:07 +00:00
proc stdoutWriter(turn: var Turn; buf: seq[byte]) =
2024-01-18 22:08:51 +00:00
## Blocking write to stdout.
let n = writeBytes(stdout, buf, 0, buf.len)
flushFile(stdout)
if n != buf.len:
stopActor(turn)
var opts = RelayActorOptions(
packetWriter: stdoutWriter,
initialCap: localDataspace,
2024-01-18 22:08:51 +00:00
initialOid: 0.Oid.some,
)
2024-03-08 15:40:07 +00:00
spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
2024-01-18 22:08:51 +00:00
let
facet = turn.facet
2024-03-04 18:20:29 +00:00
fd = stdin.getOsFileHandle()
flags = fcntl(fd.cint, F_GETFL, 0)
if flags < 0: raiseOSError(osLastError())
if fcntl(fd.cint, F_SETFL, flags or O_NONBLOCK) < 0:
raiseOSError(osLastError())
2024-03-08 12:01:20 +00:00
let entity = StdioEntity(
facet: turn.facet, relay: relay, stdin: newAsyncFile(FD fd))
2024-03-08 15:40:07 +00:00
onStop(entity.facet) do (turn: var Turn):
2024-03-08 12:01:20 +00:00
entity.alive = false
2024-03-14 13:14:51 +00:00
close(entity.stdin)
# Close stdin to remove it from the ioqueue
2024-03-08 12:01:20 +00:00
discard trampoline:
whelp loop(entity)
2024-01-18 22:08:51 +00:00
publish(turn, ds, TransportConnection(
`addr`: ta.toPreserves,
2024-03-04 18:20:29 +00:00
control: newCap(entity, turn),
resolved: localDataspace.accepted,
2024-01-18 22:08:51 +00:00
))
2024-01-16 17:59:34 +00:00
2024-03-08 15:40:07 +00:00
proc connectStdio*(turn: var Turn; ds: Cap) =
2024-01-16 17:59:34 +00:00
## Connect to an external dataspace over stdin and stdout.
2024-01-18 22:08:51 +00:00
connectTransport(turn, ds, transportAddress.Stdio())
2024-03-04 18:20:29 +00:00
type
TcpEntity = ref object of Entity
relay: Relay
sock: AsyncConn[sockets.Protocol.TCP]
alive: bool
2024-01-18 22:08:51 +00:00
2024-03-04 18:20:29 +00:00
UnixEntity = ref object of Entity
relay: Relay
sock: AsyncConn[sockets.Protocol.Unix]
alive: bool
2024-01-18 22:08:51 +00:00
2024-03-04 18:20:29 +00:00
SocketEntity = TcpEntity | UnixEntity
2024-01-18 22:08:51 +00:00
2024-03-08 15:40:07 +00:00
method message(entity: SocketEntity; turn: var Turn; ass: AssertionRef) =
2024-03-04 18:20:29 +00:00
if ass.value.preservesTo(ForceDisconnect).isSome:
2024-03-08 12:01:20 +00:00
entity.alive = false
2024-03-04 18:20:29 +00:00
type ShutdownEntity = ref object of Entity
2024-03-08 15:40:07 +00:00
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
2024-03-08 12:01:20 +00:00
stopActor(e.facet)
2024-01-18 22:08:51 +00:00
2024-03-04 18:20:29 +00:00
template bootSocketEntity() {.dirty.} =
2024-03-08 15:40:07 +00:00
proc setup(turn: var Turn) {.closure.} =
proc kill(turn: var Turn) =
2024-03-14 13:14:51 +00:00
if entity.alive:
entity.alive = false
close(entity.sock)
2024-03-08 12:01:20 +00:00
onStop(turn, kill)
2024-03-04 18:20:29 +00:00
publish(turn, ds, TransportConnection(
`addr`: ta.toPreserves,
control: newCap(entity, turn),
resolved: entity.relay.peer.accepted,
))
2024-03-08 12:01:20 +00:00
run(entity.relay.facet, setup)
let buf = new seq[byte]
entity.alive = true
while entity.alive:
buf[].setLen(0x1000)
let n = read(entity.sock, buf)
if n < 0: raiseOSError(osLastError())
elif n == 0:
stopActor(entity.facet)
else:
entity.relay.recv(buf[], 0..<n)
2024-03-14 13:14:51 +00:00
# the socket closes when the actor is stopped
2024-03-04 18:20:29 +00:00
proc boot(entity: TcpEntity; ta: transportAddress.Tcp; ds: Cap) {.asyncio.} =
entity.sock = connectTcpAsync(ta.host, Port ta.port)
bootSocketEntity()
proc boot(entity: UnixEntity; ta: transportAddress.Unix; ds: Cap) {.asyncio.} =
entity.sock = connectUnixAsync(ta.path)
bootSocketEntity()
template spawnSocketRelay() {.dirty.} =
2024-03-08 15:40:07 +00:00
proc writeConn(turn: var Turn; buf: seq[byte]) =
2024-03-04 18:20:29 +00:00
discard trampoline:
whelp write(entity.sock, buf)
2024-01-18 22:08:51 +00:00
var ops = RelayActorOptions(
2024-03-04 18:20:29 +00:00
packetWriter: writeConn,
2024-01-18 22:08:51 +00:00
initialOid: 0.Oid.some,
)
2024-03-08 15:40:07 +00:00
spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
2024-03-08 12:01:20 +00:00
entity.facet = turn.facet
2024-03-04 18:20:29 +00:00
entity.relay = relay
discard trampoline:
whelp boot(entity, ta, ds)
2024-03-08 15:40:07 +00:00
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Tcp) =
2024-03-04 18:20:29 +00:00
let entity = TcpEntity()
spawnSocketRelay()
2023-05-18 10:20:44 +00:00
2024-03-08 15:40:07 +00:00
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Unix) =
2024-03-04 18:20:29 +00:00
let entity = UnixEntity()
spawnSocketRelay()
2024-01-18 22:08:51 +00:00
2024-03-08 15:40:07 +00:00
proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) =
2024-01-18 22:08:51 +00:00
if stepOff < route.pathSteps.len:
let
step = route.pathSteps[stepOff]
rejectPat = ResolvedPathStep?:{
0: ?(origin.embed), 1: ?step, 2: ?:Rejected}
acceptPat = ResolvedPathStep?:{
0: ?(origin.embed), 1: ?step, 2: ?:ResolvedAccepted}
onPublish(turn, ds, rejectPat) do (detail: Value):
publish(turn, ds, ResolvePath(
route: route,
`addr`: route.transports[transOff],
resolved: detail.rejected,
))
during(turn, ds, acceptPat) do (next: Cap):
walk(turn, ds, next, route, transOff, stepOff.succ)
else:
publish(turn, ds, ResolvePath(
route: route,
`addr`: route.transports[transOff],
resolved: origin.accepted,
))
2024-03-08 15:40:07 +00:00
proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
2024-01-18 22:08:51 +00:00
let rejectPat = TransportConnection ?: {
0: ?route.transports[transOff],
2: ?:Rejected,
}
during(turn, ds, rejectPat) do (detail: Value):
publish(turn, ds, ResolvePath(
route: route,
`addr`: route.transports[transOff],
resolved: detail.rejected,
))
let acceptPat = TransportConnection?:{
0: ?route.transports[transOff],
2: ?:ResolvedAccepted,
}
onPublish(turn, ds, acceptPat) do (origin: Cap):
walk(turn, ds, origin, route, transOff, 0)
2024-03-08 15:40:07 +00:00
type StepCallback = proc (turn: var Turn; step: Value; origin, next: Cap) {.closure.}
2024-03-08 12:01:20 +00:00
2024-03-08 15:40:07 +00:00
proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallback) =
2024-03-08 12:01:20 +00:00
let stepPat = grabRecord(stepType, grab())
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
let step = toRecord(stepType, stepDetail.value)
2024-03-08 15:40:07 +00:00
proc duringCallback(turn: var Turn; ass: Value; h: Handle): TurnAction =
2024-03-08 12:01:20 +00:00
var res = ass.preservesTo Resolved
if res.isSome:
if res.get.orKind == ResolvedKind.accepted and
res.get.accepted.responderSession of Cap:
cb(turn, step, origin, res.get.accepted.responderSession.Cap)
else:
publish(turn, ds, ResolvedPathStep(
origin: origin, pathStep: step, resolved: res.get))
2024-03-08 15:40:07 +00:00
proc action(turn: var Turn) =
2024-03-08 12:01:20 +00:00
stop(turn)
result = action
publish(turn, origin, Resolve(
step: step, observer: newCap(turn, during(duringCallback))))
2024-03-08 15:40:07 +00:00
proc spawnRelays*(turn: var Turn; ds: Cap) =
2024-01-18 22:08:51 +00:00
## Spawn actors that manage routes and appeasing gatekeepers.
2024-03-08 12:01:20 +00:00
let transPat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
# Use a generic pattern and type matching
# in the during handler because it is easy.
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
during(turn, ds, stdioPat) do:
connectTransport(turn, ds, Stdio())
# TODO: tcp pattern
during(turn, ds, transPat) do (ta: Literal[transportAddress.Tcp]):
try: connectTransport(turn, ds, ta.value)
except CatchableError as e:
publish(turn, ds, TransportConnection(
`addr`: ta.toPreserve,
resolved: rejected(embed e),
))
# TODO: unix pattern
during(turn, ds, transPat) do (ta: Literal[transportAddress.Unix]):
try: connectTransport(turn, ds, ta.value)
except CatchableError as e:
publish(turn, ds, TransportConnection(
`addr`: ta.toPreserve,
resolved: rejected(embed e),
))
let resolvePat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
during(turn, ds, resolvePat) do (route: Literal[Route]):
for i, transAddr in route.value.transports:
connectRoute(turn, ds, route.value, i)
2024-01-18 22:08:51 +00:00
spawnStepResolver(turn, ds, "ref".toSymbol) do (
2024-03-08 15:40:07 +00:00
turn: var Turn, step: Value, origin: Cap, next: Cap):
publish(turn, ds, ResolvedPathStep(
origin: origin, pathStep: step, resolved: next.accepted))
2023-10-13 22:36:34 +00:00
2024-03-08 15:40:07 +00:00
type BootProc* = proc (turn: var Turn; ds: Cap) {.closure.}
2023-10-13 22:36:34 +00:00
2024-03-04 18:20:59 +00:00
const defaultRoute* = "<route [<stdio>]>"
2023-12-31 17:15:06 +00:00
proc envRoute*: Route =
2024-03-04 18:20:59 +00:00
## Get an route to a Syndicate capability from the calling environment.
## On UNIX this is the SYNDICATE_ROUTE environmental variable with a
## fallack to a defaultRoute_.
## See https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/raw/branch/main/schemas/gatekeeper.prs.
var text = getEnv("SYNDICATE_ROUTE", defaultRoute)
if text == "":
2023-12-31 17:15:06 +00:00
var tx = (getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace").toPreserves
result.transports = @[initRecord("unix", tx)]
2023-12-31 17:15:06 +00:00
result.pathSteps = @[capabilities.mint().toPreserves]
else:
2024-01-01 18:29:54 +00:00
var pr = parsePreserves(text)
2023-12-31 17:15:06 +00:00
if not result.fromPreserves(pr):
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr)
2024-03-08 15:40:07 +00:00
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
2024-03-04 18:20:59 +00:00
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
2024-01-18 22:08:51 +00:00
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
bootProc(turn, dst)
2024-03-08 15:40:07 +00:00
proc resolveEnvironment*(turn: var Turn; bootProc: BootProc) =
2024-03-04 18:20:59 +00:00
## Resolve a capability from the calling environment
## and call `bootProc`. See envRoute_.
2024-03-15 10:21:02 +00:00
var resolved = false
2024-03-04 18:20:59 +00:00
let
ds = newDataspace(turn)
pat = ResolvePath ?: {0: ?envRoute(), 3: ?:ResolvedAccepted}
during(turn, ds, pat) do (dst: Cap):
2024-03-15 10:21:02 +00:00
if not resolved:
resolved = true
bootProc(turn, dst)
do:
resolved = false
2024-03-04 18:20:59 +00:00
spawnRelays(turn, ds)
2024-01-18 22:08:51 +00:00
# TODO: define a runActor that comes preloaded with relaying