127 lines
3.1 KiB
JavaScript
127 lines
3.1 KiB
JavaScript
|
/*
|
||
|
* routing-stream.js: A Stream focused on connecting an arbitrary RequestStream and
|
||
|
* ResponseStream through a given Router.
|
||
|
*
|
||
|
* (C) 2011, Charlie Robbins & the Contributors
|
||
|
* MIT LICENSE
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
var util = require('util'),
|
||
|
union = require('./index'),
|
||
|
RequestStream = require('./request-stream'),
|
||
|
ResponseStream = require('./response-stream');
|
||
|
|
||
|
//
|
||
|
// ### function RoutingStream (options)
|
||
|
//
|
||
|
//
|
||
|
var RoutingStream = module.exports = function (options) {
|
||
|
options = options || {};
|
||
|
RequestStream.call(this, options);
|
||
|
|
||
|
this.before = options.before || [];
|
||
|
this.after = options.after || [];
|
||
|
this.response = options.response || options.res;
|
||
|
this.headers = options.headers || {
|
||
|
'x-powered-by': 'union ' + union.version
|
||
|
};
|
||
|
|
||
|
this.target = new ResponseStream({
|
||
|
response: this.response,
|
||
|
headers: this.headers
|
||
|
});
|
||
|
|
||
|
this.once('pipe', this.route);
|
||
|
};
|
||
|
|
||
|
util.inherits(RoutingStream, RequestStream);
|
||
|
|
||
|
//
|
||
|
// Called when this instance is piped to **by another stream**
|
||
|
//
|
||
|
RoutingStream.prototype.route = function (req) {
|
||
|
//
|
||
|
// When a `RoutingStream` is piped to:
|
||
|
//
|
||
|
// 1. Setup the pipe-chain between the `after` middleware, the abstract response
|
||
|
// and the concrete response.
|
||
|
// 2. Attempt to dispatch to the `before` middleware, which represent things such as
|
||
|
// favicon, static files, application routing.
|
||
|
// 3. If no match is found then pipe to the 404Stream
|
||
|
//
|
||
|
var self = this,
|
||
|
after,
|
||
|
error,
|
||
|
i;
|
||
|
|
||
|
//
|
||
|
// Don't allow `this.target` to be writable on HEAD requests
|
||
|
//
|
||
|
this.target.writable = req.method !== 'HEAD';
|
||
|
|
||
|
//
|
||
|
// 1. Setup the pipe-chain between the `after` middleware, the abstract response
|
||
|
// and the concrete response.
|
||
|
//
|
||
|
after = [this.target].concat(this.after, this.response);
|
||
|
for (i = 0; i < after.length - 1; i++) {
|
||
|
//
|
||
|
// attach req and res to all streams
|
||
|
//
|
||
|
after[i].req = req;
|
||
|
after[i + 1].req = req;
|
||
|
after[i].res = this.response;
|
||
|
after[i + 1].res = this.response;
|
||
|
after[i].pipe(after[i + 1]);
|
||
|
|
||
|
//
|
||
|
// prevent multiple responses and memory leaks
|
||
|
//
|
||
|
after[i].on('error', this.onError);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Helper function for dispatching to the 404 stream.
|
||
|
//
|
||
|
function notFound() {
|
||
|
error = new Error('Not found');
|
||
|
error.status = 404;
|
||
|
self.onError(error);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 2. Attempt to dispatch to the `before` middleware, which represent things such as
|
||
|
// favicon, static files, application routing.
|
||
|
//
|
||
|
(function dispatch(i) {
|
||
|
if (self.target.modified) {
|
||
|
return;
|
||
|
}
|
||
|
else if (++i === self.before.length) {
|
||
|
//
|
||
|
// 3. If no match is found then pipe to the 404Stream
|
||
|
//
|
||
|
return notFound();
|
||
|
}
|
||
|
|
||
|
self.target.once('next', dispatch.bind(null, i));
|
||
|
if (self.before[i].length === 3) {
|
||
|
self.before[i](self, self.target, function (err) {
|
||
|
if (err) {
|
||
|
self.onError(err);
|
||
|
} else {
|
||
|
self.target.emit('next');
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
self.before[i](self, self.target);
|
||
|
}
|
||
|
})(-1);
|
||
|
};
|
||
|
|
||
|
RoutingStream.prototype.onError = function (err) {
|
||
|
this.emit('error', err);
|
||
|
};
|