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