diff --git a/package.json b/package.json index 5086d50..ecb98c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@foxssake/noray", - "version": "1.3.0", + "version": "1.3.1", "description": "Online multiplayer orchestrator and potential game platform", "main": "src/noray.mjs", "bin": { diff --git a/src/connection/connection.commands.mjs b/src/connection/connection.commands.mjs index b0c43a3..9813b07 100644 --- a/src/connection/connection.commands.mjs +++ b/src/connection/connection.commands.mjs @@ -4,9 +4,6 @@ import { HostRepository } from '../hosts/host.repository.mjs' /* eslint-enable */ import assert from 'node:assert' import logger from '../logger.mjs' -import { udpRelayHandler } from '../relay/relay.mjs' -import { RelayEntry } from '../relay/relay.entry.mjs' -import { NetAddress } from '../relay/net.address.mjs' /** * @param {HostRepository} hostRepository @@ -51,7 +48,7 @@ export function handleConnectRelay (hostRepository) { * @param {ProtocolServer} server */ return function (server) { - server.on('connect-relay', async (data, socket) => { + server.on('connect-relay', (data, socket) => { const log = logger.child({ name: 'cmd:connect-relay' }) const oid = data @@ -62,13 +59,11 @@ export function handleConnectRelay (hostRepository) { 'Client attempting to connect to host' ) assert(host, 'Unknown host oid: ' + oid) + assert(host.relay, 'Host has no relay!') assert(client, 'Unknown client from address') + assert(client.relay, 'Client has no relay!') - log.debug('Ensuring relay for both parties') - host.relay = await getRelay(host.rinfo) - client.relay = await getRelay(client.rinfo) - - log.debug({ host: host.relay, client: client.relay }, 'Replying with relay') + log.debug({ relay: host.relay }, 'Replying with relay') server.send(socket, 'connect-relay', host.relay) server.send(host.socket, 'connect-relay', client.relay) log.debug( @@ -82,17 +77,3 @@ export function handleConnectRelay (hostRepository) { function stringifyAddress (address) { return `${address.address}:${address.port}` } - -async function getRelay (rinfo) { - // Attempt to create new relay on each connect - // If there's a relay already, UDPRelayHandler will return that - // If there's no relay, or it has expired, a new one will be created - const log = logger.child({ name: 'getRelay' }) - log.trace({ rinfo }, 'Ensuring relay for remote') - const relayEntry = await udpRelayHandler.createRelay( - new RelayEntry({ address: NetAddress.fromRinfo(rinfo) }) - ) - - log.trace({ relayEntry }, 'Created relay, returning with port %d', relayEntry.port) - return relayEntry.port -} diff --git a/src/relay/udp.remote.registrar.mjs b/src/relay/udp.remote.registrar.mjs index d606500..e472c52 100644 --- a/src/relay/udp.remote.registrar.mjs +++ b/src/relay/udp.remote.registrar.mjs @@ -1,12 +1,15 @@ /* eslint-disable */ +import { UDPRelayHandler } from './udp.relay.handler.mjs' import { HostRepository } from '../hosts/host.repository.mjs' /* eslint-enable */ import dgram from 'node:dgram' import assert from 'node:assert' -import logger from '../logger.mjs' +import { RelayEntry } from './relay.entry.mjs' +import { NetAddress } from './net.address.mjs' import { requireParam } from '../assertions.mjs' import * as prometheus from 'prom-client' import { metricsRegistry } from '../metrics/metrics.registry.mjs' +import logger from '../logger.mjs' const log = logger.child({ name: 'UDPRemoteRegistrar' }) @@ -46,14 +49,19 @@ export class UDPRemoteRegistrar { /** @type {HostRepository} */ #hostRepository + /** @type {UDPRelayHandler} */ + #udpRelayHandler + /** * Construct instance. * @param {object} options Options * @param {HostRepository} options.hostRepository Host repository + * @param {UDPRelayHandler} options.udpRelayHandler UDP relay handler * @param {dgram.Socket} [options.socket] Socket */ constructor (options) { this.#hostRepository = requireParam(options.hostRepository) + this.#udpRelayHandler = requireParam(options.udpRelayHandler) this.#socket = options.socket ?? dgram.createSocket('udp4') } @@ -97,14 +105,24 @@ export class UDPRemoteRegistrar { const host = this.#hostRepository.findByPid(pid) assert(host, 'Unknown host pid!') - if (host.rinfo) { - // Host has already remote info registered + if (host.relay) { + // Host has already a relay this.#socket.send('OK', rinfo.port, rinfo.address) registerRepatCounter.inc() return } host.rinfo = rinfo + const relay = await this.#udpRelayHandler.createRelay(new RelayEntry({ + address: NetAddress.fromRinfo(rinfo) + })) + host.relay = relay.port + + log.info( + { host, port: relay.port }, + 'Created relay for host' + ) + this.#socket.send('OK', rinfo.port, rinfo.address) registerSuccessCounter.inc() } catch (e) { diff --git a/test/spec/relay/udp.remote.registrar.test.mjs b/test/spec/relay/udp.remote.registrar.test.mjs index 10963ad..133b49f 100644 --- a/test/spec/relay/udp.remote.registrar.test.mjs +++ b/test/spec/relay/udp.remote.registrar.test.mjs @@ -2,9 +2,13 @@ import { describe, it, beforeEach, afterEach } from 'node:test' import assert from 'node:assert' import sinon from 'sinon' import dgram from 'node:dgram' +import { UDPRelayHandler } from '../../../src/relay/udp.relay.handler.mjs' import { UDPRemoteRegistrar } from '../../../src/relay/udp.remote.registrar.mjs' +import { NetAddress } from '../../../src/relay/net.address.mjs' +import { RelayEntry } from '../../../src/relay/relay.entry.mjs' import { HostRepository } from '../../../src/hosts/host.repository.mjs' import { HostEntity } from '../../../src/hosts/host.entity.mjs' +import { UDPSocketPool } from '../../../src/relay/udp.socket.pool.mjs' describe('UDPRemoteRegistrar', () => { /** @type {sinon.SinonFakeTimers} */ @@ -12,6 +16,8 @@ describe('UDPRemoteRegistrar', () => { /** @type {sinon.SinonStubbedInstance} */ let hostRepository + /** @type {sinon.SinonStubbedInstance} */ + let relayHandler /** @type {sinon.SinonStubbedInstance} */ let socket @@ -29,6 +35,7 @@ describe('UDPRemoteRegistrar', () => { clock = sinon.useFakeTimers() hostRepository = sinon.createStubInstance(HostRepository) + relayHandler = sinon.createStubInstance(UDPRelayHandler) socket = sinon.createStubInstance(dgram.Socket) hostRepository.findByPid.withArgs(host.pid).returns(host) @@ -39,7 +46,7 @@ describe('UDPRemoteRegistrar', () => { }) remoteRegistrar = new UDPRemoteRegistrar({ - hostRepository, socket + hostRepository, udpRelayHandler: relayHandler, socket }) }) @@ -51,15 +58,24 @@ describe('UDPRemoteRegistrar', () => { await remoteRegistrar.listen() const messageHandler = socket.on.lastCall.callback + relayHandler.createRelay.resolves({ + address: NetAddress.fromRinfo(rinfo), + port: 32768 + }) + // When await messageHandler(msg, rinfo) // Then + assert.deepEqual( + relayHandler.createRelay.lastCall?.args?.at(0), + new RelayEntry({ address: NetAddress.fromRinfo(rinfo) }) + ) assert.deepEqual( socket.send.lastCall?.args, ['OK', rinfo.port, rinfo.address] ) - assert.equal(host.rinfo, rinfo) + assert.equal(host.relay, 32768) }) it('should fail on unknown pid', async () => { @@ -76,6 +92,7 @@ describe('UDPRemoteRegistrar', () => { await messageHandler(msg, rinfo) // Then + assert(relayHandler.createRelay.notCalled, 'A relay was created!') assert.deepEqual( socket.send.lastCall?.args, ['Unknown host pid!', rinfo.port, rinfo.address] @@ -90,7 +107,7 @@ describe('UDPRemoteRegistrar', () => { await remoteRegistrar.listen() const messageHandler = socket.on.lastCall.callback - socket.send.onFirstCall().throws(new Error('Test')) + relayHandler.createRelay.throws('Error', 'Test') // When await messageHandler(msg, rinfo)