UNPKG

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