302 lines
9.8 KiB
JavaScript
302 lines
9.8 KiB
JavaScript
var _ = require('../util').lodash,
|
|
Property = require('./property').Property,
|
|
PropertyList = require('./property-list').PropertyList,
|
|
Url = require('./url').Url,
|
|
UrlMatchPattern = require('../url-pattern/url-match-pattern').UrlMatchPattern,
|
|
UrlMatchPatternList = require('../url-pattern/url-match-pattern-list').UrlMatchPatternList,
|
|
ProxyConfig,
|
|
PROTOCOL_DELIMITER = UrlMatchPattern.PROTOCOL_DELIMITER,
|
|
E = '',
|
|
COLON = ':',
|
|
DEFAULT_PORT = 8080,
|
|
PROTOCOL_HOST_SEPARATOR = '://',
|
|
MATCH_ALL_HOST_AND_PATH = '*:*/*',
|
|
AUTH_CREDENTIALS_SEPARATOR = '@',
|
|
DEFAULT_PROTOCOL = 'http',
|
|
ALLOWED_PROTOCOLS = ['http', 'https'],
|
|
// 'http+https://*:*/*'
|
|
DEFAULT_PATTERN = ALLOWED_PROTOCOLS.join(PROTOCOL_DELIMITER) + PROTOCOL_HOST_SEPARATOR + MATCH_ALL_HOST_AND_PATH;
|
|
|
|
/**
|
|
* The following is the object structure accepted as constructor parameter while calling `new ProxyConfig(...)`. It is
|
|
* also the structure exported when {@link Property#toJSON} or {@link Property#toObjectResolved} is called on a
|
|
* Proxy instance.
|
|
*
|
|
* @typedef ProxyConfig.definition
|
|
*
|
|
* @property {String=} [match = 'http+https://*\/*'] The match for which the proxy needs to be configured.
|
|
* @property {String=} [host = ''] The proxy server url.
|
|
* @property {Number=} [port = 8080] The proxy server port number.
|
|
* @property {Boolean=} [tunnel = false] The tunneling option for the proxy request.
|
|
* @property {Boolean=} [disabled = false] To override the proxy for the particular url, you need to provide true.
|
|
* @property {Boolean=} [authenticate = false] To enable authentication for the proxy, you need to provide true.
|
|
* @property {String=} [username] The proxy authentication username
|
|
* @property {String=} [password] The proxy authentication password
|
|
*
|
|
* @example <caption>JSON definition of an example proxy object</caption>
|
|
* {
|
|
* "match": "http+https://example.com/*",
|
|
* "host": "proxy.com",
|
|
* "port": "8080",
|
|
* "tunnel": true,
|
|
* "disabled": false,
|
|
* "authenticate": true,
|
|
* "username": "proxy_username",
|
|
* "password": "proxy_password"
|
|
* }
|
|
*/
|
|
_.inherit((
|
|
|
|
/**
|
|
* A ProxyConfig definition that represents the proxy configuration for an url match.
|
|
* Properties can then use the `.toObjectResolved` function to procure an object representation of the property with
|
|
* all the variable references replaced by corresponding values.
|
|
*
|
|
* @constructor
|
|
* @extends {Property}
|
|
* @param {ProxyConfig.definition=} [options] - Specifies object with props matches, server and tunnel.
|
|
*
|
|
* @example <caption>Create a new ProxyConfig</caption>
|
|
* var ProxyConfig = require('postman-collection').ProxyConfig,
|
|
* myProxyConfig = new ProxyConfig({
|
|
* host: 'proxy.com',
|
|
* match: 'http+https://example.com/*',
|
|
* port: 8080,
|
|
* tunnel: true,
|
|
* disabled: false,
|
|
* authenticate: true,
|
|
* username: 'proxy_username',
|
|
* password: 'proxy_password'
|
|
* });
|
|
*/
|
|
ProxyConfig = function ProxyConfig (options) {
|
|
// this constructor is intended to inherit and as such the super constructor is required to be executed
|
|
ProxyConfig.super_.call(this, options);
|
|
|
|
// Assign defaults before proceeding
|
|
_.assign(this, /** @lends ProxyConfig */ {
|
|
/**
|
|
* The proxy server host or ip
|
|
*
|
|
* @type {String}
|
|
*/
|
|
host: E,
|
|
|
|
/**
|
|
* The url mach for which the proxy has been associated with.
|
|
*
|
|
* @type {String}
|
|
*/
|
|
match: new UrlMatchPattern(DEFAULT_PATTERN),
|
|
|
|
/**
|
|
* The proxy server port number
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
port: DEFAULT_PORT,
|
|
|
|
/**
|
|
* This represents whether the tunneling needs to done while proxying this request.
|
|
*
|
|
* @type Boolean
|
|
*/
|
|
tunnel: false,
|
|
|
|
/**
|
|
* Proxy bypass list
|
|
*
|
|
* @type {UrlMatchPatternList}
|
|
*/
|
|
bypass: undefined,
|
|
|
|
/**
|
|
* Enable proxy authentication
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
authenticate: false,
|
|
|
|
/**
|
|
* Proxy auth username
|
|
*
|
|
* @type {String}
|
|
*/
|
|
username: undefined,
|
|
|
|
/**
|
|
* Proxy auth password
|
|
*
|
|
* @type {String}
|
|
*/
|
|
password: undefined
|
|
});
|
|
|
|
this.update(options);
|
|
}), Property);
|
|
|
|
_.assign(ProxyConfig.prototype, /** @lends ProxyConfig.prototype */ {
|
|
/**
|
|
* Defines whether this property instances requires an id
|
|
*
|
|
* @private
|
|
* @readOnly
|
|
* @type {Boolean}
|
|
*/
|
|
_postman_propertyRequiresId: true,
|
|
|
|
/**
|
|
* Updates the properties of the proxy object based on the options provided.
|
|
*
|
|
* @param {ProxyConfig.definition} options The proxy object structure.
|
|
*/
|
|
update: function (options) {
|
|
if (!_.isObject(options)) {
|
|
return;
|
|
}
|
|
|
|
var parsedUrl,
|
|
port = _.get(options, 'port') >> 0;
|
|
|
|
if (_.isString(options.host)) {
|
|
// strip the protocol from given host
|
|
parsedUrl = new Url(options.host);
|
|
this.host = parsedUrl.getHost();
|
|
}
|
|
|
|
_.isString(options.match) && (this.match = new UrlMatchPattern(options.match));
|
|
_.isString(_.get(options, 'match.pattern')) && (this.match = new UrlMatchPattern(options.match.pattern));
|
|
port && (this.port = port);
|
|
_.isBoolean(options.tunnel) && (this.tunnel = options.tunnel);
|
|
// todo: Add update method in parent class Property and call that here
|
|
_.isBoolean(options.disabled) && (this.disabled = options.disabled);
|
|
_.isBoolean(options.authenticate) && (this.authenticate = options.authenticate);
|
|
_.isString(options.username) && (this.username = options.username);
|
|
_.isString(options.password) && (this.password = options.password);
|
|
|
|
// init bypass list from the given array
|
|
if (Array.isArray(options.bypass)) {
|
|
this.bypass = new UrlMatchPatternList(null, options.bypass);
|
|
}
|
|
// or, convert existing PropertyList or UrlMatchPatternList
|
|
else if (PropertyList.isPropertyList(options.bypass)) {
|
|
this.bypass = new UrlMatchPatternList(null, options.bypass.all());
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Updates the protocols in the match pattern
|
|
*
|
|
* @param {Array.<String>} protocols The array of protocols
|
|
*/
|
|
updateProtocols: function (protocols) {
|
|
if (!protocols) {
|
|
return;
|
|
}
|
|
|
|
var updatedProtocols,
|
|
hostAndPath = _.split(this.match.pattern, PROTOCOL_HOST_SEPARATOR)[1];
|
|
|
|
if (!hostAndPath) {
|
|
return;
|
|
}
|
|
|
|
updatedProtocols = _.intersection(ALLOWED_PROTOCOLS, _.castArray(protocols));
|
|
_.isEmpty(updatedProtocols) && (updatedProtocols = ALLOWED_PROTOCOLS);
|
|
|
|
this.match.update({
|
|
pattern: updatedProtocols.join(PROTOCOL_DELIMITER) + PROTOCOL_HOST_SEPARATOR + hostAndPath
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Tests the url string with the match provided.
|
|
* Follows the https://developer.chrome.com/extensions/match_patterns pattern for pattern validation and matching
|
|
*
|
|
* @param {String=} [urlStr] The url string for which the proxy match needs to be done.
|
|
*/
|
|
test: function (urlStr) {
|
|
var protocol = Url.isUrl(urlStr) ? urlStr.protocol : (Url.parse(urlStr || E).protocol || E);
|
|
|
|
// to allow target URLs without any protocol. e.g.: 'foo.com/bar'
|
|
if (_.isEmpty(protocol)) {
|
|
protocol = DEFAULT_PROTOCOL;
|
|
urlStr = protocol + PROTOCOL_HOST_SEPARATOR + urlStr;
|
|
}
|
|
|
|
// this ensures we don't proceed any further for any non-supported protocol
|
|
if (!_.includes(ALLOWED_PROTOCOLS, protocol)) {
|
|
return false;
|
|
}
|
|
|
|
// don't proceed if the given URL should skip use of a proxy all together
|
|
if (this.bypass && this.bypass.test(urlStr)) {
|
|
return false;
|
|
}
|
|
|
|
return this.match.test(urlStr);
|
|
},
|
|
|
|
/**
|
|
* Returns the proxy server url.
|
|
*
|
|
* @returns {String}
|
|
*/
|
|
getProxyUrl: function () {
|
|
var auth = E;
|
|
|
|
// Add authentication method to URL if the same is requested. We do it this way because
|
|
// this is how `postman-request` library accepts auth credentials in its proxy configuration.
|
|
if (this.authenticate) {
|
|
auth = encodeURIComponent(this.username || E);
|
|
|
|
if (this.password) {
|
|
auth += COLON + encodeURIComponent(this.password);
|
|
}
|
|
|
|
if (auth) {
|
|
auth += AUTH_CREDENTIALS_SEPARATOR;
|
|
}
|
|
}
|
|
|
|
return DEFAULT_PROTOCOL + PROTOCOL_HOST_SEPARATOR + auth + this.host + COLON + this.port;
|
|
},
|
|
|
|
/**
|
|
* Returns the protocols supported.
|
|
*
|
|
* @returns {Array.<String>}
|
|
*/
|
|
getProtocols: function () {
|
|
return this.match.getProtocols();
|
|
}
|
|
});
|
|
|
|
_.assign(ProxyConfig, /** @lends ProxyConfig */ {
|
|
/**
|
|
* Defines the name of this property for internal use.
|
|
*
|
|
* @private
|
|
* @readOnly
|
|
* @type {String}
|
|
*/
|
|
_postman_propertyName: 'ProxyConfig',
|
|
|
|
/**
|
|
* Check whether an object is an instance of PostmanItem.
|
|
*
|
|
* @param {*} obj -
|
|
* @returns {Boolean}
|
|
*/
|
|
isProxyConfig: function (obj) {
|
|
return Boolean(obj) && ((obj instanceof ProxyConfig) ||
|
|
_.inSuperChain(obj.constructor, '_postman_propertyName', ProxyConfig._postman_propertyName));
|
|
}
|
|
});
|
|
|
|
module.exports = {
|
|
ProxyConfig,
|
|
ALLOWED_PROTOCOLS,
|
|
DEFAULT_PATTERN
|
|
};
|