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