Skip to content

Commit

Permalink
Implement uniform URL for connecting remote browsers (closes DevExpre…
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyBelym authored and kirovboris committed Dec 18, 2019
1 parent 6fe42b7 commit 88f403f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 15 deletions.
28 changes: 25 additions & 3 deletions src/browser/connection/gateway.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { readSync as read } from 'read-file-relative';
import { respond404, respond500, respondWithJSON, redirect, preventCaching } from '../../utils/http';
import RemotesQueue from './remotes-queue';


// Const
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);


// Gateway
export default class BrowserConnectionGateway {
constructor (proxy) {
this.connections = {};
this.domain = proxy.server1Info.domain;
this.connections = {};
this.remotesQueue = new RemotesQueue();
this.domain = proxy.server1Info.domain;

this.connectUrl = `${this.domain}/browser/connect`;

this._registerRoutes(proxy);
}
Expand All @@ -38,6 +41,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' });
Expand Down Expand Up @@ -111,14 +117,30 @@ export default class BrowserConnectionGateway {
}
}

async _connectNextRemoteBrowser (req, res) {
preventCaching(res);

var remoteConnection = await this.remotesQueue.shift();

if (remoteConnection)
redirect(res, remoteConnection.url);
else
respond500(res, 'There are no available connections to establish.');
}

// API
startServingConnection (connection) {
this.connections[connection.id] = connection;

if (connection.browserInfo.providerName === 'remote')
this.remotesQueue.add(connection);
}

stopServingConnection (connection) {
delete this.connections[connection.id];

if (connection.browserInfo.providerName === 'remote')
this.remotesQueue.remove(connection);
}

close () {
Expand Down
54 changes: 54 additions & 0 deletions src/browser/connection/remotes-queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Promise from 'pinkie';
import { EventEmitter } from 'events';
import promisifyEvent from 'promisify-event';
import timeLimit from 'time-limit-promise';


const REMOTE_REDIRECT_TIMEOUT = 10000;
const ADDING_CONNECTION_WAITING_TIMEOUT = 10000;

export default class RemotesQueue {
constructor () {
this.events = new EventEmitter();
this.shiftingTimeout = Promise.resolve();
this.pendingConnections = {};
}

add (remoteConnection) {
var connectionReadyPromise = promisifyEvent(remoteConnection, 'ready')
.then(() => this.remove(remoteConnection));

this.pendingConnections[remoteConnection.id] = {
connection: remoteConnection,
readyPromise: connectionReadyPromise
};

this.events.emit('connection-added', remoteConnection.id);
}

remove (remoteConnection) {
delete this.pendingConnections[remoteConnection.id];
}

shift () {
var shiftingPromise = this.shiftingTimeout
.then(async () => {
var headId = Object.keys(this.pendingConnections)[0];

if (!headId)
headId = await timeLimit(promisifyEvent(this.events, 'connection-added'), ADDING_CONNECTION_WAITING_TIMEOUT);

return headId ? this.pendingConnections[headId].connection : null;
});

this.shiftingTimeout = shiftingPromise
.then(connection => {
if (!connection)
return Promise.resolve();

return timeLimit(this.pendingConnections[connection.id].readyPromise, REMOTE_REDIRECT_TIMEOUT);
});

return shiftingPromise;
}
}
32 changes: 20 additions & 12 deletions src/cli/remotes-wizard.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
import Promise from 'pinkie';
import qrcode from 'qrcode-terminal';
import chalk from 'chalk';
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();

var description = dedent(`
Connecting ${remoteCount} remote browser(s)...
Navigate to the appropriate URL from each of the remote browsers.
Navigate to the following URL from each remote browser.
`);

log.write(description);

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

0 comments on commit 88f403f

Please sign in to comment.