diff --git a/package-lock.json b/package-lock.json index 298accf404..27cd1a5b76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,6 @@ "node-stream-zip": "^1.15.0", "react": "^18.1.0", "react-dom": "^18.1.0", - "socksv5": "0.0.6", "ssim.js": "^3.5.0", "typescript": "^5.5.3", "vite": "^5.0.13", @@ -2562,12 +2561,6 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "node_modules/async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==", - "dev": true - }, "node_modules/asynciterator.prototype": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", @@ -2826,41 +2819,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/cli": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", - "integrity": "sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==", - "dev": true, - "dependencies": { - "glob": ">= 3.1.4" - }, - "engines": { - "node": ">=0.2.5" - } - }, - "node_modules/cliff": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", - "integrity": "sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ==", - "dev": true, - "dependencies": { - "colors": "~1.0.3", - "eyes": "~0.1.8", - "winston": "0.8.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/cliff/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -3117,15 +3075,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, - "node_modules/cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -3967,15 +3916,6 @@ "@types/yauzl": "^2.9.1" } }, - "node_modules/eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", - "dev": true, - "engines": { - "node": "> 0.1.90" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5012,12 +4952,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -5843,15 +5777,6 @@ "node": ">=6" } }, - "node_modules/pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/playwright": { "resolved": "packages/playwright", "link": true @@ -6511,47 +6436,6 @@ "node": "*" } }, - "node_modules/socksv5": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/socksv5/-/socksv5-0.0.6.tgz", - "integrity": "sha512-tQpQ0MdNQAsQBDhCXy3OvGGJikh9QOl3PkbwT4POJiQCm/fK4z9AxKQQRG8WLeF6talphnPrSWiZRpTl42rApg==", - "bundleDependencies": [ - "ipv6" - ], - "dev": true, - "dependencies": { - "ipv6": "*" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/socksv5/node_modules/ipv6": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "cli": "0.4.x", - "cliff": "0.1.x", - "sprintf": "0.1.x" - }, - "bin": { - "ipv6": "bin/ipv6.js", - "ipv6grep": "bin/ipv6grep.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/socksv5/node_modules/ipv6/node_modules/sprintf": { - "version": "0.1.3", - "dev": true, - "inBundle": true, - "engines": { - "node": ">=0.2.4" - } - }, "node_modules/solid-js": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.11.tgz", @@ -6670,15 +6554,6 @@ "integrity": "sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==", "dev": true }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -7720,33 +7595,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/winston": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", - "integrity": "sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g==", - "dev": true, - "dependencies": { - "async": "0.2.x", - "colors": "0.6.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "pkginfo": "0.3.x", - "stack-trace": "0.0.x" - }, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/winston/node_modules/colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 179497da7b..9db59004be 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,6 @@ "node-stream-zip": "^1.15.0", "react": "^18.1.0", "react-dom": "^18.1.0", - "socksv5": "0.0.6", "ssim.js": "^3.5.0", "typescript": "^5.5.3", "vite": "^5.0.13", diff --git a/packages/playwright-core/src/common/socksProxy.ts b/packages/playwright-core/src/common/socksProxy.ts index ebdf7298f2..6566262d64 100644 --- a/packages/playwright-core/src/common/socksProxy.ts +++ b/packages/playwright-core/src/common/socksProxy.ts @@ -431,9 +431,9 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient { return this._port; } - async listen(port: number): Promise { + async listen(port: number, hostname?: string): Promise { return new Promise(f => { - this._server.listen(port, () => { + this._server.listen(port, hostname, () => { const port = (this._server.address() as AddressInfo).port; this._port = port; f(port); diff --git a/tests/config/serverFixtures.ts b/tests/config/serverFixtures.ts index 135458d0de..009158545c 100644 --- a/tests/config/serverFixtures.ts +++ b/tests/config/serverFixtures.ts @@ -16,13 +16,15 @@ import type { Fixtures } from '@playwright/test'; import path from 'path'; -import socks from 'socksv5'; import { TestServer } from './testserver'; import { TestProxy } from './proxy'; +import type { SocksSocketRequestedPayload } from '../../packages/playwright-core/src/common/socksProxy'; + +import { SocksProxy } from '../../packages/playwright-core/lib/common/socksProxy'; export type ServerWorkerOptions = { loopback?: string; - __servers: ServerFixtures & { socksServer: socks.SocksServer }; + __servers: ServerFixtures; }; export type ServerFixtures = { @@ -47,30 +49,9 @@ export const serverFixtures: Fixtures = { const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort, loopback); httpsServer.enableHTTPCache(cachedPath); - const socksServer = socks.createServer((info, accept, deny) => { - const socket = accept(true); - if (!socket) - return; - socket.on('data', data => { - if (!data.toString().includes('\r\n\r\n')) - return; - const body = 'Served by the SOCKS proxy'; - socket.end([ - 'HTTP/1.1 200 OK', - 'Connection: close', - 'Content-Type: text/html', - 'Content-Length: ' + Buffer.byteLength(body), - '', - body - ].join('\r\n')); - }); - // Catch and ignore ECONNRESET errors. - socket.on('error', () => {}); - setTimeout(() => socket.end(), 1000); - }); + const socksServer = new MockSocksServer(); const socksPort = port + 2; - socksServer.listen(socksPort, 'localhost'); - socksServer.useAuth(socks.auth.None()); + await socksServer.listen(socksPort, 'localhost'); const proxyPort = port + 3; const proxyServer = await TestProxy.create(proxyPort); @@ -81,7 +62,6 @@ export const serverFixtures: Fixtures = { httpsServer, socksPort, proxyServer, - socksServer, }); await Promise.all([ @@ -116,3 +96,37 @@ export const serverFixtures: Fixtures = { }, }; +export class MockSocksServer { + private _socksProxy: SocksProxy; + + constructor() { + this._socksProxy = new SocksProxy(); + this._socksProxy.setPattern('*'); + this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => { + this._socksProxy.socketConnected({ + uid: payload.uid, + host: '127.0.0.1', + port: 0, + }); + const body = 'Served by the SOCKS proxy'; + const data = Buffer.from([ + 'HTTP/1.1 200 OK', + 'Connection: close', + 'Content-Type: text/html', + 'Content-Length: ' + Buffer.byteLength(body), + '', + body + ].join('\r\n')); + this._socksProxy.sendSocketData({ uid: payload.uid, data }); + this._socksProxy.sendSocketEnd({ uid: payload.uid }); + }); + } + + async listen(port: number, hostname: string) { + await this._socksProxy.listen(port, hostname); + } + + async close() { + await this._socksProxy.close(); + } +} diff --git a/tests/index.d.ts b/tests/index.d.ts deleted file mode 100644 index 29a4157e21..0000000000 --- a/tests/index.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -declare module 'socksv5' { - import type net from 'net'; - - class Auth { } - - class SocksServer { - listen: net.Server['listen']; - useAuth(auth: Auth): void; - close: net.Server['close']; - } - - type Info = { - srcAddr: string; - srcPort: number; - dstAddr: string; - dstPort: number; - } - - function acceptHandler(intercept: true): net.Socket | undefined; - function acceptHandler(intercept: false): undefined; - export function createServer(cb: (info: Info, accept: typeof acceptHandler, deny: () => void) => void): SocksServer; - - export const auth: { - None: () => Auth - }; -} diff --git a/tests/library/proxy.spec.ts b/tests/library/proxy.spec.ts index 0425185cd5..344763bb1d 100644 --- a/tests/library/proxy.spec.ts +++ b/tests/library/proxy.spec.ts @@ -15,8 +15,9 @@ */ import { playwrightTest as it, expect } from '../config/browserTest'; -import socks from 'socksv5'; import net from 'net'; +import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../packages/playwright-core/src/common/socksProxy'; +import { SocksProxy } from '../../packages/playwright-core/lib/common/socksProxy'; it.skip(({ mode }) => mode.startsWith('service')); @@ -288,28 +289,36 @@ it('should use proxy with emulated user agent', async ({ browserType }) => { expect(requestText).toContain('MyUserAgent'); }); -async function setupSocksForwardingServer(port: number, forwardPort: number){ - const socksServer = socks.createServer((info, accept, deny) => { - if (!['127.0.0.1', 'fake-localhost-127-0-0-1.nip.io'].includes(info.dstAddr) || info.dstPort !== 1337) { - deny(); +async function setupSocksForwardingServer(port: number, forwardPort: number) { + const connections = new Map(); + const socksProxy = new SocksProxy(); + socksProxy.setPattern('*'); + socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => { + if (!['127.0.0.1', 'fake-localhost-127-0-0-1.nip.io'].includes(payload.host) || payload.port !== 1337) { + socksProxy.sendSocketError({ uid: payload.uid, error: 'ECONNREFUSED' }); return; } - const socket = accept(true); - if (socket) { - const dstSock = new net.Socket(); - socket.pipe(dstSock).pipe(socket); - socket.on('close', () => dstSock.end()); - socket.on('end', () => dstSock.end()); - dstSock.on('error', () => socket.end()); - dstSock.on('end', () => socket.end()); - dstSock.setKeepAlive(false); - dstSock.connect(forwardPort, '127.0.0.1'); - } + const target = new net.Socket(); + target.on('error', error => socksProxy.sendSocketError({ uid: payload.uid, error: error.toString() })); + target.on('end', () => socksProxy.sendSocketEnd({ uid: payload.uid })); + target.on('data', data => socksProxy.sendSocketData({ uid: payload.uid, data })); + target.setKeepAlive(false); + target.connect(forwardPort, '127.0.0.1'); + target.on('connect', () => { + connections.set(payload.uid, target); + socksProxy.socketConnected({ uid: payload.uid, host: target.localAddress, port: target.localPort }); + }); }); - await new Promise(resolve => socksServer.listen(port, 'localhost', resolve)); - socksServer.useAuth(socks.auth.None()); + socksProxy.addListener(SocksProxy.Events.SocksData, async (payload: SocksSocketDataPayload) => { + connections.get(payload.uid)?.write(payload.data); + }); + socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => { + connections.get(payload.uid)?.destroy(); + connections.delete(payload.uid); + }); + await socksProxy.listen(port, 'localhost'); return { - closeProxyServer: () => socksServer.close(), + closeProxyServer: () => socksProxy.close(), proxyServerAddr: `socks5://localhost:${port}`, }; } @@ -343,5 +352,5 @@ it('should use SOCKS proxy for websocket requests', async ({ browserName, platfo expect(value).toBe('incoming'); await browser.close(); - closeProxyServer(); + await closeProxyServer(); }); diff --git a/tests/tsconfig.json b/tests/tsconfig.json index adf10e87bd..1a1e3d7527 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -18,6 +18,6 @@ "@web/*": ["packages/web/src/*"], }, }, - "include": ["**/*.spec.js", "**/*.ts", "index.d.ts"], + "include": ["**/*.spec.js", "**/*.ts"], "exclude": ["components/", "installation/fixture-scripts/"] } diff --git a/utils/generate_third_party_notice.js b/utils/generate_third_party_notice.js index 71c250cfaf..27b5b4691d 100644 --- a/utils/generate_third_party_notice.js +++ b/utils/generate_third_party_notice.js @@ -49,8 +49,10 @@ This project incorporates components from the projects listed below. The origina const allPackages = {}; const projectDir = path.join(__dirname, '..', 'packages', project); const bundlesDir = path.join(projectDir, 'bundles'); - for (const bundle of fs.readdirSync(bundlesDir)) { - const dir = path.join(bundlesDir, bundle); + for (const bundle of fs.readdirSync(bundlesDir, { withFileTypes: true })) { + if (!bundle.isDirectory()) + continue; + const dir = path.join(bundlesDir, bundle.name); execSync('npm ci', { cwd: dir }); const packages = await checkDir(dir); for (const [key, value] of Object.entries(packages)) {