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.

203 lines
4.6 KiB

7 years ago
  1. /*
  2. * response-stream.js: A Stream focused on writing any relevant information to
  3. * a raw http.ServerResponse object.
  4. *
  5. * (C) 2011, Charlie Robbins & the Contributors
  6. * MIT LICENSE
  7. *
  8. */
  9. var util = require('util'),
  10. HttpStream = require('./http-stream');
  11. var STATUS_CODES = require('http').STATUS_CODES;
  12. //
  13. // ### function ResponseStream (options)
  14. //
  15. //
  16. var ResponseStream = module.exports = function (options) {
  17. var self = this,
  18. key;
  19. options = options || {};
  20. HttpStream.call(this, options);
  21. this.writeable = true;
  22. this.response = options.response;
  23. if (options.headers) {
  24. for (key in options.headers) {
  25. this.response.setHeader(key, options.headers[key]);
  26. }
  27. }
  28. //
  29. // Proxy `statusCode` changes to the actual `response.statusCode`.
  30. //
  31. Object.defineProperty(this, 'statusCode', {
  32. get: function () {
  33. return self.response.statusCode;
  34. },
  35. set: function (value) {
  36. self.response.statusCode = value;
  37. },
  38. enumerable: true,
  39. configurable: true
  40. });
  41. if (this.response) {
  42. this._headers = this.response._headers = this.response._headers || {};
  43. // Patch to node core
  44. this.response._headerNames = this.response._headerNames || {};
  45. //
  46. // Proxy to emit "header" event
  47. //
  48. this._renderHeaders = this.response._renderHeaders;
  49. this.response._renderHeaders = function () {
  50. if (!self._emittedHeader) {
  51. self._emittedHeader = true;
  52. self.headerSent = true;
  53. self._header = true;
  54. self.emit('header');
  55. }
  56. return self._renderHeaders.call(self.response);
  57. };
  58. }
  59. };
  60. util.inherits(ResponseStream, HttpStream);
  61. ResponseStream.prototype.writeHead = function (statusCode, statusMessage, headers) {
  62. if (typeof statusMessage === 'string') {
  63. this.response.statusMessage = statusMessage;
  64. } else {
  65. this.response.statusMessage = this.response.statusMessage
  66. || STATUS_CODES[statusCode] || 'unknown';
  67. headers = statusMessage;
  68. }
  69. this.response.statusCode = statusCode;
  70. if (headers) {
  71. var keys = Object.keys(headers);
  72. for (var i = 0; i < keys.length; i++) {
  73. var k = keys[i];
  74. if (k) this.response.setHeader(k, headers[k]);
  75. }
  76. }
  77. };
  78. //
  79. // Create pass-thru for the necessary
  80. // `http.ServerResponse` methods.
  81. //
  82. ['setHeader', 'getHeader', 'removeHeader', '_implicitHeader', 'addTrailers'].forEach(function (method) {
  83. ResponseStream.prototype[method] = function () {
  84. return this.response[method].apply(this.response, arguments);
  85. };
  86. });
  87. ResponseStream.prototype.json = function (obj) {
  88. if (!this.response.writable) {
  89. return;
  90. }
  91. if (typeof obj === 'number') {
  92. this.response.statusCode = obj;
  93. obj = arguments[1];
  94. }
  95. this.modified = true;
  96. if (!this.response._header && this.response.getHeader('content-type') !== 'application/json') {
  97. this.response.setHeader('content-type', 'application/json');
  98. }
  99. this.end(obj ? JSON.stringify(obj) : '');
  100. };
  101. ResponseStream.prototype.html = function (str) {
  102. if (!this.response.writable) {
  103. return;
  104. }
  105. if (typeof str === 'number') {
  106. this.response.statusCode = str;
  107. str = arguments[1];
  108. }
  109. this.modified = true;
  110. if (!this.response._header && this.response.getHeader('content-type') !== 'text/html') {
  111. this.response.setHeader('content-type', 'text/html');
  112. }
  113. this.end(str ? str: '');
  114. };
  115. ResponseStream.prototype.text = function (str) {
  116. if (!this.response.writable) {
  117. return;
  118. }
  119. if (typeof str === 'number') {
  120. this.response.statusCode = str;
  121. str = arguments[1];
  122. }
  123. this.modified = true;
  124. if (!this.response._header && this.response.getHeader('content-type') !== 'text/plain') {
  125. this.response.setHeader('content-type', 'text/plain');
  126. }
  127. this.end(str ? str: '');
  128. };
  129. ResponseStream.prototype.end = function (data) {
  130. if (data && this.writable) {
  131. this.emit('data', data);
  132. }
  133. this.modified = true;
  134. this.emit('end');
  135. };
  136. ResponseStream.prototype.pipe = function () {
  137. var self = this,
  138. dest;
  139. self.dest = dest = HttpStream.prototype.pipe.apply(self, arguments);
  140. dest.on('drain', function() {
  141. self.emit('drain')
  142. })
  143. return dest;
  144. };
  145. ResponseStream.prototype.write = function (data) {
  146. this.modified = true;
  147. if (this.writable) {
  148. return this.dest.write(data);
  149. }
  150. };
  151. ResponseStream.prototype.redirect = function (path, status) {
  152. var url = '';
  153. if (~path.indexOf('://')) {
  154. url = path;
  155. } else {
  156. url += this.req.connection.encrypted ? 'https://' : 'http://';
  157. url += this.req.headers.host;
  158. url += (path[0] === '/') ? path : '/' + path;
  159. }
  160. this.res.writeHead(status || 302, { 'Location': url });
  161. this.end();
  162. };