UNPKG

chai-http

Version:

Extend Chai Assertion library with tests for http apis

1,742 lines (1,575 loc) 163 kB
(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