220 lines
9.4 KiB
JavaScript
220 lines
9.4 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 event_provider_1 = __importDefault(require("../request-pipeline/request-hooks/events/event-provider"));
|
||
|
const state_snapshot_1 = __importDefault(require("./state-snapshot"));
|
||
|
const mustache_1 = __importDefault(require("mustache"));
|
||
|
const read_file_relative_1 = require("read-file-relative");
|
||
|
const url_1 = require("url");
|
||
|
const cookies_1 = __importDefault(require("./cookies"));
|
||
|
const storage_1 = __importDefault(require("../upload/storage"));
|
||
|
const command_1 = __importDefault(require("./command"));
|
||
|
const generate_unique_id_1 = __importDefault(require("../utils/generate-unique-id"));
|
||
|
const service_routes_1 = __importDefault(require("../proxy/service-routes"));
|
||
|
const configure_response_event_options_1 = __importDefault(require("../request-pipeline/request-hooks/events/configure-response-event-options"));
|
||
|
const cookie_1 = require("../utils/cookie");
|
||
|
const injectables_1 = require("./injectables");
|
||
|
const events_1 = require("events");
|
||
|
const TASK_TEMPLATE = (0, read_file_relative_1.readSync)('../client/task.js.mustache');
|
||
|
class Session extends events_1.EventEmitter {
|
||
|
constructor(uploadRoots, options) {
|
||
|
super();
|
||
|
this.id = (0, generate_unique_id_1.default)();
|
||
|
this.proxy = null;
|
||
|
this.externalProxySettings = null;
|
||
|
this.pageLoadCount = 0;
|
||
|
this.pendingStateSnapshot = null;
|
||
|
this.injectable = { scripts: [...injectables_1.SCRIPTS], styles: [], userScripts: [] };
|
||
|
this._recordMode = false;
|
||
|
this._disableHttp2 = false;
|
||
|
this._disableCrossDomain = false;
|
||
|
this.uploadStorage = new storage_1.default(uploadRoots);
|
||
|
this.options = this._getOptions(options);
|
||
|
this.cookies = this.createCookies();
|
||
|
this.requestHookEventProvider = new event_provider_1.default();
|
||
|
}
|
||
|
_getOptions(options = {}) {
|
||
|
return Object.assign({
|
||
|
disablePageCaching: false,
|
||
|
allowMultipleWindows: false,
|
||
|
windowId: '',
|
||
|
}, options);
|
||
|
}
|
||
|
createCookies() {
|
||
|
return new cookies_1.default();
|
||
|
}
|
||
|
// State
|
||
|
getStateSnapshot() {
|
||
|
return new state_snapshot_1.default(this.cookies.serializeJar(), null);
|
||
|
}
|
||
|
useStateSnapshot(snapshot) {
|
||
|
if (!snapshot)
|
||
|
throw new Error('"snapshot" parameter cannot be null. Use StateSnapshot.empty() instead of it.');
|
||
|
// NOTE: we don't perform state switch immediately, since there might be
|
||
|
// pending requests from current page. Therefore, we perform switch in
|
||
|
// onPageRequest handler when new page is requested.
|
||
|
this.pendingStateSnapshot = snapshot;
|
||
|
}
|
||
|
async handleServiceMessage(msg, serverInfo) {
|
||
|
if (this[msg.cmd])
|
||
|
return await this[msg.cmd](msg, serverInfo);
|
||
|
throw new Error('Malformed service message or message handler is not implemented');
|
||
|
}
|
||
|
takePendingSyncCookies() {
|
||
|
return this.cookies.takePendingSyncCookies().map(syncCookie => (0, cookie_1.formatSyncCookie)(Object.assign(Object.assign({}, syncCookie), { sid: this.id, isServerSync: true, domain: syncCookie.domain || '', path: syncCookie.path || '', lastAccessed: new Date(), syncKey: '' })));
|
||
|
}
|
||
|
_fillTaskScriptTemplate({ serverInfo, isFirstPageLoad, referer, cookie, iframeTaskScriptTemplate, payloadScript, allowMultipleWindows, isRecordMode, windowId }) {
|
||
|
var _a, _b;
|
||
|
referer = referer && JSON.stringify(referer) || '{{{referer}}}';
|
||
|
cookie = cookie || '{{{cookie}}}';
|
||
|
iframeTaskScriptTemplate = iframeTaskScriptTemplate || '{{{iframeTaskScriptTemplate}}}';
|
||
|
if ((_a = this.proxy) === null || _a === void 0 ? void 0 : _a.options.proxyless) {
|
||
|
referer = '""';
|
||
|
cookie = '""';
|
||
|
}
|
||
|
const { domain, crossDomainPort } = serverInfo;
|
||
|
return mustache_1.default.render(TASK_TEMPLATE, {
|
||
|
sessionId: this.id,
|
||
|
serviceMsgUrl: domain + service_routes_1.default.messaging,
|
||
|
transportWorkerUrl: domain + service_routes_1.default.transportWorker,
|
||
|
forceProxySrcForImage: this.requestHookEventProvider.hasRequestEventListeners(),
|
||
|
crossDomainPort,
|
||
|
isFirstPageLoad,
|
||
|
referer,
|
||
|
cookie,
|
||
|
iframeTaskScriptTemplate,
|
||
|
payloadScript,
|
||
|
allowMultipleWindows,
|
||
|
isRecordMode,
|
||
|
windowId: windowId || '',
|
||
|
proxyless: ((_b = this.proxy) === null || _b === void 0 ? void 0 : _b.options.proxyless) || false,
|
||
|
disableCrossDomain: this.isCrossDomainDisabled() || false,
|
||
|
});
|
||
|
}
|
||
|
async getIframeTaskScriptTemplate(serverInfo) {
|
||
|
const taskScriptTemplate = this._fillTaskScriptTemplate({
|
||
|
serverInfo,
|
||
|
isFirstPageLoad: false,
|
||
|
referer: null,
|
||
|
cookie: null,
|
||
|
iframeTaskScriptTemplate: null,
|
||
|
payloadScript: await this.getIframePayloadScript(true),
|
||
|
allowMultipleWindows: this.options.allowMultipleWindows,
|
||
|
isRecordMode: this._recordMode,
|
||
|
});
|
||
|
return JSON.stringify(taskScriptTemplate);
|
||
|
}
|
||
|
async getTaskScript({ referer, cookieUrl, serverInfo, isIframe, withPayload, windowId }) {
|
||
|
const cookies = JSON.stringify(this.cookies.getClientString(cookieUrl));
|
||
|
let payloadScript = '';
|
||
|
if (withPayload)
|
||
|
payloadScript = isIframe ? await this.getIframePayloadScript(false) : await this.getPayloadScript();
|
||
|
const taskScript = this._fillTaskScriptTemplate({
|
||
|
serverInfo,
|
||
|
isFirstPageLoad: this.pageLoadCount === 0,
|
||
|
referer,
|
||
|
cookie: cookies,
|
||
|
iframeTaskScriptTemplate: await this.getIframeTaskScriptTemplate(serverInfo),
|
||
|
payloadScript,
|
||
|
allowMultipleWindows: this.options.allowMultipleWindows,
|
||
|
isRecordMode: this._recordMode,
|
||
|
windowId,
|
||
|
});
|
||
|
this.pageLoadCount++;
|
||
|
return taskScript;
|
||
|
}
|
||
|
setExternalProxySettings(proxySettings) {
|
||
|
if (typeof proxySettings === 'string')
|
||
|
proxySettings = { url: proxySettings };
|
||
|
if (!proxySettings || !proxySettings.url)
|
||
|
return;
|
||
|
const { url, bypassRules } = proxySettings;
|
||
|
const parsedUrl = (0, url_1.parse)('http://' + url);
|
||
|
let settings = null;
|
||
|
if (parsedUrl && parsedUrl.host) {
|
||
|
settings = {
|
||
|
host: parsedUrl.host,
|
||
|
hostname: parsedUrl.hostname || '',
|
||
|
};
|
||
|
if (bypassRules)
|
||
|
settings.bypassRules = bypassRules;
|
||
|
if (parsedUrl.port)
|
||
|
settings.port = parsedUrl.port;
|
||
|
if (parsedUrl.auth) {
|
||
|
settings.proxyAuth = parsedUrl.auth;
|
||
|
settings.authHeader = 'Basic ' + Buffer.from(parsedUrl.auth).toString('base64');
|
||
|
}
|
||
|
}
|
||
|
this.externalProxySettings = settings;
|
||
|
}
|
||
|
onPageRequest(ctx) {
|
||
|
if (!this.pendingStateSnapshot)
|
||
|
return;
|
||
|
this.cookies.setJar(this.pendingStateSnapshot.cookies);
|
||
|
if (this.pendingStateSnapshot.storages)
|
||
|
ctx.restoringStorages = this.pendingStateSnapshot.storages;
|
||
|
this.pendingStateSnapshot = null;
|
||
|
}
|
||
|
// Request hooks
|
||
|
_ensureConfigureResponseEventData(eventId) {
|
||
|
let eventData = this.requestHookEventProvider.requestHookEventData.configureResponse.get(eventId);
|
||
|
if (!eventData) {
|
||
|
eventData = {
|
||
|
opts: configure_response_event_options_1.default.DEFAULT,
|
||
|
setHeaders: [],
|
||
|
removedHeaders: [],
|
||
|
};
|
||
|
}
|
||
|
return eventData;
|
||
|
}
|
||
|
_updateConfigureResponseEventData(eventId, updateFn) {
|
||
|
const eventData = this._ensureConfigureResponseEventData(eventId);
|
||
|
updateFn(eventData);
|
||
|
this.requestHookEventProvider.requestHookEventData.configureResponse.set(eventId, eventData);
|
||
|
}
|
||
|
removeConfigureResponseEventData(eventId) {
|
||
|
this.requestHookEventProvider.requestHookEventData.configureResponse.delete(eventId);
|
||
|
}
|
||
|
async setConfigureResponseEventOptions(eventId, opts) {
|
||
|
this._updateConfigureResponseEventData(eventId, eventData => {
|
||
|
eventData.opts = opts;
|
||
|
});
|
||
|
}
|
||
|
async setHeaderOnConfigureResponseEvent(eventId, headerName, headerValue) {
|
||
|
this._updateConfigureResponseEventData(eventId, eventData => {
|
||
|
eventData.setHeaders.push({ name: headerName, value: headerValue });
|
||
|
});
|
||
|
}
|
||
|
async removeHeaderOnConfigureResponseEvent(eventId, headerName) {
|
||
|
this._updateConfigureResponseEventData(eventId, eventData => {
|
||
|
eventData.removedHeaders.push(headerName);
|
||
|
});
|
||
|
}
|
||
|
setRecordMode() {
|
||
|
this._recordMode = true;
|
||
|
}
|
||
|
disableHttp2() {
|
||
|
this._disableHttp2 = true;
|
||
|
}
|
||
|
isHttp2Disabled() {
|
||
|
return this._disableHttp2;
|
||
|
}
|
||
|
disableCrossDomain() {
|
||
|
this._disableCrossDomain = true;
|
||
|
}
|
||
|
isCrossDomainDisabled() {
|
||
|
return this._disableCrossDomain;
|
||
|
}
|
||
|
// Service message handlers
|
||
|
async [command_1.default.uploadFiles](msg) {
|
||
|
return await this.uploadStorage.store(msg.fileNames, msg.data);
|
||
|
}
|
||
|
async [command_1.default.getUploadedFiles](msg) {
|
||
|
return await this.uploadStorage.get(msg.filePaths);
|
||
|
}
|
||
|
}
|
||
|
exports.default = Session;module.exports = exports.default;
|
||
|
|