get function name change

This commit is contained in:
Wise Colt
2020-03-28 03:15:21 +03:00
parent fba7b9bf01
commit 2d8a42e5fd
4896 changed files with 515350 additions and 8 deletions

338
node_modules/nock/lib/back.js generated vendored Normal file
View File

@@ -0,0 +1,338 @@
'use strict';
/* global Promise */
var _ = require('lodash');
var nock = require('./scope');
var recorder = require('./recorder');
var format = require('util').format;
var path = require('path');
var expect = require('chai').expect;
var debug = require('debug')('nock.back');
var _mode = null;
var fs;
try {
fs = require('fs');
} catch(err) {
// do nothing, probably in browser
}
var mkdirp;
try {
mkdirp = require('mkdirp');
} catch(err) {
// do nothing, probably in browser
}
/**
* nock the current function with the fixture given
*
* @param {string} fixtureName - the name of the fixture, e.x. 'foo.json'
* @param {object} options - [optional] extra options for nock with, e.x. `{ assert: true }`
* @param {function} nockedFn - [optional] callback function to be executed with the given fixture being loaded;
* if defined the function will be called with context `{ scopes: loaded_nocks || [] }`
* set as `this` and `nockDone` callback function as first and only parameter;
* if not defined a promise resolving to `{nockDone, context}` where `context` is
* aforementioned `{ scopes: loaded_nocks || [] }`
*
* List of options:
*
* @param {function} before - a preprocessing function, gets called before nock.define
* @param {function} after - a postprocessing function, gets called after nock.define
* @param {function} afterRecord - a postprocessing function, gets called after recording. Is passed the array
* of scopes recorded and should return the array scopes to save to the fixture
* @param {function} recorder - custom options to pass to the recorder
*
*/
function Back (fixtureName, options, nockedFn) {
if(!Back.fixtures) {
throw new Error( 'Back requires nock.back.fixtures to be set\n' +
'Ex:\n' +
'\trequire(nock).back.fixtures = \'/path/to/fixures/\'');
}
if (!_.isString(fixtureName)) {
throw new Error('Parameter fixtureName must be a string');
}
if( arguments.length === 1 ) {
options = {};
} else if( arguments.length === 2 ) {
// If 2nd parameter is a function then `options` has been omitted
// otherwise `options` haven't been omitted but `nockedFn` was.
if (_.isFunction(options)) {
nockedFn = options;
options = {};
}
}
_mode.setup();
var fixture = path.join(Back.fixtures, fixtureName)
, context = _mode.start(fixture, options);
var nockDone = function () {
_mode.finish(fixture, options, context);
};
debug('context:', context);
// If nockedFn is a function then invoke it, otherwise return a promise resolving to nockDone.
if (_.isFunction(nockedFn)) {
nockedFn.call(context, nockDone);
} else {
return Promise.resolve({nockDone: nockDone, context: context});
}
}
/*******************************************************************************
* Modes *
*******************************************************************************/
var wild = {
setup: function () {
nock.cleanAll();
recorder.restore();
nock.activate();
nock.enableNetConnect();
},
start: function () {
return load(); //don't load anything but get correct context
},
finish: function () {
//nothing to do
}
};
var dryrun = {
setup: function () {
recorder.restore();
nock.cleanAll();
nock.activate();
// We have to explicitly enable net connectivity as by default it's off.
nock.enableNetConnect();
},
start: function (fixture, options) {
var contexts = load(fixture, options);
nock.enableNetConnect();
return contexts;
},
finish: function () {
//nothing to do
}
};
var record = {
setup: function () {
recorder.restore();
recorder.clear();
nock.cleanAll();
nock.activate();
nock.disableNetConnect();
},
start: function (fixture, options) {
if (! fs) {
throw new Error('no fs');
}
var context = load(fixture, options);
if( !context.isLoaded ) {
recorder.record(_.assign({
dont_print: true,
output_objects: true
}, options && options.recorder));
context.isRecording = true;
}
return context;
},
finish: function (fixture, options, context) {
if( context.isRecording ) {
var outputs = recorder.outputs();
if( typeof options.afterRecord === 'function' ) {
outputs = options.afterRecord(outputs);
}
outputs = JSON.stringify(outputs, null, 4);
debug('recorder outputs:', outputs);
mkdirp.sync(path.dirname(fixture));
fs.writeFileSync(fixture, outputs);
}
}
};
var lockdown = {
setup: function () {
recorder.restore();
recorder.clear();
nock.cleanAll();
nock.activate();
nock.disableNetConnect();
},
start: function (fixture, options) {
return load(fixture, options);
},
finish: function () {
//nothing to do
}
};
function load (fixture, options) {
var context = {
scopes : [],
assertScopesFinished: function () {
assertScopes(this.scopes, fixture);
}
};
if( fixture && fixtureExists(fixture) ) {
var scopes = nock.loadDefs(fixture);
applyHook(scopes, options.before);
scopes = nock.define(scopes);
applyHook(scopes, options.after);
context.scopes = scopes;
context.isLoaded = true;
}
return context;
}
function applyHook(scopes, fn) {
if( !fn ) {
return;
}
if( typeof fn !== 'function' ) {
throw new Error ('processing hooks must be a function');
}
scopes.forEach(fn);
}
function fixtureExists(fixture) {
if (! fs) {
throw new Error('no fs');
}
return fs.existsSync(fixture);
}
function assertScopes (scopes, fixture) {
scopes.forEach(function (scope) {
expect( scope.isDone() )
.to.be.equal(
true,
format('%j was not used, consider removing %s to rerecord fixture', scope.pendingMocks(), fixture)
);
});
}
var Modes = {
wild: wild, //all requests go out to the internet, dont replay anything, doesnt record anything
dryrun: dryrun, //use recorded nocks, allow http calls, doesnt record anything, useful for writing new tests (default)
record: record, //use recorded nocks, record new nocks
lockdown: lockdown, //use recorded nocks, disables all http calls even when not nocked, doesnt record
};
Back.setMode = function(mode) {
if( !Modes.hasOwnProperty(mode) ) {
throw new Error ('Unknown mode: ' + mode);
}
Back.currentMode = mode;
debug('New nock back mode:', Back.currentMode);
_mode = Modes[mode];
_mode.setup();
};
Back.fixtures = null;
Back.currentMode = null;
Back.setMode(process.env.NOCK_BACK_MODE || 'dryrun');
module.exports = exports = Back;

402
node_modules/nock/lib/common.js generated vendored Normal file
View File

