'use strict'; /** * Module dependencies. */ const util = require('util'); const createError = require('http-errors-lite'); const delegate = require('./delegates'); const statuses = require('./statuses'); /** * Context prototype. */ const proto = module.exports = { /** * util.inspect() implementation, which * just returns the JSON output. * * @return {Object} * @api public */ inspect() { if (this === proto) return this; return this.toJSON(); }, /** * Return JSON representation. * * Here we explicitly invoke .toJSON() on each * object, as iteration will otherwise fail due * to the getters and cause utilities such as * clone() to fail. * * @return {Object} * @api public */ toJSON() { return { request: this.request.toJSON(), response: this.response.toJSON(), app: this.app.toJSON(), originalUrl: this.originalUrl, req: '', res: '', socket: '' }; }, /** * Similar to .throw(), adds assertion. * * this.assert(this.user, 401, 'Please login!'); * * @param {Mixed} test * @param {Number} status * @param {String} message * @api public */ assert: function(test, status, message, props) { if (!test) { this.throw(status, message, props) } }, /** * Throw an error with `status` (default 500) and * `msg`. Note that these are user-level * errors, and the message may be exposed to the client. * * this.throw(403) * this.throw(400, 'name required') * this.throw('something exploded') * this.throw(new Error('invalid')) * this.throw(400, new Error('invalid')) * * See: https://github.com/jshttp/http-errors * * Note: `status` should only be passed as the first parameter. * * @param {String|Number|Error} err, msg or status * @param {String|Number|Error} [err, msg or status] * @param {Object} [props] * @api public */ throw(...args) { throw createError(...args); }, /** * Default error handling. * * @param {Error} err * @api private */ onerror(err) { // don't do anything if there is no error. // this allows you to pass `this.onerror` // to node-style callbacks. if (null == err) return; if (!(err instanceof Error)) err = new Error(util.format('non-error thrown: %j', err)); let headerSent = false; if (this.headerSent || !this.writable) { headerSent = err.headerSent = true; } // delegate this.app.emit('error', err, this); // nothing we can do here other // than delegate to the app-level // handler and log. if (headerSent) { return; } const { res } = this; // first unset all headers /* istanbul ignore else */ if (typeof res.getHeaderNames === 'function') { res.getHeaderNames().forEach(name => res.removeHeader(name)); } else { res._headers = {}; // Node < 7.7 } // then set those specified this.set(err.headers); // force text/plain this.type = 'text'; // ENOENT support if ('ENOENT' == err.code) err.status = 404; // default to 500 if ('number' != typeof err.status || !statuses[err.status]) err.status = 500; // respond const code = statuses[err.status]; const msg = err.expose ? err.message : code; this.status = err.status; this.length = Buffer.byteLength(msg); res.end(msg); }, }; /** * Custom inspection implementation for newer Node.js versions. * * @return {Object} * @api public */ /* istanbul ignore else */ if (util.inspect.custom) { module.exports[util.inspect.custom] = module.exports.inspect; } /** * Response delegation. */ delegate(proto, 'response') .method('attachment') .method('redirect') .method('remove') .method('vary') .method('set') .method('append') .method('flushHeaders') .access('status') .access('message') .access('body') .access('length') .access('type') .access('lastModified') .access('etag') .getter('headerSent') .getter('writable'); /** * Request delegation. */ delegate(proto, 'request') .method('get') .method('is') .access('querystring') .access('idempotent') .access('socket') .access('search') .access('method') .access('query') .access('path') .access('url') .access('accept') .getter('origin') .getter('href') .getter('subdomains') .getter('protocol') .getter('host') .getter('hostname') .getter('URL') .getter('header') .getter('headers') .getter('secure') .getter('stale') .getter('fresh') .getter('ips') .getter('ip');