chai-http
Version:
Extend Chai Assertion library with tests for http apis
1,742 lines (1,575 loc) • 163 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.chaiHttp = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
/*!
* chai-http
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/**
* ## Assertions
*
* The Chai HTTP module provides a number of assertions
* for the `expect` and `should` interfaces.
*/
module.exports = function (chai, _) {
/*!
* Module dependencies.
*/
var net = require('net');
var qs = require('qs');
var url = require('url');
var Cookie = require('cookiejar');
/*!
* Aliases.
*/
var Assertion = chai.Assertion
, i = _.inspect;
/*!
* Expose request builder
*/
chai.request = require('./request');
/*!
* Content types hash. Used to
* define `Assertion` properties.
*
* @type {Object}
*/
var contentTypes = {
json: 'application/json'
, text: 'text/plain'
, html: 'text/html'
};
/*!
* Return a header from `Request` or `Response` object.
*
* @param {Request|Response} object
* @param {String} Header
* @returns {String|Undefined}
*/
function getHeader(obj, key) {
if (key) key = key.toLowerCase();
if (obj.getHeader) return obj.getHeader(key);
if (obj.headers) return obj.headers[key];
};
/**
* ### .status (code)
*
* Assert that a response has a supplied status.
*
* ```js
* expect(res).to.have.status(200);
* ```
*
* @param {Number} status number
* @name status
* @api public
*/
Assertion.addMethod('status', function (code) {
var hasStatus = Boolean('status' in this._obj || 'statusCode' in this._obj);
new Assertion(hasStatus).assert(
hasStatus
, "expected #{act} to have keys 'status', or 'statusCode'"
, null // never negated
, hasStatus // expected
, this._obj // actual
, false // no diff
);
var status = this._obj.status || this._obj.statusCode;
this.assert(
status == code
, 'expected #{this} to have status code #{exp} but got #{act}'
, 'expected #{this} to not have status code #{act}'
, code
, status
);
});
/**
* ### .header (key[, value])
*
* Assert that a `Response` or `Request` object has a header.
* If a value is provided, equality to value will be asserted.
* You may also pass a regular expression to check.
*
* __Note:__ When running in a web browser, the
* [same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
* only allows Chai HTTP to read
* [certain headers](https://www.w3.org/TR/cors/#simple-response-header),
* which can cause assertions to fail.
*
* ```js
* expect(req).to.have.header('x-api-key');
* expect(req).to.have.header('content-type', 'text/plain');
* expect(req).to.have.header('content-type', /^text/);
* ```
*
* @param {String} header key (case insensitive)
* @param {String|RegExp} header value (optional)
* @name header
* @api public
*/
Assertion.addMethod('header', function (key, value) {
var header = getHeader(this._obj, key);
if (arguments.length < 2) {
this.assert(
'undefined' !== typeof header || null === header
, 'expected header \'' + key + '\' to exist'
, 'expected header \'' + key + '\' to not exist'
);
} else if (arguments[1] instanceof RegExp) {
this.assert(
value.test(header)
, 'expected header \'' + key + '\' to match ' + value + ' but got ' + i(header)
, 'expected header \'' + key + '\' not to match ' + value + ' but got ' + i(header)
, value
, header
);
} else {
this.assert(
header == value
, 'expected header \'' + key + '\' to have value ' + value + ' but got ' + i(header)
, 'expected header \'' + key + '\' to not have value ' + value
, value
, header
);
}
});
/**
* ### .headers
*
* Assert that a `Response` or `Request` object has headers.
*
* __Note:__ When running in a web browser, the
* [same-origin policy](https://tools.ietf.org/html/rfc6454#section-3)
* only allows Chai HTTP to read
* [certain headers](https://www.w3.org/TR/cors/#simple-response-header),
* which can cause assertions to fail.
*
* ```js
* expect(req).to.have.headers;
* ```
*
* @name headers
* @api public
*/
Assertion.addProperty('headers', function () {
this.assert(
this._obj.headers || this._obj.getHeader
, 'expected #{this} to have headers or getHeader method'
, 'expected #{this} to not have headers or getHeader method'
);
});
/**
* ### .ip
*
* Assert that a string represents valid ip address.
*
* ```js
* expect('127.0.0.1').to.be.an.ip;
* expect('2001:0db8:85a3:0000:0000:8a2e:0370:7334').to.be.an.ip;
* ```
*
* @name ip
* @api public
*/
Assertion.addProperty('ip', function () {
this.assert(
net.isIP(this._obj)
, 'expected #{this} to be an ip'
, 'expected #{this} to not be an ip'
);
});
/**
* ### .json / .text / .html
*
* Assert that a `Response` or `Request` object has a given content-type.
*
* ```js
* expect(req).to.be.json;
* expect(req).to.be.html;
* expect(req).to.be.text;
* ```
*
* @name json
* @name html
* @name text
* @api public
*/
function checkContentType (name) {
var val = contentTypes[name];
Assertion.addProperty(name, function () {
new Assertion(this._obj).to.have.headers;
var ct = getHeader(this._obj, 'content-type')
, ins = i(ct) === 'undefined'
? 'headers'
: i(ct);
this.assert(
ct && ~ct.indexOf(val)
, 'expected ' + ins + ' to include \'' + val + '\''
, 'expected ' + ins + ' to not include \'' + val + '\''
);
});
}
Object
.keys(contentTypes)
.forEach(checkContentType);
/**
* ### .redirect
*
* Assert that a `Response` object has a redirect status code.
*
* ```js
* expect(res).to.redirect;
* ```
*
* @name redirect
* @api public
*/
Assertion.addProperty('redirect', function() {
var redirectCodes = [301, 302, 303, 307, 308]
, status = this._obj.status
, redirects = this._obj.redirects;
this.assert(
redirectCodes.indexOf(status) >= 0 || redirects && redirects.length
, "expected redirect with 30X status code but got " + status
, "expected not to redirect but got " + status + " status"
);
});
/**
* ### .redirectTo
*
* Assert that a `Response` object redirects to the supplied location.
*
* ```js
* expect(res).to.redirectTo('http://example.com');
* ```
*
* @param {String|RegExp} location url
* @name redirectTo
* @api public
*/
Assertion.addMethod('redirectTo', function(destination) {
var redirects = this._obj.redirects;
new Assertion(this._obj).to.redirect;
if(redirects && redirects.length) {
var hasRedirected;
if (Object.prototype.toString.call(destination) === '[object RegExp]') {
hasRedirected = redirects.some(redirect => destination.test(redirect));
} else {
hasRedirected = redirects.indexOf(destination) > -1;
}
this.assert(
hasRedirected
, 'expected redirect to ' + destination + ' but got ' + redirects.join(' then ')
, 'expected not to redirect to ' + destination + ' but got ' + redirects.join(' then ')
);
} else {
var assertion = new Assertion(this._obj);
_.transferFlags(this, assertion);
assertion.with.header('location', destination);
}
});
/**
* ### .param
*
* Assert that a `Request` object has a query string parameter with a given
* key, (optionally) equal to value
*
* ```js
* expect(req).to.have.param('orderby');
* expect(req).to.have.param('orderby', 'date');
* expect(req).to.not.have.param('limit');
* ```
*
* @param {String} parameter name
* @param {String} parameter value
* @name param
* @api public
*/
Assertion.addMethod('param', function(name, value) {
var assertion = new Assertion();
_.transferFlags(this, assertion);
assertion._obj = qs.parse(url.parse(this._obj.url).query);
assertion.property.apply(assertion, arguments);
});
/**
* ### .cookie
*
* Assert that a `Request`, `Response` or `Agent` object has a cookie header with a
* given key, (optionally) equal to value
*
* ```js
* expect(req).to.have.cookie('session_id');
* expect(req).to.have.cookie('session_id', '1234');
* expect(req).to.not.have.cookie('PHPSESSID');
* expect(res).to.have.cookie('session_id');
* expect(res).to.have.cookie('session_id', '1234');
* expect(res).to.not.have.cookie('PHPSESSID');
* expect(agent).to.have.cookie('session_id');
* expect(agent).to.have.cookie('session_id', '1234');
* expect(agent).to.not.have.cookie('PHPSESSID');
* ```
*
* @param {String} parameter name
* @param {String} parameter value
* @name param
* @api public
*/
Assertion.addMethod('cookie', function (key, value) {
var header = getHeader(this._obj, 'set-cookie')
, cookie;
if (!header) {
header = (getHeader(this._obj, 'cookie') || '').split(';');
}
if (this._obj instanceof chai.request.agent && this._obj.jar) {
cookie = this._obj.jar.getCookie(key, Cookie.CookieAccessInfo.All);
} else {
cookie = Cookie.CookieJar();
cookie.setCookies(header);
cookie = cookie.getCookie(key, Cookie.CookieAccessInfo.All);
}
if (arguments.length === 2) {
this.assert(
cookie.value == value
, 'expected cookie \'' + key + '\' to have value #{exp} but got #{act}'
, 'expected cookie \'' + key + '\' to not have value #{exp}'
, value
, cookie.value
);
} else {
this.assert(
'undefined' !== typeof cookie || null === cookie
, 'expected cookie \'' + key + '\' to exist'
, 'expected cookie \'' + key + '\' to not exist'
);
}
});
};
},{"./request":3,"cookiejar":6,"net":2,"qs":13,"url":26}],2:[function(require,module,exports){
/*!
* chai-http - request helper
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/*!
* net.isIP shim for browsers
*/
var isIP = require('is-ip');
exports.isIP = isIP;
exports.isIPv4 = isIP.v4;
exports.isIPv6 = isIP.v6;
},{"is-ip":8}],3:[function(require,module,exports){
/*!
* chai-http - request helper
* Copyright(c) 2011-2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/*!
* Module dependancies
*/
var http = require('http')
, https = require('https')
, methods = require('methods')
, superagent = require('superagent')
, Agent = superagent.agent
, Request = superagent.Request
, util = require('util');
/**
* ## Integration Testing
*
* Chai HTTP provides an interface for live integration
* testing via [superagent](https://github.com/visionmedia/superagent).
* To do this, you must first
* construct a request to an application or url.
*
* Upon construction you are provided a chainable api that
* allows you to specify the http VERB request (get, post, etc)
* that you wish to invoke.
*
* #### Application / Server
*
* You may use a function (such as an express or connect app)
* or a node.js http(s) server as the foundation for your request.
* If the server is not running, chai-http will find a suitable
* port to listen on for a given test.
*
* __Note:__ This feature is only supported on Node.js, not in web browsers.
*
* ```js
* chai.request(app)
* .get('/')
* ```
*
* #### URL
*
* You may also use a base url as the foundation of your request.
*
* ```js
* chai.request('http://localhost:8080')
* .get('/')
* ```
*
* #### Setting up requests
*
* Once a request is created with a given VERB, it can have headers, form data,
* json, or even file attachments added to it, all with a simple API:
*
* ```js
* // Send some JSON
* chai.request(app)
* .put('/user/me')
* .set('X-API-Key', 'foobar')
* .send({ password: '123', confirmPassword: '123' })
* ```
*
* ```js
* // Send some Form Data
* chai.request(app)
* .post('/user/me')
* .type('form')
* .send({'_method': 'put'})
* .send({'password': '123'})
* .send({'confirmPassword', '123'})
* ```
*
* ```js
* // Attach a file
* chai.request(app)
* .post('/user/avatar')
* .attach('imageField', fs.readFileSync('avatar.png'), 'avatar.png')
* ```
*
* ```js
* // Authenticate with Basic authentication
* chai.request(app)
* .get('/protected')
* .auth('user', 'pass')
* ```
*
* ```js
* // Chain some GET query parameters
* chai.request(app)
* .get('/search')
* .query({name: 'foo', limit: 10}) // /search?name=foo&limit=10
* ```
*
* #### Dealing with the response - traditional
*
* To make the request and assert on its response, the `end` method can be used:
*
* ```js
* chai.request(app)
* .put('/user/me')
* .send({ password: '123', confirmPassword: '123' })
* .end(function (err, res) {
* expect(err).to.be.null;
* expect(res).to.have.status(200);
* });
* ```
* ##### Caveat
* Because the `end` function is passed a callback, assertions are run
* asynchronously. Therefore, a mechanism must be used to notify the testing
* framework that the callback has completed. Otherwise, the test will pass before
* the assertions are checked.
*
* For example, in the [Mocha test framework](http://mochajs.org/), this is
* accomplished using the
* [`done` callback](https://mochajs.org/#asynchronous-code), which signal that the
* callback has completed, and the assertions can be verified:
*
* ```js
* it('fails, as expected', function(done) { // <= Pass in done callback
* chai.request('http://localhost:8080')
* .get('/')
* .end(function(err, res) {
* expect(res).to.have.status(123);
* done(); // <= Call done to signal callback end
* });
* }) ;
*
* it('succeeds silently!', function() { // <= No done callback
* chai.request('http://localhost:8080')
* .get('/')
* .end(function(err, res) {
* expect(res).to.have.status(123); // <= Test completes before this runs
* });
* }) ;
* ```
*
* When `done` is passed in, Mocha will wait until the call to `done()`, or until
* the [timeout](http://mochajs.org/#timeouts) expires. `done` also accepts an
* error parameter when signaling completion.
*
* #### Dealing with the response - Promises
*
* If `Promise` is available, `request()` becomes a Promise capable library -
* and chaining of `then`s becomes possible:
*
* ```js
* chai.request(app)
* .put('/user/me')
* .send({ password: '123', confirmPassword: '123' })
* .then(function (res) {
* expect(res).to.have.status(200);
* })
* .catch(function (err) {
* throw err;
* })
* ```
*
* __Note:__ Node.js version 0.10.x and some older web browsers do not have
* native promise support. You can use any spec compliant library, such as:
* - [kriskowal/q](https://github.com/kriskowal/q)
* - [stefanpenner/es6-promise](https://github.com/stefanpenner/es6-promise)
* - [petkaantonov/bluebird](https://github.com/petkaantonov/bluebird)
* - [then/promise](https://github.com/then/promise)
* You will need to set the library you use to `global.Promise`, before
* requiring in chai-http. For example:
*
* ```js
* // Add promise support if this does not exist natively.
* if (!global.Promise) {
* global.Promise = require('q');
* }
* var chai = require('chai');
* chai.use(require('chai-http'));
*
* ```
*
* #### Retaining cookies with each request
*
* Sometimes you need to keep cookies from one request, and send them with the
* next. For this, `.request.agent()` is available:
*
* ```js
* // Log in
* var agent = chai.request.agent(app)
* agent
* .post('/session')
* .send({ username: 'me', password: '123' })
* .then(function (res) {
* expect(res).to.have.cookie('sessionid');
* // The `agent` now has the sessionid cookie saved, and will send it
* // back to the server in the next request:
* return agent.get('/user/me')
* .then(function (res) {
* expect(res).to.have.status(200);
* })
* })
* ```
*
*/
module.exports = function (app) {
/*!
* @param {Mixed} function or server
* @returns {Object} API
*/
var server = ('function' === typeof app)
? http.createServer(app)
: app
, obj = {};
var keepOpen = false
if (typeof server !== 'string' && server && server.listen && server.address) {
if (!server.address()) {
server = server.listen(0)
}
}
obj.keepOpen = function() {
keepOpen = true
return this
}
obj.close = function(callback) {
if (server && server.close) {
server.close(callback);
}
else if(callback) {
callback();
}
return this
}
methods.forEach(function (method) {
obj[method] = function (path) {
return new Test(server, method, path)
.on('end', function() {
if(keepOpen === false) {
obj.close();
}
});
};
});
obj.del = obj.delete;
return obj;
};
module.exports.Test = Test;
module.exports.Request = Test;
module.exports.agent = TestAgent;
/*!
* Test
*
* An extension of superagent.Request,
* this provides the same chainable api
* as superagent so all things can be modified.
*
* @param {Object|String} server, app, or url
* @param {String} method
* @param {String} path
* @api private
*/
function Test (app, method, path) {
Request.call(this, method, path);
this.app = app;
this.url = typeof app === 'string' ? app + path : serverAddress(app, path);
this.ok(function() {
return true;
});
}
util.inherits(Test, Request);
function serverAddress (app, path) {
if ('string' === typeof app) {
return app + path;
}
var addr = app.address();
if (!addr) {
throw new Error('Server is not listening')
}
var protocol = (app instanceof https.Server) ? 'https' : 'http';
// If address is "unroutable" IPv4/6 address, then set to localhost
if (addr.address === '0.0.0.0' || addr.address === '::') {
addr.address = '127.0.0.1';
}
return protocol + '://' + addr.address + ':' + addr.port + path;
}
/*!
* agent
*
* Follows the same API as superagent.Request,
* but allows persisting of cookies between requests.
*
* @param {Object|String} server, app, or url
* @param {String} method
* @param {String} path
* @api private
*/
function TestAgent(app) {
if (!(this instanceof TestAgent)) return new TestAgent(app);
if (typeof app === 'function') app = http.createServer(app);
(Agent || Request).call(this);
this.app = app;
if (typeof app !== 'string' && app && app.listen && app.address && !app.address()) {
this.app = app.listen(0)
}
}
util.inherits(TestAgent, Agent || Request);
TestAgent.prototype.close = function close(callback) {
if (this.app && this.app.close) {
this.app.close(callback)
}
return this
}
TestAgent.prototype.keepOpen = function keepOpen() {
return this
}
// override HTTP verb methods
methods.forEach(function(method){
TestAgent.prototype[method] = function(url){
var req = new Test(this.app, method, url)
, self = this;
if (Agent) {
// When running in Node, cookies are managed via
// `Agent._saveCookies()` and `Agent._attachCookies()`.
req.on('response', function (res) { self._saveCookies(res); });
req.on('redirect', function (res) { self._saveCookies(res); });
req.on('redirect', function () { self._attachCookies(req); });
this._attachCookies(req);
}
else {
// When running in a web browser, cookies are managed via `Request.withCredentials()`.
// The browser will attach cookies based on same-origin policy.
// https://tools.ietf.org/html/rfc6454#section-3
req.withCredentials();
}
return req;
};
});
TestAgent.prototype.del = TestAgent.prototype.delete;
},{"http":4,"https":4,"methods":9,"superagent":20,"util":30}],4:[function(require,module,exports){
},{}],5:[function(require,module,exports){
/**
* Expose `Emitter`.
*/
if (typeof module !== 'undefined') {
module.exports = Emitter;
}
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
};
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks['$' + event] = this._callbacks['$' + event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
function on() {
this.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks['$' + event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks['$' + event];
return this;
}
// remove specific handler
var cb;
for (var i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = [].slice.call(arguments, 1)
, callbacks = this._callbacks['$' + event];
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks['$' + event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
},{}],6:[function(require,module,exports){
/* jshint node: true */
(function () {
"use strict";
function CookieAccessInfo(domain, path, secure, script) {
if (this instanceof CookieAccessInfo) {
this.domain = domain || undefined;
this.path = path || "/";
this.secure = !!secure;
this.script = !!script;
return this;
}
return new CookieAccessInfo(domain, path, secure, script);
}
CookieAccessInfo.All = Object.freeze(Object.create(null));
exports.CookieAccessInfo = CookieAccessInfo;
function Cookie(cookiestr, request_domain, request_path) {
if (cookiestr instanceof Cookie) {
return cookiestr;
}
if (this instanceof Cookie) {
this.name = null;
this.value = null;
this.expiration_date = Infinity;
this.path = String(request_path || "/");
this.explicit_path = false;
this.domain = request_domain || null;
this.explicit_domain = false;
this.secure = false; //how to define default?
this.noscript = false; //httponly
if (cookiestr) {
this.parse(cookiestr, request_domain, request_path);
}
return this;
}
return new Cookie(cookiestr, request_domain, request_path);
}
exports.Cookie = Cookie;
Cookie.prototype.toString = function toString() {
var str = [this.name + "=" + this.value];
if (this.expiration_date !== Infinity) {
str.push("expires=" + (new Date(this.expiration_date)).toGMTString());
}
if (this.domain) {
str.push("domain=" + this.domain);
}
if (this.path) {
str.push("path=" + this.path);
}
if (this.secure) {
str.push("secure");
}
if (this.noscript) {
str.push("httponly");
}
return str.join("; ");
};
Cookie.prototype.toValueString = function toValueString() {
return this.name + "=" + this.value;
};
var cookie_str_splitter = /[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;
Cookie.prototype.parse = function parse(str, request_domain, request_path) {
if (this instanceof Cookie) {
var parts = str.split(";").filter(function (value) {
return !!value;
}),
pair = parts[0].match(/([^=]+)=([\s\S]*)/),
key = pair[1],
value = pair[2],
i;
this.name = key;
this.value = value;
for (i = 1; i < parts.length; i += 1) {
pair = parts[i].match(/([^=]+)(?:=([\s\S]*))?/);
key = pair[1].trim().toLowerCase();
value = pair[2];
switch (key) {
case "httponly":
this.noscript = true;
break;
case "expires":
this.expiration_date = value ?
Number(Date.parse(value)) :
Infinity;
break;
case "path":
this.path = value ?
value.trim() :
"";
this.explicit_path = true;
break;
case "domain":
this.domain = value ?
value.trim() :
"";
this.explicit_domain = !!this.domain;
break;
case "secure":
this.secure = true;
break;
}
}
if (!this.explicit_path) {
this.path = request_path || "/";
}
if (!this.explicit_domain) {
this.domain = request_domain;
}
return this;
}
return new Cookie().parse(str, request_domain, request_path);
};
Cookie.prototype.matches = function matches(access_info) {
if (access_info === CookieAccessInfo.All) {
return true;
}
if (this.noscript && access_info.script ||
this.secure && !access_info.secure ||
!this.collidesWith(access_info)) {
return false;
}
return true;
};
Cookie.prototype.collidesWith = function collidesWith(access_info) {
if ((this.path && !access_info.path) || (this.domain && !access_info.domain)) {
return false;
}
if (this.path && access_info.path.indexOf(this.path) !== 0) {
return false;
}
if (this.explicit_path && access_info.path.indexOf( this.path ) !== 0) {
return false;
}
var access_domain = access_info.domain && access_info.domain.replace(/^[\.]/,'');
var cookie_domain = this.domain && this.domain.replace(/^[\.]/,'');
if (cookie_domain === access_domain) {
return true;
}
if (cookie_domain) {
if (!this.explicit_domain) {
return false; // we already checked if the domains were exactly the same
}
var wildcard = access_domain.indexOf(cookie_domain);
if (wildcard === -1 || wildcard !== access_domain.length - cookie_domain.length) {
return false;
}
return true;
}
return true;
};
function CookieJar() {
var cookies, cookies_list, collidable_cookie;
if (this instanceof CookieJar) {
cookies = Object.create(null); //name: [Cookie]
this.setCookie = function setCookie(cookie, request_domain, request_path) {
var remove, i;
cookie = new Cookie(cookie, request_domain, request_path);
//Delete the cookie if the set is past the current time
remove = cookie.expiration_date <= Date.now();
if (cookies[cookie.name] !== undefined) {
cookies_list = cookies[cookie.name];
for (i = 0; i < cookies_list.length; i += 1) {
collidable_cookie = cookies_list[i];
if (collidable_cookie.collidesWith(cookie)) {
if (remove) {
cookies_list.splice(i, 1);
if (cookies_list.length === 0) {
delete cookies[cookie.name];
}
return false;
}
cookies_list[i] = cookie;
return cookie;
}
}
if (remove) {
return false;
}
cookies_list.push(cookie);
return cookie;
}
if (remove) {
return false;
}
cookies[cookie.name] = [cookie];
return cookies[cookie.name];
};
//returns a cookie
this.getCookie = function getCookie(cookie_name, access_info) {
var cookie, i;
cookies_list = cookies[cookie_name];
if (!cookies_list) {
return;
}
for (i = 0; i < cookies_list.length; i += 1) {
cookie = cookies_list[i];
if (cookie.expiration_date <= Date.now()) {
if (cookies_list.length === 0) {
delete cookies[cookie.name];
}
continue;
}
if (cookie.matches(access_info)) {
return cookie;
}
}
};
//returns a list of cookies
this.getCookies = function getCookies(access_info) {
var matches = [], cookie_name, cookie;
for (cookie_name in cookies) {
cookie = this.getCookie(cookie_name, access_info);
if (cookie) {
matches.push(cookie);
}
}
matches.toString = function toString() {
return matches.join(":");
};
matches.toValueString = function toValueString() {
return matches.map(function (c) {
return c.toValueString();
}).join(';');
};
return matches;
};
return this;
}
return new CookieJar();
}
exports.CookieJar = CookieJar;
//returns list of cookies that were set correctly. Cookies that are expired and removed are not returned.
CookieJar.prototype.setCookies = function setCookies(cookies, request_domain, request_path) {
cookies = Array.isArray(cookies) ?
cookies :
cookies.split(cookie_str_splitter);
var successful = [],
i,
cookie;
cookies = cookies.map(function(item){
return new Cookie(item, request_domain, request_path);
});
for (i = 0; i < cookies.length; i += 1) {
cookie = cookies[i];
if (this.setCookie(cookie, request_domain, request_path)) {
successful.push(cookie);
}
}
return successful;
};
}());
},{}],7:[function(require,module,exports){
'use strict';
const v4 = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}';
const v6seg = '[0-9a-fA-F]{1,4}';
const v6 = `
(
(?:${v6seg}:){7}(?:${v6seg}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
(?:${v6seg}:){6}(?:${v4}|:${v6seg}|:)| // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4
(?:${v6seg}:){5}(?::${v4}|(:${v6seg}){1,2}|:)| // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4
(?:${v6seg}:){4}(?:(:${v6seg}){0,1}:${v4}|(:${v6seg}){1,3}|:)| // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4
(?:${v6seg}:){3}(?:(:${v6seg}){0,2}:${v4}|(:${v6seg}){1,4}|:)| // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4
(?:${v6seg}:){2}(?:(:${v6seg}){0,3}:${v4}|(:${v6seg}){1,5}|:)| // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4
(?:${v6seg}:){1}(?:(:${v6seg}){0,4}:${v4}|(:${v6seg}){1,6}|:)| // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4
(?::((?::${v6seg}){0,5}:${v4}|(?::${v6seg}){1,7}|:)) // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4
)(%[0-9a-zA-Z]{1,})? // %eth0 %1
`.replace(/\s*\/\/.*$/gm, '').replace(/\n/g, '').trim();
const ip = module.exports = opts => opts && opts.exact ?
new RegExp(`(?:^${v4}$)|(?:^${v6}$)`) :
new RegExp(`(?:${v4})|(?:${v6})`, 'g');
ip.v4 = opts => opts && opts.exact ? new RegExp(`^${v4}$`) : new RegExp(v4, 'g');
ip.v6 = opts => opts && opts.exact ? new RegExp(`^${v6}$`) : new RegExp(v6, 'g');
},{}],8:[function(require,module,exports){
'use strict';
const ipRegex = require('ip-regex');
const isIp = module.exports = x => ipRegex({exact: true}).test(x);
isIp.v4 = x => ipRegex.v4({exact: true}).test(x);
isIp.v6 = x => ipRegex.v6({exact: true}).test(x);
},{"ip-regex":7}],9:[function(require,module,exports){
/*!
* methods
* Copyright(c) 2013-2014 TJ Holowaychuk
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var http = require('http');
/**
* Module exports.
* @public
*/
module.exports = getCurrentNodeMethods() || getBasicNodeMethods();
/**
* Get the current Node.js methods.
* @private
*/
function getCurrentNodeMethods() {
return http.METHODS && http.METHODS.map(function lowerCaseMethod(method) {
return method.toLowerCase();
});
}
/**
* Get the "basic" Node.js methods, a snapshot from Node.js 0.10.
* @private
*/
function getBasicNodeMethods() {
return [
'get',
'post',
'put',
'head',
'delete',
'options',
'trace',
'copy',
'lock',
'mkcol',
'move',
'purge',
'propfind',
'proppatch',
'unlock',
'report',
'mkactivity',
'checkout',
'merge',
'm-search',
'notify',
'subscribe',
'unsubscribe',
'patch',
'search',
'connect'
];
}
},{"http":4}],10:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],11:[function(require,module,exports){
(function (global){
/*! https://mths.be/punycode v1.4.1 by @mathias */
;(function(root) {
/** Detect free variables */
var freeExports = typeof exports == 'object' && exports &&
!exports.nodeType && exports;
var freeModule = typeof module == 'object' && module &&
!module.nodeType && module;
var freeGlobal = typeof global == 'object' && global;
if (
freeGlobal.global === freeGlobal ||
freeGlobal.window === freeGlobal ||
freeGlobal.self === freeGlobal
) {
root = freeGlobal;
}
/**
* The `punycode` object.
* @name punycode
* @type Object
*/
var punycode,
/** Highest positive signed 32-bit float value */
maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
/** Bootstring parameters */
base = 36,
tMin = 1,
tMax = 26,
skew = 38,
damp = 700,
initialBias = 72,
initialN = 128, // 0x80
delimiter = '-', // '\x2D'
/** Regular expressions */
regexPunycode = /^xn--/,
regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
/** Error messages */
errors = {
'overflow': 'Overflow: input needs wider integers to process',
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
'invalid-input': 'Invalid input'
},
/** Convenience shortcuts */
baseMinusTMin = base - tMin,
floor = Math.floor,
stringFromCharCode = String.fromCharCode,
/** Temporary variable */
key;
/*--------------------------------------------------------------------------*/
/**
* A generic error utility function.
* @private
* @param {String} type The error type.
* @returns {Error} Throws a `RangeError` with the applicable error message.
*/
function error(type) {
throw new RangeError(errors[type]);
}
/**
* A generic `Array#map` utility function.
* @private
* @param {Array} array The array to iterate over.
* @param {Function} callback The function that gets called for every array
* item.
* @returns {Array} A new array of values returned by the callback function.
*/
function map(array, fn) {
var length = array.length;
var result = [];
while (length--) {
result[length] = fn(array[length]);
}
return result;
}
/**
* A simple `Array#map`-like wrapper to work with domain name strings or email
* addresses.
* @private
* @param {String} domain The domain name or email address.
* @param {Function} callback The function that gets called for every
* character.
* @returns {Array} A new string of characters returned by the callback
* function.
*/
function mapDomain(string, fn) {
var parts = string.split('@');
var result = '';
if (parts.length > 1) {
// In email addresses, only the domain name should be punycoded. Leave
// the local part (i.e. everything up to `@`) intact.
result = parts[0] + '@';
string = parts[1];
}
// Avoid `split(regex)` for IE8 compatibility. See #17.
string = string.replace(regexSeparators, '\x2E');
var labels = string.split('.');
var encoded = map(labels, fn).join('.');
return result + encoded;
}
/**
* Creates an array containing the numeric code points of each Unicode
* character in the string. While JavaScript uses UCS-2 internally,
* this function will convert a pair of surrogate halves (each of which
* UCS-2 exposes as separate characters) into a single code point,
* matching UTF-16.
* @see `punycode.ucs2.encode`
* @see <https://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode.ucs2
* @name decode
* @param {String} string The Unicode input string (UCS-2).
* @returns {Array} The new array of code points.
*/
function ucs2decode(string) {
var output = [],
counter = 0,
length = string.length,
value,
extra;
while (counter < length) {
value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// high surrogate, and there is a next character
extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) { // low surrogate
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
/**
* Creates a string based on an array of numeric code points.
* @see `punycode.ucs2.decode`
* @memberOf punycode.ucs2
* @name encode
* @param {Array} codePoints The array of numeric code points.
* @returns {String} The new Unicode string (UCS-2).
*/
function ucs2encode(array) {
return map(array, function(value) {
var output = '';
if (value > 0xFFFF) {
value -= 0x10000;
output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
value = 0xDC00 | value & 0x3FF;
}
output += stringFromCharCode(value);
return output;
}).join('');
}
/**
* Converts a basic code point into a digit/integer.
* @see `digitToBasic()`
* @private
* @param {Number} codePoint The basic numeric code point value.
* @returns {Number} The numeric value of a basic code point (for use in
* representing integers) in the range `0` to `base - 1`, or `base` if
* the code point does not represent a value.
*/
function basicToDigit(codePoint) {
if (codePoint - 48 < 10) {
return codePoint - 22;
}
if (codePoint - 65 < 26) {
return codePoint - 65;
}
if (codePoint - 97 < 26) {
return codePoint - 97;
}
return base;
}
/**
* Converts a digit/integer into a basic code point.
* @see `basicToDigit()`
* @private
* @param {Number} digit The numeric value of a basic code point.
* @returns {Number} The basic code point whose value (when used for
* representing integers) is `digit`, which needs to be in the range
* `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
* used; else, the lowercase form is used. The behavior is undefined
* if `flag` is non-zero and `digit` has no uppercase form.
*/
function digitToBasic(digit, flag) {
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
}
/**
* Bias adaptation function as per section 3.4 of RFC 3492.
* https://tools.ietf.org/html/rfc3492#section-3.4
* @private
*/
function adapt(delta, numPoints, firstTime) {
var k = 0;
delta = firstTime ? floor(delta / damp) : delta >> 1;
delta += floor(delta / numPoints);
for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
delta = floor(delta / baseMinusTMin);
}
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
}
/**
* Converts a Punycode string of ASCII-only symbols to a string of Unicode
* symbols.
* @memberOf punycode
* @param {String} input The Punycode string of ASCII-only symbols.
* @returns {String} The resulting string of Unicode symbols.
*/
function decode(input) {
// Don't use UCS-2
var output = [],
inputLength = input.length,
out,
i = 0,
n = initialN,
bias = initialBias,
basic,
j,
index,
oldi,
w,
k,
digit,
t,
/** Cached calculation results */
baseMinusT;
// Handle the basic code points: let `basic` be the number of input code
// points before the la