@@ -0,0 +1,402 @@
'use strict';
var _ = require('lodash');
var debug = require('debug')('nock.common');
var semver = require('semver')
/**
* Normalizes the request options so that it always has `host` property.
*
* @param {Object} options - a parsed options object of the request
*/
var normalizeRequestOptions = function(options) {
options.proto = options.proto || (options._https_ ? 'https': 'http');
options.port = options.port || ((options.proto === 'http') ? 80 : 443);
if (options.host) {
debug('options.host:', options.host);
if (! options.hostname) {
if (options.host.split(':').length == 2) {
options.hostname = options.host.split(':')[0];
} else {
options.hostname = options.host;
}
}
}
debug('options.hostname in the end: %j', options.hostname);
options.host = (options.hostname || 'localhost') + ':' + options.port;
debug('options.host in the end: %j', options.host);
/// lowercase host names
['hostname', 'host'].forEach(function(attr) {
if (options[attr]) {
options[attr] = options[attr].toLowerCase();
}
});
return options;
};
/**
* Returns true if the data contained in buffer is binary which in this case means
* that it cannot be reconstructed from its utf8 representation.
*
* @param {Object} buffer - a Buffer object
*/
var isBinaryBuffer = function(buffer) {
if(!Buffer.isBuffer(buffer)) {
return false;
}
// Test if the buffer can be reconstructed verbatim from its utf8 encoding.
var utfEncodedBuffer = buffer.toString('utf8');
var reconstructedBuffer = Buffer.from(utfEncodedBuffer, 'utf8');
var compareBuffers = function(lhs, rhs) {
if(lhs.length !== rhs.length) {
return false;
}
for(var i = 0; i < lhs.length; ++i) {
if(lhs[i] !== rhs[i]) {
return false;
}
}
return true;
};
// If the buffers are *not* equal then this is a "binary buffer"
// meaning that it cannot be faitfully represented in utf8.
return !compareBuffers(buffer, reconstructedBuffer);
};
/**
* If the chunks are Buffer objects then it returns a single Buffer object with the data from all the chunks.
* If the chunks are strings then it returns a single string value with data from all the chunks.
*
* @param {Array} chunks - an array of Buffer objects or strings
*/
var mergeChunks = function(chunks) {
if(_.isEmpty(chunks)) {
return Buffer.alloc(0);
}
// We assume that all chunks are Buffer objects if the first is buffer object.
var areBuffers = Buffer.isBuffer(_.first(chunks));
if(!areBuffers) {
// When the chunks are not buffers we assume that they are strings.
return chunks.join('');
}
// Merge all the buffers into a single Buffer object.
return Buffer.concat(chunks);
};
// Array where all information about all the overridden requests are held.
var requestOverride = [];
/**
* Overrides the current `request` function of `http` and `https` modules with
* our own version which intercepts issues HTTP/HTTPS requests and forwards them
* to the given `newRequest` function.
*
* @param {Function} newRequest - a function handling requests; it accepts four arguments:
* - proto - a string with the overridden module's protocol name (either `http` or `https`)
* - overriddenRequest - the overridden module's request function already bound to module's object
* - options - the options of the issued request
* - callback - the callback of the issued request
*/
var overrideRequests = function(newRequest) {
debug('overriding requests');
['http', 'https'].forEach(function(proto) {
debug('- overriding request for', proto);
var moduleName = proto, // 1 to 1 match of protocol and module is fortunate :)
module = {
http: require('http'),
https: require('https')
}[moduleName],
overriddenRequest = module.request,
overriddenGet = module.get;
if(requestOverride[moduleName]) {
throw new Error('Module\'s request already overridden for ' + moduleName + ' protocol.');
}
// Store the properties of the overridden request so that it can be restored later on.
requestOverride[moduleName] = {
module: module,
request: overriddenRequest,
get: overriddenGet
};
module.request = function(options, callback) {
// debug('request options:', options);
return newRequest(proto, overriddenRequest.bind(module), options, callback);
};
if (semver.satisfies(process.version, '>=8')) {
module.get = function(options, callback) {
var req = newRequest(proto, overriddenRequest.bind(module), options, callback);
req.end();
return req;
}
}
debug('- overridden request for', proto);
});
};
/**
* Restores `request` function of `http` and `https` modules to values they
* held before they were overridden by us.
*/
var restoreOverriddenRequests = function() {
debug('restoring requests');
// Restore any overridden requests.
_(requestOverride).keys().each(function(proto) {
debug('- restoring request for', proto);
var override = requestOverride[proto];
if(override) {
override.module.request = override.request;
override.module.get = override.get;
debug('- restored request for', proto);
}
});
requestOverride = [];
};
/**
* Get high level information about request as string
* @param {Object} options
* @param {string} options.method
* @param {string} options.port
* @param {string} options.proto
* @param {string} options.hostname
* @param {string} options.path
* @param {Object} options.headers
* @param {string|object} body
* @return {string}
*/
function stringifyRequest(options, body) {
var method = options.method || 'GET';
var port = options.port;
if (! port) port = (options.proto == 'https' ? '443' : '80');
if (options.proto == 'https' && port == '443' ||
options.proto == 'http' && port == '80') {
port = '';
}
if (port) port = ':' + port;
var path = options.path ? options.path : '';
var log = {
method: method,
url: options.proto + '://' + options.hostname + port + path,
headers: options.headers
};
if (body) {
log.body = body;
}
return JSON.stringify(log, null, 2);
}
function isContentEncoded(headers) {
var contentEncoding = _.get(headers, 'content-encoding');
return _.isString(contentEncoding) && contentEncoding !== '';
}
function contentEncoding(headers, encoder) {
var contentEncoding = _.get(headers, 'content-encoding');
return contentEncoding === encoder;
}
function isJSONContent(headers) {
var contentType = _.get(headers, 'content-type');
if (Array.isArray(contentType)) {
contentType = contentType[0];
}
contentType = (contentType || '').toLocaleLowerCase();
return contentType === 'application/json';
}
var headersFieldNamesToLowerCase = function(headers) {
if(!_.isObject(headers)) {
return headers;
}
// For each key in the headers, delete its value and reinsert it with lower-case key.
// Keys represent headers field names.
var lowerCaseHeaders = {};
_.forOwn(headers, function(fieldVal, fieldName) {
var lowerCaseFieldName = fieldName.toLowerCase();
if(!_.isUndefined(lowerCaseHeaders[lowerCaseFieldName])) {
throw new Error('Failed to convert header keys to lower case due to field name conflict: ' + lowerCaseFieldName);
}
lowerCaseHeaders[lowerCaseFieldName] = fieldVal;
});
return lowerCaseHeaders;
};
var headersFieldsArrayToLowerCase = function (headers) {
return _.uniq(_.map(headers, function (fieldName) {
return fieldName.toLowerCase();
}));
};
var headersArrayToObject = function (rawHeaders) {
if(!_.isArray(rawHeaders)) {
return rawHeaders;
}
var headers = {};
for (var i=0, len=rawHeaders.length; i<len; i=i+2) {
var key = rawHeaders[i].toLowerCase();
var value = rawHeaders[i+1];
if (headers[key]) {
headers[key] = _.isArray(headers[key]) ? headers[key] : [headers[key]];
headers[key].push(value);
} else {
headers[key] = value;
}
}
return headers;
};
/**
* Deletes the given `fieldName` property from `headers` object by performing
* case-insensitive search through keys.
*
* @headers {Object} headers - object of header field names and values
* @fieldName {String} field name - string with the case-insensitive field name
*/
var deleteHeadersField = function(headers, fieldNameToDelete) {
if(!_.isObject(headers) || !_.isString(fieldNameToDelete)) {
return;
}
var lowerCaseFieldNameToDelete = fieldNameToDelete.toLowerCase();
// Search through the headers and delete all values whose field name matches the given field name.
_(headers).keys().each(function(fieldName) {
var lowerCaseFieldName = fieldName.toLowerCase();
if(lowerCaseFieldName === lowerCaseFieldNameToDelete) {
delete headers[fieldName];
// We don't stop here but continue in order to remove *all* matching field names
// (even though if seen regorously there shouldn't be any)
}
});
};
function percentDecode (str) {
try {
return decodeURIComponent(str.replace(/\+/g, ' '));
} catch (e) {
return str;
}
}
function percentEncode(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
});
}
function matchStringOrRegexp(target, pattern) {
var str = (!_.isUndefined(target) && target.toString && target.toString()) || '';
return pattern instanceof RegExp ? str.match(pattern) : str === String(pattern);
}
/**
* Formats a query parameter.
*
* @param key The key of the query parameter to format.
* @param value The value of the query parameter to format.
* @param stringFormattingFn The function used to format string values. Can
* be used to encode or decode the query value.
*
* @returns the formatted [key, value] pair.
*/
function formatQueryValue(key, value, stringFormattingFn) {
switch (true) {
case _.isNumber(value): // fall-through
case _.isBoolean(value):
value = value.toString();
break;
case _.isUndefined(value): // fall-through
case _.isNull(value):
value = '';
break;
case _.isString(value):
if(stringFormattingFn) {
value = stringFormattingFn(value);
}
break;
case (value instanceof RegExp):
break;
case _.isArray(value):
var tmpArray = new Array(value.length);
for (var i = 0; i < value.length; ++i) {
tmpArray[i] = formatQueryValue(i, value[i], stringFormattingFn)[1];
}
value = tmpArray;
break;
case _.isObject(value):
var tmpObj = {};
_.forOwn(value, function(subVal, subKey){
var subPair = formatQueryValue(subKey, subVal, stringFormattingFn);
tmpObj[subPair[0]] = subPair[1];
});
value = tmpObj;
break;
}
if (stringFormattingFn) key = stringFormattingFn(key);
return [key, value];
}
function isStream(obj) {
return obj &&
(typeof a !== 'string') &&
(! Buffer.isBuffer(obj)) &&
_.isFunction(obj.setEncoding);
}
exports.normalizeRequestOptions = normalizeRequestOptions;
exports.isBinaryBuffer = isBinaryBuffer;
exports.mergeChunks = mergeChunks;
exports.overrideRequests = overrideRequests;
exports.restoreOverriddenRequests = restoreOverriddenRequests;
exports.stringifyRequest = stringifyRequest;
exports.isContentEncoded = isContentEncoded;
exports.contentEncoding = contentEncoding;
exports.isJSONContent = isJSONContent;
exports.headersFieldNamesToLowerCase = headersFieldNamesToLowerCase;
exports.headersFieldsArrayToLowerCase = headersFieldsArrayToLowerCase;
exports.headersArrayToObject = headersArrayToObject;
exports.deleteHeadersField = deleteHeadersField;
exports.percentEncode = percentEncode;
exports.percentDecode = percentDecode;
exports.matchStringOrRegexp = matchStringOrRegexp;
exports.formatQueryValue = formatQueryValue;
exports.isStream = isStream;

81
node_modules/nock/lib/delayed_body.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
'use strict';
/**
* Creates a stream which becomes the response body of the interceptor when a
* delay is set. The stream outputs the intended body and EOF after the delay.
*
* @param {String|Buffer|Stream} body - the body to write/pipe out
* @param {Integer} ms - The delay in milliseconds
* @constructor
*/
module.exports = DelayedBody;
var Transform = require('stream').Transform;
var EventEmitter = require('events').EventEmitter;
var noop = function () {};
var util = require('util');
var common = require('./common');
if (!Transform) {
// for barebones compatibility for node < 0.10
var FakeTransformStream = function () {
EventEmitter.call(this);
};
util.inherits(FakeTransformStream, EventEmitter);
FakeTransformStream.prototype.pause = noop;
FakeTransformStream.prototype.resume = noop;
FakeTransformStream.prototype.setEncoding = noop;
FakeTransformStream.prototype.write = function (chunk, encoding) {
var self = this;
process.nextTick(function () {
self.emit('data', chunk, encoding);
});
};
FakeTransformStream.prototype.end = function (chunk) {
var self = this;
if (chunk) {
self.write(chunk);
}
process.nextTick(function () {
self.emit('end');
});
};
Transform = FakeTransformStream;
}
function DelayedBody(ms, body) {
Transform.call(this);
var self = this;
var data = '';
var ended = false;
if (common.isStream(body)) {
body.on('data', function (chunk) {
data += Buffer.isBuffer(chunk) ? chunk.toString() : chunk;
});
body.once('end', function () {
ended = true;
});
body.resume();
}
setTimeout(function () {
if (common.isStream(body) && !ended) {
body.once('end', function () {
self.end(data);
});
} else {
self.end(data || body);
}
}, ms);
}
util.inherits(DelayedBody, Transform);
DelayedBody.prototype._transform = function (chunk, encoding, cb) {
this.push(chunk);
process.nextTick(cb);
};

5
node_modules/nock/lib/global_emitter.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
'use strict';
var EventEmitter = require('events').EventEmitter
module.exports = new EventEmitter();

429
node_modules/nock/lib/intercept.js generated vendored Normal file
View File

