broadway
Version:
Lightweight App extensibility and hookable middleware customization.
141 lines (125 loc) • 3.55 kB
JavaScript
var createServers = require('create-servers'),
mixin = require('merge-descriptors'),
Understudy = require('understudy');
/*
* function App(given?, [base])
* Creates a new App instance with the `given` supplied;
* will optionally mixin any properties on the `base` onto
* this instance.
*
*/
var App = module.exports = function App(given, base) {
Understudy.call(this);
this.given = given || {};
if (base) {
this.mixin(base);
}
//
// This is a simple internal shim for other config loaders
//
var self = this;
this.config = {
get: function (key) {
return self.given[key];
}
};
};
/*
* function preboot(fn)
* Adds the "start middleware" to this instance.
*/
App.prototype.preboot = function preboot(fn) {
this.before('start', fn);
};
/*
* function mixin (ext, redine)
* Mixes in any properties in `ext`. If `redefine` is
* set to true, any properties on `ext` already existing
* on this instance will be redefined.
*/
App.prototype.mixin = function mixin_(ext, redefine) {
mixin(this, ext, redefine || false);
};
/*
* function start ([options], callback)
* Attempts to listen on HTTP(S) servers after
* executing all "start middlewares" defined by
*
* app.preboot(function initFn(app, options, next) {
* // Do startup things here.
* next();
* });
*/
App.prototype.start = function start(options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
}
mixin(this.given, options, true);
this.perform('setup', this, this.given, function (setup) {
//
// This is a no-op because we want all after "setup" hooks
// to complete before any "start" hooks
//
setup();
}, function (err) {
if (err) { return callback(err); }
this.perform('start', this, this.given, this._listen, callback);
});
};
/*
* function close (callback)
* Closes all HTTP(S) servers on the App,
* if they are defined.
*/
App.prototype.close = function close(callback) {
if (!this.servers) {
return callback(new Error('No servers to close.'));
}
this.perform('close', this, this.given, function (next) {
const servers = Object.values(this.servers).reduce((all, serverOrServers) => all.concat(serverOrServers), []);
let closed = 0;
/*
* Invokes the callback once all the servers have
* closed.
*/
function onClosed() {
if (++closed === servers.length) {
return void next();
}
}
servers.forEach(function closeServer(server) {
server.close(onClosed);
});
}, callback);
};
/*
* function _listen (callback)
* Starts up an HTTP and/or HTTPS server depending
* on the respective options on this instance.
*/
App.prototype._listen = function _listen(callback) {
var self = this;
if (typeof this.handle !== 'function') {
return callback(new Error('A handle function must be defined.'));
}
var ports = {
http: this.config.get('http'),
https: this.config.get('https')
};
createServers({
http: typeof ports.http !== 'undefined' ? ports.http : this.given.http,
https: typeof ports.https !== 'undefined' ? ports.https : this.given.https,
//
// Remark: is not doing a `bind` a performance optimization
// from express?
//
// https://github.com/strongloop/express/blob/master/lib/express.js#L28
//
handler: this.handle.bind(this)
}, function onListen(err, servers) {
if (err) { return callback(err); }
self.servers = servers;
callback();
});
};