MWSE/internal/services/datatransfer.go

111 lines
3.1 KiB
Go

package services
import (
"git.saqut.com/saqut/mwse/internal/protocol"
"git.saqut.com/saqut/mwse/internal/ws"
)
// handshakeResult returns the optional acknowledgement for a relay handler. When
// the caller did not ask for a handshake the relay is fire-and-forget (nil), so
// the generic dispatcher sends no reply.
func handshakeResult(m protocol.Message, success bool) any {
if !m.Truthy("handshake") {
return nil
}
if success {
return map[string]any{"type": "success"}
}
return map[string]any{"type": "fail"}
}
func registerDataTransfer(hub *ws.Hub) {
// pack/to: relay a data pack to another peer, honouring both peers' relay
// flags and the target's pairing policy. When the target does not require
// pairing, an implicit pairing is established so subsequent traffic flows.
hub.Register("pack/to", func(c *ws.Client, m protocol.Message) any {
if !c.PackReadable() {
return handshakeResult(m, false)
}
to := m.Str("to")
other, ok := hub.Client(to)
if !ok {
return handshakeResult(m, false)
}
if other.RequiredPair() {
if !other.HasPair(c.ID) {
return handshakeResult(m, false)
}
} else if !other.HasPair(c.ID) {
other.AddPair(c.ID)
c.AddPair(other.ID)
}
if !other.PackWriteable() {
return handshakeResult(m, false)
}
other.Signal("pack", map[string]any{"from": c.ID, "pack": m.Get("pack")})
return handshakeResult(m, true)
})
// request/to: relay a request to a peer. The reply travels back later via
// response/to, carrying the original request id, so this handler itself does
// not produce the answer.
hub.Register("request/to", func(c *ws.Client, m protocol.Message) any {
other, ok := hub.Client(m.Str("to"))
if !ok {
return nil
}
if other.RequiredPair() {
if !other.HasPair(c.ID) {
return nil
}
} else {
other.AddPair(c.ID)
c.AddPair(other.ID)
}
other.Signal("request", map[string]any{"from": c.ID, "pack": m.Get("pack")})
return nil
})
// response/to: deliver a peer's response back to the original requester. The
// frame uses the numeric request id in the signal slot so the requester's
// event pool resolves the pending promise.
hub.Register("response/to", func(c *ws.Client, m protocol.Message) any {
other, ok := hub.Client(m.Str("to"))
if !ok {
return nil
}
if other.RequiredPair() && !other.HasPair(c.ID) {
return nil
}
other.Send([]any{map[string]any{"from": c.ID, "pack": m.Get("pack")}, m.Get("id")})
return nil
})
// pack/room: relay a data pack to every writable member of a room the sender
// belongs to. "wom" (without me) excludes the sender.
hub.Register("pack/room", func(c *ws.Client, m protocol.Message) any {
if !c.PackReadable() {
return handshakeResult(m, false)
}
to := m.Str("to")
room, ok := hub.Room(to)
if !ok {
return handshakeResult(m, false)
}
if !c.InRoom(to) {
return handshakeResult(m, false)
}
except := ""
if m.Truthy("wom") {
except = c.ID
}
room.Broadcast(
"pack/room",
map[string]any{"from": to, "pack": m.Get("pack"), "sender": c.ID},
except,
(*ws.Client).PackWriteable,
)
return handshakeResult(m, true)
})
}