context.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. 'use strict';
  2. /**
  3. * Module dependencies.
  4. */
  5. const createError = require('http-errors');
  6. const httpAssert = require('http-assert');
  7. const delegate = require('delegates');
  8. const statuses = require('statuses');
  9. /**
  10. * Context prototype.
  11. */
  12. const proto = module.exports = {
  13. /**
  14. * util.inspect() implementation, which
  15. * just returns the JSON output.
  16. *
  17. * @return {Object}
  18. * @api public
  19. */
  20. inspect() {
  21. return this.toJSON();
  22. },
  23. /**
  24. * Return JSON representation.
  25. *
  26. * Here we explicitly invoke .toJSON() on each
  27. * object, as iteration will otherwise fail due
  28. * to the getters and cause utilities such as
  29. * clone() to fail.
  30. *
  31. * @return {Object}
  32. * @api public
  33. */
  34. toJSON() {
  35. return {
  36. request: this.request.toJSON(),
  37. response: this.response.toJSON(),
  38. app: this.app.toJSON(),
  39. originalUrl: this.originalUrl,
  40. req: '<original node req>',
  41. res: '<original node res>',
  42. socket: '<original node socket>'
  43. };
  44. },
  45. /**
  46. * Similar to .throw(), adds assertion.
  47. *
  48. * this.assert(this.user, 401, 'Please login!');
  49. *
  50. * See: https://github.com/jshttp/http-assert
  51. *
  52. * @param {Mixed} test
  53. * @param {Number} status
  54. * @param {String} message
  55. * @api public
  56. */
  57. assert: httpAssert,
  58. /**
  59. * Throw an error with `msg` and optional `status`
  60. * defaulting to 500. Note that these are user-level
  61. * errors, and the message may be exposed to the client.
  62. *
  63. * this.throw(403)
  64. * this.throw('name required', 400)
  65. * this.throw(400, 'name required')
  66. * this.throw('something exploded')
  67. * this.throw(new Error('invalid'), 400);
  68. * this.throw(400, new Error('invalid'));
  69. *
  70. * See: https://github.com/jshttp/http-errors
  71. *
  72. * @param {String|Number|Error} err, msg or status
  73. * @param {String|Number|Error} [err, msg or status]
  74. * @param {Object} [props]
  75. * @api public
  76. */
  77. throw(...args) {
  78. throw createError(...args);
  79. },
  80. /**
  81. * Default error handling.
  82. *
  83. * @param {Error} err
  84. * @api private
  85. */
  86. onerror(err) {
  87. // don't do anything if there is no error.
  88. // this allows you to pass `this.onerror`
  89. // to node-style callbacks.
  90. if (null == err) return;
  91. if (!(err instanceof Error)) err = new Error(`non-error thrown: ${err}`);
  92. let headerSent = false;
  93. if (this.headerSent || !this.writable) {
  94. headerSent = err.headerSent = true;
  95. }
  96. // delegate
  97. this.app.emit('error', err, this);
  98. // nothing we can do here other
  99. // than delegate to the app-level
  100. // handler and log.
  101. if (headerSent) {
  102. return;
  103. }
  104. const { res } = this;
  105. // first unset all headers
  106. if (typeof res.getHeaderNames === 'function') {
  107. res.getHeaderNames().forEach(name => res.removeHeader(name));
  108. } else {
  109. res._headers = {}; // Node < 7.7
  110. }
  111. // then set those specified
  112. this.set(err.headers);
  113. // force text/plain
  114. this.type = 'text';
  115. // ENOENT support
  116. if ('ENOENT' == err.code) err.status = 404;
  117. // default to 500
  118. if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;
  119. // respond
  120. const code = statuses[err.status];
  121. const msg = err.expose ? err.message : code;
  122. this.status = err.status;
  123. this.length = Buffer.byteLength(msg);
  124. this.res.end(msg);
  125. }
  126. };
  127. /**
  128. * Response delegation.
  129. */
  130. delegate(proto, 'response')
  131. .method('attachment')
  132. .method('redirect')
  133. .method('remove')
  134. .method('vary')
  135. .method('set')
  136. .method('append')
  137. .method('flushHeaders')
  138. .access('status')
  139. .access('message')
  140. .access('body')
  141. .access('length')
  142. .access('type')
  143. .access('lastModified')
  144. .access('etag')
  145. .getter('headerSent')
  146. .getter('writable');
  147. /**
  148. * Request delegation.
  149. */
  150. delegate(proto, 'request')
  151. .method('acceptsLanguages')
  152. .method('acceptsEncodings')
  153. .method('acceptsCharsets')
  154. .method('accepts')
  155. .method('get')
  156. .method('is')
  157. .access('querystring')
  158. .access('idempotent')
  159. .access('socket')
  160. .access('search')
  161. .access('method')
  162. .access('query')
  163. .access('path')
  164. .access('url')
  165. .getter('origin')
  166. .getter('href')
  167. .getter('subdomains')
  168. .getter('protocol')
  169. .getter('host')
  170. .getter('hostname')
  171. .getter('URL')
  172. .getter('header')
  173. .getter('headers')
  174. .getter('secure')
  175. .getter('stale')
  176. .getter('fresh')
  177. .getter('ips')
  178. .getter('ip');