@@ -0,0 +1,429 @@
'use strict';
/**
* @module nock/intercepts
*/
var RequestOverrider = require('./request_overrider'),
common = require('./common'),
inherits = require('util').inherits,
Interceptor = require('./interceptor'),
http = require('http'),
parse = require('url').parse,
URL = require('url').URL,
_ = require('lodash'),
debug = require('debug')('nock.intercept'),
EventEmitter = require('events').EventEmitter,
globalEmitter = require('./global_emitter'),
timers = require('timers');
/**
* @name NetConnectNotAllowedError
* @private
* @desc Error trying to make a connection when disabled external access.
* @class
* @example
* nock.disableNetConnect();
* http.get('http://zombo.com');
* // throw NetConnectNotAllowedError
*/
function NetConnectNotAllowedError(host, path) {
Error.call(this);
this.name = 'NetConnectNotAllowedError';
this.code = 'ENETUNREACH'
this.message = 'Nock: Disallowed net connect for "' + host + path + '"';
Error.captureStackTrace(this, this.constructor);
}
inherits(NetConnectNotAllowedError, Error);
var allInterceptors = {},
allowNetConnect;
/**
* Enabled real request.
* @public
* @param {String|RegExp} matcher=RegExp.new('.*') Expression to match
* @example
* // Enables all real requests
* nock.enableNetConnect();
* @example
* // Enables real requests for url that matches google
* nock.enableNetConnect('google');
* @example
* // Enables real requests for url that matches google and amazon
* nock.enableNetConnect(/(google|amazon)/);
*/
function enableNetConnect(matcher) {
if (_.isString(matcher)) {
allowNetConnect = new RegExp(matcher);
} else if (_.isObject(matcher) && _.isFunction(matcher.test)) {
allowNetConnect = matcher;
} else {
allowNetConnect = /.*/;
}
}
function isEnabledForNetConnect(options) {
common.normalizeRequestOptions(options);
var enabled = allowNetConnect && allowNetConnect.test(options.host);
debug('Net connect', enabled ? '' : 'not', 'enabled for', options.host);
return enabled;
}
/**
* Disable all real requests.
* @public
* @param {String|RegExp} matcher=RegExp.new('.*') Expression to match
* @example
* nock.disableNetConnect();
*/
function disableNetConnect() {
allowNetConnect = undefined;
}
function isOn() {
return !isOff();
}
function isOff() {
return process.env.NOCK_OFF === 'true';
}
function add(key, interceptor, scope, scopeOptions, host) {
if (! allInterceptors.hasOwnProperty(key)) {
allInterceptors[key] = { key: key, scopes: [] };
}
interceptor.__nock_scope = scope;
// We need scope's key and scope options for scope filtering function (if defined)
interceptor.__nock_scopeKey = key;
interceptor.__nock_scopeOptions = scopeOptions;
// We need scope's host for setting correct request headers for filtered scopes.
interceptor.__nock_scopeHost = host;
interceptor.interceptionCounter = 0;
if (scopeOptions.allowUnmocked)
allInterceptors[key].allowUnmocked = true;
allInterceptors[key].scopes.push(interceptor);
}
function remove(interceptor) {
if (interceptor.__nock_scope.shouldPersist() || --interceptor.counter > 0) {
return;
}
var basePath = interceptor.basePath;
var interceptors = allInterceptors[basePath] && allInterceptors[basePath].scopes || [];
interceptors.some(function (thisInterceptor, i) {
return (thisInterceptor === interceptor) ? interceptors.splice(i, 1) : false;
});
}
function removeAll() {
Object.keys(allInterceptors).forEach(function(key) {
allInterceptors[key].scopes.forEach(function(interceptor) {
interceptor.scope.keyedInterceptors = {};
});
});
allInterceptors = {};
}
function interceptorsFor(options) {
var basePath,
matchingInterceptor;
common.normalizeRequestOptions(options);
debug('interceptors for %j', options.host);
basePath = options.proto + '://' + options.host;
debug('filtering interceptors for basepath', basePath);
// First try to use filteringScope if any of the interceptors has it defined.
_.each(allInterceptors, function(interceptor, k) {
_.each(interceptor.scopes, function(scope) {
var filteringScope = scope.__nock_scopeOptions.filteringScope;
// If scope filtering function is defined and returns a truthy value
// then we have to treat this as a match.
if(filteringScope && filteringScope(basePath)) {
debug('found matching scope interceptor');
// Keep the filtered scope (its key) to signal the rest of the module
// that this wasn't an exact but filtered match.
scope.__nock_filteredScope = scope.__nock_scopeKey;
matchingInterceptor = interceptor.scopes;
// Break out of _.each for scopes.
return false;
}
});
if (!matchingInterceptor && common.matchStringOrRegexp(basePath, interceptor.key)) {
if (interceptor.scopes.length === 0 && interceptor.allowUnmocked) {
matchingInterceptor = [
{
options: { allowUnmocked: true },
matchIndependentOfBody: function() { return false }
}
];
} else {
matchingInterceptor = interceptor.scopes;
}
// false to short circuit the .each
return false;
}
// Returning falsy value here (which will happen if we have found our matching interceptor)
// will break out of _.each for all interceptors.
return !matchingInterceptor;
});
return matchingInterceptor;
}
function removeInterceptor(options) {
var baseUrl, key, method, proto;
if (options instanceof Interceptor) {
baseUrl = options.basePath;
key = options._key;
} else {
proto = options.proto ? options.proto : 'http';
common.normalizeRequestOptions(options);
baseUrl = proto + '://' + options.host;
method = options.method && options.method.toUpperCase() || 'GET';
key = method + ' ' + baseUrl + (options.path || '/');
}
if (allInterceptors[baseUrl] && allInterceptors[baseUrl].scopes.length > 0) {
if (key) {
for (var i = 0; i < allInterceptors[baseUrl].scopes.length; i++) {
var interceptor = allInterceptors[baseUrl].scopes[i];
if (interceptor._key === key) {
allInterceptors[baseUrl].scopes.splice(i, 1);
interceptor.scope.remove(key, interceptor);
break;
}
}
} else {
allInterceptors[baseUrl].scopes.length = 0;
}
return true;
}
return false;
}
// Variable where we keep the ClientRequest we have overridden
// (which might or might not be node's original http.ClientRequest)
var originalClientRequest;
function ErroringClientRequest(error) {
if (http.OutgoingMessage) http.OutgoingMessage.call(this);
process.nextTick(function() {
this.emit('error', error);
}.bind(this));
}
if (http.ClientRequest) {
inherits(ErroringClientRequest, http.ClientRequest);
}
function overrideClientRequest() {
debug('Overriding ClientRequest');
if(originalClientRequest) {
throw new Error('Nock already overrode http.ClientRequest');
}
// ----- Extending http.ClientRequest
// Define the overriding client request that nock uses internally.
function OverriddenClientRequest(options, cb) {
if (http.OutgoingMessage) http.OutgoingMessage.call(this);
// Filter the interceptors per request options.
var interceptors = interceptorsFor(options);
if (isOn() && interceptors) {
debug('using', interceptors.length, 'interceptors');
// Use filtered interceptors to intercept requests.
var overrider = RequestOverrider(this, options, interceptors, remove, cb);
for(var propName in overrider) {
if (overrider.hasOwnProperty(propName)) {
this[propName] = overrider[propName];
}
}
} else {
debug('falling back to original ClientRequest');
// Fallback to original ClientRequest if nock is off or the net connection is enabled.
if(isOff() || isEnabledForNetConnect(options)) {
originalClientRequest.apply(this, arguments);
} else {
timers.setImmediate(function () {
var error = new NetConnectNotAllowedError(options.host, options.path);
this.emit('error', error);
}.bind(this));
}
}
}
if (http.ClientRequest) {
inherits(OverriddenClientRequest, http.ClientRequest);
} else {
inherits(OverriddenClientRequest, EventEmitter);
}
// Override the http module's request but keep the original so that we can use it and later restore it.
// NOTE: We only override http.ClientRequest as https module also uses it.
originalClientRequest = http.ClientRequest;
http.ClientRequest = OverriddenClientRequest;
debug('ClientRequest overridden');
}
function restoreOverriddenClientRequest() {
debug('restoring overridden ClientRequest');
// Restore the ClientRequest we have overridden.
if(!originalClientRequest) {
debug('- ClientRequest was not overridden');
} else {
http.ClientRequest = originalClientRequest;
originalClientRequest = undefined;
debug('- ClientRequest restored');
}
}
function isActive() {
// If ClientRequest has been overwritten by Nock then originalClientRequest is not undefined.
// This means that Nock has been activated.
return !_.isUndefined(originalClientRequest);
}
function interceptorScopes() {
return _.reduce(allInterceptors, function(result, interceptors) {
for (var interceptor in interceptors.scopes) {
result = result.concat(interceptors.scopes[interceptor].__nock_scope);
}
return result;
}, []);
}
function isDone() {
return _.every(interceptorScopes(), function(scope) {
return scope.isDone();
});
}
function pendingMocks() {
return _.flatten(_.map(interceptorScopes(), function(scope) {
return scope.pendingMocks();
}));
}
function activeMocks() {
return _.flatten(_.map(interceptorScopes(), function(scope) {
return scope.activeMocks();
}));
}
function activate() {
if(originalClientRequest) {
throw new Error('Nock already active');
}
overrideClientRequest();
// ----- Overriding http.request and https.request:
common.overrideRequests(function(proto, overriddenRequest, options, callback) {
// NOTE: overriddenRequest is already bound to its module.
var req,
res;
if (typeof options === 'string') {
options = parse(options);
} else if (URL && options instanceof URL) {
options = parse(options.toString());
}
options.proto = proto;
var interceptors = interceptorsFor(options)
if (isOn() && interceptors) {
var matches = false,
allowUnmocked = false;
matches = !! _.find(interceptors, function(interceptor) {
return interceptor.matchIndependentOfBody(options);
});
allowUnmocked = !! _.find(interceptors, function(interceptor) {
return interceptor.options.allowUnmocked;
});
if (! matches && allowUnmocked) {
if (proto === 'https') {
var ClientRequest = http.ClientRequest;
http.ClientRequest = originalClientRequest;
req = overriddenRequest(options, callback);
http.ClientRequest = ClientRequest;
} else {
req = overriddenRequest(options, callback);
}
globalEmitter.emit('no match', req);
return req;
}
// NOTE: Since we already overrode the http.ClientRequest we are in fact constructing
// our own OverriddenClientRequest.
req = new http.ClientRequest(options);
res = RequestOverrider(req, options, interceptors, remove);
if (callback) {
res.on('response', callback);
}
return req;
} else {
globalEmitter.emit('no match', options);
if (isOff() || isEnabledForNetConnect(options)) {
return overriddenRequest(options, callback);
} else {
var error = new NetConnectNotAllowedError(options.host, options.path);
return new ErroringClientRequest(error);
}
}
});
}
activate();
module.exports = add;
module.exports.removeAll = removeAll;
module.exports.removeInterceptor = removeInterceptor;
module.exports.isOn = isOn;
module.exports.activate = activate;
module.exports.isActive = isActive;
module.exports.isDone = isDone;
module.exports.pendingMocks = pendingMocks;
module.exports.activeMocks = activeMocks;
module.exports.enableNetConnect = enableNetConnect;
module.exports.disableNetConnect = disableNetConnect;
module.exports.overrideClientRequest = overrideClientRequest;
module.exports.restoreOverriddenClientRequest = restoreOverriddenClientRequest;

569
node_modules/nock/lib/interceptor.js generated vendored Normal file
View File

