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