From 152f6c6f1ca58009953523e24bfe709c189a6313 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 3 Sep 2021 17:52:22 -0700 Subject: [PATCH] fix: support connectOverCDP over https (#8703) --- src/server/chromium/chromium.ts | 4 +++- tests/browsertype-connect.spec.ts | 35 +++++++++++++++++++++++++++++++ tests/chromium/chromium.spec.ts | 33 +++++++++++++++++++++++++++++ utils/testserver/index.d.ts | 2 ++ utils/testserver/index.js | 4 ++++ 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/server/chromium/chromium.ts b/src/server/chromium/chromium.ts index f294856a89..73d2dd2e73 100644 --- a/src/server/chromium/chromium.ts +++ b/src/server/chromium/chromium.ts @@ -34,6 +34,7 @@ import { TimeoutSettings } from '../../utils/timeoutSettings'; import { helper } from '../helper'; import { CallMetadata } from '../instrumentation'; import http from 'http'; +import https from 'https'; import { registry } from '../../utils/registry'; const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-'); @@ -219,8 +220,9 @@ async function urlToWSEndpoint(endpointURL: string) { if (endpointURL.startsWith('ws')) return endpointURL; const httpURL = endpointURL.endsWith('/') ? `${endpointURL}json/version/` : `${endpointURL}/json/version/`; + const request = endpointURL.startsWith('https') ? https : http; const json = await new Promise((resolve, reject) => { - http.get(httpURL, resp => { + request.get(httpURL, resp => { if (resp.statusCode! < 200 || resp.statusCode! >= 400) { reject(new Error(`Unexpected status ${resp.statusCode} when connecting to ${httpURL}.\n` + `This does not look like a DevTools server, try connecting via ws://.`)); diff --git a/tests/browsertype-connect.spec.ts b/tests/browsertype-connect.spec.ts index d1b9f792d7..04acbdd202 100644 --- a/tests/browsertype-connect.spec.ts +++ b/tests/browsertype-connect.spec.ts @@ -19,9 +19,44 @@ import { playwrightTest as test, expect } from './config/browserTest'; import fs from 'fs'; import * as path from 'path'; import { getUserAgent } from '../lib/utils/utils'; +import WebSocket from 'ws'; test.slow(true, 'All connect tests are slow'); +test('should connect over wss', async ({browserType , startRemoteServer, httpsServer}) => { + const remoteServer = await startRemoteServer(); + + const oldValue = process.env['NODE_TLS_REJECT_UNAUTHORIZED']; + // https://stackoverflow.com/a/21961005/552185 + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; + try { + httpsServer.onceWebSocketConnection((ws, request) => { + const remote = new WebSocket(remoteServer.wsEndpoint(), [], { + perMessageDeflate: false, + maxPayload: 256 * 1024 * 1024, // 256Mb, + }); + const remoteReadyPromise = new Promise((f, r) => { + remote.once('open', f); + remote.once('error', r); + }); + remote.on('close', () => ws.close()); + remote.on('error', error => ws.close()); + remote.on('message', message => ws.send(message)); + ws.on('message', async message => { + await remoteReadyPromise; + remote.send(message); + }); + ws.on('close', () => remote.close()); + ws.on('error', () => remote.close()); + }); + const browser = await browserType.connect(`wss://localhost:${httpsServer.PORT}/ws`); + expect(browser.version()).toBeTruthy(); + await browser.close(); + } finally { + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue; + } +}); + test('should be able to reconnect to a browser', async ({browserType, startRemoteServer, server}) => { const remoteServer = await startRemoteServer(); { diff --git a/tests/chromium/chromium.spec.ts b/tests/chromium/chromium.spec.ts index 48ebdac696..e6fd8ac594 100644 --- a/tests/chromium/chromium.spec.ts +++ b/tests/chromium/chromium.spec.ts @@ -286,6 +286,39 @@ playwrightTest('should report all pages in an existing browser', async ({ browse } }); +playwrightTest('should connect via https', async ({ browserType, browserOptions, httpsServer }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] + }); + const json = await new Promise((resolve, reject) => { + http.get(`http://localhost:${port}/json/version/`, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); + }); + httpsServer.setRoute('/json/version/', (req, res) => { + res.writeHead(200); + res.end(json); + }); + const oldValue = process.env['NODE_TLS_REJECT_UNAUTHORIZED']; + // https://stackoverflow.com/a/21961005/552185 + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; + try { + const cdpBrowser = await browserType.connectOverCDP(`https://localhost:${httpsServer.PORT}/`); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + for (let i = 0; i < 3; i++) + await contexts[0].newPage(); + await cdpBrowser.close(); + } finally { + process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = oldValue; + await browserServer.close(); + } +}); + playwrightTest('should return valid browser from context.browser()', async ({ browserType, browserOptions }, testInfo) => { const port = 9339 + testInfo.workerIndex; const browserServer = await browserType.launch({ diff --git a/utils/testserver/index.d.ts b/utils/testserver/index.d.ts index 6a954dc2ca..46467c5af7 100644 --- a/utils/testserver/index.d.ts +++ b/utils/testserver/index.d.ts @@ -16,6 +16,7 @@ type ServerResponse = import('http').ServerResponse; type IncomingMessage = import('http').IncomingMessage; +import WebSocket from 'ws'; export class TestServer { static create(dirPath: string, port: number, loopback?: string): Promise; @@ -30,6 +31,7 @@ export class TestServer { setRedirect(from: string, to: string); waitForRequest(path: string): Promise }>; waitForWebSocketConnectionRequest(): Promise; + onceWebSocketConnection(handler: (ws: WebSocket, request: IncomingMessage) => void); sendOnWebSocketConnection(data: string); reset(); serveFile(request: IncomingMessage, response: ServerResponse); diff --git a/utils/testserver/index.js b/utils/testserver/index.js index 354781c098..077315bf24 100644 --- a/utils/testserver/index.js +++ b/utils/testserver/index.js @@ -323,6 +323,10 @@ class TestServer { } } + onceWebSocketConnection(handler) { + this._wsServer.once('connection', handler); + } + waitForWebSocketConnectionRequest() { return new Promise(fullfil => { this._wsServer.once('connection', (ws, req) => fullfil(req));