@@ -0,0 +1,569 @@
'use strict';
var mixin = require('./mixin')
, matchBody = require('./match_body')
, common = require('./common')
, _ = require('lodash')
, debug = require('debug')('nock.interceptor')
, stringify = require('json-stringify-safe')
, qs = require('qs');
var fs;
try {
fs = require('fs');
} catch (err) {
// do nothing, we're in the browser
}
module.exports = Interceptor;
function Interceptor(scope, uri, method, requestBody, interceptorOptions) {
this.scope = scope;
this.interceptorMatchHeaders = [];
if (typeof method === 'undefined' || !method) {
throw new Error('The "method" parameter is required for an intercept call.');
}
this.method = method.toUpperCase();
this.uri = uri;
this._key = this.method + ' ' + scope.basePath + scope.basePathname + (typeof uri === 'string' ? '' : '/') + uri;
this.basePath = this.scope.basePath;
this.path = (typeof uri === 'string') ? scope.basePathname + uri : uri;
this.baseUri = this.method + ' ' + scope.basePath + scope.basePathname;
this.options = interceptorOptions || {};
this.counter = 1;
this._requestBody = requestBody;
// We use lower-case header field names throughout Nock.
this.reqheaders = common.headersFieldNamesToLowerCase((scope.scopeOptions && scope.scopeOptions.reqheaders) || {});
this.badheaders = common.headersFieldsArrayToLowerCase((scope.scopeOptions && scope.scopeOptions.badheaders) || []);
this.delayInMs = 0;
this.delayConnectionInMs = 0;
this.optional = false;
}
Interceptor.prototype.optionally = function optionally(value) {
// The default behaviour of optionally() with no arguments is to make the mock optional.
value = (typeof value === 'undefined') ? true : value;
this.optional = value;
return this;
}
Interceptor.prototype.replyWithError = function replyWithError(errorMessage) {
this.errorMessage = errorMessage;
_.defaults(this.options, this.scope.scopeOptions);
this.scope.add(this._key, this, this.scope, this.scopeOptions);
return this.scope;
};
Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
if (arguments.length <= 2 && _.isFunction(statusCode)) {
body = statusCode;
statusCode = 200;
}
this.statusCode = statusCode;
_.defaults(this.options, this.scope.scopeOptions);
// convert rawHeaders from Array to Object
var headers = common.headersArrayToObject(rawHeaders);
if (this.scope._defaultReplyHeaders) {
headers = headers || {};
headers = common.headersFieldNamesToLowerCase(headers);
headers = mixin(this.scope._defaultReplyHeaders, headers);
}
if (this.scope.date) {
headers = headers || {};
headers['date'] = this.scope.date.toUTCString();
}
if (headers !== undefined) {
this.rawHeaders = [];
// makes sure all keys in headers are in lower case
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
this.rawHeaders.push(key);
this.rawHeaders.push(headers[key]);
}
}
// We use lower-case headers throughout Nock.
this.headers = common.headersFieldNamesToLowerCase(headers);
debug('reply.headers:', this.headers);
debug('reply.rawHeaders:', this.rawHeaders);
}
// If the content is not encoded we may need to transform the response body.
// Otherwise we leave it as it is.
if (!common.isContentEncoded(this.headers)) {
if (body && typeof (body) !== 'string' &&
typeof (body) !== 'function' &&
!Buffer.isBuffer(body) &&
!common.isStream(body)) {
try {
body = stringify(body);
if (!this.headers) {
this.headers = {};
}
if (!this.headers['content-type']) {
this.headers['content-type'] = 'application/json';
}
if (this.scope.contentLen) {
this.headers['content-length'] = body.length;
}
} catch (err) {
throw new Error('Error encoding response body into JSON');
}
}
}
this.body = body;
this.scope.add(this._key, this, this.scope, this.scopeOptions);
return this.scope;
};
Interceptor.prototype.replyWithFile = function replyWithFile(statusCode, filePath, headers) {
if (!fs) {
throw new Error('No fs');
}
var readStream = fs.createReadStream(filePath);
readStream.pause();
this.filePath = filePath;
return this.reply(statusCode, readStream, headers);
};
// Also match request headers
// https://github.com/pgte/nock/issues/163
Interceptor.prototype.reqheaderMatches = function reqheaderMatches(options, key) {
// We don't try to match request headers if these weren't even specified in the request.
if (!options.headers) {
return true;
}
var reqHeader = this.reqheaders[key];
var header = options.headers[key];
if (header && (typeof header !== 'string') && header.toString) {
header = header.toString();
}
// We skip 'host' header comparison unless it's available in both mock and actual request.
// This because 'host' may get inserted by Nock itself and then get recorder.
// NOTE: We use lower-case header field names throughout Nock.
if (key === 'host' &&
(_.isUndefined(header) ||
_.isUndefined(reqHeader))) {
return true;
}
if (!_.isUndefined(reqHeader) && !_.isUndefined(header)) {
if (_.isFunction(reqHeader)) {
return reqHeader(header);
} else if (common.matchStringOrRegexp(header, reqHeader)) {
return true;
}
}
debug('request header field doesn\'t match:', key, header, reqHeader);
return false;
};
Interceptor.prototype.match = function match(options, body, hostNameOnly) {
if (debug.enabled) {
debug('match %s, body = %s', stringify(options), stringify(body));
}
if (hostNameOnly) {
return options.hostname === this.scope.urlParts.hostname;
}
var method = (options.method || 'GET').toUpperCase()
, path = options.path
, matches
, matchKey
, proto = options.proto;
if (this.scope.transformPathFunction) {
path = this.scope.transformPathFunction(path);
}
if (typeof (body) !== 'string') {
body = body.toString();
}
if (this.scope.transformRequestBodyFunction) {
body = this.scope.transformRequestBodyFunction(body, this._requestBody);
}
var checkHeaders = function (header) {
if (_.isFunction(header.value)) {
return header.value(options.getHeader(header.name));
}
return common.matchStringOrRegexp(options.getHeader(header.name), header.value);
};
if (!this.scope.matchHeaders.every(checkHeaders) ||
!this.interceptorMatchHeaders.every(checkHeaders)) {
this.scope.logger('headers don\'t match');
return false;
}
var reqHeadersMatch =
!this.reqheaders ||
Object.keys(this.reqheaders).every(this.reqheaderMatches.bind(this, options));
if (!reqHeadersMatch) {
return false;
}
function reqheaderContains(header) {
return _.has(options.headers, header);
}
var reqContainsBadHeaders =
this.badheaders &&
_.some(this.badheaders, reqheaderContains);
if (reqContainsBadHeaders) {
return false;
}
// If we have a filtered scope then we use it instead reconstructing
// the scope from the request options (proto, host and port) as these
// two won't necessarily match and we have to remove the scope that was
// matched (vs. that was defined).
if (this.__nock_filteredScope) {
matchKey = this.__nock_filteredScope;
} else {
matchKey = proto + '://' + options.host;
if (
options.port && options.host.indexOf(':') < 0 &&
(options.port !== 80 || options.proto !== 'http') &&
(options.port !== 443 || options.proto !== 'https')
) {
matchKey += ":" + options.port;
}
}
// Match query strings when using query()
var matchQueries = true;
var queryIndex = -1;
var queryString;
var queries;
if (this.queries) {
queryIndex = path.indexOf('?');
queryString = (queryIndex !== -1) ? path.slice(queryIndex + 1) : '';
queries = qs.parse(queryString);
// Only check for query string matches if this.queries is an object
if (_.isObject(this.queries)) {
if (_.isFunction(this.queries)) {
matchQueries = this.queries(queries);
} else {
// Make sure that you have an equal number of keys. We are
// looping through the passed query params and not the expected values
// if the user passes fewer query params than expected but all values
// match this will throw a false positive. Testing that the length of the
// passed query params is equal to the length of expected keys will prevent
// us from doing any value checking BEFORE we know if they have all the proper
// params
debug('this.queries: %j', this.queries);
debug('queries: %j', queries);
if (_.size(this.queries) !== _.size(queries)) {
matchQueries = false;
} else {
var self = this;
_.forOwn(queries, function matchOneKeyVal(val, key) {
var expVal = self.queries[key];
var isMatch = true;
if (val === undefined || expVal === undefined) {
isMatch = false;
} else if (expVal instanceof RegExp) {
isMatch = common.matchStringOrRegexp(val, expVal);
} else if (_.isArray(expVal) || _.isObject(expVal)) {
isMatch = _.isEqual(val, expVal);
} else {
isMatch = common.matchStringOrRegexp(val, expVal);
}
matchQueries = matchQueries && !!isMatch;
});
}
debug('matchQueries: %j', matchQueries);
}
}
// Remove the query string from the path
if (queryIndex !== -1) {
path = path.substr(0, queryIndex);
}
}
if (typeof this.uri === 'function') {
matches = matchQueries &&
method === this.method &&
common.matchStringOrRegexp(matchKey, this.basePath) &&
this.uri.call(this, path);
} else {
matches = method === this.method &&
common.matchStringOrRegexp(matchKey, this.basePath) &&
common.matchStringOrRegexp(path, this.path) &&
matchQueries;
}
// special logger for query()
if (queryIndex !== -1) {
this.scope.logger('matching ' + matchKey + path + '?' + queryString + ' to ' + this._key +
' with query(' + stringify(this.queries) + '): ' + matches);
} else {
this.scope.logger('matching ' + matchKey + path + ' to ' + this._key + ': ' + matches);
}
if (matches) {
matches = (matchBody.call(options, this._requestBody, body));
if (!matches) {
this.scope.logger('bodies don\'t match: \n', this._requestBody, '\n', body);
}
}
return matches;
};
Interceptor.prototype.matchIndependentOfBody = function matchIndependentOfBody(options) {
var isRegex = _.isRegExp(this.path);
var isRegexBasePath = _.isRegExp(this.scope.basePath);
var method = (options.method || 'GET').toUpperCase()
, path = options.path
, proto = options.proto;
// NOTE: Do not split off the query params as the regex could use them
if (!isRegex) {
path = path ? path.split('?')[0] : '';
}
if (this.scope.transformPathFunction) {
path = this.scope.transformPathFunction(path);
}
var checkHeaders = function (header) {
return options.getHeader && common.matchStringOrRegexp(options.getHeader(header.name), header.value);
};
if (!this.scope.matchHeaders.every(checkHeaders) ||
!this.interceptorMatchHeaders.every(checkHeaders)) {
return false;
}
var comparisonKey = isRegex ? this.__nock_scopeKey : this._key;
var matchKey = method + ' ' + proto + '://' + options.host + path;
if (isRegex && !isRegexBasePath) {
return !!matchKey.match(comparisonKey) && !!path.match(this.path);
}
if(isRegexBasePath) {
return !!matchKey.match(this.scope.basePath) && !!path.match(this.path);
}
return comparisonKey === matchKey;
};
Interceptor.prototype.filteringPath = function filteringPath() {
if (_.isFunction(arguments[0])) {
this.scope.transformFunction = arguments[0];
}
return this;
};
Interceptor.prototype.discard = function discard() {
if ((this.scope.shouldPersist() || this.counter > 0) && this.filePath) {
this.body = fs.createReadStream(this.filePath);
this.body.pause();
}
if (!this.scope.shouldPersist() && this.counter < 1) {
this.scope.remove(this._key, this);
}
};
Interceptor.prototype.matchHeader = function matchHeader(name, value) {
this.interceptorMatchHeaders.push({ name: name, value: value });
return this;
};
Interceptor.prototype.basicAuth = function basicAuth(options) {
var username = options['user'];
var password = options['pass'] || '';
var name = 'authorization';
var value = 'Basic ' + Buffer.from(username + ':' + password).toString('base64');
this.interceptorMatchHeaders.push({ name: name, value: value });
return this;
};
/**
* Set query strings for the interceptor
* @name query
* @param Object Object of query string name,values (accepts regexp values)
* @public
* @example
* // Will match 'http://zombo.com/?q=t'
* nock('http://zombo.com').get('/').query({q: 't'});
*/
Interceptor.prototype.query = function query(queries) {
this.queries = this.queries || {};
// Allow all query strings to match this route
if (queries === true) {
this.queries = queries;
return this;
}
if (_.isFunction(queries)) {
this.queries = queries;
return this;
}
var stringFormattingFn;
if (this.scope.scopeOptions.encodedQueryParams) {
stringFormattingFn = common.percentDecode;
}
for (var key in queries) {
if (_.isUndefined(this.queries[key])) {
var formattedPair = common.formatQueryValue(key, queries[key], stringFormattingFn);
this.queries[formattedPair[0]] = formattedPair[1];
}
}
return this;
};
/**
* Set number of times will repeat the interceptor
* @name times
* @param Integer Number of times to repeat (should be > 0)
* @public
* @example
* // Will repeat mock 5 times for same king of request
* nock('http://zombo.com).get('/').times(5).reply(200, 'Ok');
*/
Interceptor.prototype.times = function times(newCounter) {
if (newCounter < 1) {
return this;
}
this.counter = newCounter;
return this;
};
/**
* An sugar syntax for times(1)
* @name once
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').once.reply(200, 'Ok');
*/
Interceptor.prototype.once = function once() {
return this.times(1);
};
/**
* An sugar syntax for times(2)
* @name twice
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').twice.reply(200, 'Ok');
*/
Interceptor.prototype.twice = function twice() {
return this.times(2);
};
/**
* An sugar syntax for times(3).
* @name thrice
* @see {@link times}
* @public
* @example
* nock('http://zombo.com).get('/').thrice.reply(200, 'Ok');
*/
Interceptor.prototype.thrice = function thrice() {
return this.times(3);
};
/**
* Delay the response by a certain number of ms.
*
* @param {(integer|object)} opts - Number of milliseconds to wait, or an object
* @param {integer} [opts.head] - Number of milliseconds to wait before response is sent
* @param {integer} [opts.body] - Number of milliseconds to wait before response body is sent
* @return {interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delay = function delay(opts) {
var headDelay = 0;
var bodyDelay = 0;
if (_.isNumber(opts)) {
headDelay = opts;
} else if (_.isObject(opts)) {
headDelay = opts.head || 0;
bodyDelay = opts.body || 0;
} else {
throw new Error("Unexpected input opts" + opts);
}
return this.delayConnection(headDelay)
.delayBody(bodyDelay);
};
/**
* Delay the response body by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait before response is sent
* @return {interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delayBody = function delayBody(ms) {
this.delayInMs += ms;
return this;
};
/**
* Delay the connection by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait
* @return {interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delayConnection = function delayConnection(ms) {
this.delayConnectionInMs += ms;
return this;
};
Interceptor.prototype.getTotalDelay = function getTotalDelay() {
return this.delayInMs + this.delayConnectionInMs;
};
/**
* Make the socket idle for a certain number of ms (simulated).
*
* @param {integer} ms - Number of milliseconds to wait
* @return {interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.socketDelay = function socketDelay(ms) {
this.socketDelayInMs = ms;
return this;
};

117
node_modules/nock/lib/match_body.js generated vendored Normal file
View File

@@ -0,0 +1,117 @@
'use strict';
var deepEqual = require('deep-equal');
var qs = require('qs');
var _ = require('lodash')
var common = require('./common');
module.exports =
function matchBody(spec, body) {
if (typeof spec === 'undefined') {
return true;
}
var options = this || {};
if (Buffer.isBuffer(body)) {
body = body.toString();
}
if (spec instanceof RegExp) {
if (typeof body === "string") {
return body.match(spec);
}
}
if (Buffer.isBuffer(spec)) {
if (common.isBinaryBuffer(spec)) {
spec = spec.toString('hex');
} else {
spec = spec.toString('utf8');
}
}
var contentType = (
options.headers &&
(options.headers['Content-Type'] || options.headers['content-type']) ||
''
).toString();
var isMultipart = contentType.indexOf('multipart') >= 0;
var isUrlencoded = contentType.indexOf('application/x-www-form-urlencoded') >= 0;
// try to transform body to json
var json;
if (typeof spec === 'object' || typeof spec === 'function') {
try { json = JSON.parse(body);} catch(err) {}
if (json !== undefined) {
body = json;
} else if (isUrlencoded) {
body = qs.parse(body, { allowDots: true });
}
}
if (typeof spec === "function") {
return spec.call(this, body);
}
//strip line endings from both so that we get a match no matter what OS we are running on
//if Content-Type does not contains 'multipart'
if (!isMultipart && typeof body === "string") {
body = body.replace(/\r?\n|\r/g, '');
}
if (!isMultipart && typeof spec === "string") {
spec = spec.replace(/\r?\n|\r/g, '');
}
if (isUrlencoded) {
spec = mapValuesDeep(spec, function(val) {
if (_.isRegExp(val)) {
return val
}
return val + ''
})
}
return deepEqualExtended(spec, body);
};
/**
* Based on lodash issue discussion
* https://github.com/lodash/lodash/issues/1244
*/
function mapValuesDeep(obj, cb) {
if (_.isArray(obj)) {
return obj.map(function(v) {
return mapValuesDeep(v, cb)
})
}
if (_.isPlainObject(obj)) {
return _.mapValues(obj, function(v) {
return mapValuesDeep(v, cb)
})
}
return cb(obj)
}
function deepEqualExtended(spec, body) {
if (spec && spec.constructor === RegExp) {
return spec.test(body);
}
if (spec && (spec.constructor === Object || spec.constructor === Array) && body) {
var keys = Object.keys(spec);
var bodyKeys = Object.keys(body);
if (keys.length !== bodyKeys.length) {
return false;
}
for (var i = 0; i < keys.length; i++) {
if (!deepEqualExtended(spec[keys[i]], body[keys[i]])) {
return false;
}
}
return true;
}
return deepEqual(spec, body, { strict: true });
}

