PageRenderTime 90ms CodeModel.GetById 62ms RepoModel.GetById 2ms app.codeStats 0ms

/node_modules/railway/node_modules/express/lib/response.js

https://github.com/anatoliychakkaev/node-modules
JavaScript | 459 lines | 192 code | 49 blank | 218 comment | 58 complexity | cca32d314e100194c2cdce9777b6c648 MD5 | raw file
  1. /*!
  2. * Express - response
  3. * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var fs = require('fs')
  10. , http = require('http')
  11. , path = require('path')
  12. , connect = require('connect')
  13. , utils = connect.utils
  14. , parseRange = require('./utils').parseRange
  15. , res = http.ServerResponse.prototype
  16. , send = connect.static.send
  17. , mime = require('mime')
  18. , basename = path.basename
  19. , join = path.join;
  20. /**
  21. * Send a response with the given `body` and optional `headers` and `status` code.
  22. *
  23. * Examples:
  24. *
  25. * res.send();
  26. * res.send(new Buffer('wahoo'));
  27. * res.send({ some: 'json' });
  28. * res.send('<p>some html</p>');
  29. * res.send('Sorry, cant find that', 404);
  30. * res.send('text', { 'Content-Type': 'text/plain' }, 201);
  31. * res.send(404);
  32. *
  33. * @param {String|Object|Number|Buffer} body or status
  34. * @param {Object|Number} headers or status
  35. * @param {Number} status
  36. * @return {ServerResponse}
  37. * @api public
  38. */
  39. res.send = function(body, headers, status){
  40. // allow status as second arg
  41. if ('number' == typeof headers) {
  42. status = headers,
  43. headers = null;
  44. }
  45. // default status
  46. status = status || this.statusCode;
  47. // allow 0 args as 204
  48. if (!arguments.length || undefined === body) body = status = 204;
  49. // determine content type
  50. switch (typeof body) {
  51. case 'number':
  52. if (!this.header('Content-Type')) {
  53. this.contentType('.txt');
  54. }
  55. body = http.STATUS_CODES[status = body];
  56. break;
  57. case 'string':
  58. if (!this.header('Content-Type')) {
  59. this.charset = this.charset || 'utf-8';
  60. this.contentType('.html');
  61. }
  62. break;
  63. case 'boolean':
  64. case 'object':
  65. if (Buffer.isBuffer(body)) {
  66. if (!this.header('Content-Type')) {
  67. this.contentType('.bin');
  68. }
  69. } else {
  70. if (!this.header('Content-Type')) {
  71. this.charset = this.charset || 'utf-8';
  72. this.contentType('.json');
  73. }
  74. body = JSON.stringify(body);
  75. if (this.req.query.callback && this.app.set('jsonp callback')) {
  76. this.charset = this.charset || 'utf-8';
  77. this.header('Content-Type', 'text/javascript');
  78. body = this.req.query.callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
  79. }
  80. }
  81. break;
  82. }
  83. // populate Content-Length
  84. if (!this.header('Content-Length')) {
  85. this.header('Content-Length', Buffer.isBuffer(body)
  86. ? body.length
  87. : Buffer.byteLength(body));
  88. }
  89. // merge headers passed
  90. if (headers) {
  91. var fields = Object.keys(headers);
  92. for (var i = 0, len = fields.length; i < len; ++i) {
  93. var field = fields[i];
  94. this.header(field, headers[field]);
  95. }
  96. }
  97. // strip irrelevant headers
  98. if (204 == status || 304 == status) {
  99. this.removeHeader('Content-Type');
  100. this.removeHeader('Content-Length');
  101. }
  102. // respond
  103. this.statusCode = status;
  104. this.end('HEAD' == this.req.method ? undefined : body);
  105. return this;
  106. };
  107. /**
  108. * Send JSON response with `obj`, optional `headers`, and optional `status`.
  109. *
  110. * Examples:
  111. *
  112. * res.json(null);
  113. * res.json({ user: 'tj' });
  114. * res.json('oh noes!', 500);
  115. * res.json('I dont have that', 404);
  116. *
  117. * @param {Mixed} obj
  118. * @param {Object|Number} headers or status
  119. * @param {Number} status
  120. * @return {ServerResponse}
  121. * @api public
  122. */
  123. res.json = function(obj, headers, status){
  124. this.charset = this.charset || 'utf-8';
  125. this.header('Content-Type', 'application/json');
  126. return this.send(JSON.stringify(obj), headers, status);
  127. };
  128. /**
  129. * Set status `code`.
  130. *
  131. * @param {Number} code
  132. * @return {ServerResponse}
  133. * @api public
  134. */
  135. res.status = function(code){
  136. this.statusCode = code;
  137. return this;
  138. };
  139. /**
  140. * Transfer the file at the given `path`. Automatically sets
  141. * the _Content-Type_ response header field. `next()` is called
  142. * when `path` is a directory, or when an error occurs.
  143. *
  144. * Options:
  145. *
  146. * - `maxAge` defaulting to 0
  147. * - `root` root directory for relative filenames
  148. *
  149. * @param {String} path
  150. * @param {Object|Function} options or fn
  151. * @param {Function} fn
  152. * @api public
  153. */
  154. res.sendfile = function(path, options, fn){
  155. var next = this.req.next;
  156. options = options || {};
  157. // support function as second arg
  158. if ('function' == typeof options) {
  159. fn = options;
  160. options = {};
  161. }
  162. options.path = encodeURIComponent(path);
  163. options.callback = fn;
  164. send(this.req, this, next, options);
  165. };
  166. /**
  167. * Set _Content-Type_ response header passed through `mime.lookup()`.
  168. *
  169. * Examples:
  170. *
  171. * var filename = 'path/to/image.png';
  172. * res.contentType(filename);
  173. * // res.headers['Content-Type'] is now "image/png"
  174. *
  175. * res.contentType('.html');
  176. * res.contentType('html');
  177. * res.contentType('json');
  178. * res.contentType('png');
  179. *
  180. * @param {String} type
  181. * @return {String} the resolved mime type
  182. * @api public
  183. */
  184. res.contentType = function(type){
  185. return this.header('Content-Type', mime.lookup(type));
  186. };
  187. /**
  188. * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
  189. *
  190. * @param {String} filename
  191. * @return {ServerResponse}
  192. * @api public
  193. */
  194. res.attachment = function(filename){
  195. if (filename) this.contentType(filename);
  196. this.header('Content-Disposition', filename
  197. ? 'attachment; filename="' + basename(filename) + '"'
  198. : 'attachment');
  199. return this;
  200. };
  201. /**
  202. * Transfer the file at the given `path`, with optional
  203. * `filename` as an attachment and optional callback `fn(err)`,
  204. * and optional `fn2(err)` which is invoked when an error has
  205. * occurred after header has been sent.
  206. *
  207. * @param {String} path
  208. * @param {String|Function} filename or fn
  209. * @param {Function} fn
  210. * @param {Function} fn2
  211. * @api public
  212. */
  213. res.download = function(path, filename, fn, fn2){
  214. var self = this;
  215. // support callback as second arg
  216. if ('function' == typeof filename) {
  217. fn2 = fn;
  218. fn = filename;
  219. filename = null;
  220. }
  221. // transfer the file
  222. this.attachment(filename || path).sendfile(path, function(err){
  223. var sentHeader = self._header;
  224. if (err) {
  225. if (!sentHeader) self.removeHeader('Content-Disposition');
  226. if (sentHeader) {
  227. fn2 && fn2(err);
  228. } else if (fn) {
  229. fn(err);
  230. } else {
  231. self.req.next(err);
  232. }
  233. } else if (fn) {
  234. fn();
  235. }
  236. });
  237. };
  238. /**
  239. * Set or get response header `name` with optional `val`.
  240. *
  241. * @param {String} name
  242. * @param {String} val
  243. * @return {String}
  244. * @api public
  245. */
  246. res.header = function(name, val){
  247. if (val === undefined) {
  248. return this.getHeader(name);
  249. } else {
  250. this.setHeader(name, val);
  251. return val;
  252. }
  253. };
  254. /**
  255. * Clear cookie `name`.
  256. *
  257. * @param {String} name
  258. * @param {Object} options
  259. * @api public
  260. */
  261. res.clearCookie = function(name, options){
  262. var opts = { expires: new Date(1) };
  263. this.cookie(name, '', options
  264. ? utils.merge(options, opts)
  265. : opts);
  266. };
  267. /**
  268. * Set cookie `name` to `val`, with the given `options`.
  269. *
  270. * Options:
  271. *
  272. * - `maxAge` max-age in milliseconds, converted to `expires`
  273. *
  274. * Examples:
  275. *
  276. * // "Remember Me" for 15 minutes
  277. * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
  278. *
  279. * // save as above
  280. * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
  281. *
  282. * @param {String} name
  283. * @param {String} val
  284. * @param {Options} options
  285. * @api public
  286. */
  287. res.cookie = function(name, val, options){
  288. options = options || {};
  289. if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
  290. var cookie = utils.serializeCookie(name, val, options);
  291. this.header('Set-Cookie', cookie);
  292. };
  293. /**
  294. * Redirect to the given `url` with optional response `status`
  295. * defauling to 302.
  296. *
  297. * The given `url` can also be the name of a mapped url, for
  298. * example by default express supports "back" which redirects
  299. * to the _Referrer_ or _Referer_ headers or the application's
  300. * "home" setting. Express also supports "home" out of the box,
  301. * which can be set via `app.set('home', '/blog');`, and defaults
  302. * to '/'.
  303. *
  304. * Redirect Mapping:
  305. *
  306. * To extend the redirect mapping capabilities that Express provides,
  307. * we may use the `app.redirect()` method:
  308. *
  309. * app.redirect('google', 'http://google.com');
  310. *
  311. * Now in a route we may call:
  312. *
  313. * res.redirect('google');
  314. *
  315. * We may also map dynamic redirects:
  316. *
  317. * app.redirect('comments', function(req, res){
  318. * return '/post/' + req.params.id + '/comments';
  319. * });
  320. *
  321. * So now we may do the following, and the redirect will dynamically adjust to
  322. * the context of the request. If we called this route with _GET /post/12_ our
  323. * redirect _Location_ would be _/post/12/comments_.
  324. *
  325. * app.get('/post/:id', function(req, res){
  326. * res.redirect('comments');
  327. * });
  328. *
  329. * Unless an absolute `url` is given, the app's mount-point
  330. * will be respected. For example if we redirect to `/posts`,
  331. * and our app is mounted at `/blog` we will redirect to `/blog/posts`.
  332. *
  333. * @param {String} url
  334. * @param {Number} code
  335. * @api public
  336. */
  337. res.redirect = function(url, status){
  338. var app = this.app
  339. , req = this.req
  340. , base = app.set('home') || '/'
  341. , status = status || 302
  342. , body;
  343. // Setup redirect map
  344. var map = {
  345. back: req.header('Referrer', base)
  346. , home: base
  347. };
  348. // Support custom redirect map
  349. map.__proto__ = app.redirects;
  350. // Attempt mapped redirect
  351. var mapped = 'function' == typeof map[url]
  352. ? map[url](req, this)
  353. : map[url];
  354. // Perform redirect
  355. url = mapped || url;
  356. // Relative
  357. if (!~url.indexOf('://')) {
  358. // Respect mount-point
  359. if (app.route) url = join(app.route, url);
  360. // Absolute
  361. var host = req.headers.host
  362. , tls = req.connection.encrypted;
  363. url = 'http' + (tls ? 's' : '') + '://' + host + url;
  364. }
  365. // Support text/{plain,html} by default
  366. if (req.accepts('html')) {
  367. body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
  368. this.header('Content-Type', 'text/html');
  369. } else {
  370. body = http.STATUS_CODES[status] + '. Redirecting to ' + url;
  371. this.header('Content-Type', 'text/plain');
  372. }
  373. // Respond
  374. this.statusCode = status;
  375. this.header('Location', url);
  376. this.end(body);
  377. };
  378. /**
  379. * Assign the view local variable `name` to `val` or return the
  380. * local previously assigned to `name`.
  381. *
  382. * @param {String} name
  383. * @param {Mixed} val
  384. * @return {Mixed} val
  385. * @api public
  386. */
  387. res.local = function(name, val){
  388. this._locals = this._locals || {};
  389. return undefined === val
  390. ? this._locals[name]
  391. : this._locals[name] = val;
  392. };
  393. /**
  394. * Assign several locals with the given `obj`,
  395. * or return the locals.
  396. *
  397. * @param {Object} obj
  398. * @return {Object|Undefined}
  399. * @api public
  400. */
  401. res.locals =
  402. res.helpers = function(obj){
  403. if (obj) {
  404. for (var key in obj) {
  405. this.local(key, obj[key]);
  406. }
  407. } else {
  408. return this._locals;
  409. }
  410. };