From 6e9ff1a69514deae01001593c6be30fd1b2191d7 Mon Sep 17 00:00:00 2001 From: Vittorio Palmisano Date: Wed, 15 May 2024 21:34:36 +0200 Subject: [PATCH] added debug port forwarder --- src/config.ts | 3 +-- src/session.ts | 21 ++++++++++++++++---- src/throttle.ts | 18 ++++++++--------- src/utils.ts | 53 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/config.ts b/src/config.ts index 6d25870..73d38d0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -525,8 +525,7 @@ will listen on the provided port + the start-session-id value.`, arg: 'debugging-port', }, debuggingAddress: { - doc: `The chrome debugging listen address. Valid only if \`debugging-port\` \ -is provided.`, + doc: `The chrome debugging listening address. If unset, the network default interface address will be used.`, format: String, nullable: true, default: '127.0.0.1', diff --git a/src/session.ts b/src/session.ts index a327400..28c4476 100644 --- a/src/session.ts +++ b/src/session.ts @@ -31,6 +31,7 @@ import { logger, PeerConnectionExternal, PeerConnectionExternalMethod, + portForwarder, resolveIP, resolvePackagePath, sha256, @@ -238,6 +239,7 @@ export class Session extends EventEmitter { private running = false private browser?: Browser private context?: BrowserContext + private stopPortForwarder?: () => void /** The numeric id assigned to the session. */ readonly id: number @@ -359,7 +361,7 @@ export class Session extends EventEmitter { this.windowHeight = windowHeight || 1080 this.deviceScaleFactor = deviceScaleFactor || 1 this.debuggingPort = debuggingPort || 0 - this.debuggingAddress = debuggingAddress || '127.0.0.1' + this.debuggingAddress = debuggingAddress || '' this.display = display /* this.audioRedForOpus = !!audioRedForOpus */ this.url = url @@ -536,7 +538,6 @@ export class Session extends EventEmitter { `--remote-debugging-port=${ this.debuggingPort ? this.debuggingPort + this.id : 0 }`, - `--remote-debugging-address=${this.debuggingAddress}`, ] // 'WebRTC-VP8ConferenceTemporalLayers/2', @@ -680,7 +681,7 @@ exec sg ${group} -c /tmp/webrtcperf-launcher-${mark}-browser`, try { // log.debug('defaultArgs:', puppeteer.defaultArgs()); - this.browser = (await puppeteer.launch({ + this.browser = await puppeteer.launch({ headless: this.display ? false : 'new', executablePath, handleSIGINT: false, @@ -698,7 +699,7 @@ exec sg ${group} -c /tmp/webrtcperf-launcher-${mark}-browser`, }, ignoreDefaultArgs, args, - })) as Browser + }) // const version = await this.browser.version(); // console.log(`[session ${this.id}] Using chrome version: ${version}`); } catch (err) { @@ -710,6 +711,14 @@ exec sg ${group} -c /tmp/webrtcperf-launcher-${mark}-browser`, } assert(this.browser, 'BrowserNotCreated') + + if (this.debuggingPort && this.debuggingAddress !== '127.0.0.1') { + this.stopPortForwarder = await portForwarder( + this.debuggingPort + this.id, + this.debuggingAddress, + ) + } + this.browser.once('disconnected', () => { log.warn('browser disconnected') return this.stop() @@ -1709,6 +1718,10 @@ window.SERVER_USE_HTTPS = ${this.serverUseHttps}; this.running = false log.info(`${this.id} stop`) + if (this.stopPortForwarder) { + this.stopPortForwarder() + } + if (this.browser) { // close the opened tabs log.debug(`${this.id} closing ${this.pages.size} pages`) diff --git a/src/throttle.ts b/src/throttle.ts index 02d1fbb..2e1cd7e 100644 --- a/src/throttle.ts +++ b/src/throttle.ts @@ -1,7 +1,12 @@ import JSON5 from 'json5' import os from 'os' -import { logger, runShellCommand, toPrecision } from './utils' +import { + getDefaultNetworkInterface, + logger, + runShellCommand, + toPrecision, +} from './utils' const log = logger('webrtcperf:throttle') @@ -35,13 +40,6 @@ const throttleCurrentValues = { >(), } -async function getDefaultInterface(): Promise { - const { stdout } = await runShellCommand( - `ip route | awk '/default/ {print $5; exit}' | tr -d ''`, - ) - return stdout.trim() -} - async function cleanup(): Promise { ruleTimeouts.forEach(timeoutId => clearTimeout(timeoutId)) ruleTimeouts.clear() @@ -49,7 +47,7 @@ async function cleanup(): Promise { throttleCurrentValues.down.clear() let device = throttleConfig?.length ? throttleConfig[0].device : '' if (!device) { - device = await getDefaultInterface() + device = await getDefaultNetworkInterface() } await runShellCommand(`\ sudo -n tc qdisc del dev ${device} root || true; @@ -253,7 +251,7 @@ async function start(): Promise { let device = throttleConfig[0].device if (!device) { - device = await getDefaultInterface() + device = await getDefaultNetworkInterface() } await runShellCommand( diff --git a/src/utils.ts b/src/utils.ts index 3cf4d89..72aaee0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,9 +6,10 @@ import FormData from 'form-data' import fs, { createWriteStream, WriteStream } from 'fs' import { Agent } from 'https' import * as ipaddrJs from 'ipaddr.js' +import net from 'net' import NodeCache from 'node-cache' import * as OSUtils from 'node-os-utils' -import os from 'os' +import os, { networkInterfaces } from 'os' import path, { dirname } from 'path' import pidtree from 'pidtree' import pidusage from 'pidusage' @@ -1008,3 +1009,53 @@ export function toPrecision(value: number, precision = 3): string { precision, ) } + +export async function getDefaultNetworkInterface(): Promise { + const { stdout } = await runShellCommand( + `ip route | awk '/default/ {print $5; exit}' | tr -d ''`, + ) + return stdout.trim() +} + +export async function portForwarder(port: number, listenInterface?: string) { + if (!listenInterface) { + listenInterface = await getDefaultNetworkInterface() + } + const controller = new AbortController() + Object.entries(networkInterfaces()).forEach(([iface, nets]) => { + if (listenInterface !== '0.0.0.0' && iface !== listenInterface) return + if (!nets) return + for (const n of nets) { + if (n.internal || n.address === '127.0.0.1' || n.family !== 'IPv4') { + continue + } + const msg = `portForwarder on ${iface} (${n.address}:${port})` + const server = net + .createServer(from => { + const to = net.createConnection({ host: '127.0.0.1', port }) + from.once('error', err => { + log.error(`${msg} error: ${(err as Error).stack}`) + to.destroy() + }) + to.once('error', err => { + log.error(`${msg} error: ${(err as Error).stack}`) + from.destroy() + }) + from.pipe(to) + to.pipe(from) + }) + .listen({ port, host: n.address, signal: controller.signal }) + server.on('listening', () => { + log.debug(`${msg} listening`) + }) + server.once('error', err => { + log.error(`${msg} error: ${(err as Error).stack}`) + }) + } + }) + + return () => { + log.debug(`portForwarder on port ${port} stop`) + controller.abort() + } +}