socketioMEANseed/www/node_modules/union/lib/routing-stream.js
www/node_modules/union/lib/routing-stream.jsRaw
1/*
2 * routing-stream.js: A Stream focused on connecting an arbitrary RequestStream and
3 * ResponseStream through a given Router.
4 *
5 * (C) 2011, Charlie Robbins & the Contributors
6 * MIT LICENSE
7 *
8 */
9
10var util = require('util'),
11 union = require('./index'),
12 RequestStream = require('./request-stream'),
13 ResponseStream = require('./response-stream');
14
15//
16// ### function RoutingStream (options)
17//
18//
19var RoutingStream = module.exports = function (options) {
20 options = options || {};
21 RequestStream.call(this, options);
22
23 this.before = options.before || [];
24 this.after = options.after || [];
25 this.response = options.response || options.res;
26 this.headers = options.headers || {
27 'x-powered-by': 'union ' + union.version
28 };
29
30 this.target = new ResponseStream({
31 response: this.response,
32 headers: this.headers
33 });
34
35 this.once('pipe', this.route);
36};
37
38util.inherits(RoutingStream, RequestStream);
39
40//
41// Called when this instance is piped to **by another stream**
42//
43RoutingStream.prototype.route = function (req) {
44 //
45 // When a `RoutingStream` is piped to:
46 //
47 // 1. Setup the pipe-chain between the `after` middleware, the abstract response
48 // and the concrete response.
49 // 2. Attempt to dispatch to the `before` middleware, which represent things such as
50 // favicon, static files, application routing.
51 // 3. If no match is found then pipe to the 404Stream
52 //
53 var self = this,
54 after,
55 error,
56 i;
57
58 //
59 // Don't allow `this.target` to be writable on HEAD requests
60 //
61 this.target.writable = req.method !== 'HEAD';
62
63 //
64 // 1. Setup the pipe-chain between the `after` middleware, the abstract response
65 // and the concrete response.
66 //
67 after = [this.target].concat(this.after, this.response);
68 for (i = 0; i < after.length - 1; i++) {
69 //
70 // attach req and res to all streams
71 //
72 after[i].req = req;
73 after[i + 1].req = req;
74 after[i].res = this.response;
75 after[i + 1].res = this.response;
76 after[i].pipe(after[i + 1]);
77
78 //
79 // prevent multiple responses and memory leaks
80 //
81 after[i].on('error', this.onError);
82 }
83
84 //
85 // Helper function for dispatching to the 404 stream.
86 //
87 function notFound() {
88 error = new Error('Not found');
89 error.status = 404;
90 self.onError(error);
91 }
92
93 //
94 // 2. Attempt to dispatch to the `before` middleware, which represent things such as
95 // favicon, static files, application routing.
96 //
97 (function dispatch(i) {
98 if (self.target.modified) {
99 return;
100 }
101 else if (++i === self.before.length) {
102 //
103 // 3. If no match is found then pipe to the 404Stream
104 //
105 return notFound();
106 }
107
108 self.target.once('next', dispatch.bind(null, i));
109 if (self.before[i].length === 3) {
110 self.before[i](self, self.target, function (err) {
111 if (err) {
112 self.onError(err);
113 } else {
114 self.target.emit('next');
115 }
116 });
117 }
118 else {
119 self.before[i](self, self.target);
120 }
121 })(-1);
122};
123
124RoutingStream.prototype.onError = function (err) {
125 this.emit('error', err);
126};