205 lines
37 KiB
JavaScript
205 lines
37 KiB
JavaScript
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const load_assets_1 = __importDefault(require("../../load-assets"));
|
||
|
const http_1 = require("../../utils/http");
|
||
|
const remotes_queue_1 = __importDefault(require("./remotes-queue"));
|
||
|
const testcafe_hammerhead_1 = require("testcafe-hammerhead");
|
||
|
const service_routes_1 = __importDefault(require("./service-routes"));
|
||
|
const empty_page_markup_1 = __importDefault(require("../../proxyless/empty-page-markup"));
|
||
|
const error_route_1 = __importDefault(require("../../proxyless/error-route"));
|
||
|
class BrowserConnectionGateway {
|
||
|
constructor(proxy, options) {
|
||
|
this._connections = {};
|
||
|
this._remotesQueue = new remotes_queue_1.default();
|
||
|
this.connectUrl = proxy.resolveRelativeServiceUrl(service_routes_1.default.connect);
|
||
|
this.retryTestPages = options.retryTestPages;
|
||
|
this.proxyless = options.proxyless;
|
||
|
this.proxy = proxy;
|
||
|
this._registerRoutes(proxy);
|
||
|
}
|
||
|
_dispatch(url, proxy, handler, method = 'GET', shouldAcceptCrossOrigin) {
|
||
|
// @ts-ignore Need to improve typings of the 'testcafe-hammerhead' module
|
||
|
proxy[method](url, (req, res, serverInfo, params) => {
|
||
|
const connection = this._connections[params.id];
|
||
|
(0, http_1.preventCaching)(res);
|
||
|
if (shouldAcceptCrossOrigin)
|
||
|
(0, testcafe_hammerhead_1.acceptCrossOrigin)(res);
|
||
|
if (connection)
|
||
|
handler(req, res, connection);
|
||
|
else
|
||
|
(0, http_1.respond404)(res);
|
||
|
});
|
||
|
}
|
||
|
_registerRoutes(proxy) {
|
||
|
const { idlePageScript, idlePageStyle, idlePageLogo, serviceWorkerScript, } = (0, load_assets_1.default)();
|
||
|
this._dispatch(`${service_routes_1.default.connect}/{id}`, proxy, BrowserConnectionGateway._onConnection);
|
||
|
this._dispatch(`${service_routes_1.default.heartbeat}/{id}`, proxy, BrowserConnectionGateway._onHeartbeat, 'GET', this.proxyless);
|
||
|
this._dispatch(`${service_routes_1.default.idle}/{id}`, proxy, BrowserConnectionGateway._onIdle);
|
||
|
this._dispatch(`${service_routes_1.default.idleForced}/{id}`, proxy, BrowserConnectionGateway._onIdleForced);
|
||
|
this._dispatch(`${service_routes_1.default.status}/{id}`, proxy, BrowserConnectionGateway._onStatusRequest);
|
||
|
this._dispatch(`${service_routes_1.default.statusDone}/{id}`, proxy, BrowserConnectionGateway._onStatusRequestOnTestDone, 'GET', this.proxyless);
|
||
|
this._dispatch(`${service_routes_1.default.initScript}/{id}`, proxy, BrowserConnectionGateway._onInitScriptRequest);
|
||
|
this._dispatch(`${service_routes_1.default.initScript}/{id}`, proxy, BrowserConnectionGateway._onInitScriptResponse, 'POST');
|
||
|
this._dispatch(`${service_routes_1.default.activeWindowId}/{id}`, proxy, BrowserConnectionGateway._onGetActiveWindowIdRequest, 'GET', this.proxyless);
|
||
|
this._dispatch(`${service_routes_1.default.activeWindowId}/{id}`, proxy, BrowserConnectionGateway._onSetActiveWindowIdRequest, 'POST', this.proxyless);
|
||
|
this._dispatch(`${service_routes_1.default.closeWindow}/{id}`, proxy, BrowserConnectionGateway._onCloseWindowRequest, 'POST');
|
||
|
this._dispatch(`${service_routes_1.default.openFileProtocol}/{id}`, proxy, BrowserConnectionGateway._onOpenFileProtocolRequest, 'POST');
|
||
|
this._dispatch(`${service_routes_1.default.dispatchProxylessEvent}/{id}`, proxy, BrowserConnectionGateway._onDispatchProxylessEvent, 'POST', this.proxyless);
|
||
|
proxy.GET(service_routes_1.default.connect, (req, res) => this._connectNextRemoteBrowser(req, res));
|
||
|
proxy.GET(service_routes_1.default.connectWithTrailingSlash, (req, res) => this._connectNextRemoteBrowser(req, res));
|
||
|
proxy.GET(service_routes_1.default.serviceWorker, { content: serviceWorkerScript, contentType: 'application/x-javascript' });
|
||
|
proxy.GET(service_routes_1.default.assets.index, { content: idlePageScript, contentType: 'application/x-javascript' });
|
||
|
proxy.GET(service_routes_1.default.assets.styles, { content: idlePageStyle, contentType: 'text/css' });
|
||
|
proxy.GET(service_routes_1.default.assets.logo, { content: idlePageLogo, contentType: 'image/svg+xml' });
|
||
|
if (this.proxyless) {
|
||
|
proxy.GET(error_route_1.default, (req, res) => {
|
||
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||
|
res.end(empty_page_markup_1.default);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
// Helpers
|
||
|
static _ensureConnectionReady(res, connection) {
|
||
|
if (!connection.isReady()) {
|
||
|
(0, http_1.respond500)(res, 'The connection is not ready yet.');
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
static _fetchRequestData(req, callback) {
|
||
|
let data = '';
|
||
|
req.on('data', chunk => {
|
||
|
data += chunk;
|
||
|
});
|
||
|
req.on('end', () => {
|
||
|
callback(data.toString());
|
||
|
});
|
||
|
}
|
||
|
// Route handlers
|
||
|
static async _onConnection(req, res, connection) {
|
||
|
if (connection.isReady())
|
||
|
(0, http_1.respond500)(res, 'The connection is already established.');
|
||
|
else {
|
||
|
const userAgent = req.headers['user-agent'];
|
||
|
await connection.establish(userAgent);
|
||
|
(0, http_1.redirect)(res, connection.idleUrl);
|
||
|
}
|
||
|
}
|
||
|
static _onHeartbeat(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
const status = connection.heartbeat();
|
||
|
(0, http_1.respondWithJSON)(res, status);
|
||
|
}
|
||
|
}
|
||
|
static _onIdle(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection))
|
||
|
res.end(connection.renderIdlePage());
|
||
|
}
|
||
|
static async _onIdleForced(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
const status = await connection.getStatus(true);
|
||
|
(0, http_1.redirect)(res, status.url);
|
||
|
}
|
||
|
}
|
||
|
static async _onStatusRequest(req, res, connection) {
|
||
|
return BrowserConnectionGateway._onStatusRequestCore(req, res, connection, false);
|
||
|
}
|
||
|
static async _onStatusRequestOnTestDone(req, res, connection) {
|
||
|
return BrowserConnectionGateway._onStatusRequestCore(req, res, connection, true);
|
||
|
}
|
||
|
static async _onStatusRequestCore(req, res, connection, isTestDone) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
const status = await connection.getStatus(isTestDone);
|
||
|
(0, http_1.respondWithJSON)(res, status);
|
||
|
}
|
||
|
}
|
||
|
static _onInitScriptRequest(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
const script = connection.getInitScript();
|
||
|
(0, http_1.respondWithJSON)(res, script);
|
||
|
}
|
||
|
}
|
||
|
static _onInitScriptResponse(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
BrowserConnectionGateway._fetchRequestData(req, data => {
|
||
|
connection.handleInitScriptResult(data);
|
||
|
res.end();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
static _onGetActiveWindowIdRequest(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
(0, http_1.respondWithJSON)(res, {
|
||
|
activeWindowId: connection.activeWindowId,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
static _onSetActiveWindowIdRequest(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
BrowserConnectionGateway._fetchRequestData(req, data => {
|
||
|
const parsedData = JSON.parse(data);
|
||
|
connection.activeWindowId = parsedData.windowId;
|
||
|
(0, http_1.respondWithJSON)(res);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
static _onCloseWindowRequest(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
connection.provider.closeBrowserChildWindow(connection.id)
|
||
|
.then(() => {
|
||
|
(0, http_1.respondWithJSON)(res);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
static _onOpenFileProtocolRequest(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
BrowserConnectionGateway._fetchRequestData(req, data => {
|
||
|
const parsedData = JSON.parse(data);
|
||
|
connection.openFileProtocol(parsedData.url)
|
||
|
.then(() => {
|
||
|
(0, http_1.respondWithJSON)(res);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
static _onDispatchProxylessEvent(req, res, connection) {
|
||
|
if (BrowserConnectionGateway._ensureConnectionReady(res, connection)) {
|
||
|
BrowserConnectionGateway._fetchRequestData(req, data => {
|
||
|
const { type, options } = JSON.parse(data);
|
||
|
connection.dispatchProxylessEvent(type, options)
|
||
|
.then(() => {
|
||
|
(0, http_1.respondWithJSON)(res);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
async _connectNextRemoteBrowser(req, res) {
|
||
|
(0, http_1.preventCaching)(res);
|
||
|
const remoteConnection = await this._remotesQueue.shift();
|
||
|
if (remoteConnection)
|
||
|
(0, http_1.redirect)(res, remoteConnection.url);
|
||
|
else
|
||
|
(0, http_1.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);
|
||
|
}
|
||
|
async close() {
|
||
|
for (const id in this._connections)
|
||
|
await this._connections[id].close();
|
||
|
}
|
||
|
}
|
||
|
exports.default = BrowserConnectionGateway;
|
||
|
module.exports = exports.default;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2F0ZXdheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9icm93c2VyL2Nvbm5lY3Rpb24vZ2F0ZXdheS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG9FQUEyQztBQUMzQywyQ0FNMEI7QUFFMUIsb0VBQTJDO0FBQzNDLDZEQUErRDtBQUkvRCxzRUFBOEM7QUFDOUMsMEZBQWtFO0FBQ2xFLDhFQUFnRTtBQUVoRSxNQUFxQix3QkFBd0I7SUFRekMsWUFBb0IsS0FBWSxFQUFFLE9BQXdEO1FBUGxGLGlCQUFZLEdBQWtDLEVBQUUsQ0FBQztRQVFyRCxJQUFJLENBQUMsYUFBYSxHQUFLLElBQUksdUJBQVksRUFBRSxDQUFDO1FBQzFDLElBQUksQ0FBQyxVQUFVLEdBQVEsS0FBSyxDQUFDLHlCQUF5QixDQUFDLHdCQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0UsSUFBSSxDQUFDLGNBQWMsR0FBSSxPQUFPLENBQUMsY0FBYyxDQUFDO1FBQzlDLElBQUksQ0FBQyxTQUFTLEdBQVMsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUN6QyxJQUFJLENBQUMsS0FBSyxHQUFhLEtBQUssQ0FBQztRQUU3QixJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2hDLENBQUM7SUFFTyxTQUFTLENBQUUsR0FBVyxFQUFFLEtBQVksRUFBRSxPQUFpQixFQUFFLE1BQU0sR0FBRyxLQUFLLEVBQUUsdUJBQWlDO1FBQzlHLHlFQUF5RTtRQUN6RSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUMsR0FBb0IsRUFBRSxHQUFtQixFQUFFLFVBQVUsRUFBRSxNQUEwQixFQUFFLEVBQUU7WUFDckcsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7WUFFaEQsSUFBQSxxQkFBYyxFQUFDLEdBQUcsQ0FBQyxDQUFDO1lBRXBCLElBQUksdUJBQXVCO2dCQUN2QixJQUFBLHVDQUFpQixFQUFDLEdBQUcsQ0FBQyxDQUFDO1lBRTNCLElBQUksVUFBVTtnQkFDVixPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQzs7Z0JBRTlCLElBQUEsaUJBQVUsRUFBQyxHQUFHLENBQUMsQ0FBQztRQUN4QixDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxlQUFlLENBQUUsS0FBWTtRQUNqQyxNQUFNLEVBQ0YsY0FBYyxFQUNkLGFBQWEsRUFDYixZQUFZLEVBQ1osbUJBQW1CLEdBQ3RCLEdBQUcsSUFBQSxxQkFBVSxHQUFFLENBQUM7UUFFakIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLHdCQUFjLENBQUMsT0FBTyxPQUFPLEVBQUUsS0FBSyxFQUFFLHdCQUF3QixDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ2hHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyx3QkFBYyxDQUFDLFNBQVMsT0FBTyxFQUFFLEtBQUssRUFBRSx3QkFBd0IsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN4SCxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsd0JBQWMsQ0FBQyxJQUFJLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDdkYsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLHdCQUFjLENBQUMsVUFBVSxPQUFPLEVBQUUsS0FBSyxFQUFFLHdCQUF3QixDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ25HLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyx3QkFBYyxDQUFDLE1BQU0sT0FBTyxFQUFFLEtBQUssRUFBRSx3QkFBd0IsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ2xHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyx3QkFBYyxDQUFDLFVBQVUsT0FBTyxFQUFFLEtBQUssRUFBRSx3QkFBd0IsQ0FBQywwQkFBMEIsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3ZJLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyx3QkFBYyxDQUFDLFVBQVUsT0FBTyxFQUFFLEtBQUssRUFBRSx3QkFBd0IsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQzFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyx3QkFBYyxDQUFDLFVBQVUsT0FBTyxFQUFFLEtBQUssRUFBRSx3QkFBd0IsQ0FBQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNuSCxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsd0JBQWMsQ0FBQyxjQUFjLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMsMkJBQTJCLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM1SSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsd0JBQWMsQ0FBQyxjQUFjLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMsMkJBQTJCLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM3SSxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsd0JBQWMsQ0FBQyxXQUFXLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMscUJBQXFCLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDcEgsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLHdCQUFjLENBQUMsZ0JBQWdCLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMsMEJBQTBCLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDOUgsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLHdCQUFjLENBQUMsc0JBQXNCLE9BQU8sRUFBRSxLQUFLLEVBQUUsd0JBQXdCLENBQUMseUJBQXlCLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUVuSixLQUFLLENBQUMsR0FBRyxDQUFDLHdCQUFjLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBb0IsRUFBRSxHQUFtQixFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMseUJBQXlCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDM0gsS0FBSyxDQUFDLEdBQUcsQ0FBQyx3QkFBYyxDQUFDLHdCQUF3QixFQUFFLENBQUMsR0FBb0IsRUFBRSxHQUFtQixFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMseUJBQXlCLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFFNUksS0FBSyxDQUFDLEdBQUcsQ0FBQyx3QkFBYyxDQUFDLGFBQWEsRUFBRSxFQUFFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxXQUFXLEVBQUUsMEJBQTBCLEVBQUUsQ0FBQyxDQUFDO1FBQ25ILEtBQUssQ0FBQyxHQUFHLENBQUMsd0JBQWMsQ0FBQyxNQUFNLENBQUMsS
|