From 9f8d92cffa274120e1b6e2faf2efcf9813595662 Mon Sep 17 00:00:00 2001 From: Andrey Belym Date: Mon, 15 May 2017 18:26:08 +0300 Subject: [PATCH] Implement uniform URL for connecting remote browsers (closes #1476) --- src/browser/connection/gateway.js | 45 +++++++++++++++++++++++++++++-- src/cli/remotes-wizard.js | 29 ++++++++++++-------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/browser/connection/gateway.js b/src/browser/connection/gateway.js index 1eeaccc78a8..f1fee3b9c32 100644 --- a/src/browser/connection/gateway.js +++ b/src/browser/connection/gateway.js @@ -1,3 +1,6 @@ +import Promise from 'pinkie'; +import promisifyEvent from 'promisify-event'; +import timeLimit from 'time-limit-promise'; import { readSync as read } from 'read-file-relative'; import { respond404, respond500, respondWithJSON, redirect, preventCaching } from '../../utils/http'; @@ -7,12 +10,16 @@ const IDLE_PAGE_SCRIPT = read('../../client/browser/idle-page/index.js'); const IDLE_PAGE_STYLE = read('../../client/browser/idle-page/styles.css'); const IDLE_PAGE_LOGO = read('../../client/browser/idle-page/logo.svg', true); +const REMOTE_REDIRECT_TIMEOUT = 10000; // Gateway export default class BrowserConnectionGateway { constructor (proxy) { - this.connections = {}; - this.domain = proxy.server1Info.domain; + this.connections = {}; + this.pendingRemotesPromise = Promise.resolve({}); + this.domain = proxy.server1Info.domain; + + this.connectUrl = `${this.domain}/browser/connect`; this._registerRoutes(proxy); } @@ -38,6 +45,9 @@ export default class BrowserConnectionGateway { this._dispatch('/browser/init-script/{id}', proxy, BrowserConnectionGateway.onInitScriptRequest); this._dispatch('/browser/init-script/{id}', proxy, BrowserConnectionGateway.onInitScriptResponse, 'POST'); + proxy.GET('/browser/connect', (req, res) => this._connectNextRemoteBrowser(req, res)); + proxy.GET('/browser/connect/', (req, res) => this._connectNextRemoteBrowser(req, res)); + proxy.GET('/browser/assets/index.js', { content: IDLE_PAGE_SCRIPT, contentType: 'application/x-javascript' }); proxy.GET('/browser/assets/styles.css', { content: IDLE_PAGE_STYLE, contentType: 'text/css' }); proxy.GET('/browser/assets/logo.svg', { content: IDLE_PAGE_LOGO, contentType: 'image/svg+xml' }); @@ -111,14 +121,45 @@ export default class BrowserConnectionGateway { } } + async _connectNextRemoteBrowser (req, res) { + var pendingRemotes = await this.pendingRemotesPromise; + var pendingId = Object.keys(pendingRemotes)[0]; + + if (!pendingId) + return; + + this.pendingRemotesPromise = timeLimit(pendingRemotes[pendingId], REMOTE_REDIRECT_TIMEOUT) + .then(() => pendingRemotes); + + redirect(res, this.connections[pendingId].url); + } // API startServingConnection (connection) { this.connections[connection.id] = connection; + + if (connection.browserInfo.providerName === 'remote') { + this.pendingRemotesPromise = this.pendingRemotesPromise + .then(pendingRemotes => { + pendingRemotes[connection.id] = promisifyEvent(connection, 'ready') + .then(() => delete pendingRemotes[connection.id]); + + return pendingRemotes; + }); + } } stopServingConnection (connection) { delete this.connections[connection.id]; + + if (connection.browserInfo.providerName === 'remote') { + this.pendingRemotesPromise = this.pendingRemotesPromise + .then(pendingRemotes => { + delete pendingRemotes[connection.id]; + + return pendingRemotes; + }); + } } close () { diff --git a/src/cli/remotes-wizard.js b/src/cli/remotes-wizard.js index 7309186ce35..3c39e1ed16a 100644 --- a/src/cli/remotes-wizard.js +++ b/src/cli/remotes-wizard.js @@ -4,8 +4,9 @@ import log from './log'; import promisifyEvent from 'promisify-event'; import dedent from 'dedent'; + export default async function (testCafe, remoteCount, showQRCode) { - var connections = []; + var connectionPromises = []; if (remoteCount) { log.hideSpinner(); @@ -20,22 +21,28 @@ export default async function (testCafe, remoteCount, showQRCode) { if (showQRCode) log.write('You can either enter the URL or scan the QR-code.'); - for (var i = 0; i < remoteCount; i++) { - var browserConnection = await testCafe.createBrowserConnection(); - - log.write(`Browser #${i + 1}: ${chalk.underline.blue(browserConnection.url)}`); + var connectionUrl = testCafe.browserConnectionGateway.connectUrl; - if (showQRCode) - qrcode.generate(browserConnection.url); + log.write(`Connect URL: ${chalk.underline.blue(connectionUrl)}`); - await promisifyEvent(browserConnection, 'ready'); + if (showQRCode) + qrcode.generate(connectionUrl); - connections.push(browserConnection); - log.write(`${chalk.green('CONNECTED')} ${browserConnection.userAgent}\n`); + for (var i = 0; i < remoteCount; i++) { + connectionPromises.push(testCafe + .createBrowserConnection() + .then(bc => promisifyEvent(bc, 'ready').then(() => bc)) + .then(bc => { + log.hideSpinner(); + log.write(`${chalk.green('CONNECTED')} ${bc.userAgent}`); + log.showSpinner(); + return bc; + }) + ); } log.showSpinner(); } - return connections; + return await Promise.all(connectionPromises); }