chore: use own socks5 server for tests (#31639)

This commit is contained in:
Max Schmitt 2024-07-11 14:12:48 +02:00 committed by GitHub
parent 2b77ed4d7a
commit 89eef55dc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 230 deletions

152
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -431,9 +431,9 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
return this._port;
}
async listen(port: number): Promise<number> {
async listen(port: number, hostname?: string): Promise<number> {
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);

View File

@ -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<ServerFixtures, ServerWorkerOptions> = {
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 = '<html><title>Served by the SOCKS proxy</title></html>';
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<ServerFixtures, ServerWorkerOptions> = {
httpsServer,
socksPort,
proxyServer,
socksServer,
});
await Promise.all([
@ -116,3 +96,37 @@ export const serverFixtures: Fixtures<ServerFixtures, ServerWorkerOptions> = {
},
};
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 = '<html><title>Served by the SOCKS proxy</title></html>';
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();
}
}

26
tests/index.d.ts vendored
View File

@ -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
};
}

View File

@ -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<string, net.Socket>();
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<void>(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();
});

View File

@ -18,6 +18,6 @@
"@web/*": ["packages/web/src/*"],
},
},
"include": ["**/*.spec.js", "**/*.ts", "index.d.ts"],
"include": ["**/*.spec.js", "**/*.ts"],
"exclude": ["components/", "installation/fixture-scripts/"]
}

View File

@ -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)) {