UNPKG

12.2 kBJavaScriptView Raw
1'use strict';
2
3const Hoek = require('@hapi/hoek');
4
5
6const internals = {
7 codes: new Map([
8 [100, 'Continue'],
9 [101, 'Switching Protocols'],
10 [102, 'Processing'],
11 [200, 'OK'],
12 [201, 'Created'],
13 [202, 'Accepted'],
14 [203, 'Non-Authoritative Information'],
15 [204, 'No Content'],
16 [205, 'Reset Content'],
17 [206, 'Partial Content'],
18 [207, 'Multi-Status'],
19 [300, 'Multiple Choices'],
20 [301, 'Moved Permanently'],
21 [302, 'Moved Temporarily'],
22 [303, 'See Other'],
23 [304, 'Not Modified'],
24 [305, 'Use Proxy'],
25 [307, 'Temporary Redirect'],
26 [400, 'Bad Request'],
27 [401, 'Unauthorized'],
28 [402, 'Payment Required'],
29 [403, 'Forbidden'],
30 [404, 'Not Found'],
31 [405, 'Method Not Allowed'],
32 [406, 'Not Acceptable'],
33 [407, 'Proxy Authentication Required'],
34 [408, 'Request Time-out'],
35 [409, 'Conflict'],
36 [410, 'Gone'],
37 [411, 'Length Required'],
38 [412, 'Precondition Failed'],
39 [413, 'Request Entity Too Large'],
40 [414, 'Request-URI Too Large'],
41 [415, 'Unsupported Media Type'],
42 [416, 'Requested Range Not Satisfiable'],
43 [417, 'Expectation Failed'],
44 [418, 'I\'m a teapot'],
45 [422, 'Unprocessable Entity'],
46 [423, 'Locked'],
47 [424, 'Failed Dependency'],
48 [425, 'Too Early'],
49 [426, 'Upgrade Required'],
50 [428, 'Precondition Required'],
51 [429, 'Too Many Requests'],
52 [431, 'Request Header Fields Too Large'],
53 [451, 'Unavailable For Legal Reasons'],
54 [500, 'Internal Server Error'],
55 [501, 'Not Implemented'],
56 [502, 'Bad Gateway'],
57 [503, 'Service Unavailable'],
58 [504, 'Gateway Time-out'],
59 [505, 'HTTP Version Not Supported'],
60 [506, 'Variant Also Negotiates'],
61 [507, 'Insufficient Storage'],
62 [509, 'Bandwidth Limit Exceeded'],
63 [510, 'Not Extended'],
64 [511, 'Network Authentication Required']
65 ])
66};
67
68
69exports.Boom = class extends Error {
70
71 constructor(message, options = {}) {
72
73 if (message instanceof Error) {
74 return exports.boomify(Hoek.clone(message), options);
75 }
76
77 const { statusCode = 500, data = null, ctor = exports.Boom } = options;
78 const error = new Error(message ? message : undefined); // Avoids settings null message
79 Error.captureStackTrace(error, ctor); // Filter the stack to our external API
80 error.data = data;
81 const boom = internals.initialize(error, statusCode);
82
83 Object.defineProperty(boom, 'typeof', { value: ctor });
84
85 if (options.decorate) {
86 Object.assign(boom, options.decorate);
87 }
88
89 return boom;
90 }
91
92 static [Symbol.hasInstance](instance) {
93
94 return exports.isBoom(instance);
95 }
96};
97
98
99exports.isBoom = function (err, statusCode) {
100
101 return err instanceof Error && !!err.isBoom && (!statusCode || err.output.statusCode === statusCode);
102};
103
104
105exports.boomify = function (err, options) {
106
107 Hoek.assert(err instanceof Error, 'Cannot wrap non-Error object');
108
109 options = options || {};
110
111 if (options.data !== undefined) {
112 err.data = options.data;
113 }
114
115 if (options.decorate) {
116 Object.assign(err, options.decorate);
117 }
118
119 if (!err.isBoom) {
120 return internals.initialize(err, options.statusCode || 500, options.message);
121 }
122
123 if (options.override === false || // Defaults to true
124 !options.statusCode && !options.message) {
125
126 return err;
127 }
128
129 return internals.initialize(err, options.statusCode || err.output.statusCode, options.message);
130};
131
132
133// 4xx Client Errors
134
135exports.badRequest = function (message, data) {
136
137 return new exports.Boom(message, { statusCode: 400, data, ctor: exports.badRequest });
138};
139
140
141exports.unauthorized = function (message, scheme, attributes) { // Or (message, wwwAuthenticate[])
142
143 const err = new exports.Boom(message, { statusCode: 401, ctor: exports.unauthorized });
144
145 // function (message)
146
147 if (!scheme) {
148 return err;
149 }
150
151 // function (message, wwwAuthenticate[])
152
153 if (typeof scheme !== 'string') {
154 err.output.headers['WWW-Authenticate'] = scheme.join(', ');
155 return err;
156 }
157
158 // function (message, scheme, attributes)
159
160 let wwwAuthenticate = `${scheme}`;
161
162 if (attributes ||
163 message) {
164
165 err.output.payload.attributes = {};
166 }
167
168 if (attributes) {
169 if (typeof attributes === 'string') {
170 wwwAuthenticate += ' ' + Hoek.escapeHeaderAttribute(attributes);
171 err.output.payload.attributes = attributes;
172 }
173 else {
174 wwwAuthenticate += ' ' + Object.keys(attributes).map((name) => {
175
176 let value = attributes[name];
177 if (value === null ||
178 value === undefined) {
179
180 value = '';
181 }
182
183 err.output.payload.attributes[name] = value;
184 return `${name}="${Hoek.escapeHeaderAttribute(value.toString())}"`;
185 })
186 .join(', ');
187 }
188 }
189
190 if (message) {
191 if (attributes) {
192 wwwAuthenticate += ',';
193 }
194
195 wwwAuthenticate += ` error="${Hoek.escapeHeaderAttribute(message)}"`;
196 err.output.payload.attributes.error = message;
197 }
198 else {
199 err.isMissing = true;
200 }
201
202 err.output.headers['WWW-Authenticate'] = wwwAuthenticate;
203 return err;
204};
205
206
207exports.paymentRequired = function (message, data) {
208
209 return new exports.Boom(message, { statusCode: 402, data, ctor: exports.paymentRequired });
210};
211
212
213exports.forbidden = function (message, data) {
214
215 return new exports.Boom(message, { statusCode: 403, data, ctor: exports.forbidden });
216};
217
218
219exports.notFound = function (message, data) {
220
221 return new exports.Boom(message, { statusCode: 404, data, ctor: exports.notFound });
222};
223
224
225exports.methodNotAllowed = function (message, data, allow) {
226
227 const err = new exports.Boom(message, { statusCode: 405, data, ctor: exports.methodNotAllowed });
228
229 if (typeof allow === 'string') {
230 allow = [allow];
231 }
232
233 if (Array.isArray(allow)) {
234 err.output.headers.Allow = allow.join(', ');
235 }
236
237 return err;
238};
239
240
241exports.notAcceptable = function (message, data) {
242
243 return new exports.Boom(message, { statusCode: 406, data, ctor: exports.notAcceptable });
244};
245
246
247exports.proxyAuthRequired = function (message, data) {
248
249 return new exports.Boom(message, { statusCode: 407, data, ctor: exports.proxyAuthRequired });
250};
251
252
253exports.clientTimeout = function (message, data) {
254
255 return new exports.Boom(message, { statusCode: 408, data, ctor: exports.clientTimeout });
256};
257
258
259exports.conflict = function (message, data) {
260
261 return new exports.Boom(message, { statusCode: 409, data, ctor: exports.conflict });
262};
263
264
265exports.resourceGone = function (message, data) {
266
267 return new exports.Boom(message, { statusCode: 410, data, ctor: exports.resourceGone });
268};
269
270
271exports.lengthRequired = function (message, data) {
272
273 return new exports.Boom(message, { statusCode: 411, data, ctor: exports.lengthRequired });
274};
275
276
277exports.preconditionFailed = function (message, data) {
278
279 return new exports.Boom(message, { statusCode: 412, data, ctor: exports.preconditionFailed });
280};
281
282
283exports.entityTooLarge = function (message, data) {
284
285 return new exports.Boom(message, { statusCode: 413, data, ctor: exports.entityTooLarge });
286};
287
288
289exports.uriTooLong = function (message, data) {
290
291 return new exports.Boom(message, { statusCode: 414, data, ctor: exports.uriTooLong });
292};
293
294
295exports.unsupportedMediaType = function (message, data) {
296
297 return new exports.Boom(message, { statusCode: 415, data, ctor: exports.unsupportedMediaType });
298};
299
300
301exports.rangeNotSatisfiable = function (message, data) {
302
303 return new exports.Boom(message, { statusCode: 416, data, ctor: exports.rangeNotSatisfiable });
304};
305
306
307exports.expectationFailed = function (message, data) {
308
309 return new exports.Boom(message, { statusCode: 417, data, ctor: exports.expectationFailed });
310};
311
312
313exports.teapot = function (message, data) {
314
315 return new exports.Boom(message, { statusCode: 418, data, ctor: exports.teapot });
316};
317
318
319exports.badData = function (message, data) {
320
321 return new exports.Boom(message, { statusCode: 422, data, ctor: exports.badData });
322};
323
324
325exports.locked = function (message, data) {
326
327 return new exports.Boom(message, { statusCode: 423, data, ctor: exports.locked });
328};
329
330
331exports.failedDependency = function (message, data) {
332
333 return new exports.Boom(message, { statusCode: 424, data, ctor: exports.failedDependency });
334};
335
336exports.tooEarly = function (message, data) {
337
338 return new exports.Boom(message, { statusCode: 425, data, ctor: exports.tooEarly });
339};
340
341
342exports.preconditionRequired = function (message, data) {
343
344 return new exports.Boom(message, { statusCode: 428, data, ctor: exports.preconditionRequired });
345};
346
347
348exports.tooManyRequests = function (message, data) {
349
350 return new exports.Boom(message, { statusCode: 429, data, ctor: exports.tooManyRequests });
351};
352
353
354exports.illegal = function (message, data) {
355
356 return new exports.Boom(message, { statusCode: 451, data, ctor: exports.illegal });
357};
358
359
360// 5xx Server Errors
361
362exports.internal = function (message, data, statusCode = 500) {
363
364 return internals.serverError(message, data, statusCode, exports.internal);
365};
366
367
368exports.notImplemented = function (message, data) {
369
370 return internals.serverError(message, data, 501, exports.notImplemented);
371};
372
373
374exports.badGateway = function (message, data) {
375
376 return internals.serverError(message, data, 502, exports.badGateway);
377};
378
379
380exports.serverUnavailable = function (message, data) {
381
382 return internals.serverError(message, data, 503, exports.serverUnavailable);
383};
384
385
386exports.gatewayTimeout = function (message, data) {
387
388 return internals.serverError(message, data, 504, exports.gatewayTimeout);
389};
390
391
392exports.badImplementation = function (message, data) {
393
394 const err = internals.serverError(message, data, 500, exports.badImplementation);
395 err.isDeveloperError = true;
396 return err;
397};
398
399
400internals.initialize = function (err, statusCode, message) {
401
402 const numberCode = parseInt(statusCode, 10);
403 Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);
404
405 err.isBoom = true;
406 err.isServer = numberCode >= 500;
407
408 if (!err.hasOwnProperty('data')) {
409 err.data = null;
410 }
411
412 err.output = {
413 statusCode: numberCode,
414 payload: {},
415 headers: {}
416 };
417
418 Object.defineProperty(err, 'reformat', { value: internals.reformat });
419
420 if (!message &&
421 !err.message) {
422
423 err.reformat();
424 message = err.output.payload.error;
425 }
426
427 if (message) {
428 const props = Object.getOwnPropertyDescriptor(err, 'message') || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(err), 'message');
429 Hoek.assert(!props || props.configurable && !props.get, 'The error is not compatible with boom');
430
431 err.message = message + (err.message ? ': ' + err.message : '');
432 err.output.payload.message = err.message;
433 }
434
435 err.reformat();
436 return err;
437};
438
439
440internals.reformat = function (debug = false) {
441
442 this.output.payload.statusCode = this.output.statusCode;
443 this.output.payload.error = internals.codes.get(this.output.statusCode) || 'Unknown';
444
445 if (this.output.statusCode === 500 && debug !== true) {
446 this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user
447 }
448 else if (this.message) {
449 this.output.payload.message = this.message;
450 }
451};
452
453
454internals.serverError = function (message, data, statusCode, ctor) {
455
456 if (data instanceof Error &&
457 !data.isBoom) {
458
459 return exports.boomify(data, { statusCode, message });
460 }
461
462 return new exports.Boom(message, { statusCode, data, ctor });
463};