Innovenergy_trunk/frontend/node_modules/testcafe-hammerhead/lib/request-pipeline/destination-request/index.js

229 lines
10 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 http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const lodash_1 = require("lodash");
const requestAgent = __importStar(require("./agent"));
const events_1 = require("events");
const webauth_1 = require("webauth");
const connection_reset_guard_1 = require("../connection-reset-guard");
const messages_1 = require("../../messages");
const logger_1 = __importDefault(require("../../utils/logger"));
const requestCache = __importStar(require("../cache"));
const http2_1 = require("./http2");
const default_request_timeout_1 = __importDefault(require("./default-request-timeout"));
const TUNNELING_SOCKET_ERR_RE = /tunneling socket could not be established/i;
const TUNNELING_AUTHORIZE_ERR_RE = /statusCode=407/i;
const SOCKET_HANG_UP_ERR_RE = /socket hang up/i;
const IS_DNS_ERR_MSG_RE = /ECONNREFUSED|ENOTFOUND|EPROTO/;
const IS_DNS_ERR_CODE_RE = /ECONNRESET/;
class DestinationRequest extends events_1.EventEmitter {
constructor(opts, cache = false) {
super();
this.opts = opts;
this.cache = cache;
this.hasResponse = false;
this.credentialsSent = false;
this.aborted = false;
this.protocolInterface = this.opts.isHttps ? https_1.default : http_1.default;
this.timeout = this._getTimeout();
if (this.opts.isHttps)
opts.ignoreSSLAuth();
requestAgent.assign(this.opts);
this._send();
}
_getTimeout() {
return this.opts.isAjax
? this.opts.requestTimeout && this.opts.requestTimeout.ajax || default_request_timeout_1.default.ajax
: this.opts.requestTimeout && this.opts.requestTimeout.page || default_request_timeout_1.default.page;
}
static _isHttp2ProtocolError(err) {
return err.code === 'ERR_HTTP2_STREAM_ERROR' &&
(err.message.includes('NGHTTP2_PROTOCOL_ERROR') || err.message.includes('NGHTTP2_HTTP_1_1_REQUIRED'));
}
_sendRealThroughHttp2(session) {
const reqHeaders = (0, http2_1.formatRequestHttp2Headers)(this.opts);
const endStream = !this.opts.body.length;
const stream = session.request(reqHeaders, { endStream });
stream.setTimeout(this.timeout, () => this._onTimeout());
stream.on('error', (err) => {
if (DestinationRequest._isHttp2ProtocolError(err)) {
session.destroy();
this._sendReal();
}
else
this._onError(err);
});
stream.on('response', headers => {
const http2res = (0, http2_1.createResponseLike)(stream, headers);
this._onResponse(http2res);
});
if (!endStream) {
stream.write(this.opts.body);
stream.end();
}
this.req = stream;
logger_1.default.destination.onHttp2Stream(this.opts.requestId, reqHeaders);
}
_sendReal(waitForData) {
(0, connection_reset_guard_1.connectionResetGuard)(() => {
const preparedOpts = this.opts.prepare();
this.req = this.protocolInterface.request(preparedOpts, res => {
if (waitForData) {
res.on('data', lodash_1.noop);
res.once('end', () => this._onResponse(res));
}
});
if (logger_1.default.destinationSocket.enabled) {
this.req.on('socket', socket => {
socket.once('data', data => logger_1.default.destinationSocket.onFirstChunk(this.opts, data));
socket.once('error', err => logger_1.default.destinationSocket.onError(this.opts, err));
});
}
if (!waitForData)
this.req.on('response', (res) => this._onResponse(res));
this.req.on('upgrade', (res, socket, head) => this._onUpgrade(res, socket, head));
this.req.on('error', (err) => this._onError(err));
this.req.setTimeout(this.timeout, () => this._onTimeout());
this.req.write(this.opts.body);
this.req.end();
logger_1.default.destination.onRequest(this.opts);
});
}
async _send(waitForData) {
if (this.cache) {
const cachedResponse = requestCache.getResponse(this.opts);
if (cachedResponse) {
// NOTE: To store async order of the 'response' event
setImmediate(() => this._emitOnResponse(cachedResponse.res));
logger_1.default.destination.onCachedRequest(this.opts, cachedResponse.hitCount);
return;
}
}
const http2Session = !this.opts.disableHttp2 && this.opts.isHttps && !this.opts.isWebSocket && !this.opts.proxy &&
await (0, http2_1.getHttp2Session)(this.opts.requestId, this.opts.protocol + '//' + this.opts.host);
if (http2Session && !http2Session.closed)
this._sendRealThroughHttp2(http2Session);
else
this._sendReal(waitForData);
}
_shouldResendWithCredentials(res) {
if (res.statusCode === 401 && this.opts.credentials) {
const authInfo = (0, webauth_1.getAuthInfo)(res);
// NOTE: If we get 401 status code after credentials are sent, we should stop trying to authenticate.
if (!authInfo.isChallengeMessage && this.credentialsSent)
return false;
return authInfo.canAuthorize;
}
return false;
}
_onResponse(res) {
logger_1.default.destination.onResponse(this.opts, res);
if (this._shouldResendWithCredentials(res))
this._resendWithCredentials(res);
else if (!this.opts.isHttps && this.opts.proxy && res.statusCode === 407) {
logger_1.default.destination.onProxyAuthenticationError(this.opts);
this._fatalError(messages_1.MESSAGE.cantAuthorizeToProxy, this.opts.proxy.host);
}
else
this._emitOnResponse(res);
}
_emitOnResponse(res) {
this.hasResponse = true;
this.emit('response', res);
}
_onUpgrade(res, socket, head) {
logger_1.default.destination.onUpgradeRequest(this.opts, res);
if (head && head.length)
socket.unshift(head);
this._onResponse(res);
}
_resendWithCredentials(res) {
logger_1.default.destination.onResendWithCredentials(this.opts);
(0, webauth_1.addCredentials)(this.opts.credentials, this.opts, res, this.protocolInterface);
this.credentialsSent = true;
// NOTE: NTLM authentication requires using the same socket for the "negotiate" and "authenticate" requests.
// So, before sending the "authenticate" message, we should wait for data from the "challenge" response. It
// will mean that the socket is free.
this._send((0, webauth_1.requiresResBody)(res));
}
_fatalError(msg, url) {
if (!this.aborted) {
this.aborted = true;
this.req.destroy();
this.emit('fatalError', (0, messages_1.getText)(msg, { url: url || this.opts.url }));
}
}
_isDNSErr(err) {
return err.message && IS_DNS_ERR_MSG_RE.test(err.message) ||
!this.aborted && !this.hasResponse && err.code && IS_DNS_ERR_CODE_RE.test(err.code);
}
_isTunnelingErr(err) {
return this.opts.isHttps && err.message && TUNNELING_SOCKET_ERR_RE.test(err.message);
}
_isSocketHangUpErr(err) {
return err.message && SOCKET_HANG_UP_ERR_RE.test(err.message) &&
// NOTE: At this moment, we determinate the socket hand up error by internal stack trace.
// TODO: After what we will change minimal node.js version up to 8 need to rethink this code.
err.stack && (err.stack.includes('createHangUpError') || err.stack.includes('connResetException'));
}
_onTimeout() {
logger_1.default.destination.onTimeoutError(this.opts, this.timeout);
// NOTE: this handler is also called if we get an error response (for example, 404). So, we should check
// for the response presence before raising the timeout error.
if (!this.hasResponse)
this._fatalError(messages_1.MESSAGE.destRequestTimeout);
}
_onError(err) {
logger_1.default.destination.onError(this.opts, err);
if (this._isSocketHangUpErr(err))
this.emit('socketHangUp');
else if (requestAgent.shouldRegressHttps(err, this.opts)) {
requestAgent.regressHttps(this.opts);
this._send();
}
else if (this.opts.proxy && this._isTunnelingErr(err)) {
if (TUNNELING_AUTHORIZE_ERR_RE.test(err.message))
this._fatalError(messages_1.MESSAGE.cantAuthorizeToProxy, this.opts.proxy.host);
else
this._fatalError(messages_1.MESSAGE.cantEstablishTunnelingConnection, this.opts.proxy.host);
}
else if (this._isDNSErr(err)) {
if (!this.opts.isHttps && this.opts.proxy)
this._fatalError(messages_1.MESSAGE.cantEstablishProxyConnection, this.opts.proxy.host);
else
this._fatalError(messages_1.MESSAGE.cantResolveUrl);
}
else
this.emit('error', err);
}
}
exports.default = DestinationRequest;module.exports = exports.default;