mirror of
https://github.com/http-party/node-http-proxy.git
synced 2025-12-08 20:59:18 +00:00
216 lines
5.6 KiB
JavaScript
216 lines
5.6 KiB
JavaScript
const common = exports,
|
||
extend = require('util')._extend,
|
||
required = require('requires-port');
|
||
|
||
const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i,
|
||
isSSL = /^https|wss/;
|
||
|
||
/**
|
||
* Simple Regex for testing if protocol is https
|
||
*/
|
||
common.isSSL = isSSL;
|
||
/**
|
||
* Copies the right headers from `options` and `req` to
|
||
* `outgoing` which is then used to fire the proxied
|
||
* request.
|
||
*
|
||
* Examples:
|
||
*
|
||
* common.setupOutgoing(outgoing, options, req)
|
||
* // => { host: ..., hostname: ...}
|
||
*
|
||
* @param {Object} Outgoing Base object to be filled with required properties
|
||
* @param {Object} Options Config object passed to the proxy
|
||
* @param {ClientRequest} Req Request Object
|
||
* @param {String} Forward String to select forward or target
|
||
*
|
||
* @return {Object} Outgoing Object with all required properties set
|
||
*
|
||
* @api private
|
||
*/
|
||
|
||
common.setupOutgoing = function(outgoing, options, req, forward) {
|
||
const target = options[forward || 'target'];
|
||
const sslEnabled = isSSL.test(target.protocol)
|
||
|
||
outgoing.port = target.port || (sslEnabled ? 443 : 80);
|
||
|
||
['host', 'hostname', 'socketPath', 'pfx', 'key',
|
||
'passphrase', 'cert', 'ca', 'ciphers', 'secureProtocol'].forEach(
|
||
function(e) { outgoing[e] = target[e]; }
|
||
);
|
||
|
||
outgoing.method = options.method || req.method;
|
||
outgoing.headers = extend({}, req.headers);
|
||
|
||
if (options.headers) {
|
||
extend(outgoing.headers, options.headers);
|
||
}
|
||
|
||
if (options.auth) {
|
||
outgoing.auth = options.auth;
|
||
}
|
||
|
||
if (options.ca) {
|
||
outgoing.ca = options.ca;
|
||
}
|
||
|
||
if (sslEnabled) {
|
||
outgoing.rejectUnauthorized = (typeof options.secure === 'undefined') ? true : options.secure;
|
||
}
|
||
|
||
outgoing.agent = options.agent || false;
|
||
outgoing.localAddress = options.localAddress;
|
||
|
||
//
|
||
// Remark: If we are false and not upgrading, set the connection: close. This is the right thing to do
|
||
// as node core doesn't handle this COMPLETELY properly yet.
|
||
//
|
||
if (!outgoing.agent) {
|
||
outgoing.headers = outgoing.headers || {};
|
||
if (typeof outgoing.headers.connection !== 'string'
|
||
|| !upgradeHeader.test(outgoing.headers.connection)
|
||
) { outgoing.headers.connection = 'close'; }
|
||
}
|
||
|
||
|
||
// the final path is target path + relative path requested by user:
|
||
const targetPath = target && options.prependPath !== false
|
||
? (target.pathname || '')
|
||
: '';
|
||
|
||
// Base just needs to resemble a valid URL,
|
||
// we only care about the parsing of the path & params
|
||
const reqUrl = new URL(req.url, 'http://doesntmatter.com')
|
||
|
||
for(entry of target.searchParams.entries()) {
|
||
reqUrl.searchParams.set(entry[0], entry[1])
|
||
}
|
||
|
||
const params = reqUrl.search
|
||
|
||
let outgoingPath = !options.toProxy
|
||
? (reqUrl.pathname || '')
|
||
: req.url;
|
||
|
||
//
|
||
// Remark: ignorePath will just straight up ignore whatever the request's
|
||
// path is. This can be labeled as FOOT-GUN material if you do not know what
|
||
// you are doing and are using conflicting options.
|
||
//
|
||
outgoingPath = !options.ignorePath ? outgoingPath : '';
|
||
|
||
outgoing.path = [targetPath, outgoingPath].filter(Boolean).join('/').replace(/\/+/g, '/') + params
|
||
|
||
if (options.changeOrigin) {
|
||
outgoing.headers.host =
|
||
required(outgoing.port, target.protocol) && !hasPort(outgoing.host)
|
||
? outgoing.host + ':' + outgoing.port
|
||
: outgoing.host;
|
||
}
|
||
|
||
return outgoing;
|
||
};
|
||
|
||
/**
|
||
* Set the proper configuration for sockets,
|
||
* set no delay and set keep alive, also set
|
||
* the timeout to 0.
|
||
*
|
||
* Examples:
|
||
*
|
||
* common.setupSocket(socket)
|
||
* // => Socket
|
||
*
|
||
* @param {Socket} Socket instance to setup
|
||
*
|
||
* @return {Socket} Return the configured socket.
|
||
*
|
||
* @api private
|
||
*/
|
||
|
||
common.setupSocket = function(socket) {
|
||
socket.setTimeout(0);
|
||
socket.setNoDelay(true);
|
||
|
||
socket.setKeepAlive(true, 0);
|
||
|
||
return socket;
|
||
};
|
||
|
||
/**
|
||
* Get the port number from the host. Or guess it based on the connection type.
|
||
*
|
||
* @param {Request} req Incoming HTTP request.
|
||
*
|
||
* @return {String} The port number.
|
||
*
|
||
* @api private
|
||
*/
|
||
common.getPort = function(req) {
|
||
const res = req.headers.host ? req.headers.host.match(/:(\d+)/) : '';
|
||
|
||
return res ?
|
||
res[1] :
|
||
common.hasEncryptedConnection(req) ? '443' : '80';
|
||
};
|
||
|
||
/**
|
||
* Check if the request has an encrypted connection.
|
||
*
|
||
* @param {Request} req Incoming HTTP request.
|
||
*
|
||
* @return {Boolean} Whether the connection is encrypted or not.
|
||
*
|
||
* @api private
|
||
*/
|
||
common.hasEncryptedConnection = function(req) {
|
||
return Boolean(req.connection.encrypted || req.connection.pair);
|
||
};
|
||
|
||
/**
|
||
* Rewrites or removes the domain of a cookie header
|
||
*
|
||
* @param {String|Array} Header
|
||
* @param {Object} Config, mapping of domain to rewritten domain.
|
||
* '*' key to match any domain, null value to remove the domain.
|
||
*
|
||
* @api private
|
||
*/
|
||
common.rewriteCookieProperty = function rewriteCookieProperty(header, config, property) {
|
||
if (Array.isArray(header)) {
|
||
return header.map(function (headerElement) {
|
||
return rewriteCookieProperty(headerElement, config, property);
|
||
});
|
||
}
|
||
return header.replace(new RegExp("(;\\s*" + property + "=)([^;]+)", 'i'), function(match, prefix, previousValue) {
|
||
let newValue;
|
||
if (previousValue in config) {
|
||
newValue = config[previousValue];
|
||
} else if ('*' in config) {
|
||
newValue = config['*'];
|
||
} else {
|
||
//no match, return previous value
|
||
return match;
|
||
}
|
||
if (newValue) {
|
||
//replace value
|
||
return prefix + newValue;
|
||
} else {
|
||
//remove value
|
||
return '';
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Check the host and see if it potentially has a port in it (keep it simple)
|
||
*
|
||
* @returns {Boolean} Whether we have one or not
|
||
*
|
||
* @api private
|
||
*/
|
||
function hasPort(host) {
|
||
return !!~host.indexOf(':');
|
||
};
|