punchd/examples/rendezvous_server/rendezvous_server.nim

101 lines
2.7 KiB
Nim

import asyncdispatch, asyncnet, os, strformat, strutils, tables
from net import IpAddress, Port, `$`
import ../../message
# TODO: we need to find out our own external IP and return it to clients who
# connect from our LAN
type
Register = object
peerId: string
ip: IpAddress
ports: array[3, Port]
GetInfo = object
peerId: string
NotifyPeer = object
sender: string
recipient: string
data: string
Client = object
sock: AsyncSocket
ip: IpAddress
ports: array[3, Port]
proc removeClient(clients: TableRef[string, Client], peerId: string) =
if peerId.len > 0: clients.del(peerId)
proc processClient(client: AsyncSocket, clients: TableRef[string, Client]) {.async.} =
let (address, port) = client.getPeerAddr
await client.send(&"notify-endpoint|{address}|{port.int}\n")
var id = ""
var peerId = ""
while true:
let line = await client.recvLine(maxLength = 400)
if line.len == 0:
removeClient(clients, peerId)
break
try:
let args = line.parseArgs(3)
id = args[1]
case args[0]:
of "register":
let req = parseMessage[Register](args[2])
peerId = req.peerId
clients[peerId] = Client(sock: client, ip: req.ip, ports: req.ports)
asyncCheck client.send(&"ok|{id}\n")
of "get-info":
let req = parseMessage[GetInfo](args[2])
let peer = clients[req.peerId]
let peerPorts = peer.ports.join(",")
asyncCheck client.send(&"ok|{id}|{peer.ip}|{peerPorts}\n")
of "notify-peer":
let req = parseMessage[NotifyPeer](args[2])
let recipient = clients[req.recipient]
asyncCheck recipient.sock.send(&"notify-peer|{req.sender}|{req.recipient}|{req.data}\n")
asyncCheck client.send(&"ok|{id}\n")
else:
client.close()
removeClient(clients, peerId)
break
except KeyError:
asyncCheck client.send(&"error|{id}|peer not registered\n")
except ValueError:
client.close
removeClient(clients, peerId)
break
proc serve(port: Port) {.async.} =
var clients = newTable[string, Client]()
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(port)
server.listen()
while true:
let client = await server.accept()
asyncCheck processClient(client, clients)
proc main() =
if paramCount() != 1:
echo(fmt"usage: {paramStr(0)} PORT")
quit(1)
try:
let portNumber = paramStr(1).parseUInt
if portNumber > uint16.high:
raise newException(ValueError, "port out of range")
let port = Port(portNumber)
asyncCheck serve(port)
runForever()
except ValueError as e:
echo e.msg
when isMainModule:
main()