211 lines
4.2 KiB
JavaScript
211 lines
4.2 KiB
JavaScript
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var util = require('util');
|
||
|
var utils = require('./utils');
|
||
|
var Stream = require('stream');
|
||
|
|
||
|
/**
|
||
|
* Expose `Response`.
|
||
|
*/
|
||
|
|
||
|
module.exports = Response;
|
||
|
|
||
|
/**
|
||
|
* Initialize a new `Response` with the given `xhr`.
|
||
|
*
|
||
|
* - set flags (.ok, .error, etc)
|
||
|
* - parse header
|
||
|
*
|
||
|
* @param {Request} req
|
||
|
* @param {Object} options
|
||
|
* @constructor
|
||
|
* @extends {Stream}
|
||
|
* @implements {ReadableStream}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function Response(req, options) {
|
||
|
Stream.call(this);
|
||
|
options = options || {};
|
||
|
var res = this.res = req.res;
|
||
|
this.request = req;
|
||
|
this.req = req.req;
|
||
|
this.links = {};
|
||
|
this.text = res.text;
|
||
|
this.body = res.body !== undefined ? res.body : {};
|
||
|
this.files = res.files || {};
|
||
|
this.buffered = 'string' == typeof this.text;
|
||
|
this.header = this.headers = res.headers;
|
||
|
this.setStatusProperties(res.statusCode);
|
||
|
this.setHeaderProperties(this.header);
|
||
|
this.setEncoding = res.setEncoding.bind(res);
|
||
|
res.on('data', this.emit.bind(this, 'data'));
|
||
|
res.on('end', this.emit.bind(this, 'end'));
|
||
|
res.on('close', this.emit.bind(this, 'close'));
|
||
|
res.on('error', this.emit.bind(this, 'error'));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inherit from `Stream`.
|
||
|
*/
|
||
|
|
||
|
util.inherits(Response, Stream);
|
||
|
|
||
|
/**
|
||
|
* Get case-insensitive `field` value.
|
||
|
*
|
||
|
* @param {String} field
|
||
|
* @return {String}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
Response.prototype.get = function(field){
|
||
|
return this.header[field.toLowerCase()];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Implements methods of a `ReadableStream`
|
||
|
*/
|
||
|
|
||
|
Response.prototype.destroy = function(err){
|
||
|
this.res.destroy(err);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Pause.
|
||
|
*/
|
||
|
|
||
|
Response.prototype.pause = function(){
|
||
|
this.res.pause();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Resume.
|
||
|
*/
|
||
|
|
||
|
Response.prototype.resume = function(){
|
||
|
this.res.resume();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return an `Error` representative of this response.
|
||
|
*
|
||
|
* @return {Error}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
Response.prototype.toError = function(){
|
||
|
var req = this.req;
|
||
|
var method = req.method;
|
||
|
var path = req.path;
|
||
|
|
||
|
var msg = 'cannot ' + method + ' ' + path + ' (' + this.status + ')';
|
||
|
var err = new Error(msg);
|
||
|
err.status = this.status;
|
||
|
err.text = this.text;
|
||
|
err.method = method;
|
||
|
err.path = path;
|
||
|
|
||
|
return err;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set header related properties:
|
||
|
*
|
||
|
* - `.type` the content type without params
|
||
|
*
|
||
|
* A response of "Content-Type: text/plain; charset=utf-8"
|
||
|
* will provide you with a `.type` of "text/plain".
|
||
|
*
|
||
|
* @param {Object} header
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Response.prototype.setHeaderProperties = function(header){
|
||
|
// TODO: moar!
|
||
|
// TODO: make this a util
|
||
|
|
||
|
// content-type
|
||
|
var ct = this.header['content-type'] || '';
|
||
|
|
||
|
// params
|
||
|
var params = utils.params(ct);
|
||
|
for (var key in params) this[key] = params[key];
|
||
|
|
||
|
this.type = utils.type(ct);
|
||
|
|
||
|
// links
|
||
|
try {
|
||
|
if (header.link) this.links = utils.parseLinks(header.link);
|
||
|
} catch (err) {
|
||
|
// ignore
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set flags such as `.ok` based on `status`.
|
||
|
*
|
||
|
* For example a 2xx response will give you a `.ok` of __true__
|
||
|
* whereas 5xx will be __false__ and `.error` will be __true__. The
|
||
|
* `.clientError` and `.serverError` are also available to be more
|
||
|
* specific, and `.statusType` is the class of error ranging from 1..5
|
||
|
* sometimes useful for mapping respond colors etc.
|
||
|
*
|
||
|
* "sugar" properties are also defined for common cases. Currently providing:
|
||
|
*
|
||
|
* - .noContent
|
||
|
* - .badRequest
|
||
|
* - .unauthorized
|
||
|
* - .notAcceptable
|
||
|
* - .notFound
|
||
|
*
|
||
|
* @param {Number} status
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Response.prototype.setStatusProperties = function(status){
|
||
|
var type = status / 100 | 0;
|
||
|
|
||
|
// status / class
|
||
|
this.status = this.statusCode = status;
|
||
|
this.statusType = type;
|
||
|
|
||
|
// basics
|
||
|
this.info = 1 == type;
|
||
|
this.ok = 2 == type;
|
||
|
this.redirect = 3 == type;
|
||
|
this.clientError = 4 == type;
|
||
|
this.serverError = 5 == type;
|
||
|
this.error = (4 == type || 5 == type)
|
||
|
? this.toError()
|
||
|
: false;
|
||
|
|
||
|
// sugar
|
||
|
this.accepted = 202 == status;
|
||
|
this.noContent = 204 == status;
|
||
|
this.badRequest = 400 == status;
|
||
|
this.unauthorized = 401 == status;
|
||
|
this.notAcceptable = 406 == status;
|
||
|
this.forbidden = 403 == status;
|
||
|
this.notFound = 404 == status;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* To json.
|
||
|
*
|
||
|
* @return {Object}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
Response.prototype.toJSON = function(){
|
||
|
return {
|
||
|
req: this.request.toJSON(),
|
||
|
header: this.header,
|
||
|
status: this.status,
|
||
|
text: this.text
|
||
|
};
|
||
|
};
|