Spaces:
Running
Running
/* | |
* 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); | |
}; | |