14
node_modules/nock/lib/mixin.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
'use strict';
var _ = require("lodash");
function mixin(a, b) {
if (! a) { a = {}; }
if (! b) {b = {}; }
a = _.cloneDeep(a);
for(var prop in b) {
a[prop] = b[prop];
}
return a;
}
module.exports = mixin;

406
node_modules/nock/lib/recorder.js generated vendored Normal file
View File

@@ -0,0 +1,406 @@
'use strict';
var inspect = require('util').inspect;
var parse = require('url').parse;
var common = require('./common');
var intercept = require('./intercept');
var debug = require('debug')('nock.recorder');
var _ = require('lodash');
var URL = require('url');
var semver = require('semver')
var qs = require('qs');
var SEPARATOR = '\n<<<<<<-- cut here -->>>>>>\n';
var recordingInProgress = false;
var outputs = [];
function getScope(options) {
common.normalizeRequestOptions(options);
var scope = [];
if (options._https_) {
scope.push('https://');
} else {
scope.push('http://');
}
scope.push(options.host);
// If a non-standard port wasn't specified in options.host, include it from options.port.
if(options.host.indexOf(':') === -1 &&
options.port &&
((options._https_ && options.port.toString() !== '443') ||
(!options._https_ && options.port.toString() !== '80'))) {
scope.push(':');
scope.push(options.port);
}
return scope.join('');
}
function getMethod(options) {
return (options.method || 'GET');
}
var getBodyFromChunks = function(chunks, headers) {
// If we have headers and there is content-encoding it means that
// the body shouldn't be merged but instead persisted as an array
// of hex strings so that the responses can be mocked one by one.
if(common.isContentEncoded(headers)) {
return {
body: _.map(chunks, function(chunk) {
if(!Buffer.isBuffer(chunk)) {
if (typeof chunk === 'string') {
chunk = Buffer.from(chunk);
} else {
throw new Error('content-encoded responses must all be binary buffers');
}
}
return chunk.toString('hex');
})
};
}
var mergedBuffer = common.mergeChunks(chunks);
// The merged buffer can be one of three things:
// 1. A binary buffer which then has to be recorded as a hex string.
// 2. A string buffer which represents a JSON object.
// 3. A string buffer which doesn't represent a JSON object.
var isBinary = common.isBinaryBuffer(mergedBuffer);
if(isBinary) {
return {
body: mergedBuffer.toString('hex'),
isBinary: true
}
} else {
var maybeStringifiedJson = mergedBuffer.toString('utf8');
try {
return {
body: JSON.parse(maybeStringifiedJson),
isBinary: false
};
} catch(err) {
return {
body: maybeStringifiedJson,
isBinary: false
};
}
}
};
function generateRequestAndResponseObject(req, bodyChunks, options, res, dataChunks) {
var response = getBodyFromChunks(dataChunks, res.headers)
options.path = req.path;
var nockDef = {
scope: getScope(options),
method: getMethod(options),
path: options.path,
body: getBodyFromChunks(bodyChunks).body,
status: res.statusCode,
response: response.body,
rawHeaders: res.rawHeaders || res.headers,
reqheaders: req._headers
};
if (response.isBinary) {
nockDef.responseIsBinary = true
}
return nockDef;
}
function generateRequestAndResponse(req, bodyChunks, options, res, dataChunks) {
var requestBody = getBodyFromChunks(bodyChunks).body;
var responseBody = getBodyFromChunks(dataChunks, res.headers).body;
// Remove any query params from options.path so they can be added in the query() function
var path = options.path;
var queryIndex = 0;
var queryObj = {};
if ((queryIndex = req.path.indexOf('?')) !== -1) {
// Remove the query from the path
path = path.substring(0, queryIndex);
// Create the query() object
var queryStr = req.path.slice(queryIndex + 1);
queryObj = qs.parse(queryStr);
}
// Always encoding the query parameters when recording.
var encodedQueryObj = {};
for (var key in queryObj) {
var formattedPair = common.formatQueryValue(key, queryObj[key], common.percentEncode);
encodedQueryObj[formattedPair[0]] = formattedPair[1];
}
var ret = [];
ret.push('\nnock(\'');
ret.push(getScope(options));
ret.push('\', ');
ret.push(JSON.stringify({ encodedQueryParams: true }));
ret.push(')\n');
ret.push(' .');
ret.push(getMethod(options).toLowerCase());
ret.push('(\'');
ret.push(path);
ret.push("'");
if (requestBody) {
ret.push(', ');
ret.push(JSON.stringify(requestBody));
}
ret.push(")\n");
if (req.headers) {
for (var k in req.headers) {
ret.push(' .matchHeader(' + JSON.stringify(k) + ', ' + JSON.stringify(req.headers[k]) + ')\n');
}
}
if (queryIndex !== -1) {
ret.push(' .query(');
ret.push(JSON.stringify(encodedQueryObj));
ret.push(')\n');
}
ret.push(' .reply(');
ret.push(res.statusCode.toString());
ret.push(', ');
ret.push(JSON.stringify(responseBody));
if (res.rawHeaders) {
ret.push(', ');
ret.push(inspect(res.rawHeaders));
} else if (res.headers) {
ret.push(', ');
ret.push(inspect(res.headers));
}
ret.push(');\n');
return ret.join('');
}
// This module variable is used to identify a unique recording ID in order to skip
// spurious requests that sometimes happen. This problem has been, so far,
// exclusively detected in nock's unit testing where 'checks if callback is specified'
// interferes with other tests as its t.end() is invoked without waiting for request
// to finish (which is the point of the test).
var currentRecordingId = 0;
function record(rec_options) {
// Set the new current recording ID and capture its value in this instance of record().
currentRecordingId = currentRecordingId + 1;
var thisRecordingId = currentRecordingId;
debug('start recording', thisRecordingId, JSON.stringify(rec_options));
// Trying to start recording with recording already in progress implies an error
// in the recording configuration (double recording makes no sense and used to lead
// to duplicates in output)
if(recordingInProgress) {
throw new Error('Nock recording already in progress');
}
recordingInProgress = true;
// Originaly the parameters was a dont_print boolean flag.
// To keep the existing code compatible we take that case into account.
var optionsIsObject = typeof rec_options === 'object';
var dont_print = (typeof rec_options === 'boolean' && rec_options) ||
(optionsIsObject && rec_options.dont_print);
var output_objects = optionsIsObject && rec_options.output_objects;
var enable_reqheaders_recording = optionsIsObject && rec_options.enable_reqheaders_recording;
// eslint-disable-next-line no-console
var logging = (optionsIsObject && rec_options.logging) || console.log;
var use_separator = true;
if (optionsIsObject && _.has(rec_options, 'use_separator')) {
use_separator = rec_options.use_separator;
}
debug(thisRecordingId, 'restoring overridden requests before new overrides');
// To preserve backward compatibility (starting recording wasn't throwing if nock was already active)
// we restore any requests that may have been overridden by other parts of nock (e.g. intercept)
// NOTE: This is hacky as hell but it keeps the backward compatibility *and* allows correct
// behavior in the face of other modules also overriding ClientRequest.
common.restoreOverriddenRequests();
// We restore ClientRequest as it messes with recording of modules that also override ClientRequest (e.g. xhr2)
intercept.restoreOverriddenClientRequest();
// We override the requests so that we can save information on them before executing.
common.overrideRequests(function(proto, overriddenRequest, options, callback) {
var bodyChunks = [];
if (typeof options == 'string') {
var url = URL.parse(options);
options = {
hostname: url.hostname,
method: 'GET',
port: url.port,
path: url.path
};
}
// Node 0.11 https.request calls http.request -- don't want to record things
// twice.
if (options._recording) {
return overriddenRequest(options, callback);
}
options._recording = true;
var req = overriddenRequest(options, function(res) {
debug(thisRecordingId, 'intercepting', proto, 'request to record');
if (typeof options === 'string') {
options = parse(options);
}
// We put our 'end' listener to the front of the listener array.
res.once('end', function() {
debug(thisRecordingId, proto, 'intercepted request ended');
var out;
if(output_objects) {
out = generateRequestAndResponseObject(req, bodyChunks, options, res, dataChunks);
if(out.reqheaders) {
// We never record user-agent headers as they are worse than useless -
// they actually make testing more difficult without providing any benefit (see README)
common.deleteHeadersField(out.reqheaders, 'user-agent');
// Remove request headers completely unless it was explicitly enabled by the user (see README)
if(!enable_reqheaders_recording) {
delete out.reqheaders;
}
}
} else {
out = generateRequestAndResponse(req, bodyChunks, options, res, dataChunks);
}
debug('out:', out);
// Check that the request was made during the current recording.
// If it hasn't then skip it. There is no other simple way to handle
// this as it depends on the timing of requests and responses. Throwing
// will make some recordings/unit tests faily randomly depending on how
// fast/slow the response arrived.
// If you are seeing this error then you need to make sure that all
// the requests made during a single recording session finish before
// ending the same recording session.
if(thisRecordingId !== currentRecordingId) {
debug('skipping recording of an out-of-order request', out);
return;
}
outputs.push(out);
if (!dont_print) {
if (use_separator) {
if (typeof out !== 'string') {
out = JSON.stringify(out, null, 2);
}
logging(SEPARATOR + out + SEPARATOR);
} else {
logging(out);
}
}
});
var dataChunks = [];
var encoding;
// We need to be aware of changes to the stream's encoding so that we
// don't accidentally mangle the data.
var setEncoding = res.setEncoding;
res.setEncoding = function (newEncoding) {
encoding = newEncoding;
return setEncoding.apply(this, arguments);
};
// Replace res.push with our own implementation that stores chunks
var origResPush = res.push;
res.push = function(data) {
if (data) {
if (encoding) {
data = Buffer.from(data, encoding);
}
dataChunks.push(data);
}
return origResPush.call(res, data);
}
if (callback) {
callback(res, options, callback);
} else {
res.resume();
}
debug('finished setting up intercepting');
if (proto === 'https') {
options._https_ = true;
}
});
var oldWrite = req.write;
req.write = function(data, encoding) {
if ('undefined' !== typeof(data)) {
if (data) {
debug(thisRecordingId, 'new', proto, 'body chunk');
if (! Buffer.isBuffer(data)) {
data = Buffer.from(data, encoding);
}
bodyChunks.push(data);
}
oldWrite.apply(req, arguments);
}
};
// in Node 8, res.end() does not call res.write() directly
if (semver.satisfies(process.version, '>=8')) {
var oldEnd = req.end;
req.end = function(data, encoding) {
if (data) {
debug(thisRecordingId, 'new', proto, 'body chunk');
if (! Buffer.isBuffer(data)) {
data = Buffer.from(data, encoding);
}
bodyChunks.push(data);
}
oldEnd.apply(req, arguments);
};
}
return req;
});
}
// Restores *all* the overridden http/https modules' properties.
function restore() {
debug(currentRecordingId, 'restoring all the overridden http/https properties');
common.restoreOverriddenRequests();
intercept.restoreOverriddenClientRequest();
recordingInProgress = false;
}
function clear() {
outputs = [];
}
exports.record = record;
exports.outputs = function() {
return outputs;
};
exports.restore = restore;
exports.clear = clear;

