/node_modules/railway/node_modules/express/lib/response.js
JavaScript | 459 lines | 192 code | 49 blank | 218 comment | 58 complexity | cca32d314e100194c2cdce9777b6c648 MD5 | raw file
- /*!
- * Express - response
- * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
- * MIT Licensed
- */
- /**
- * Module dependencies.
- */
- var fs = require('fs')
- , http = require('http')
- , path = require('path')
- , connect = require('connect')
- , utils = connect.utils
- , parseRange = require('./utils').parseRange
- , res = http.ServerResponse.prototype
- , send = connect.static.send
- , mime = require('mime')
- , basename = path.basename
- , join = path.join;
- /**
- * Send a response with the given `body` and optional `headers` and `status` code.
- *
- * Examples:
- *
- * res.send();
- * res.send(new Buffer('wahoo'));
- * res.send({ some: 'json' });
- * res.send('<p>some html</p>');
- * res.send('Sorry, cant find that', 404);
- * res.send('text', { 'Content-Type': 'text/plain' }, 201);
- * res.send(404);
- *
- * @param {String|Object|Number|Buffer} body or status
- * @param {Object|Number} headers or status
- * @param {Number} status
- * @return {ServerResponse}
- * @api public
- */
- res.send = function(body, headers, status){
- // allow status as second arg
- if ('number' == typeof headers) {
- status = headers,
- headers = null;
- }
- // default status
- status = status || this.statusCode;
- // allow 0 args as 204
- if (!arguments.length || undefined === body) body = status = 204;
- // determine content type
- switch (typeof body) {
- case 'number':
- if (!this.header('Content-Type')) {
- this.contentType('.txt');
- }
- body = http.STATUS_CODES[status = body];
- break;
- case 'string':
- if (!this.header('Content-Type')) {
- this.charset = this.charset || 'utf-8';
- this.contentType('.html');
- }
- break;
- case 'boolean':
- case 'object':
- if (Buffer.isBuffer(body)) {
- if (!this.header('Content-Type')) {
- this.contentType('.bin');
- }
- } else {
- if (!this.header('Content-Type')) {
- this.charset = this.charset || 'utf-8';
- this.contentType('.json');
- }
- body = JSON.stringify(body);
- if (this.req.query.callback && this.app.set('jsonp callback')) {
- this.charset = this.charset || 'utf-8';
- this.header('Content-Type', 'text/javascript');
- body = this.req.query.callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
- }
- }
- break;
- }
- // populate Content-Length
- if (!this.header('Content-Length')) {
- this.header('Content-Length', Buffer.isBuffer(body)
- ? body.length
- : Buffer.byteLength(body));
- }
- // merge headers passed
- if (headers) {
- var fields = Object.keys(headers);
- for (var i = 0, len = fields.length; i < len; ++i) {
- var field = fields[i];
- this.header(field, headers[field]);
- }
- }
- // strip irrelevant headers
- if (204 == status || 304 == status) {
- this.removeHeader('Content-Type');
- this.removeHeader('Content-Length');
- }
- // respond
- this.statusCode = status;
- this.end('HEAD' == this.req.method ? undefined : body);
- return this;
- };
- /**
- * Send JSON response with `obj`, optional `headers`, and optional `status`.
- *
- * Examples:
- *
- * res.json(null);
- * res.json({ user: 'tj' });
- * res.json('oh noes!', 500);
- * res.json('I dont have that', 404);
- *
- * @param {Mixed} obj
- * @param {Object|Number} headers or status
- * @param {Number} status
- * @return {ServerResponse}
- * @api public
- */
- res.json = function(obj, headers, status){
- this.charset = this.charset || 'utf-8';
- this.header('Content-Type', 'application/json');
- return this.send(JSON.stringify(obj), headers, status);
- };
- /**
- * Set status `code`.
- *
- * @param {Number} code
- * @return {ServerResponse}
- * @api public
- */
- res.status = function(code){
- this.statusCode = code;
- return this;
- };
- /**
- * Transfer the file at the given `path`. Automatically sets
- * the _Content-Type_ response header field. `next()` is called
- * when `path` is a directory, or when an error occurs.
- *
- * Options:
- *
- * - `maxAge` defaulting to 0
- * - `root` root directory for relative filenames
- *
- * @param {String} path
- * @param {Object|Function} options or fn
- * @param {Function} fn
- * @api public
- */
- res.sendfile = function(path, options, fn){
- var next = this.req.next;
- options = options || {};
- // support function as second arg
- if ('function' == typeof options) {
- fn = options;
- options = {};
- }
- options.path = encodeURIComponent(path);
- options.callback = fn;
- send(this.req, this, next, options);
- };
- /**
- * Set _Content-Type_ response header passed through `mime.lookup()`.
- *
- * Examples:
- *
- * var filename = 'path/to/image.png';
- * res.contentType(filename);
- * // res.headers['Content-Type'] is now "image/png"
- *
- * res.contentType('.html');
- * res.contentType('html');
- * res.contentType('json');
- * res.contentType('png');
- *
- * @param {String} type
- * @return {String} the resolved mime type
- * @api public
- */
- res.contentType = function(type){
- return this.header('Content-Type', mime.lookup(type));
- };
- /**
- * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
- *
- * @param {String} filename
- * @return {ServerResponse}
- * @api public
- */
- res.attachment = function(filename){
- if (filename) this.contentType(filename);
- this.header('Content-Disposition', filename
- ? 'attachment; filename="' + basename(filename) + '"'
- : 'attachment');
- return this;
- };
- /**
- * Transfer the file at the given `path`, with optional
- * `filename` as an attachment and optional callback `fn(err)`,
- * and optional `fn2(err)` which is invoked when an error has
- * occurred after header has been sent.
- *
- * @param {String} path
- * @param {String|Function} filename or fn
- * @param {Function} fn
- * @param {Function} fn2
- * @api public
- */
- res.download = function(path, filename, fn, fn2){
- var self = this;
- // support callback as second arg
- if ('function' == typeof filename) {
- fn2 = fn;
- fn = filename;
- filename = null;
- }
- // transfer the file
- this.attachment(filename || path).sendfile(path, function(err){
- var sentHeader = self._header;
- if (err) {
- if (!sentHeader) self.removeHeader('Content-Disposition');
- if (sentHeader) {
- fn2 && fn2(err);
- } else if (fn) {
- fn(err);
- } else {
- self.req.next(err);
- }
- } else if (fn) {
- fn();
- }
- });
- };
- /**
- * Set or get response header `name` with optional `val`.
- *
- * @param {String} name
- * @param {String} val
- * @return {String}
- * @api public
- */
- res.header = function(name, val){
- if (val === undefined) {
- return this.getHeader(name);
- } else {
- this.setHeader(name, val);
- return val;
- }
- };
- /**
- * Clear cookie `name`.
- *
- * @param {String} name
- * @param {Object} options
- * @api public
- */
- res.clearCookie = function(name, options){
- var opts = { expires: new Date(1) };
- this.cookie(name, '', options
- ? utils.merge(options, opts)
- : opts);
- };
- /**
- * Set cookie `name` to `val`, with the given `options`.
- *
- * Options:
- *
- * - `maxAge` max-age in milliseconds, converted to `expires`
- *
- * Examples:
- *
- * // "Remember Me" for 15 minutes
- * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
- *
- * // save as above
- * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
- *
- * @param {String} name
- * @param {String} val
- * @param {Options} options
- * @api public
- */
- res.cookie = function(name, val, options){
- options = options || {};
- if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
- var cookie = utils.serializeCookie(name, val, options);
- this.header('Set-Cookie', cookie);
- };
- /**
- * Redirect to the given `url` with optional response `status`
- * defauling to 302.
- *
- * The given `url` can also be the name of a mapped url, for
- * example by default express supports "back" which redirects
- * to the _Referrer_ or _Referer_ headers or the application's
- * "home" setting. Express also supports "home" out of the box,
- * which can be set via `app.set('home', '/blog');`, and defaults
- * to '/'.
- *
- * Redirect Mapping:
- *
- * To extend the redirect mapping capabilities that Express provides,
- * we may use the `app.redirect()` method:
- *
- * app.redirect('google', 'http://google.com');
- *
- * Now in a route we may call:
- *
- * res.redirect('google');
- *
- * We may also map dynamic redirects:
- *
- * app.redirect('comments', function(req, res){
- * return '/post/' + req.params.id + '/comments';
- * });
- *
- * So now we may do the following, and the redirect will dynamically adjust to
- * the context of the request. If we called this route with _GET /post/12_ our
- * redirect _Location_ would be _/post/12/comments_.
- *
- * app.get('/post/:id', function(req, res){
- * res.redirect('comments');
- * });
- *
- * Unless an absolute `url` is given, the app's mount-point
- * will be respected. For example if we redirect to `/posts`,
- * and our app is mounted at `/blog` we will redirect to `/blog/posts`.
- *
- * @param {String} url
- * @param {Number} code
- * @api public
- */
- res.redirect = function(url, status){
- var app = this.app
- , req = this.req
- , base = app.set('home') || '/'
- , status = status || 302
- , body;
- // Setup redirect map
- var map = {
- back: req.header('Referrer', base)
- , home: base
- };
- // Support custom redirect map
- map.__proto__ = app.redirects;
- // Attempt mapped redirect
- var mapped = 'function' == typeof map[url]
- ? map[url](req, this)
- : map[url];
- // Perform redirect
- url = mapped || url;
- // Relative
- if (!~url.indexOf('://')) {
- // Respect mount-point
- if (app.route) url = join(app.route, url);
- // Absolute
- var host = req.headers.host
- , tls = req.connection.encrypted;
- url = 'http' + (tls ? 's' : '') + '://' + host + url;
- }
-
- // Support text/{plain,html} by default
- if (req.accepts('html')) {
- body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
- this.header('Content-Type', 'text/html');
- } else {
- body = http.STATUS_CODES[status] + '. Redirecting to ' + url;
- this.header('Content-Type', 'text/plain');
- }
- // Respond
- this.statusCode = status;
- this.header('Location', url);
- this.end(body);
- };
- /**
- * Assign the view local variable `name` to `val` or return the
- * local previously assigned to `name`.
- *
- * @param {String} name
- * @param {Mixed} val
- * @return {Mixed} val
- * @api public
- */
- res.local = function(name, val){
- this._locals = this._locals || {};
- return undefined === val
- ? this._locals[name]
- : this._locals[name] = val;
- };
- /**
- * Assign several locals with the given `obj`,
- * or return the locals.
- *
- * @param {Object} obj
- * @return {Object|Undefined}
- * @api public
- */
- res.locals =
- res.helpers = function(obj){
- if (obj) {
- for (var key in obj) {
- this.local(key, obj[key]);
- }
- } else {
- return this._locals;
- }
- };