Skip to content

Commit

Permalink
fix: increase initial gateway search interval (#2936)
Browse files Browse the repository at this point in the history
During startup, search for gatweways at a more rapid pace, then
slow down the search once gateways have been discovered.
  • Loading branch information
achingbrain authored Feb 4, 2025
1 parent c1d0b7f commit 66c3ec5
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 16 deletions.
5 changes: 5 additions & 0 deletions packages/upnp-nat/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export const DEFAULT_INITIAL_GATEWAY_SEARCH_INTERVAL = 5_000
export const DEFAULT_INITIAL_GATEWAY_SEARCH_TIMEOUT = 5_000
export const DEFAULT_INITIAL_GATEWAY_SEARCH_MESSAGE_INTERVAL = 1_000

export const DEFAULT_GATEWAY_SEARCH_TIMEOUT = 60_000
export const DEFAULT_GATEWAY_SEARCH_INTERVAL = 300_000
export const DEFAULT_GATEWAY_SEARCH_MESSAGE_INTERVAL = 10_000
55 changes: 40 additions & 15 deletions packages/upnp-nat/src/gateway-finder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TypedEventEmitter, start, stop } from '@libp2p/interface'
import { repeatingTask } from '@libp2p/utils/repeating-task'
import { DEFAULT_GATEWAY_SEARCH_INTERVAL, DEFAULT_GATEWAY_SEARCH_TIMEOUT } from './constants.js'
import { DEFAULT_GATEWAY_SEARCH_INTERVAL, DEFAULT_GATEWAY_SEARCH_MESSAGE_INTERVAL, DEFAULT_GATEWAY_SEARCH_TIMEOUT, DEFAULT_INITIAL_GATEWAY_SEARCH_INTERVAL, DEFAULT_INITIAL_GATEWAY_SEARCH_MESSAGE_INTERVAL, DEFAULT_INITIAL_GATEWAY_SEARCH_TIMEOUT } from './constants.js'
import type { Gateway, UPnPNAT } from '@achingbrain/nat-port-mapper'
import type { ComponentLogger, Logger } from '@libp2p/interface'
import type { RepeatingTask } from '@libp2p/utils/repeating-task'
Expand All @@ -11,6 +11,12 @@ export interface GatewayFinderComponents {

export interface GatewayFinderInit {
portMappingClient: UPnPNAT
initialSearchInterval?: number
initialSearchTimeout?: number
initialSearchMessageInterval?: number
searchInterval?: number
searchTimeout?: number
searchMessageInterval?: number
}

export interface GatewayFinderEvents {
Expand All @@ -34,25 +40,44 @@ export class GatewayFinder extends TypedEventEmitter<GatewayFinderEvents> {

// every five minutes, search for network gateways for one minute
this.findGateways = repeatingTask(async (options) => {
for await (const gateway of this.portMappingClient.findGateways({
...options,
searchInterval: 10000
})) {
if (this.gateways.some(g => {
return g.id === gateway.id && g.family === gateway.family
try {
const searchMessageInterval = this.gateways.length > 0
? init.searchMessageInterval ?? DEFAULT_GATEWAY_SEARCH_MESSAGE_INTERVAL
: init.initialSearchMessageInterval ?? DEFAULT_INITIAL_GATEWAY_SEARCH_MESSAGE_INTERVAL

this.log('begin gateway search, sending M-SEARCH every %dms', searchMessageInterval)

for await (const gateway of this.portMappingClient.findGateways({
...options,
searchInterval: searchMessageInterval
})) {
// already seen this gateway
continue
if (this.gateways.some(g => {
return g.id === gateway.id && g.family === gateway.family
})) {
// already seen this gateway
continue
}

this.gateways.push(gateway)
this.safeDispatchEvent('gateway', {
detail: gateway
})

// we've found a gateway, wait for longer before searching again
const searchInterval = init.searchTimeout ?? DEFAULT_GATEWAY_SEARCH_INTERVAL
const searchTimeout = init.searchTimeout ?? DEFAULT_GATEWAY_SEARCH_TIMEOUT
this.log('switching gateway search to every %dms, timing out after %dms', searchInterval, searchTimeout)
this.findGateways.setInterval(searchInterval)
this.findGateways.setTimeout(searchTimeout)
}

this.gateways.push(gateway)
this.safeDispatchEvent('gateway', {
detail: gateway
})
this.log('gateway search finished, found %d gateways', this.gateways.length)
} catch (err) {
this.log.error('gateway search errored - %e', err)
}
}, DEFAULT_GATEWAY_SEARCH_INTERVAL, {
}, init.initialSearchInterval ?? DEFAULT_INITIAL_GATEWAY_SEARCH_INTERVAL, {
runImmediately: true,
timeout: DEFAULT_GATEWAY_SEARCH_TIMEOUT
timeout: init.initialSearchTimeout ?? DEFAULT_INITIAL_GATEWAY_SEARCH_TIMEOUT
})
}

Expand Down
68 changes: 68 additions & 0 deletions packages/upnp-nat/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,74 @@ export interface UPnPNATInit {
* @default false
*/
autoConfirmAddress?: boolean

/**
* How often to search for network gateways in ms.
*
* This interval is used before a gateway has been found on the network, after
* that it switches to `gatewaySearchInterval` which lowers the frequency of
* the search.
*
* @default 5_000
*/
initialGatewaySearchInterval?: number

/**
* How often to send the `M-SEARCH` SSDP message during a gateway search in
* ms.
*
* Some routers are flaky and may not respond to every query so decreasing
* this will increase the number of search messages sent before the timeout.
*
* This interval is used before a gateway has been found on the network, after
* that it switches to `gatewaySearchMessageInterval` which lowers the
* frequency of search messages sent.
*
* @default 1_000
*/
initialGatewaySearchMessageInterval?: number

/**
* How long to search for gateways for before giving up in ms.
*
* This timeout is used before a gateway has been found on the network, after
* that it switches to `gatewaySearchTimeout` which increases the timeout to
* give gateways more time to respond.
*
* @default 5_000
*/
initialGatewaySearchTimeout?: number

/**
* How often to search for network gateways in ms.
*
* This interval is used after a gateway has been found on the network.
*
* @default 300_000
*/
gatewaySearchInterval?: number

/**
* How often to send the `M-SEARCH` SSDP message during a gateway search in
* ms.
*
* Some routers are flaky and may not respond to every query so decreasing
* this will increase the number of search messages sent before the timeout.
*
* This interval is used after a gateway has been found on the network.
*
* @default 10_000
*/
gatewaySearchMessageInterval?: number

/**
* How long to search for gateways for before giving up in ms.
*
* This timeout is used after a gateway has been found on the network.
*
* @default 60_000
*/
gatewaySearchTimeout?: number
}

export interface UPnPNATComponents {
Expand Down
8 changes: 7 additions & 1 deletion packages/upnp-nat/src/upnp-nat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ export class UPnPNAT implements Startable, UPnPNATInterface {

// trigger update when we discovery gateways on the network
this.gatewayFinder = new GatewayFinder(components, {
portMappingClient: this.portMappingClient
portMappingClient: this.portMappingClient,
initialSearchInterval: init.initialGatewaySearchInterval,
initialSearchTimeout: init.initialGatewaySearchTimeout,
initialSearchMessageInterval: init.initialGatewaySearchMessageInterval,
searchInterval: init.gatewaySearchInterval,
searchTimeout: init.gatewaySearchTimeout,
searchMessageInterval: init.gatewaySearchMessageInterval
})

this.onGatewayDiscovered = this.onGatewayDiscovered.bind(this)
Expand Down

0 comments on commit 66c3ec5

Please sign in to comment.