563
node_modules/nock/lib/request_overrider.js generated vendored Normal file
View File

@@ -0,0 +1,563 @@
'use strict';
var EventEmitter = require('events').EventEmitter,
http = require('http'),
propagate = require('propagate'),
DelayedBody = require('./delayed_body'),
IncomingMessage = http.IncomingMessage,
ClientRequest = http.ClientRequest,
common = require('./common'),
Socket = require('./socket'),
_ = require('lodash'),
debug = require('debug')('nock.request_overrider'),
ReadableStream = require('stream').Readable,
globalEmitter = require('./global_emitter'),
zlib = require('zlib'),
timers = require('timers');
function getHeader(request, name) {
if (!request._headers) {
return;
}
var key = name.toLowerCase();
return request.getHeader ? request.getHeader(key) : request._headers[key];
}
function setHeader(request, name, value) {
debug('setHeader', name, value);
var key = name.toLowerCase();
request._headers = request._headers || {};
request._headerNames = request._headerNames || {};
request._removedHeader = request._removedHeader || {};
if (request.setHeader) {
request.setHeader(key, value);
} else {
request._headers[key] = value;
request._headerNames[key] = name;
}
if (name == 'expect' && value == '100-continue') {
timers.setImmediate(function() {
debug('continue');
request.emit('continue');
});
}
}
// Sets request headers of the given request. This is needed during both matching phase
// (in case header filters were specified) and mocking phase (to correctly pass mocked
// request headers).
function setRequestHeaders(req, options, interceptor) {
// If a filtered scope is being used we have to use scope's host
// in the header, otherwise 'host' header won't match.
// NOTE: We use lower-case header field names throught Nock.
var HOST_HEADER = 'host';
if(interceptor.__nock_filteredScope && interceptor.__nock_scopeHost) {
if(options && options.headers) {
options.headers[HOST_HEADER] = interceptor.__nock_scopeHost;
}
setHeader(req, HOST_HEADER, interceptor.__nock_scopeHost);
} else {
// For all other cases, we always add host header equal to the
// requested host unless it was already defined.
if (options.host && !getHeader(req, HOST_HEADER)) {
var hostHeader = options.host;
if (options.port === 80 || options.port === 443) {
hostHeader = hostHeader.split(':')[0];
}
setHeader(req, HOST_HEADER, hostHeader);
}
}
}
function RequestOverrider(req, options, interceptors, remove, cb) {
var response;
if (IncomingMessage) {
response = new IncomingMessage(new EventEmitter());
} else {
response = new ReadableStream();
response._read = function() {};
}
var requestBodyBuffers = [],
emitError,
end,
ended,
headers;
// We may be changing the options object and we don't want those
// changes affecting the user so we use a clone of the object.
options = _.clone(options) || {};
response.req = req;
if (options.headers) {
// We use lower-case header field names throught Nock.
options.headers = common.headersFieldNamesToLowerCase(options.headers);
headers = options.headers;
_.forOwn(headers, function(val, key) {
setHeader(req, key, val);
});
}
/// options.auth
if (options.auth && (! options.headers || ! options.headers.authorization)) {
setHeader(req, 'Authorization', 'Basic ' + (Buffer.from(options.auth)).toString('base64'));
}
if (! req.connection) {
req.connection = new EventEmitter();
}
req.path = options.path;
options.getHeader = function(name) {
return getHeader(req, name);
};
req.socket = response.socket = Socket({ proto: options.proto });
req.write = function(buffer, encoding, callback) {
debug('write', arguments);
if (!req.aborted) {
if (buffer) {
if (!Buffer.isBuffer(buffer)) {
buffer = Buffer.from(buffer, encoding);
}
requestBodyBuffers.push(buffer);
}
if (typeof callback === 'function') {
callback();
}
}
else {
emitError(new Error('Request aborted'));
}
timers.setImmediate(function() {
req.emit('drain');
});
return false;
};
req.end = function(buffer, encoding, callback) {
debug('req.end');
if (!req.aborted && !ended) {
req.write(buffer, encoding, function () {
if (typeof callback === 'function') {
callback();
}
end(cb);
req.emit('finish');
req.emit('end');
});
}
if (req.aborted) {
emitError(new Error('Request aborted'));
}
};
req.flushHeaders = function() {
debug('req.flushHeaders');
if (!req.aborted && !ended) {
end(cb);
}
if (req.aborted) {
emitError(new Error('Request aborted'));
}
};
req.abort = function() {
if (req.aborted) {
return;
}
debug('req.abort');
req.aborted = Date.now();
if (!ended) {
end();
}
var err = new Error();
err.code = 'aborted';
response.emit('close', err);
req.socket.destroy();
req.emit('abort');
var connResetError = new Error('socket hang up');
connResetError.code = 'ECONNRESET';
emitError(connResetError);
};
// restify listens for a 'socket' event to
// be emitted before calling end(), which causes
// nock to hang with restify. The following logic
// fakes the socket behavior for restify,
// Fixes: https://github.com/pgte/nock/issues/79
req.once = req.on = function(event, listener) {
// emit a fake socket.
if (event == 'socket') {
listener.call(req, req.socket);
req.socket.emit('connect', req.socket);
req.socket.emit('secureConnect', req.socket);
}
EventEmitter.prototype.on.call(this, event, listener);
return this;
};
emitError = function(error) {
process.nextTick(function () {
req.emit('error', error);
});
};
end = function(cb) {
debug('ending');
ended = true;
var requestBody,
responseBody,
responseBuffers,
interceptor;
var continued = false;
// When request body is a binary buffer we internally use in its hexadecimal representation.
var requestBodyBuffer = common.mergeChunks(requestBodyBuffers);
var isBinaryRequestBodyBuffer = common.isBinaryBuffer(requestBodyBuffer);
if(isBinaryRequestBodyBuffer) {
requestBody = requestBodyBuffer.toString('hex');
} else {
requestBody = requestBodyBuffer.toString('utf8');
}
/// put back the path into options
/// because bad behaving agents like superagent
/// like to change request.path in mid-flight.
options.path = req.path;
// fixes #976
options.protocol = options.proto + ':';
interceptors.forEach(function(interceptor) {
// For correct matching we need to have correct request headers - if these were specified.
setRequestHeaders(req, options, interceptor);
});
interceptor = _.find(interceptors, function(interceptor) {
return interceptor.match(options, requestBody);
});
if (!interceptor) {
globalEmitter.emit('no match', req, options, requestBody);
// Try to find a hostname match
interceptor = _.find(interceptors, function(interceptor) {
return interceptor.match(options, requestBody, true);
});
if (interceptor && req instanceof ClientRequest) {
if (interceptor.options.allowUnmocked) {
var newReq = new ClientRequest(options, cb);
propagate(newReq, req);
// We send the raw buffer as we received it, not as we interpreted it.
newReq.end(requestBodyBuffer);
return;
}
}
var err = new Error("Nock: No match for request " + common.stringifyRequest(options, requestBody));
err.statusCode = err.status = 404;
emitError(err);
return;
}
debug('interceptor identified, starting mocking');
// We again set request headers, now for our matched interceptor.
setRequestHeaders(req, options, interceptor);
interceptor.req = req;
req.headers = req.getHeaders ? req.getHeaders() : req._headers;
interceptor.scope.emit('request', req, interceptor, requestBody);
if (typeof interceptor.errorMessage !== 'undefined') {
interceptor.interceptionCounter++;
remove(interceptor);
interceptor.discard();
var error;
if (_.isObject(interceptor.errorMessage)) {
error = interceptor.errorMessage;
} else {
error = new Error(interceptor.errorMessage);
}
timers.setTimeout(emitError, interceptor.getTotalDelay(), error);
return;
}
response.statusCode = Number(interceptor.statusCode) || 200;
// Clone headers/rawHeaders to not override them when evaluating later
response.headers = _.extend({}, interceptor.headers);
response.rawHeaders = (interceptor.rawHeaders || []).slice();
debug('response.rawHeaders:', response.rawHeaders);
if (typeof interceptor.body === 'function') {
if (requestBody && common.isJSONContent(req.headers)) {
if (requestBody && common.contentEncoding(req.headers, 'gzip')) {
if (typeof zlib.gunzipSync !== 'function') {
emitError(new Error('Gzip encoding is currently not supported in this version of Node.'));
return;
}
requestBody = String(zlib.gunzipSync(Buffer.from(requestBody, 'hex')), 'hex')
} else if (requestBody && common.contentEncoding(req.headers, 'deflate')) {
if (typeof zlib.deflateSync !== 'function') {
emitError(new Error('Deflate encoding is currently not supported in this version of Node.'));
return;
}
requestBody = String(zlib.inflateSync(Buffer.from(requestBody, 'hex')), 'hex')
}
requestBody = JSON.parse(requestBody);
}
// In case we are waiting for a callback
if (interceptor.body.length === 3) {
return interceptor.body(options.path, requestBody || '', continueWithResponseBody);
}
responseBody = interceptor.body(options.path, requestBody) || '';
} else {
// If the content is encoded we know that the response body *must* be an array
// of response buffers which should be mocked one by one.
// (otherwise decompressions after the first one fails as unzip expects to receive
// buffer by buffer and not one single merged buffer)
if(common.isContentEncoded(response.headers) && ! common.isStream(interceptor.body)) {
if (interceptor.delayInMs) {
emitError(new Error('Response delay is currently not supported with content-encoded responses.'));
return;
}
var buffers = interceptor.body;
if(!_.isArray(buffers)) {
buffers = [buffers];
}
responseBuffers = _.map(buffers, function(buffer) {
return Buffer.from(buffer, 'hex');
});
} else {
responseBody = interceptor.body;
// If the request was binary then we assume that the response will be binary as well.
// In that case we send the response as a Buffer object as that's what the client will expect.
if(isBinaryRequestBodyBuffer && typeof(responseBody) === 'string') {
// Try to create the buffer from the interceptor's body response as hex.
try {
responseBody = Buffer.from(responseBody, 'hex');
} catch(err) {
debug('exception during Buffer construction from hex data:', responseBody, '-', err);
}
// Creating buffers does not necessarily throw errors, check for difference in size
if (!responseBody || (interceptor.body.length > 0 && responseBody.length === 0)) {
// We fallback on constructing buffer from utf8 representation of the body.
responseBody = Buffer.from(interceptor.body, 'utf8');
}
}
}
}
return continueWithResponseBody(null, responseBody);
function continueWithResponseBody(err, responseBody) {
if (continued) {
return;
}
continued = true;
if (err) {
response.statusCode = 500;
responseBody = err.stack;
}
// Transform the response body if it exists (it may not exist
// if we have `responseBuffers` instead)
if (responseBody) {
debug('transform the response body');
if (Array.isArray(responseBody)) {
debug('response body is array: %j', responseBody);
if (!isNaN(Number(responseBody[0])))
{
response.statusCode = Number(responseBody[0]);
}
if (responseBody.length >= 2 && responseBody.length <= 3)
{
debug('new headers: %j', responseBody[2]);
if (!response.headers) response.headers = {};
_.assign(response.headers, responseBody[2] || {});
debug('response.headers after: %j', response.headers);
responseBody = responseBody[1];
response.rawHeaders = response.rawHeaders || [];
Object.keys(response.headers).forEach(function(key) {
response.rawHeaders.push(key, response.headers[key]);
});
}
}
if (interceptor.delayInMs) {
debug('delaying the response for', interceptor.delayInMs, 'milliseconds');
// Because setTimeout is called immediately in DelayedBody(), so we
// need count in the delayConnectionInMs.
responseBody = new DelayedBody(interceptor.getTotalDelay(), responseBody);
}
if (common.isStream(responseBody)) {
debug('response body is a stream');
responseBody.pause();
responseBody.on('data', function(d) {
response.push(d);
});
responseBody.on('end', function() {
response.push(null);
});
responseBody.on('error', function(err) {
response.emit('error', err);
});
} else if (responseBody && !Buffer.isBuffer(responseBody)) {
if (typeof responseBody === 'string') {
responseBody = Buffer.from(responseBody);
} else {
responseBody = JSON.stringify(responseBody);
response.headers['content-type'] = 'application/json';
}
}
}
interceptor.interceptionCounter++;
remove(interceptor);
interceptor.discard();
if (req.aborted) { return; }
/// response.client.authorized = true
/// fixes https://github.com/pgte/nock/issues/158
response.client = _.extend(response.client || {}, {
authorized: true
});
// Account for updates to Node.js response interface
// cf https://github.com/request/request/pull/1615
response.socket = _.extend(response.socket || {}, {
authorized: true
});
// Evaluate functional headers.
var evaluatedHeaders = {}
Object.keys(response.headers).forEach(function (key) {
var value = response.headers[key];
if (typeof value === "function") {
response.headers[key] = evaluatedHeaders[key] = value(req, response, responseBody);
}
});
for(var rawHeaderIndex = 0 ; rawHeaderIndex < response.rawHeaders.length ; rawHeaderIndex += 2) {
var key = response.rawHeaders[rawHeaderIndex];
var value = response.rawHeaders[rawHeaderIndex + 1];
if (typeof value === "function") {
response.rawHeaders[rawHeaderIndex + 1] = evaluatedHeaders[key.toLowerCase()];
}
}
process.nextTick(respond);
function respond() {
if (req.aborted) { return; }
if (interceptor.socketDelayInMs && interceptor.socketDelayInMs > 0) {
req.socket.applyDelay(interceptor.socketDelayInMs);
}
if (interceptor.delayConnectionInMs && interceptor.delayConnectionInMs > 0) {
req.socket.applyDelay(interceptor.delayConnectionInMs);
setTimeout(_respond, interceptor.delayConnectionInMs);
} else {
_respond();
}
function _respond() {
if (req.aborted) { return; }
debug('emitting response');
if (typeof cb === 'function') {
debug('callback with response');
cb(response);
}
if (req.aborted) {
emitError(new Error('Request aborted'));
}
else {
req.emit('response', response);
}
if (common.isStream(responseBody)) {
debug('resuming response stream');
responseBody.resume();
}
else {
responseBuffers = responseBuffers || [];
if (typeof responseBody !== "undefined") {
debug('adding body to buffer list');
responseBuffers.push(responseBody);
}
// Stream the response chunks one at a time.
timers.setImmediate(function emitChunk() {
var chunk = responseBuffers.shift();
if (chunk) {
debug('emitting response chunk');
response.push(chunk);
timers.setImmediate(emitChunk);
}
else {
debug('ending response stream');
response.push(null);
interceptor.scope.emit('replied', req, interceptor);
}
});
}
}
}
}
};
return req;
}
module.exports = RequestOverrider;

