UNPKG

koa

Version:

Koa web app framework

249 lines (214 loc) 5.51 kB
'use strict' /** * Module dependencies. */ const util = require('util') const createError = require('http-errors') const httpAssert = require('http-assert') const delegate = require('delegates') const statuses = require('statuses') const Cookies = require('cookies') const COOKIES = Symbol('context#cookies') /** * 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: '<original node req>', res: '<original node res>', socket: '<original node socket>' } }, /** * Similar to .throw(), adds assertion. * * this.assert(this.user, 401, 'Please login!'); * * See: https://github.com/jshttp/http-assert * * @param {Mixed} test * @param {Number} status * @param {String} message * @api public */ assert: httpAssert, /** * 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 (err == null) return // When dealing with cross-globals a normal `instanceof` check doesn't work properly. // See https://github.com/koajs/koa/issues/1466 // We can probably remove it once jest fixes https://github.com/facebook/jest/issues/2549. const isNativeError = Object.prototype.toString.call(err) === '[object Error]' || err instanceof Error if (!isNativeError) 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' let statusCode = err.status || err.statusCode // default to 500 if (typeof statusCode !== 'number' || !statuses.message[statusCode]) statusCode = 500 // respond const code = statuses.message[statusCode] const msg = err.expose ? err.message : code this.status = err.status = statusCode this.length = Buffer.byteLength(msg) res.end(msg) }, get cookies () { if (!this[COOKIES]) { this[COOKIES] = new Cookies(this.req, this.res, { keys: this.app.keys, secure: this.request.secure }) } return this[COOKIES] }, set cookies (_cookies) { this[COOKIES] = _cookies } } /** * 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('has') .method('set') .method('append') .method('flushHeaders') .method('back') .access('status') .access('message') .access('body') .access('length') .access('type') .access('lastModified') .access('etag') .getter('headerSent') .getter('writable') /** * Request delegation. */ delegate(proto, 'request') .method('acceptsLanguages') .method('acceptsEncodings') .method('acceptsCharsets') .method('accepts') .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')