/*
|
|
* response-stream.js: A Stream focused on writing any relevant information to
|
|
* a raw http.ServerResponse object.
|
|
*
|
|
* (C) 2011, Charlie Robbins & the Contributors
|
|
* MIT LICENSE
|
|
*
|
|
*/
|
|
|
|
var util = require('util'),
|
|
HttpStream = require('./http-stream');
|
|
|
|
var STATUS_CODES = require('http').STATUS_CODES;
|
|
|
|
//
|
|
// ### function ResponseStream (options)
|
|
//
|
|
//
|
|
var ResponseStream = module.exports = function (options) {
|
|
var self = this,
|
|
key;
|
|
|
|
options = options || {};
|
|
HttpStream.call(this, options);
|
|
|
|
this.writeable = true;
|
|
this.response = options.response;
|
|
|
|
if (options.headers) {
|
|
for (key in options.headers) {
|
|
this.response.setHeader(key, options.headers[key]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Proxy `statusCode` changes to the actual `response.statusCode`.
|
|
//
|
|
Object.defineProperty(this, 'statusCode', {
|
|
get: function () {
|
|
return self.response.statusCode;
|
|
},
|
|
set: function (value) {
|
|
self.response.statusCode = value;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
if (this.response) {
|
|
this._headers = this.response._headers = this.response._headers || {};
|
|
|
|
// Patch to node core
|
|
this.response._headerNames = this.response._headerNames || {};
|
|
|
|
//
|
|
// Proxy to emit "header" event
|
|
//
|
|
this._renderHeaders = this.response._renderHeaders;
|
|
this.response._renderHeaders = function () {
|
|
if (!self._emittedHeader) {
|
|
self._emittedHeader = true;
|
|
self.headerSent = true;
|
|
self._header = true;
|
|
self.emit('header');
|
|
}
|
|
|
|
return self._renderHeaders.call(self.response);
|
|
};
|
|
}
|
|
};
|
|
|
|
util.inherits(ResponseStream, HttpStream);
|
|
|
|
ResponseStream.prototype.writeHead = function (statusCode, statusMessage, headers) {
|
|
if (typeof statusMessage === 'string') {
|
|
this.response.statusMessage = statusMessage;
|
|
} else {
|
|
this.response.statusMessage = this.response.statusMessage
|
|
|| STATUS_CODES[statusCode] || 'unknown';
|
|
headers = statusMessage;
|
|
}
|
|
|
|
this.response.statusCode = statusCode;
|
|
|
|
if (headers) {
|
|
var keys = Object.keys(headers);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var k = keys[i];
|
|
if (k) this.response.setHeader(k, headers[k]);
|
|
}
|
|
}
|
|
};
|
|
|
|
//
|
|
// Create pass-thru for the necessary
|
|
// `http.ServerResponse` methods.
|
|
//
|
|
['setHeader', 'getHeader', 'removeHeader', '_implicitHeader', 'addTrailers'].forEach(function (method) {
|
|
ResponseStream.prototype[method] = function () {
|
|
return this.response[method].apply(this.response, arguments);
|
|
};
|
|
});
|
|
|
|
ResponseStream.prototype.json = function (obj) {
|
|
if (!this.response.writable) {
|
|
return;
|
|
}
|
|
|
|
if (typeof obj === 'number') {
|
|
this.response.statusCode = obj;
|
|
obj = arguments[1];
|
|
}
|
|
|
|
this.modified = true;
|
|
|
|
if (!this.response._header && this.response.getHeader('content-type') !== 'application/json') {
|
|
this.response.setHeader('content-type', 'application/json');
|
|
}
|
|
|
|
this.end(obj ? JSON.stringify(obj) : '');
|
|
};
|
|
|
|
ResponseStream.prototype.html = function (str) {
|
|
if (!this.response.writable) {
|
|
return;
|
|
}
|
|
|
|
if (typeof str === 'number') {
|
|
this.response.statusCode = str;
|
|
str = arguments[1];
|
|
}
|
|
|
|
this.modified = true;
|
|
|
|
if (!this.response._header && this.response.getHeader('content-type') !== 'text/html') {
|
|
this.response.setHeader('content-type', 'text/html');
|
|
}
|
|
|
|
this.end(str ? str: '');
|
|
};
|
|
|
|
ResponseStream.prototype.text = function (str) {
|
|
if (!this.response.writable) {
|
|
return;
|
|
}
|
|
|
|
if (typeof str === 'number') {
|
|
this.response.statusCode = str;
|
|
str = arguments[1];
|
|
}
|
|
|
|
this.modified = true;
|
|
|
|
if (!this.response._header && this.response.getHeader('content-type') !== 'text/plain') {
|
|
this.response.setHeader('content-type', 'text/plain');
|
|
}
|
|
|
|
this.end(str ? str: '');
|
|
};
|
|
|
|
ResponseStream.prototype.end = function (data) {
|
|
if (data && this.writable) {
|
|
this.emit('data', data);
|
|
}
|
|
|
|
this.modified = true;
|
|
this.emit('end');
|
|
};
|
|
|
|
ResponseStream.prototype.pipe = function () {
|
|
var self = this,
|
|
dest;
|
|
|
|
self.dest = dest = HttpStream.prototype.pipe.apply(self, arguments);
|
|
|
|
dest.on('drain', function() {
|
|
self.emit('drain')
|
|
})
|
|
return dest;
|
|
};
|
|
|
|
ResponseStream.prototype.write = function (data) {
|
|
this.modified = true;
|
|
|
|
if (this.writable) {
|
|
return this.dest.write(data);
|
|
}
|
|
};
|
|
|
|
ResponseStream.prototype.redirect = function (path, status) {
|
|
var url = '';
|
|
|
|
if (~path.indexOf('://')) {
|
|
url = path;
|
|
} else {
|
|
url += this.req.connection.encrypted ? 'https://' : 'http://';
|
|
url += this.req.headers.host;
|
|
url += (path[0] === '/') ? path : '/' + path;
|
|
}
|
|
|
|
this.res.writeHead(status || 302, { 'Location': url });
|
|
this.end();
|
|
};
|