Innovenergy_trunk/frontend/node_modules/testcafe-hammerhead/lib/proxy/index.js

246 lines
11 KiB
JavaScript
Raw Normal View History

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const dns_1 = __importDefault(require("dns"));
const router_1 = __importDefault(require("./router"));
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const urlUtils = __importStar(require("../utils/url"));
const script_1 = __importDefault(require("../processing/resources/script"));
const http_2 = require("../utils/http");
const request_pipeline_1 = require("../request-pipeline");
const create_shadow_stylesheet_1 = __importDefault(require("../shadow-ui/create-shadow-stylesheet"));
const agent_1 = require("../request-pipeline/destination-request/agent");
const service_routes_1 = __importDefault(require("./service-routes"));
const builtin_header_names_1 = __importDefault(require("../request-pipeline/builtin-header-names"));
const logger_1 = __importDefault(require("../utils/logger"));
const err_to_string_1 = __importDefault(require("../utils/err-to-string"));
const json_1 = require("../utils/json");
const load_client_script_1 = __importDefault(require("../utils/load-client-script"));
const SESSION_IS_NOT_OPENED_ERR = 'Session is not opened in proxy';
function parseAsJson(msg) {
try {
return (0, json_1.parse)(msg.toString());
}
catch (err) {
return null;
}
}
function createServerInfo(hostname, port, crossDomainPort, protocol, cacheRequests) {
return {
hostname,
port,
crossDomainPort,
protocol,
cacheRequests,
domain: `${protocol}//${hostname}:${port}`,
};
}
const DEFAULT_PROXY_OPTIONS = {
developmentMode: false,
cache: false,
proxyless: false,
};
class Proxy extends router_1.default {
constructor(hostname, port1, port2, options) {
const prepareOptions = Object.assign({}, DEFAULT_PROXY_OPTIONS, options);
super(prepareOptions);
this.openSessions = new Map();
// NOTE: to avoid https://github.com/DevExpress/testcafe/issues/7447
if (typeof dns_1.default.setDefaultResultOrder === 'function')
// NOTE: to avoid https://github.com/nodejs/node/issues/40537
dns_1.default.setDefaultResultOrder('ipv4first');
const { ssl, developmentMode, cache, } = prepareOptions;
const protocol = ssl ? 'https:' : 'http:';
const opts = this._getOpts(ssl);
const createServer = this._getCreateServerMethod(ssl);
this.server1Info = createServerInfo(hostname, port1, port2, protocol, cache);
this.server2Info = createServerInfo(hostname, port2, port1, protocol, cache);
this.server1 = createServer(opts, (req, res) => this._onRequest(req, res, this.server1Info));
this.server2 = createServer(opts, (req, res) => this._onRequest(req, res, this.server2Info));
this.server1.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server1Info));
this.server2.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server2Info));
this.server1.listen(port1);
this.server2.listen(port2);
this.sockets = new Set();
// BUG: GH-89
this._startSocketsCollecting();
this._registerServiceRoutes(developmentMode);
}
_getOpts(ssl) {
let opts = {};
if (ssl)
opts = ssl;
opts.maxHeaderSize = Proxy.MAX_REQUEST_HEADER_SIZE;
return opts;
}
_getCreateServerMethod(ssl) {
return ssl ? https_1.default.createServer : http_1.default.createServer;
}
_closeSockets() {
this.sockets.forEach(socket => socket.destroy());
}
_startSocketsCollecting() {
const handler = (socket) => {
this.sockets.add(socket);
socket.on('close', () => this.sockets.delete(socket));
};
this.server1.on('connection', handler);
this.server2.on('connection', handler);
}
_registerServiceRoutes(developmentMode) {
const hammerheadScriptContent = (0, load_client_script_1.default)(service_routes_1.default.hammerhead, developmentMode);
const transportWorkerContent = (0, load_client_script_1.default)(service_routes_1.default.transportWorker, developmentMode);
const workerHammerheadContent = (0, load_client_script_1.default)(service_routes_1.default.workerHammerhead, developmentMode);
this.GET(service_routes_1.default.hammerhead, {
contentType: 'application/x-javascript',
content: hammerheadScriptContent,
});
this.GET(service_routes_1.default.transportWorker, {
contentType: 'application/x-javascript',
content: transportWorkerContent,
});
this.GET(service_routes_1.default.workerHammerhead, {
contentType: 'application/x-javascript',
content: workerHammerheadContent,
});
this.POST(service_routes_1.default.messaging, (req, res, serverInfo) => this._onServiceMessage(req, res, serverInfo));
if (this.options.proxyless)
this.OPTIONS(service_routes_1.default.messaging, (req, res) => this._onServiceMessagePreflight(req, res));
this.GET(service_routes_1.default.task, (req, res, serverInfo) => this._onTaskScriptRequest(req, res, serverInfo, false));
this.GET(service_routes_1.default.iframeTask, (req, res, serverInfo) => this._onTaskScriptRequest(req, res, serverInfo, true));
}
async _onServiceMessage(req, res, serverInfo) {
const body = await (0, http_2.fetchBody)(req);
const msg = parseAsJson(body);
const session = msg && this.openSessions.get(msg.sessionId);
if (msg && session) {
try {
const result = await session.handleServiceMessage(msg, serverInfo);
logger_1.default.serviceMsg.onMessage(msg, result);
res.setHeader(builtin_header_names_1.default.setCookie, session.takePendingSyncCookies());
(0, http_2.respondWithJSON)(res, result, false, this.options.proxyless);
}
catch (err) {
logger_1.default.serviceMsg.onError(msg, err);
(0, http_2.respond500)(res, (0, err_to_string_1.default)(err));
}
}
else
(0, http_2.respond500)(res, SESSION_IS_NOT_OPENED_ERR);
}
_onServiceMessagePreflight(_req, res) {
// NOTE: 'Cache-control' header set in the 'Transport' sandbox on the client side.
// Request becomes non-simple (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)
// and initiates the CORS preflight request.
res.setHeader('access-control-allow-headers', builtin_header_names_1.default.cacheControl);
(0, http_2.acceptCrossOrigin)(res);
(0, http_2.respond204)(res);
}
async _onTaskScriptRequest(req, res, serverInfo, isIframe) {
const referer = req.headers[builtin_header_names_1.default.referer];
const refererDest = referer && urlUtils.parseProxyUrl(referer);
const session = refererDest && this.openSessions.get(refererDest.sessionId);
const windowId = refererDest && refererDest.windowId || void 0;
if (session) {
if (referer && !isIframe)
session.options.referer = referer;
res.setHeader(builtin_header_names_1.default.contentType, 'application/x-javascript');
(0, http_2.addPreventCachingHeaders)(res);
const taskScript = await session.getTaskScript({
referer,
cookieUrl: refererDest ? refererDest.destUrl : '',
serverInfo,
isIframe,
withPayload: true,
windowId,
});
res.end(taskScript);
}
else
(0, http_2.respond500)(res, SESSION_IS_NOT_OPENED_ERR);
}
_onRequest(req, res, serverInfo) {
// NOTE: Not a service request, execute the proxy pipeline.
if (!this._route(req, res, serverInfo))
(0, request_pipeline_1.run)(req, res, serverInfo, this.openSessions, this.options.proxyless);
}
_onUpgradeRequest(req, socket, head, serverInfo) {
if (head && head.length)
socket.unshift(head);
this._onRequest(req, socket, serverInfo);
}
_processStaticContent(handler) {
if (handler.isShadowUIStylesheet)
handler.content = (0, create_shadow_stylesheet_1.default)(handler.content);
}
// API
close() {
script_1.default.jsCache.reset();
this.server1.close();
this.server2.close();
this._closeSockets();
(0, agent_1.resetKeepAliveConnections)();
}
openSession(url, session, externalProxySettings) {
session.proxy = this;
this.openSessions.set(session.id, session);
if (externalProxySettings)
session.setExternalProxySettings(externalProxySettings);
if (this.options.disableHttp2)
session.disableHttp2();
if (this.options.disableCrossDomain)
session.disableCrossDomain();
url = urlUtils.prepareUrl(url);
if (this.options.proxyless)
return url;
return urlUtils.getProxyUrl(url, {
proxyHostname: this.server1Info.hostname,
proxyPort: this.server1Info.port.toString(),
proxyProtocol: this.server1Info.protocol,
sessionId: session.id,
windowId: session.options.windowId,
});
}
closeSession(session) {
session.proxy = null;
this.openSessions.delete(session.id);
}
resolveRelativeServiceUrl(relativeServiceUrl, domain = this.server1Info.domain) {
return new URL(relativeServiceUrl, domain).toString();
}
}
exports.default = Proxy;
// Max header size for incoming HTTP requests
// Set to 80 KB as it was the original limit:
// https://github.com/nodejs/node/blob/186035243fad247e3955fa0c202987cae99e82db/deps/http_parser/http_parser.h#L63
// Before the change to 8 KB:
// https://github.com/nodejs/node/commit/186035243fad247e3955fa0c202987cae99e82db#diff-1d0d420098503156cddb601e523b82e7R59
Proxy.MAX_REQUEST_HEADER_SIZE = 80 * 1024;module.exports = exports.default;