'use strict';
Object.defineProperty(exports, “__esModule”, {
value: true
}); exports.maybeAddProxyAuthorizationHeader = exports.findFreePort = exports.PORT_SELECTION_CONFIG = exports.addHeader = exports.parseProxyAuthorizationHeader = exports.redactParsedUrl = exports.redactUrl = exports.parseUrl = exports.isInvalidHeader = exports.isHopByHopHeader = exports.parseHostHeader = undefined;
var _url = require('url');
var _url2 = _interopRequireDefault(_url);
var _http_common = require('_http_common');
var _portastic = require('portastic');
var _portastic2 = _interopRequireDefault(_portastic);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// import through from 'through';
var HOST_HEADER_REGEX = /^((([a-zA-Z0-9]|[a-zA-Z0-9-]*).)*([A-Za-z0-9]|[A-Za-z0-9-]*))(:([0-9]+))?$/;
/**
* Parsed the 'Host' HTTP header and returns an object with { host: String, port: Number }. * For example, for 'www.example.com:80' it returns { host: 'www.example.com', port: 80 }. * If port is not present, the function * If the header is invalid, returns null. * @param hostHeader * @return {*} */
// eslint-disable-line var parseHostHeader = exports.parseHostHeader = function parseHostHeader(hostHeader) {
var matches = HOST_HEADER_REGEX.exec(hostHeader || ''); if (!matches) return null; var hostname = matches[1]; if (hostname.length > 255) return null; var port = null; if (matches[5]) { port = parseInt(matches[6], 10); if (!(port > 0 && port <= 65535)) return null; } return { hostname: hostname, port: port };
};
var HOP_BY_HOP_HEADERS = ['Connection', 'Keep-Alive', 'Proxy-Authenticate', 'Proxy-Authorization', 'TE', 'Trailers', 'Transfer-Encoding', 'Upgrade'];
var HOP_BY_HOP_HEADERS_REGEX = new RegExp('^(' + HOP_BY_HOP_HEADERS.join('|') + ')$', 'i');
var isHopByHopHeader = exports.isHopByHopHeader = function isHopByHopHeader(header) {
return HOP_BY_HOP_HEADERS_REGEX.test(header);
};
// This code is based on Node.js' validateHeader() function from _http_outgoing.js module // (see github.com/nodejs/node/blob/189d29f39e6de9ccf10682bfd1341819b4a2291f/lib/_http_outgoing.js#L485) var isInvalidHeader = exports.isInvalidHeader = function isInvalidHeader(name, value) {
// NOTE: These are internal Node.js functions, they might stop working in the future! return typeof name !== 'string' || !name || !(0, _http_common._checkIsHttpToken)(name) || value === undefined || (0, _http_common._checkInvalidHeaderChar)(value);
};
/**
* Sames are Node's url.parse() just adds the 'username', 'password' and 'scheme' fields. * Also this method makes sure "port" is a number rather than a string. * Note that `scheme` is always lower-cased (e.g. `ftp`). * @param url * @ignore */
var parseUrl = exports.parseUrl = function parseUrl(url) {
var parsed = _url2.default.parse(url); parsed.username = null; parsed.password = null; parsed.scheme = null; if (parsed.auth) { var matches = /^([^:]+)(:?)(.*)$/.exec(parsed.auth); if (matches && matches.length === 4) { parsed.username = matches[1]; if (matches[2] === ':') parsed.password = matches[3]; } } if (parsed.protocol) { var _matches = /^([a-z0-9]+):$/i.exec(parsed.protocol); if (_matches && _matches.length === 2) { parsed.scheme = _matches[1]; } } if (parsed.port) { parsed.port = parseInt(parsed.port, 10); } return parsed;
};
/**
* Redacts password from a URL, so that it can be shown in logs, results etc. * For example, converts URL such as * 'https://username:password@www.example.com/path#hash' * to 'https://username:<redacted>@www.example.com/path#hash' * @param url URL, it must contain at least protocol and hostname * @param passwordReplacement The string that replaces password, by default it is '<redacted>' * @returns {string} * @ignore */
var redactUrl = exports.redactUrl = function redactUrl(url, passwordReplacement) {
return redactParsedUrl(parseUrl(url), passwordReplacement);
};
var redactParsedUrl = exports.redactParsedUrl = function redactParsedUrl(parsedUrl) {
var passwordReplacement = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '<redacted>'; var p = parsedUrl; var auth = null; if (p.username) { if (p.password) { auth = p.username + ':' + passwordReplacement; } else { auth = '' + p.username; } } return p.protocol + '//' + (auth || '') + (auth ? '@' : '') + p.host + (p.path || '') + (p.hash || '');
};
var PROXY_AUTH_HEADER_REGEX = /^([a-z0-9-]+) ([a-z0-9+/=]+)$/i;
/**
* Parses the content of the Proxy-Authorization HTTP header. * @param header * @returns {*} Object with fields { type: String, username: String, password: String } * or null if string parsing failed. Note that password and username might be empty strings. */
var parseProxyAuthorizationHeader = exports.parseProxyAuthorizationHeader = function parseProxyAuthorizationHeader(header) {
var matches = PROXY_AUTH_HEADER_REGEX.exec(header); if (!matches) return null; var auth = Buffer.from(matches[2], 'base64').toString(); if (!auth) return null; var index = auth.indexOf(':'); return { type: matches[1], username: index >= 0 ? auth.substr(0, index) : auth, password: index >= 0 ? auth.substr(index + 1) : '' };
};
/**
* Works like Bash tee, but instead of passing output to file, * passes output to log * * @param {String} name identifier * @param {Boolean} initialOnly log only initial chunk of data * @return {through} duplex stream (pipe)
export const tee = (name, initialOnly = true) => {
console.log('tee'); let maxChunks = 2; const duplex = through((chunk) => { if (maxChunks || !initialOnly) { // let msg = chunk.toString(); // msg += ''; maxChunks--; console.log(`pipe: ${JSON.stringify({ context: name, chunkHead: chunk.toString().slice(0, 100), })}`); } duplex.queue(chunk); }); return duplex;
}; */
var addHeader = exports.addHeader = function addHeader(headers, name, value) {
if (headers[name] === undefined) { headers[name] = value; } else if (Array.isArray(headers[name])) { headers[name].push(value); } else { headers[name] = [headers[name], value]; }
};
var PORT_SELECTION_CONFIG = exports.PORT_SELECTION_CONFIG = {
FROM: 20000, TO: 60000, RETRY_COUNT: 10
};
var findFreePort = exports.findFreePort = function findFreePort() {
// Let 'min' be a random value in the first half of the PORT_FROM-PORT_TO range, // to reduce a chance of collision if other ProxyChain is started at the same time. var half = Math.floor((PORT_SELECTION_CONFIG.TO - PORT_SELECTION_CONFIG.FROM) / 2); var opts = { min: PORT_SELECTION_CONFIG.FROM + Math.floor(Math.random() * half), max: PORT_SELECTION_CONFIG.TO, retrieve: 1 }; return _portastic2.default.find(opts).then(function (ports) { if (ports.length < 1) throw new Error('There are no more free ports in range from ' + PORT_SELECTION_CONFIG.FROM + ' to ' + PORT_SELECTION_CONFIG.TO); // eslint-disable-line max-len return ports[0]; });
};
var maybeAddProxyAuthorizationHeader = exports.maybeAddProxyAuthorizationHeader = function maybeAddProxyAuthorizationHeader(parsedUrl, headers) {
if (parsedUrl && parsedUrl.username) { var auth = parsedUrl.username; if (parsedUrl.password || parsedUrl.password === '') auth += ':' + parsedUrl.password; headers['Proxy-Authorization'] = 'Basic ' + Buffer.from(auth).toString('base64'); }
};