365
node_modules/nock/lib/scope.js generated vendored Normal file
View File

@@ -0,0 +1,365 @@
/* eslint-disable strict */
/**
* @module nock/scope
*/
var globalIntercept = require('./intercept')
, common = require('./common')
, assert = require('assert')
, url = require('url')
, _ = require('lodash')
, debug = require('debug')('nock.scope')
, EventEmitter = require('events').EventEmitter
, globalEmitter = require('./global_emitter')
, util = require('util')
, Interceptor = require('./interceptor') ;
var fs;
try {
fs = require('fs');
} catch(err) {
// do nothing, we're in the browser
}
function startScope(basePath, options) {
return new Scope(basePath, options);
}
function Scope(basePath, options) {
if (!(this instanceof Scope)) {
return new Scope(basePath, options);
}
EventEmitter.apply(this);
this.keyedInterceptors = {};
this.interceptors = [];
this.transformPathFunction = null;
this.transformRequestBodyFunction = null;
this.matchHeaders = [];
this.logger = debug;
this.scopeOptions = options || {};
this.urlParts = {};
this._persist = false;
this.contentLen = false;
this.date = null;
this.basePath = basePath;
this.basePathname = '';
this.port = null;
if (!(basePath instanceof RegExp)) {
this.urlParts = url.parse(basePath);
this.port = this.urlParts.port || ((this.urlParts.protocol === 'http:') ? 80 : 443);
this.basePathname = this.urlParts.pathname.replace(/\/$/, '');
this.basePath = this.urlParts.protocol + '//' + this.urlParts.hostname + ':' + this.port;
}
}
util.inherits(Scope, EventEmitter);
Scope.prototype.add = function add(key, interceptor, scope) {
if (! this.keyedInterceptors.hasOwnProperty(key)) {
this.keyedInterceptors[key] = [];
}
this.keyedInterceptors[key].push(interceptor);
globalIntercept(this.basePath,
interceptor,
this,
this.scopeOptions,
this.urlParts.hostname);
};
Scope.prototype.remove = function remove(key, interceptor) {
if (this._persist) {
return;
}
var arr = this.keyedInterceptors[key];
if (arr) {
arr.splice(arr.indexOf(interceptor), 1);
if (arr.length === 0) {
delete this.keyedInterceptors[key];
}
}
};
Scope.prototype.intercept = function intercept(uri, method, requestBody, interceptorOptions) {
var ic = new Interceptor(this, uri, method, requestBody, interceptorOptions);
this.interceptors.push(ic);
return ic;
};
Scope.prototype.get = function get(uri, requestBody, options) {
return this.intercept(uri, 'GET', requestBody, options);
};
Scope.prototype.post = function post(uri, requestBody, options) {
return this.intercept(uri, 'POST', requestBody, options);
};
Scope.prototype.put = function put(uri, requestBody, options) {
return this.intercept(uri, 'PUT', requestBody, options);
};
Scope.prototype.head = function head(uri, requestBody, options) {
return this.intercept(uri, 'HEAD', requestBody, options);
};
Scope.prototype.patch = function patch(uri, requestBody, options) {
return this.intercept(uri, 'PATCH', requestBody, options);
};
Scope.prototype.merge = function merge(uri, requestBody, options) {
return this.intercept(uri, 'MERGE', requestBody, options);
};
Scope.prototype.delete = function _delete(uri, requestBody, options) {
return this.intercept(uri, 'DELETE', requestBody, options);
};
Scope.prototype.options = function _options(uri, requestBody, options) {
return this.intercept(uri, 'OPTIONS', requestBody, options);
};
Scope.prototype.pendingMocks = function pendingMocks() {
var self = this;
var pendingInterceptorKeys = Object.keys(this.keyedInterceptors).filter(function (key) {
var interceptorList = self.keyedInterceptors[key];
var pendingInterceptors = interceptorList.filter(function (interceptor) {
// TODO: This assumes that completed mocks are removed from the keyedInterceptors list
// (when persistence is off). We should change that (and this) in future.
var persistedAndUsed = self._persist && interceptor.interceptionCounter > 0;
return !persistedAndUsed && !interceptor.optional;
});
return pendingInterceptors.length > 0;
});
return pendingInterceptorKeys;
};
// Returns all keyedInterceptors that are active.
// This incomplete interceptors, persisted but complete interceptors, and
// optional interceptors, but not non-persisted and completed interceptors.
Scope.prototype.activeMocks = function activeMocks() {
return Object.keys(this.keyedInterceptors);
}
Scope.prototype.isDone = function isDone() {
// if nock is turned off, it always says it's done
if (! globalIntercept.isOn()) { return true; }
return this.pendingMocks().length === 0;
};
Scope.prototype.done = function done() {
assert.ok(this.isDone(), "Mocks not yet satisfied:\n" + this.pendingMocks().join("\n"));
};
Scope.prototype.buildFilter = function buildFilter() {
var filteringArguments = arguments;
if (arguments[0] instanceof RegExp) {
return function(candidate) {
if (candidate) {
candidate = candidate.replace(filteringArguments[0], filteringArguments[1]);
}
return candidate;
};
} else if (_.isFunction(arguments[0])) {
return arguments[0];
}
};
Scope.prototype.filteringPath = function filteringPath() {
this.transformPathFunction = this.buildFilter.apply(this, arguments);
if (!this.transformPathFunction) {
throw new Error('Invalid arguments: filtering path should be a function or a regular expression');
}
return this;
};
Scope.prototype.filteringRequestBody = function filteringRequestBody() {
this.transformRequestBodyFunction = this.buildFilter.apply(this, arguments);
if (!this.transformRequestBodyFunction) {
throw new Error('Invalid arguments: filtering request body should be a function or a regular expression');
}
return this;
};
Scope.prototype.matchHeader = function matchHeader(name, value) {
// We use lower-case header field names throughout Nock.
this.matchHeaders.push({ name: name.toLowerCase(), value: value });
return this;
};
Scope.prototype.defaultReplyHeaders = function defaultReplyHeaders(headers) {
this._defaultReplyHeaders = common.headersFieldNamesToLowerCase(headers);
return this;
};
Scope.prototype.log = function log(newLogger) {
this.logger = newLogger;
return this;
};
Scope.prototype.persist = function persist(flag) {
this._persist = flag == null ? true : flag;
if (typeof this._persist !== 'boolean') {
throw new Error('Invalid arguments: argument should be a boolean');
}
return this;
};
Scope.prototype.shouldPersist = function shouldPersist() {
return this._persist;
};
Scope.prototype.replyContentLength = function replyContentLength() {
this.contentLen = true;
return this;
};
Scope.prototype.replyDate = function replyDate(d) {
this.date = d || new Date();
return this;
};
function cleanAll() {
globalIntercept.removeAll();
return module.exports;
}
function loadDefs(path) {
if (! fs) {
throw new Error('No fs');
}
var contents = fs.readFileSync(path);
return JSON.parse(contents);
}
function load(path) {
return define(loadDefs(path));
}
function getStatusFromDefinition(nockDef) {
// Backward compatibility for when `status` was encoded as string in `reply`.
if (!_.isUndefined(nockDef.reply)) {
// Try parsing `reply` property.
var parsedReply = parseInt(nockDef.reply, 10);
if (_.isNumber(parsedReply)) {
return parsedReply;
}
}
var DEFAULT_STATUS_OK = 200;
return nockDef.status || DEFAULT_STATUS_OK;
}
function getScopeFromDefinition(nockDef) {
// Backward compatibility for when `port` was part of definition.
if (!_.isUndefined(nockDef.port)) {
// Include `port` into scope if it doesn't exist.
var options = url.parse(nockDef.scope);
if (_.isNull(options.port)) {
return nockDef.scope + ':' + nockDef.port;
} else {
if (parseInt(options.port) !== parseInt(nockDef.port)) {
throw new Error('Mismatched port numbers in scope and port properties of nock definition.');
}
}
}
return nockDef.scope;
}
function tryJsonParse(string) {
try {
return JSON.parse(string);
} catch(err) {
return string;
}
}
function define(nockDefs) {
var nocks = [];
nockDefs.forEach(function(nockDef) {
var nscope = getScopeFromDefinition(nockDef)
, npath = nockDef.path
, method = nockDef.method.toLowerCase() || "get"
, status = getStatusFromDefinition(nockDef)
, rawHeaders = nockDef.rawHeaders || []
, reqheaders = nockDef.reqheaders || {}
, badheaders = nockDef.badheaders || []
, body = nockDef.body || ''
, options = nockDef.options || {};
// We use request headers for both filtering (see below) and mocking.
// Here we are setting up mocked request headers but we don't want to
// be changing the user's options object so we clone it first.
options = _.clone(options) || {};
options.reqheaders = reqheaders;
options.badheaders = badheaders;
// Response is not always JSON as it could be a string or binary data or
// even an array of binary buffers (e.g. when content is enconded)
var response;
if (!nockDef.response) {
response = '';
} else if (nockDef.responseIsBinary) {
response = Buffer.from(nockDef.response, 'hex')
} else {
response = _.isString(nockDef.response) ? tryJsonParse(nockDef.response) : nockDef.response;
}
var nock;
if (body==="*") {
nock = startScope(nscope, options).filteringRequestBody(function() {
return "*";
})[method](npath, "*").reply(status, response, rawHeaders);
} else {
nock = startScope(nscope, options);
// If request headers were specified filter by them.
if (_.size(reqheaders) > 0) {
for (var k in reqheaders) {
nock.matchHeader(k, reqheaders[k]);
}
}
var acceptableFilters = ['filteringRequestBody', 'filteringPath'];
acceptableFilters.forEach((filter) => {
if (nockDef[filter]) {
nock[filter](nockDef[filter]);
}
});
nock.intercept(npath, method, body).reply(status, response, rawHeaders);
}
nocks.push(nock);
});
return nocks;
}
module.exports = Object.assign(startScope, {
cleanAll: cleanAll,
activate: globalIntercept.activate,
isActive: globalIntercept.isActive,
isDone: globalIntercept.isDone,
pendingMocks: globalIntercept.pendingMocks,
activeMocks: globalIntercept.activeMocks,
removeInterceptor: globalIntercept.removeInterceptor,
disableNetConnect: globalIntercept.disableNetConnect,
enableNetConnect: globalIntercept.enableNetConnect,
load: load,
loadDefs: loadDefs,
define: define,
emitter: globalEmitter,
});

