Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: frontend should handle peer discovery #550

Merged
merged 7 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 13 additions & 23 deletions src/discovery/local-discovery.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { TypedEmitter } from 'tiny-typed-emitter'
import net from 'node:net'
import { randomBytes } from 'node:crypto'
import NoiseSecretStream from '@hyperswarm/secret-stream'
import { once } from 'node:events'
import { noop } from '../utils.js'
import { DnsSd } from './dns-sd.js'
import { isPrivate } from 'bogon'
import StartStopStateMachine from 'start-stop-state-machine'
import pTimeout from 'p-timeout'
Expand All @@ -25,10 +25,10 @@ export const ERR_DUPLICATE = 'Duplicate connection'
*/
export class LocalDiscovery extends TypedEmitter {
#identityKeypair
#name = randomBytes(8).toString('hex')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do you think it will it ever be useful to provide a custom name? can consider a constructor opt for this if so

Copy link
Contributor Author

@EvanHahn EvanHahn Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be useful in future but not at present, so I'm going to skip it for now.

#server
/** @type {Map<string, OpenedNoiseStream>} */
#noiseConnections = new Map()
#dnssd
#sm
#log
/** @type {(e: Error) => void} */
Expand All @@ -38,20 +38,12 @@ export class LocalDiscovery extends TypedEmitter {
/**
* @param {Object} opts
* @param {Keypair} opts.identityKeypair
* @param {DnsSd} [opts.dnssd] Optional DnsSd instance, used for testing
* @param {Logger} [opts.logger]
*/
constructor({ identityKeypair, dnssd, logger }) {
constructor({ identityKeypair, logger }) {
super()
this.#l = Logger.create('mdns', logger)
this.#log = this.#l.log.bind(this.#l)
this.#dnssd =
dnssd ||
new DnsSd({
name: keyToPublicId(identityKeypair.publicKey),
logger: this.#l,
})
this.#dnssd.on('up', this.#handleServiceUp.bind(this))
this.#sm = new StartStopStateMachine({
start: this.#start.bind(this),
stop: this.#stop.bind(this),
Expand All @@ -74,29 +66,29 @@ export class LocalDiscovery extends TypedEmitter {
return this.#server.address()
}

/** @returns {Promise<{ name: string, port: number }>} */
async start() {
return this.#sm.start()
await this.#sm.start()
return { name: this.#name, port: getAddress(this.#server).port }
}

async #start() {
// start browsing straight away
this.#dnssd.browse()

// Let OS choose port, listen on ip4, all interfaces
this.#server.listen(0, '0.0.0.0')
await once(this.#server, 'listening')
const addr = getAddress(this.#server)
this.#log('server listening on port ' + addr.port)
await this.#dnssd.advertise(addr.port)
this.#log('advertising service on port ' + addr.port)
}

/**
*
* @param {import('./dns-sd.js').MapeoService} service
* @returns
* @param {object} peer
* @param {string} peer.address
* @param {number} peer.port
* @param {string} peer.name
* @returns {void}
*/
#handleServiceUp({ address, port, name }) {
connectPeer({ address, port, name }) {
if (this.#name === name) return
this.#log('serviceUp', name.slice(0, 7), address, port)
EvanHahn marked this conversation as resolved.
Show resolved Hide resolved
if (this.#noiseConnections.has(name)) {
this.#log(`Already connected to ${name.slice(0, 7)}`)
Expand Down Expand Up @@ -271,7 +263,6 @@ export class LocalDiscovery extends TypedEmitter {
this.#log('stopping')
const { port } = getAddress(this.#server)
this.#server.close()
const destroyPromise = this.#dnssd.destroy()
const closePromise = once(this.#server, 'close')
await pTimeout(closePromise, {
milliseconds: force ? (timeout === 0 ? 1 : timeout) : Infinity,
Expand All @@ -282,7 +273,6 @@ export class LocalDiscovery extends TypedEmitter {
return pTimeout(closePromise, { milliseconds: 500 })
},
})
await destroyPromise
this.#log(`stopped for ${port}`)
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,15 +731,21 @@ export class MapeoManager extends TypedEmitter {
return this.#invite
}

async startLocalPeerDiscovery() {
/** @returns {Promise<{ name: string, port: number }>} */
startLocalPeerDiscoveryServer() {
return this.#localDiscovery.start()
}

/** @type {LocalDiscovery['stop']} */
async stopLocalPeerDiscovery(opts) {
stopLocalPeerDiscoveryServer(opts) {
return this.#localDiscovery.stop(opts)
}

/** @type {LocalDiscovery['connectPeer']} */
connectPeer(peer) {
this.#localDiscovery.connectPeer(peer)
}

/**
* @returns {Promise<PublicPeerInfo[]>}
*/
Expand Down