index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. 'use strict';
  2. // Load modules
  3. const Hoek = require('hoek');
  4. // Declare internals
  5. const internals = {
  6. STATUS_CODES: Object.setPrototypeOf({
  7. '100': 'Continue',
  8. '101': 'Switching Protocols',
  9. '102': 'Processing',
  10. '200': 'OK',
  11. '201': 'Created',
  12. '202': 'Accepted',
  13. '203': 'Non-Authoritative Information',
  14. '204': 'No Content',
  15. '205': 'Reset Content',
  16. '206': 'Partial Content',
  17. '207': 'Multi-Status',
  18. '300': 'Multiple Choices',
  19. '301': 'Moved Permanently',
  20. '302': 'Moved Temporarily',
  21. '303': 'See Other',
  22. '304': 'Not Modified',
  23. '305': 'Use Proxy',
  24. '307': 'Temporary Redirect',
  25. '400': 'Bad Request',
  26. '401': 'Unauthorized',
  27. '402': 'Payment Required',
  28. '403': 'Forbidden',
  29. '404': 'Not Found',
  30. '405': 'Method Not Allowed',
  31. '406': 'Not Acceptable',
  32. '407': 'Proxy Authentication Required',
  33. '408': 'Request Time-out',
  34. '409': 'Conflict',
  35. '410': 'Gone',
  36. '411': 'Length Required',
  37. '412': 'Precondition Failed',
  38. '413': 'Request Entity Too Large',
  39. '414': 'Request-URI Too Large',
  40. '415': 'Unsupported Media Type',
  41. '416': 'Requested Range Not Satisfiable',
  42. '417': 'Expectation Failed',
  43. '418': 'I\'m a teapot',
  44. '422': 'Unprocessable Entity',
  45. '423': 'Locked',
  46. '424': 'Failed Dependency',
  47. '425': 'Unordered Collection',
  48. '426': 'Upgrade Required',
  49. '428': 'Precondition Required',
  50. '429': 'Too Many Requests',
  51. '431': 'Request Header Fields Too Large',
  52. '451': 'Unavailable For Legal Reasons',
  53. '500': 'Internal Server Error',
  54. '501': 'Not Implemented',
  55. '502': 'Bad Gateway',
  56. '503': 'Service Unavailable',
  57. '504': 'Gateway Time-out',
  58. '505': 'HTTP Version Not Supported',
  59. '506': 'Variant Also Negotiates',
  60. '507': 'Insufficient Storage',
  61. '509': 'Bandwidth Limit Exceeded',
  62. '510': 'Not Extended',
  63. '511': 'Network Authentication Required'
  64. }, null)
  65. };
  66. exports.boomify = function (error, options) {
  67. Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object');
  68. options = options || {};
  69. if (!error.isBoom) {
  70. return internals.initialize(error, options.statusCode || 500, options.message);
  71. }
  72. if (options.override === false || // Defaults to true
  73. (!options.statusCode && !options.message)) {
  74. return error;
  75. }
  76. return internals.initialize(error, options.statusCode || error.output.statusCode, options.message);
  77. };
  78. exports.wrap = function (error, statusCode, message) {
  79. Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object');
  80. Hoek.assert(!error.isBoom || (!statusCode && !message), 'Cannot provide statusCode or message with boom error');
  81. return (error.isBoom ? error : internals.initialize(error, statusCode || 500, message));
  82. };
  83. exports.create = function (statusCode, message, data) {
  84. return internals.create(statusCode, message, data, exports.create);
  85. };
  86. internals.create = function (statusCode, message, data, ctor) {
  87. if (message instanceof Error) {
  88. if (data) {
  89. message.data = data;
  90. }
  91. return exports.wrap(message, statusCode);
  92. }
  93. const error = new Error(message ? message : undefined); // Avoids settings null message
  94. Error.captureStackTrace(error, ctor); // Filter the stack to our external API
  95. error.data = data || null;
  96. internals.initialize(error, statusCode);
  97. error.typeof = ctor;
  98. return error;
  99. };
  100. internals.initialize = function (error, statusCode, message) {
  101. const numberCode = parseInt(statusCode, 10);
  102. Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);
  103. error.isBoom = true;
  104. error.isServer = numberCode >= 500;
  105. if (!error.hasOwnProperty('data')) {
  106. error.data = null;
  107. }
  108. error.output = {
  109. statusCode: numberCode,
  110. payload: {},
  111. headers: {}
  112. };
  113. error.reformat = internals.reformat;
  114. if (!message &&
  115. !error.message) {
  116. error.reformat();
  117. message = error.output.payload.error;
  118. }
  119. if (message) {
  120. error.message = (message + (error.message ? ': ' + error.message : ''));
  121. error.output.payload.message = error.message;
  122. }
  123. error.reformat();
  124. return error;
  125. };
  126. internals.reformat = function () {
  127. this.output.payload.statusCode = this.output.statusCode;
  128. this.output.payload.error = internals.STATUS_CODES[this.output.statusCode] || 'Unknown';
  129. if (this.output.statusCode === 500) {
  130. this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user
  131. }
  132. else if (this.message) {
  133. this.output.payload.message = this.message;
  134. }
  135. };
  136. // 4xx Client Errors
  137. exports.badRequest = function (message, data) {
  138. return internals.create(400, message, data, exports.badRequest);
  139. };
  140. exports.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[])
  141. const err = internals.create(401, message, undefined, exports.unauthorized);
  142. if (!scheme) {
  143. return err;
  144. }
  145. let wwwAuthenticate = '';
  146. if (typeof scheme === 'string') {
  147. // function (message, scheme, attributes)
  148. wwwAuthenticate = scheme;
  149. if (attributes || message) {
  150. err.output.payload.attributes = {};
  151. }
  152. if (attributes) {
  153. if (typeof attributes === 'string') {
  154. wwwAuthenticate = wwwAuthenticate + ' ' + Hoek.escapeHeaderAttribute(attributes);
  155. err.output.payload.attributes = attributes;
  156. }
  157. else {
  158. const names = Object.keys(attributes);
  159. for (let i = 0; i < names.length; ++i) {
  160. const name = names[i];
  161. if (i) {
  162. wwwAuthenticate = wwwAuthenticate + ',';
  163. }
  164. let value = attributes[name];
  165. if (value === null ||
  166. value === undefined) { // Value can be zero
  167. value = '';
  168. }
  169. wwwAuthenticate = wwwAuthenticate + ' ' + name + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';
  170. err.output.payload.attributes[name] = value;
  171. }
  172. }
  173. }
  174. if (message) {
  175. if (attributes) {
  176. wwwAuthenticate = wwwAuthenticate + ',';
  177. }
  178. wwwAuthenticate = wwwAuthenticate + ' error="' + Hoek.escapeHeaderAttribute(message) + '"';
  179. err.output.payload.attributes.error = message;
  180. }
  181. else {
  182. err.isMissing = true;
  183. }
  184. }
  185. else {
  186. // function (message, wwwAuthenticate[])
  187. const wwwArray = scheme;
  188. for (let i = 0; i < wwwArray.length; ++i) {
  189. if (i) {
  190. wwwAuthenticate = wwwAuthenticate + ', ';
  191. }
  192. wwwAuthenticate = wwwAuthenticate + wwwArray[i];
  193. }
  194. }
  195. err.output.headers['WWW-Authenticate'] = wwwAuthenticate;
  196. return err;
  197. };
  198. exports.paymentRequired = function (message, data) {
  199. return internals.create(402, message, data, exports.paymentRequired);
  200. };
  201. exports.forbidden = function (message, data) {
  202. return internals.create(403, message, data, exports.forbidden);
  203. };
  204. exports.notFound = function (message, data) {
  205. return internals.create(404, message, data, exports.notFound);
  206. };
  207. exports.methodNotAllowed = function (message, data, allow) {
  208. const err = internals.create(405, message, data, exports.methodNotAllowed);
  209. if (typeof allow === 'string') {
  210. allow = [allow];
  211. }
  212. if (Array.isArray(allow)) {
  213. err.output.headers.Allow = allow.join(', ');
  214. }
  215. return err;
  216. };
  217. exports.notAcceptable = function (message, data) {
  218. return internals.create(406, message, data, exports.notAcceptable);
  219. };
  220. exports.proxyAuthRequired = function (message, data) {
  221. return internals.create(407, message, data, exports.proxyAuthRequired);
  222. };
  223. exports.clientTimeout = function (message, data) {
  224. return internals.create(408, message, data, exports.clientTimeout);
  225. };
  226. exports.conflict = function (message, data) {
  227. return internals.create(409, message, data, exports.conflict);
  228. };
  229. exports.resourceGone = function (message, data) {
  230. return internals.create(410, message, data, exports.resourceGone);
  231. };
  232. exports.lengthRequired = function (message, data) {
  233. return internals.create(411, message, data, exports.lengthRequired);
  234. };
  235. exports.preconditionFailed = function (message, data) {
  236. return internals.create(412, message, data, exports.preconditionFailed);
  237. };
  238. exports.entityTooLarge = function (message, data) {
  239. return internals.create(413, message, data, exports.entityTooLarge);
  240. };
  241. exports.uriTooLong = function (message, data) {
  242. return internals.create(414, message, data, exports.uriTooLong);
  243. };
  244. exports.unsupportedMediaType = function (message, data) {
  245. return internals.create(415, message, data, exports.unsupportedMediaType);
  246. };
  247. exports.rangeNotSatisfiable = function (message, data) {
  248. return internals.create(416, message, data, exports.rangeNotSatisfiable);
  249. };
  250. exports.expectationFailed = function (message, data) {
  251. return internals.create(417, message, data, exports.expectationFailed);
  252. };
  253. exports.teapot = function (message, data) {
  254. return internals.create(418, message, data, exports.teapot);
  255. };
  256. exports.badData = function (message, data) {
  257. return internals.create(422, message, data, exports.badData);
  258. };
  259. exports.locked = function (message, data) {
  260. return internals.create(423, message, data, exports.locked);
  261. };
  262. exports.preconditionRequired = function (message, data) {
  263. return internals.create(428, message, data, exports.preconditionRequired);
  264. };
  265. exports.tooManyRequests = function (message, data) {
  266. return internals.create(429, message, data, exports.tooManyRequests);
  267. };
  268. exports.illegal = function (message, data) {
  269. return internals.create(451, message, data, exports.illegal);
  270. };
  271. // 5xx Server Errors
  272. exports.internal = function (message, data, statusCode) {
  273. return internals.serverError(message, data, statusCode, exports.internal);
  274. };
  275. internals.serverError = function (message, data, statusCode, ctor) {
  276. if (data instanceof Error &&
  277. !data.isBoom) {
  278. return exports.wrap(data, statusCode, message);
  279. }
  280. const error = internals.create(statusCode || 500, message, undefined, ctor);
  281. error.data = data;
  282. return error;
  283. };
  284. exports.notImplemented = function (message, data) {
  285. return internals.serverError(message, data, 501, exports.notImplemented);
  286. };
  287. exports.badGateway = function (message, data) {
  288. return internals.serverError(message, data, 502, exports.badGateway);
  289. };
  290. exports.serverUnavailable = function (message, data) {
  291. return internals.serverError(message, data, 503, exports.serverUnavailable);
  292. };
  293. exports.gatewayTimeout = function (message, data) {
  294. return internals.serverError(message, data, 504, exports.gatewayTimeout);
  295. };
  296. exports.badImplementation = function (message, data) {
  297. const err = internals.serverError(message, data, 500, exports.badImplementation);
  298. err.isDeveloperError = true;
  299. return err;
  300. };