Name: js-handler/node_modules/restify/lib/response.js
| 1: | // Copyright 2012 Mark Cavage, Inc. All rights reserved. |
| 2: | |
| 3: | var crypto = require('crypto'); |
| 4: | var http = require('http'); |
| 5: | var sprintf = require('util').format; |
| 6: | |
| 7: | var assert = require('assert-plus'); |
| 8: | var mime = require('mime'); |
| 9: | var once = require('once'); |
| 10: | |
| 11: | var errors = require('./errors'); |
| 12: | var httpDate = require('./http_date'); |
| 13: | |
| 14: | |
| 15: | |
| 16: | ///--- Globals |
| 17: | |
| 18: | var HttpError = errors.HttpError; |
| 19: | var RestError = errors.RestError; |
| 20: | |
| 21: | var Response = http.ServerResponse; |
| 22: | |
| 23: | |
| 24: | |
| 25: | ///--- API |
| 26: | |
| 27: | Response.prototype.cache = function cache(type, options) { |
| 28: | if (typeof (type) !== 'string') { |
| 29: | options = type; |
| 30: | type = 'public'; |
| 31: | } |
| 32: | |
| 33: | if (options && options.maxAge !== undefined) { |
| 34: | assert.number(options.maxAge, 'options.maxAge'); |
| 35: | type += ', max-age=' + options.maxAge; |
| 36: | } |
| 37: | |
| 38: | return (this.header('Cache-Control', type)); |
| 39: | }; |
| 40: | |
| 41: | |
| 42: | Response.prototype.charSet = function charSet(type) { |
| 43: | assert.string(type, 'charset'); |
| 44: | |
| 45: | this._charSet = type; |
| 46: | |
| 47: | return (this); |
| 48: | }; |
| 49: | |
| 50: | |
| 51: | Response.prototype.format = function format(body, cb) { |
| 52: | var log = this.log; |
| 53: | var formatter; |
| 54: | var type = this.contentType || this.getHeader('Content-Type'); |
| 55: | var self = this; |
| 56: | |
| 57: | if (type && type.indexOf(';') !== '-1') |
| 58: | type = type.split(';')[0]; |
| 59: | |
| 60: | if (!(formatter = this.formatters[type])) { |
| 61: | if (!type) { |
| 62: | for (var i = 0; i < this.acceptable.length; i++) { |
| 63: | if (this.req.accepts(this.acceptable[i])) { |
| 64: | type = this.acceptable[i]; |
| 65: | break; |
| 66: | } |
| 67: | } |
| 68: | } else { |
| 69: | if (type.indexOf('/') === -1) |
| 70: | type = mime.lookup(type); |
| 71: | |
| 72: | if (this.acceptable.indexOf(type) === -1) |
| 73: | type = 'application/octet-stream'; |
| 74: | } |
| 75: | |
| 76: | formatter = this.formatters[type] || this.formatters['*/*']; |
| 77: | if (!formatter) { |
| 78: | log.warn({ |
| 79: | req: self.req |
| 80: | }, 'no formatter found. Returning 500.'); |
| 81: | this.statusCode = 500; |
| 82: | return (null); |
| 83: | } |
| 84: | this.setHeader('Content-Type', type); |
| 85: | } |
| 86: | |
| 87: | if (this._charSet) { |
| 88: | type = type + '; charset=' + this._charSet; |
| 89: | this.setHeader('Content-Type', type); |
| 90: | } |
| 91: | |
| 92: | if (body instanceof Error && body.statusCode !== undefined) |
| 93: | this.statusCode = body.statusCode; |
| 94: | return (formatter.call(this, this.req, this, body, cb)); |
| 95: | }; |
| 96: | |
| 97: | |
| 98: | Response.prototype.get = function get(name) { |
| 99: | assert.string(name, 'name'); |
| 100: | |
| 101: | return (this.getHeader(name)); |
| 102: | }; |
| 103: | |
| 104: | |
| 105: | Response.prototype.getHeaders = function getHeaders() { |
| 106: | return (this._headers || {}); |
| 107: | }; |
| 108: | Response.prototype.headers = Response.prototype.getHeaders; |
| 109: | |
| 110: | |
| 111: | Response.prototype.header = function header(name, value) { |
| 112: | assert.string(name, 'name'); |
| 113: | |
| 114: | if (value === undefined) |
| 115: | return (this.getHeader(name)); |
| 116: | |
| 117: | if (value instanceof Date) { |
| 118: | value = httpDate(value); |
| 119: | } else if (arguments.length > 2) { |
| 120: | // Support res.header('foo', 'bar %s', 'baz'); |
| 121: | var arg = Array.prototype.slice.call(arguments).slice(2); |
| 122: | value = sprintf(value, arg); |
| 123: | } |
| 124: | |
| 125: | this.setHeader(name, value); |
| 126: | return (value); |
| 127: | }; |
| 128: | |
| 129: | |
| 130: | Response.prototype.json = function json(code, object, headers) { |
| 131: | if (!/application\/json/.test(this.header('content-type'))) |
| 132: | this.header('Content-Type', 'application/json'); |
| 133: | |
| 134: | return (this.send(code, object, headers)); |
| 135: | }; |
| 136: | |
| 137: | |
| 138: | Response.prototype.link = function link(l, rel) { |
| 139: | assert.string(l, 'link'); |
| 140: | assert.string(rel, 'rel'); |
| 141: | |
| 142: | var _link = sprintf('<%s>; rel="%s"', l, rel); |
| 143: | return (this.header('Link', _link)); |
| 144: | }; |
| 145: | |
| 146: | |
| 147: | Response.prototype.send = function send(code, body, headers) { |
| 148: | var isHead = (this.req.method === 'HEAD'); |
| 149: | var log = this.log; |
| 150: | var self = this; |
| 151: | |
| 152: | if (code === undefined) { |
| 153: | this.statusCode = 200; |
| 154: | } else if (code.constructor.name === 'Number') { |
| 155: | this.statusCode = code; |
| 156: | } else { |
| 157: | headers = body; |
| 158: | body = code; |
| 159: | code = null; |
| 160: | } |
| 161: | |
| 162: | headers = headers || {}; |
| 163: | |
| 164: | if (log.trace()) { |
| 165: | var _props = { |
| 166: | code: self.statusCode, |
| 167: | headers: headers |
| 168: | }; |
| 169: | if (body instanceof Error) { |
| 170: | _props.err = body; |
| 171: | } else { |
| 172: | _props.body = body; |
| 173: | } |
| 174: | log.trace(_props, 'response::send entered'); |
| 175: | } |
| 176: | |
| 177: | this._body = body; |
| 178: | |
| 179: | function _cb(err, _body) { |
| 180: | self._data = _body; |
| 181: | Object.keys(headers).forEach(function (k) { |
| 182: | self.setHeader(k, headers[k]); |
| 183: | }); |
| 184: | |
| 185: | self.writeHead(self.statusCode); |
| 186: | |
| 187: | if (self._data && !(isHead || code === 204 || code === 304)) |
| 188: | self.write(self._data); |
| 189: | |
| 190: | self.end(); |
| 191: | |
| 192: | if (log.trace()) |
| 193: | log.trace({res: self}, 'response sent'); |
| 194: | } |
| 195: | |
| 196: | if (body) { |
| 197: | var ret = this.format(body, _cb); |
| 198: | if (!(ret instanceof Response)) { |
| 199: | _cb(null, ret); |
| 200: | } |
| 201: | } else { |
| 202: | _cb(null, null); |
| 203: | } |
| 204: | |
| 205: | return (this); |
| 206: | }; |
| 207: | |
| 208: | |
| 209: | Response.prototype.set = function set(name, val) { |
| 210: | var self = this; |
| 211: | |
| 212: | if (arguments.length === 2) { |
| 213: | assert.string(name, 'name'); |
| 214: | this.header(name, val); |
| 215: | } else { |
| 216: | assert.object(name, 'object'); |
| 217: | Object.keys(name).forEach(function (k) { |
| 218: | self.header(k, name[k]); |
| 219: | }); |
| 220: | } |
| 221: | |
| 222: | return (this); |
| 223: | }; |
| 224: | |
| 225: | |
| 226: | Response.prototype.status = function status(code) { |
| 227: | assert.number(code, 'code'); |
| 228: | |
| 229: | this.statusCode = code; |
| 230: | return (code); |
| 231: | }; |
| 232: | |
| 233: | |
| 234: | Response.prototype.toString = function toString() { |
| 235: | var headers = this.getHeaders(); |
| 236: | var headerString = ''; |
| 237: | var str; |
| 238: | |
| 239: | Object.keys(headers).forEach(function (k) { |
| 240: | headerString += k + ': ' + headers[k] + '\n'; |
| 241: | }); |
| 242: | str = sprintf('HTTP/1.1 %s %s\n%s', |
| 243: | this.statusCode, |
| 244: | http.STATUS_CODES[this.statusCode], |
| 245: | headerString); |
| 246: | |
| 247: | return (str); |
| 248: | }; |
| 249: | |
| 250: | if (!Response.prototype.hasOwnProperty('_writeHead')) |
| 251: | Response.prototype._writeHead = Response.prototype.writeHead; |
| 252: | |
| 253: | |
| 254: | Response.prototype.writeHead = function restifyWriteHead() { |
| 255: | this.emit('header'); |
| 256: | |
| 257: | if (this.statusCode === 204 || this.statusCode === 304) { |
| 258: | this.removeHeader('Content-Length'); |
| 259: | this.removeHeader('Content-MD5'); |
| 260: | this.removeHeader('Content-Type'); |
| 261: | } |
| 262: | |
| 263: | this._writeHead.apply(this, arguments); |
| 264: | }; |
