File size: 3,147 Bytes
05a77ff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
 * 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);
};