You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
3.1 KiB

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