ngn-idk-core
Version:
1,322 lines (1,177 loc) • 36.7 kB
JavaScript
var fs = require('fs'),
path = require('path'),
Base = require('./Server');
/**
* @class NGN.core.HttpTransport
* A generic utility/base class representing a HTTP server.
* This class typically isn't invoked directly. It is designed as a base class
* for different server types like NGN.http.Server, NGN.http.APIServer, etc.
* @private
* @extends NGN.core.Server
*/
var Class = Base.extend({
/**
* @constructor
* Create a new server.
* @param {Object} config
*/
constructor: function(config){
config = config || {};
config.type = 'HTTP';
Class.super.constructor.call(this, config);
Object.defineProperties(this,{
/**
* @cfg {Boolean} [autoRefreshRoutes=true]
* Setting this to `true` turns on a file watcher on the #routes directory.
* Anytime a file change is detected, the routes will be automatically
* reloaded without requiring a server restart.
*/
autoRefreshRoutes: {
enumerable: true,
writable: false,
configurable: false,
value: NGN.coalesce(config.autoRefreshRoutes,true)
},
/**
* @property {Object}
* The core server object.
* @private
* @readonly
*/
server: {
value: {},
enumerable: true,
writable: true,
configurable:true
},
/**
* @property {Object} _serverOptions
* The core server options object.
* @private
* @readonly.
*/
_serverOptions: {
value: config.ssl || null,
enumerable: false,
writable: true,
configurable:true
},
/*
* @property {Object} _router
* Holds the router information relevant to the source file.
* This is for internal use only.
* @readonly
*/
_router: {
enumerable: false,
writable: true,
configurable: false,
value: {}
},
/**
* @cfg {String[]} [customHeaders=[]]
* One or more custom HTTP headers
* explicitly allowed by `Access-Control-Allow-Headers`.
* This is most commonly used to support specific headers
* from cross-origin sources (ex: `X-Requested-With` or `X-API-Key`).
*/
customHeaders: {
value: config.customHeaders || [],
enumerable: true,
writable: true
},
/**
* @cfg {String/Array} routes (required)
* Path to directory where routes are stored. Also accepts an
* array of file paths. Routes are created by recursively
* processing each specified directory and importing each `.js` file
* it finds. Supports relative and absolute paths.
*/
routes: {
value: config.routes || [],
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Object} [ssl=null]
* The certificate and key used for encrypting HTTP**S** traffic.
*
* {
* cert: '/path/to/cert.pem',
* key: '/path/to/key.pem',
* ca: '/path/to/ca.crt' // Optional
* }
* For more information about generating SSL certificates, please see the [NGN Command Line Interface Guide](#!/guide/cli).
*/
ssl: {
value: config.ssl || null,
enumerable: true,
configurable:true
},
/**
* @cfg {Boolean} [requireClientSsl=false]
* Require a client/browser SSL certificate to access the server.
*/
requireClientCert: {
value: NGN.coalesce(config.requireClientSsl,false),
enumerable: true,
writable: true
},
/**
* @cfg {Boolean} [rejectUnauthorizedClientCert=false]
* Reject a client connection if the certificate presented is not assigned by the CA cert (see #ssl), has expired, or has been revoked.
*
* **Only applicable when #requireClientSsl is** `true`**.**
*/
rejectUnauthorizedCert: {
value: NGN.coalesce(config.rejectUnauthorizedClientCert,false),
enumerable: true,
writable: true
},
/**
* @cfg {Number} [port=80|443]
* The port on which the web server will listen. Each web server must
* listen on a unique port. If no port is defined, the default port `80`
* will be used. If #ssl is supplied, the port will be set to `443` by default.
*/
port: {
value: config.port || (this.ssl == null ? 80 : 443),
enumerable: true,
writable: true
},
/**
* @property {Object}
* The raw HTTP Server.
* @readonly
* @private
*/
_httpServer: {
value: null,
enumerable: false,
writable: true
},
/**
* @cfg {Boolean} [enableCompression=false]
* Compress output sent to the browser using gzip.
*/
enableCompression: {
value: NGN.coalesce(config.enableCompression,false),
enumerable: true,
writable: true
},
/**
* @cfg {Function}
* A middleware function designed for authenticating client requests before processing them.
*
* The function will receive three arguments, including `request`, `response`, and `next` (optional).
* If the function returns `false`, the server will respond with a `401 - Unauthorized`. If it returns
* `true`, processing continues.
*
* **Example**
* var memberIds = [123,456,789,1011,1213];
*
* {
* authenticate: function(req,res,next){
* if (memberIds.indexOf(req.user.id) !== -1) {
* return true;
* } else {
* return false;
* }
* }
* }
*
* If the application requires custom authentication responses (such as sending a `403 -Forbidden` instead of `401`), this configuration
* should be ignored. A custom #preRequestMiddleware method can be used instead.
*
*/
authenticate: {
value: config.authenticate || function(req,res,next){next();},
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Function}
* A middleware function designed for authorizing client requests before processing them. This is always
* executed _after_ #authenticate.
*
* The function will receive three arguments, including `request`, `response`, and `next` (optional).
* If the function returns `false`, the server will respond with a `401 - Unauthorized`. If it returns
* `true`, processing continues.
*
* **Example**
* var adminIds = [123,456];
*
* {
* authorize: function(req,res,next){
* if (adminIds.indexOf(req.user.id) !== -1) {
* return true;
* } else {
* return false;
* }
* }
* }
*
* If the application requires custom authentication responses (such as sending a `403 -Forbidden` instead of `401`), this configuration
* should be ignored. A custom #preRequestMiddleware method can be used instead.
*
*/
authorize: {
value: config.authorize || function(req,res,next){next();},
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Boolean/String/Array} [allowCrossOrigin=false]
* Set to `true` to enable requests from all origins. Alternatively,
* provide a {String} domain, such as `http://my.safedomain.com` or `https://my.safedomain.com, https://other.domain.net`.
* Also accepts an array of domains:
*
* ['https://first.safedomain.com','https://second.safedomain.com']
*/
_ALLOWCORS: {
value: config.allowCrossOrigin || null,
enumerable: false,
writable: true
},
allowCrossOrigin: {
enumerable: true,
get: function(){
if (this._ALLOWCORS === null){
return false;
}
if (typeof this._ALLOWCORS === 'boolean'){
return this._ALLOWCORS;
}
if (Array.isArray(this._ALLOWCORS)){
return true;
}
if (this._ALLOWCORS === '*'){
return true;
}
return false;
}
},
CORSDOMAINS: {
enumerable: false,
get: function(){
return this.allowCrossOrigin === true ? (
Array.isArray(this._ALLOWCORS) ? this._ALLOWCORS : (
typeof this._ALLOWCORS === 'boolean' ? ['*'] : this._ALLOWCORS.split(',')
)
) : [];
}
},
/**
* @cfg {Boolean} [allowCrossOriginCookies=false]
* By default, cookies are not included in CORS requests. Use this header to indicate that cookies should be included in CORS requests.
*/
_ALLOWCORSCREDENTIALS: {
value: NGN.coalesce(config.allowCrossOriginCookies,false),
enumerable: false,
writable: false,
configurable: false
},
_ALLOWMETHODS: {
value: config.allowedMethods || null,
enumerable: false,
writable: true
},
/**
* @property {Array} allowedMethods
* An array of the HTTP methods allowed by the server. By default,
* all methods are allowed. Methods may be restricted explicitly
* setting the #allowedMethods configuration parameter.
*/
/**
* @cfg {String/Array}
* Restrict traffic to specific HTTP methods/verbs such as `GET`,`POST`,`PUT`, and `DELETE`.
* By default, everything is allowed. Only configure this when the application needs to explicitly
* use a limited number of verbs. For example, a read-only site may set this to `GET`.
*/
allowedMethods: {
enumerable: true,
get: function(){
if (this._ALLOWMETHODS == null){
return ['GET','HEAD','POST','PUT','DELETE','TRACE','OPTIONS','CONNECT','PATCH'];
} else {
return this._ALLOWMETHODS.toString().toUpperCase().split(',');
}
},
set: function(value){
this._ALLOWMETHODS = (Array.isArray(value) == true ? value.join() : value).toUpperCase();
}
},
/*
* @cfg {Object}
* A key/value object containing custom middleware functions executed before.
*/
/*preRequestMiddleware: {
value: config.middleware || {},
enumerable: true,
configurable:true
}*/
/**
* @cfg {Object}
* A key/value object containing `<username>:<password>`.
*
* **Example**
* {
* jdoe: 'password1',
* msmith: 'password2',
* bpatel: 'password3'
* }
* By providing a basic authorization map, the server will automatically enable BasicAuth restrictions.
* These are applied _instead of_ #authenticate and #authorize middleware, unless BasicAuth is explicitly
* disabled using #disableBasicAuth. It is possible to configure code with all three auth middleware features and
* toggle them on/off by enabling/disabling BasicAuth. However; most applications only require one feature.
*/
basicAuthUsers: {
value: config.basicAuthUsers || null,
enumerable: false,
writable: true,
configurable:true
},
/**
* @cfg {Boolean} [basicAuthCaseSensitive=true]
* When using #basicAuthUsers, all password matching will be done with respect to upper/lower case.
*/
basicAuthCaseSensitive: {
value: NGN.coalesce(config.basicAuthCaseSensitive,true),
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Boolean} [enableBasicAuth=false]
* Setting this to true will require basic authentication before completing a request. This should be
* used in conjunction with #basicAuthUsers. If no users are specified, every request will fail!
*
* Basic authentication is executed _before_ #authenticate and #authorize.
*/
disableBasicAuth: {
value: NGN.coalesce(config.enableBasicAuth,false),
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Function/Array/NGN.http.Processor} [processors=[]]
* Supply a middleware function, processor, or array of functions/processors.
*/
processor: {
value: config.processors || config.processor || [],
enumerable: true,
writable: true,
configurable:true
},
/**
* @property {Boolean}
* Indicates custom request processors have been configured.
*/
_hasProcessors: {
value: false,
enumerable: false,
writable: true
}
});
if (!Array.isArray(this.processor)){
this.processor = [this.processor];
}
for(var i=0;i<this.processor.length;i++){
if (this.processor[i].type == undefined)
this.processor[i] = new NGN.http.Processor({fn:this.processor[i]});
}
if (this.requireClientCert || this.rejectUnauthorizedCert) {
this.serverOptions = this.serverOptions || {};
this.serverOptions.requireClientCert = true;
if (this.rejectUnauthorizedCert)
this.serverOptions.rejectUnauthorizedCert = true;
}
if (!this.customHeaders instanceof Array)
this.customHeaders = this.customHeaders.split(',');
if (this.customHeaders.length > 0)
this.allowCustomHeader(this.customHeaders);
this.routes = (typeof this.routes == 'string' ? this.routes.split(',') : this.routes);
var chk = this.routes.slice(0);
var me = this;
this.routes = [];
chk.forEach(function(rt){
var absRoute = NGN.absolutePath(rt);
if (!fs.existsSync(absRoute)){
me.fireWarning(rt+' not found/resolved while loading routes for '+me.name);
me.routes.splice(me.routes.indexOf(rt),1);
} else {
chk[chk.indexOf(rt)] = absRoute;
}
});
this.routes = chk;
// Setup file watcher
if (this.autoRefreshRoutes){
var me = this;
Object.defineProperty(this,'clearRouteSource',{
enumerable: false,
writable: true,
configurable: false,
value: function(filename,remove){
try {
remove = NGN.coalesce(remove,false);
var routes = me.server.routes,
unaffected = {};
for (var method in routes){
if (['get','post','put','delete','options','head','trace'].indexOf(method.toLowerCase()) >= 0){
var arr = routes[method].slice(0);
unaffected[method] = unaffected[method] || [];
unaffected[method] = unaffected[method].concat(arr.filter(function(route){
return !route.source;
}));
var newRt = routes[method].slice(0).filter(function(route){return route.source && route.source !== filename;});
delete me.server.routes[method];
}
}
UTIL.unrequire(filename);
if (!remove) {
var rt = require(filename);
me.server.maproute(rt);
for (var method in me.server.routes){
for (var route in me.server.routes[method]){
Object.keys(rt).forEach(function(path){
if (me.server.routes[method][route].path){
if (me.server.routes[method][route].path.toString().indexOf(path) == 0){
me.server.routes[method][route].source = filename;
}
}
});
}
}
}
for (var method in unaffected){
me.server.routes[method] = (me.server.routes[method]||[]).concat(unaffected[method]||[]);
}
} catch (e) {
me.fireWarning(filename+' failed to hot-load. Error: '+e.message);
}
}
});
this.routes.forEach(function(rt){
UTIL.watch.createMonitor(path.resolve(rt),function(monitor){
monitor.on("created", function (filename, stat) {
me.clearRouteSource(filename);
console.log(('New routes were automatically added to the '+me.type+' server running on port '+me.port+' from '+filename).green);
});
monitor.on("changed", function (filename, curr, prev) {
me.clearRouteSource(filename);
console.log(('The routes for the '+me.type+' server running on port '+me.port+' were automatically refreshed to incorporate changes from '+filename).green);
});
monitor.on("removed", function (filename, stat) {
me.clearRouteSource(filename,true);
console.log(('Routes were automatically removed from the '+me.type+' server running on port '+me.port+' when '+filename+' was removed.').green);
});
});
});
}
},
/**
* @method
* Apply a middleware processor to the server.
* @param {String} [type=request]
* The type of processor to apply. Valid values include:
* - preauthentication
* - postauthentication
* - preauthorization
* - postauthorization
* - prerequest
* - postrequest
*/
applyProcessor: function(type){
var me = this;
for(var i=0;i<this.processor.length;i++){
if (this.processor[i].type == type && !this.processor[i]._used){
this.server.use(me.processor[i].processor);
this.processor[i].used = true;
}
}
},
/**
* @method
* Add a custom middleware function or request processor.
* @param {Function/NGN.http.Processor} requestProcessor
*/
addProcessor: function(rp){
if (rp.type == undefined)
this.processor.push(new NGN.http.Processor({fn:rp}));
else
this.processor.push(rp);
},
/**
* @method
* A basic authentication method for testing a user against the #basicAuthUsers mapping.
* @param {String} username
* Account/login.
* @param {String} password
* Plain text password.
* @returns {Boolean}
*/
testBasicAuth: function(username, password) {
if (disaleBasicAuth)
return true;
if (this.basicAuthUsers[username] == undefined)
return false;
if (!this.basicAuthCaseSensitive) {
if (this.basicAuthUsers[username].toLowerCase() == password.toLowerCase())
return true;
} else if (this.basicAuthUsers[username] == password)
return true;
return false;
},
/**
* @method
* Generates server routes. If no #routes are configured, a generic
* route will be created for the URL root.
* @param {Function} [callback]
* The callback sends a single {Boolean} argument to a method indicating a root route has been created.
* @returns {Boolean}
*/
generateRoutes: function(callback) {
return this._generateRoutes(callback);
},
_generateRoutes: function(callback){
callback = callback || null;
var me = this;
// Create a mapper
if (!this.server.hasOwnProperty('maproute')){
Object.defineProperty(this.server,'maproute',{
value: function(a, route){
route = route || '';
for (var key in a) {
if (a.hasOwnProperty(key)) {
switch (typeof a[key]) {
case 'object':
me.server.maproute(a[key], route + key);
break;
case 'function':
//console.log('%s %s', key, route);
//me.server[key](route, a[key]);
// Add special pointer variables for simpler processing.
// If the route is a regex, support it
if (route.trim().substr(0,1) == route.substr(route.trim().length-1,1) == '/')
route = new RegExp(route.trim().substr(1,route.trim().length-2));
me.server[key.toLowerCase()](route,a[key]);
break;
}
}
}
},
enumerable: true,
writable: false
});
}
// If the routes configuration provides an array of filepaths, loop
// through them to process them all
this.routes = (typeof this.routes == 'string' ? this.routes.split(',') : this.routes);
var _hasRootRoute = false;
if (Array.isArray(this.routes)) {
for (var i=0; i<this.routes.length; i++) {
// Loop through the directories and add each JS file, assuming each is a route.
var rt = this.routes[i].trim();
// Attempt to load absolute path first. Then local path.
if (fs.existsSync(path.resolve(rt)))
rt = path.resolve(rt);
else
rt = path.join(NGN.rootDir,rt);
// If a path is defined, make sure it exists.
if (!fs.existsSync(rt)){
this.fireError('The route path does not exist or could not be found. '+rt);
return;
}
// Sync Route Loader
var filepath = UTIL.wrench.readdirSyncRecursive(rt);
for (var n=0;n<filepath.length;n++){
if (path.extname(filepath[n]).toLowerCase() == '.js' && filepath[n].toString().trim().toLowerCase() !== 'root.js'){
var _rt = require(path.join(rt,filepath[n]));
if (typeof _rt == 'function'){
_rt = _rt(me);
}
if (!_hasRootRoute && _rt['/'] !== undefined)
_hasRootRoute = true;
this.server.maproute(_rt);
if (this.autoRefreshRoutes) {
for (var method in this.server.routes){
for (var route in this.server.routes[method]){
var paths = Object.keys(_rt);
for (var i=0; i<paths.length; i++){
if (this.server.routes[method][route].path){
if (this.server.routes[method][route].path.indexOf(paths[i]) == 0){
this.server.routes[method][route].source = path.join(rt,filepath[n]);
}
}
}
}
}
}
}
}
if (!_hasRootRoute && require('fs').existsSync(path.join(rt,'root.js'))) {
var _rt = require(path.join(rt,'root.js'));
if (typeof _rt == 'function'){
_rt = _rt(me);
}
if (!_hasRootRoute && _rt.hasOwnProperty('/'))
_hasRootRoute = true;
this.server.maproute(_rt);
if(this.autoRefreshRoutes){
for (var method in this.server.routes){
for (var route in this.server.routes[method]){
var paths = Object.keys(_rt);
for (var i=0; i<paths.length; i++){
if (this.server.routes[method][route].path){
if (this.server.routes[method][route].path.indexOf(paths[i]) == 0 && paths[i] !== '/'){
this.server.routes[method][route].source = path.join(rt,'root.js');
}
}
}
}
}
}
}
}
} else {
if (this.routes.hasOwnProperty('/'))
_hasRootRoute = true;
this.server.maproute(this.routes);
}
if (callback !== null)
callback(_hasRootRoute);
else
return _hasRootRoute;
},
/**
* @method allowCustomHeader
* Allow a custom header. For example, `X-Requested-With`.
* @param {String[]} header
* The header(s) allowed on a request.
* @private
*/
allowCustomHeader: function(header){
if (this.allowCrossOrigin){
var http = require("http").ServerResponse,
original_writeHead = http.prototype.writeHead;
header = typeof header === 'string' ? header.trim().replace(/\s/gi,'').split(',') : header;
http.prototype.writeHead = function () {
var default_headers = (this.getHeader("Access-Control-Allow-Headers")||'').toString().replace(/\s/gi,'').split(',');
header.forEach(function(hdr){
if (default_headers.indexOf(hdr) < 0){
default_headers.push(hdr);
}
});
this.setHeader("Access-Control-Allow-Headers", default_headers.join(', '));
original_writeHead.apply(this, arguments);
};
}
},
/**
* @method handleCrossOrigin
* The method for applying Cross Origin Policy across the entire server.
*
* This method typically does not need to be overridden or used directly.
* This method is enabled when #allowCrossOrigin is enabled.
* @param {Object} req
* Request stream.
* @param {Object} res
* Response stream.
* @param {Function} next
* Processing method.
* @private
*/
handleCrossOrigin: function(req, res, next) {
if (this.allowCrossOrigin){
res.header('Access-Control-Allow-Origin', this.CORSDOMAINS);
}
if ( req.method == 'OPTIONS')
res.send(204);
else
next();
},
/**
* @method handleMethods
* The method for restricting traffic to specific HTTP methods, such as `GET`, `POST`, `PUT`, `DELETE`, `HEAD`, and `OPTIONS`.
* @param {Object} req
* Request stream.
* @param {Object} res
* Response stream.
* @param {Function} next
* Processing method.
*/
handleMethods: function(req, res, next) {
res.header('Access-Control-Allow-Methods', this._ALLOWMETHODS);
if ( req.method == 'OPTIONS')
res.send(204);
else
next();
},
/**
* @method onMethodNotAllowed
* Executed when a `MethodNotAllowed` is triggered by an HTTP request.
* @param {Object} req
* The raw request object.
* @param {Object} res
* The raw response object.
*/
onMethodNotAllowed: function(req,res){
if (res.methods.indexOf('OPTIONS') === -1){
if (this.allowedMethods.indexOf('OPTIONS') !== -1){
res.methods.push('OPTIONS');
if (this.allowCrossOrigin){
if (this.CORSDOMAINS.indexOf('*') >= 0 || this.CORSDOMAINS.indexOf(req.headers.origin) >= 0 ){
if (this._ALLOWCORSCREDENTIALS){
res.header('Access-Control-Allow-Credentials', true);
}
if (this.customHeaders.length > 0){
res.header('Access-Control-Allow-Headers', this.customHeaders.join(', '));
}
res.header('Access-Control-Allow-Methods', this.allowedMethods.join(', '));
res.header('Access-Control-Allow-Origin', req.headers.origin);
}
}
res.send(204);
return;
}
}
res.send(405);
},
/**
* @method onNotFound
* Fired when a route is not found.
* @param {Object} request
* The request object.
* @param {Object} response
* The response object.
*/
onNotFound: function(request, response){
this.emit('NotFound',{request:request||null,response:response||null});
},
/**
* @event sessionstart
* Fired when a user session starts.
*/
onSessionStart: function(){
this.fireEvent('sessionstart');
},
/**
* @event sessionend
* Fired when a session ends.
*/
onSessionEnd: function(){
this.fireEvent('sessionend');
},
/**
* @event requeststart
* Fired when a request starts.
*/
onRequestStart: function(req){
this.emit('requeststart',req||null);
},
/**
* @event requestend
* Fired just before the request ends
*/
onRequestEnd: function(req,res){
this.emit('requestend',{request:req||null,response:res||null});
},
/**
* @event 1xx
* Fired on every HTTP 100-level response.
*/
on1xx: function(code,res){
this.emit('1xx',{code:code,response:(response||null)});
this.onXXX(code,response||null);
},
/**
* @event 100
* Fired on HTTP Status Code 100
*/
on100: function(response){
this.emit('100',{code:100,response:(response||null)});
this.on1xx(100,response||null);
},
/**
* @event 101
* Fired on HTTP Status Code 101
*/
on101: function(response){
this.emit('101',{code:101,response:(response||null)});
this.on1xx(101,response||null);
},
/**
* @event 2xx
* Fired on every HTTP 200-level response.
*/
on2xx: function(code,response){
this.emit('2xx',{code:code,response:(response||null)});
this.onXXX(code,response||null);
},
/**
* @event 200
* Fired on HTTP Status Code 200
*/
on200: function(response){
this.emit('200',{code:200,response:(response||null)});
this.on2xx(200,response||null);
},
/**
* @event 201
* Fired on HTTP Status Code 201
*/
on201: function(response){
this.emit('201',{code:201,response:(response||null)});
this.on2xx(201,response||null);
},
/**
* @event 202
* Fired on HTTP Status Code 202
*/
on202: function(response){
this.emit('202',{code:202,response:(response||null)});
this.on2xx(202,response||null);
},
/**
* @event 203
* Fired on HTTP Status Code 203
*/
on203: function(response){
this.emit('203',{code:203,response:(response||null)});
this.on2xx(203,response||null);
},
/**
* @event 204
* Fired on HTTP Status Code 204
*/
on204: function(response){
this.emit('204',{code:204,response:(response||null)});
this.on2xx(204,response||null);
},
/**
* @event 205
* Fired on HTTP Status Code 205
*/
on205: function(response){
this.emit('205',{code:205,response:(response||null)});
this.on2xx(205,response||null);
},
/**
* @event 206
* Fired on HTTP Status Code 206
*/
on206: function(response){
this.emit('206',{code:206,response:(response||null)});
this.on2xx(206,response||null);
},
/**
* @event 3xx
* Fired on every HTTP 300-level response.
*/
on3xx: function(code,response){
this.emit('3xx',{code:code,response:(response||null)});
this.onXXX(code,response||null);
},
/**
* @event 300
* Fired on HTTP Status Code 300
*/
on300: function(response){
this.emit('300',{code:300,response:(response||null)});
this.on3xx(300,response||null);
},
/**
* @event 301
* Fired on HTTP Status Code 301
*/
on301: function(response){
this.emit('301',{code:301,response:(response||null)});
this.on3xx(301,response||null);
},
/**
* @event 302
* Fired on HTTP Status Code 302
*/
on302: function(response){
this.emit('302',{code:302,response:(response||null)});
this.on3xx(302,response||null);
},
/**
* @event 303
* Fired on HTTP Status Code 303
*/
on303: function(response){
this.emit('303',{code:303,response:(response||null)});
this.on3xx(303,response||null);
},
/**
* @event 304
* Fired on HTTP Status Code 304
*/
on304: function(response){
this.emit('304',{code:304,response:(response||null)});
this.on3xx(304,response||null);
},
/**
* @event 305
* Fired on HTTP Status Code 305
*/
on305: function(response){
this.emit('305',{code:305,response:(response||null)});
this.on3xx(305,response||null);
},
/**
* @event 306
* Fired on HTTP Status Code 306
*/
on306: function(response){
this.emit('306',{code:306,response:(response||null)});
this.on3xx(306,response||null);
},
/**
* @event 307
* Fired on HTTP Status Code 307
*/
on307: function(response){
this.emit('307',{code:307,response:(response||null)});
this.on3xx(307,response||null);
},
/**
* @event 4xx
* Fired on every HTTP 400-level response.
*/
on4xx: function(code,response){
this.emit('4xx',{code:code,response:(response||null)});
this.onXXX(code,response||null);
},
/**
* @event 400
* Fired on HTTP Status Code 400
*/
on400: function(response){
this.emit('400',{code:400,response:(response||null)});
this.on4xx(400,response||null);
},
/**
* @event 401
* Fired on HTTP Status Code 401
*/
on401: function(response){
this.emit('401',{code:401,response:(response||null)});
this.on4xx(401,response||null);
},
/**
* @event 402
* Fired on HTTP Status Code 402
*/
on402: function(response){
this.emit('402',{code:402,response:(response||null)});
this.on4xx(402,response||null);
},
/**
* @event 403
* Fired on HTTP Status Code 403
*/
on403: function(response){
this.emit('403',{code:403,response:(response||null)});
this.on4xx(403,response||null);
},
/**
* @event 404
* Fired on HTTP Status Code 404
*/
on404: function(response){
this.emit('404',{code:404,response:(response||null)});
this.on4xx(404,response||null);
},
/**
* @event 405
* Fired on HTTP Status Code 405
*/
on405: function(response){
this.emit('405',{code:405,response:(response||null)});
this.on4xx(405,response||null);
},
/**
* @event 406
* Fired on HTTP Status Code 406
*/
on406: function(response){
this.emit('406',{code:406,response:(response||null)});
this.on4xx(406,response||null);
},
/**
* @event 407
* Fired on HTTP Status Code 407
*/
on407: function(response){
this.emit('407',{code:407,response:(response||null)});
this.on4xx(407,response||null);
},
/**
* @event 408
* Fired on HTTP Status Code 408
*/
on408: function(response){
this.emit('408',{code:408,response:(response||null)});
this.on4xx(408,response||null);
},
/**
* @event 409
* Fired on HTTP Status Code 409
*/
on409: function(response){
this.emit('409',{code:409,response:(response||null)});
this.on4xx(409,response||null);
},
/**
* @event 410
* Fired on HTTP Status Code 410
*/
on410: function(response){
this.emit('410',{code:410,response:(response||null)});
this.on4xx(410,response||null);
},
/**
* @event 411
* Fired on HTTP Status Code 411
*/
on411: function(response){
this.emit('411',{code:411,response:(response||null)});
this.on4xx(411,response||null);
},
/**
* @event 412
* Fired on HTTP Status Code 412
*/
on412: function(response){
this.emit('412',{code:412,response:(response||null)});
this.on4xx(412,response||null);
},
/**
* @event 413
* Fired on HTTP Status Code 413
*/
on413: function(response){
this.emit('413',{code:413,response:(response||null)});
this.on4xx(413,response||null);
},
/**
* @event 414
* Fired on HTTP Status Code 414
*/
on414: function(response){
this.emit('414',{code:414,response:(response||null)});
this.on4xx(414,response||null);
},
/**
* @event 415
* Fired on HTTP Status Code 415
*/
on415: function(response){
this.emit('415',{code:415,response:(response||null)});
this.on4xx(415,response||null);
},
/**
* @event 416
* Fired on HTTP Status Code 416
*/
on416: function(response){
this.emit('416',{code:416,response:(response||null)});
this.on4xx(416,response||null);
},
/**
* @event 417
* Fired on HTTP Status Code 417
*/
on417: function(response){
this.emit('417',{code:417,response:(response||null)});
this.on4xx(417,response||null);
},
/**
* @event 5xx
* Fired on every HTTP 500-level response.
*/
on5xx: function(code,response){
this.emit('5xx',{code:code,response:(response||null)});
this.onXXX(code,response||null);
},
/**
* @event 500
* Fired on HTTP Status Code 500
*/
on500: function(response){
this.emit('500',{code:500,response:(response||null)});
this.on5xx(500,response||null);
},
/**
* @event 501
* Fired on HTTP Status Code 501
*/
on501: function(response){
this.emit('501',{code:501,response:(response||null)});
this.on5xx(501,response||null);
},
/**
* @event 502
* Fired on HTTP Status Code 502
*/
on502: function(response){
this.emit('502',{code:502,response:(response||null)});
this.on5xx(502,response||null);
},
/**
* @event 503
* Fired on HTTP Status Code 503
*/
on503: function(response){
this.emit('503',{code:503,response:(response||null)});
this.on5xx(503,response||null);
},
/**
* @event 504
* Fired on HTTP Status Code 504
*/
on504: function(response){
this.emit('504',{code:504,response:(response||null)});
this.on5xx(504,response||null);
},
/**
* @event 505
* Fired on HTTP Status Code 505
*/
on505: function(response){
this.emit('505',{code:505,response:(response||null)});
this.on5xx(505,response||null);
},
/**
* @event xxx
* Fired on every HTTP response.
*/
onXXX: function(code,response){
this.emit('xxx',{code:code,response:(response||null)});
},
/**
* @event beforerequest
* Fired before a request.
* @param {Object} req
* Request stream object.
*/
onBeforeRequest: function(req){
this.emit('beforerequest',req||null);
},
/**
* @event afterrequest
* Fired after a request.
* @param {Object} req
* Request stream object.
*/
onAfterRequest: function(req){
this.emit('afterrequest',req);
}
});
module.exports = Class;