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()