70
node_modules/nock/lib/socket.js generated vendored Normal file
View File

@@ -0,0 +1,70 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('nock.socket');
var util = require('util');
module.exports = Socket;
function Socket(options) {
if (!(this instanceof Socket)) {
return new Socket(options);
}
EventEmitter.apply(this);
options = options || {};
if (options.proto === 'https') {
this.authorized = true;
}
this.writable = true;
this.readable = true;
this.destroyed = false;
this.connecting = false;
this.setNoDelay = noop;
this.setKeepAlive = noop;
this.resume = noop;
// totalDelay that has already been applied to the current
// request/connection, timeout error will be generated if
// it is timed-out.
this.totalDelayMs = 0;
// Maximum allowed delay. Null means unlimited.
this.timeoutMs = null;
}
util.inherits(Socket, EventEmitter);
Socket.prototype.setTimeout = function setTimeout(timeoutMs, fn) {
this.timeoutMs = timeoutMs;
this.timeoutFunction = fn;
};
Socket.prototype.applyDelay = function applyDelay(delayMs) {
this.totalDelayMs += delayMs;
if (this.timeoutMs && this.totalDelayMs > this.timeoutMs) {
debug('socket timeout');
if (this.timeoutFunction) {
this.timeoutFunction();
}
else {
this.emit('timeout');
}
}
};
Socket.prototype.getPeerCertificate = function getPeerCertificate() {
return Buffer.from((Math.random() * 10000 + Date.now()).toString()).toString('base64');
};
Socket.prototype.destroy = function destroy() {
this.destroyed = true;
this.readable = this.writable = false;
};
function noop() {}