index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // Load modules
  2. var Http = require('http');
  3. var NodeUtil = require('util');
  4. var Hoek = require('hoek');
  5. // Declare internals
  6. var internals = {};
  7. exports = module.exports = internals.Boom = function (/* (new Error) or (code, message) */) {
  8. var self = this;
  9. Hoek.assert(this.constructor === internals.Boom, 'Error must be instantiated using new');
  10. Error.call(this);
  11. this.isBoom = true;
  12. this.response = {
  13. code: 0,
  14. payload: {},
  15. headers: {}
  16. // type: 'content-type'
  17. };
  18. if (arguments[0] instanceof Error) {
  19. // Error
  20. var error = arguments[0];
  21. this.data = error;
  22. this.response.code = error.code || 500;
  23. if (error.message) {
  24. this.message = error.message;
  25. }
  26. }
  27. else {
  28. // code, message
  29. var code = arguments[0];
  30. var message = arguments[1];
  31. Hoek.assert(!isNaN(parseFloat(code)) && isFinite(code) && code >= 400, 'First argument must be a number (400+)');
  32. this.response.code = code;
  33. if (message) {
  34. this.message = message;
  35. }
  36. }
  37. // Response format
  38. this.reformat();
  39. return this;
  40. };
  41. NodeUtil.inherits(internals.Boom, Error);
  42. internals.Boom.prototype.reformat = function () {
  43. this.response.payload.code = this.response.code;
  44. this.response.payload.error = Http.STATUS_CODES[this.response.code] || 'Unknown';
  45. if (this.message) {
  46. this.response.payload.message = Hoek.escapeHtml(this.message); // Prevent XSS from error message
  47. }
  48. };
  49. // Utilities
  50. internals.Boom.badRequest = function (message) {
  51. return new internals.Boom(400, message);
  52. };
  53. internals.Boom.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[])
  54. var err = new internals.Boom(401, message);
  55. if (!scheme) {
  56. return err;
  57. }
  58. var wwwAuthenticate = '';
  59. if (typeof scheme === 'string') {
  60. // function (message, scheme, attributes)
  61. wwwAuthenticate = scheme;
  62. if (attributes) {
  63. var names = Object.keys(attributes);
  64. for (var i = 0, il = names.length; i < il; ++i) {
  65. if (i) {
  66. wwwAuthenticate += ',';
  67. }
  68. var value = attributes[names[i]];
  69. if (value === null ||
  70. value === undefined) { // Value can be zero
  71. value = '';
  72. }
  73. wwwAuthenticate += ' ' + names[i] + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"';
  74. }
  75. }
  76. if (message) {
  77. if (attributes) {
  78. wwwAuthenticate += ',';
  79. }
  80. wwwAuthenticate += ' error="' + Hoek.escapeHeaderAttribute(message) + '"';
  81. }
  82. else {
  83. err.isMissing = true;
  84. }
  85. }
  86. else {
  87. // function (message, wwwAuthenticate[])
  88. var wwwArray = scheme;
  89. for (var i = 0, il = wwwArray.length; i < il; ++i) {
  90. if (i) {
  91. wwwAuthenticate += ', ';
  92. }
  93. wwwAuthenticate += wwwArray[i];
  94. }
  95. }
  96. err.response.headers['WWW-Authenticate'] = wwwAuthenticate;
  97. return err;
  98. };
  99. internals.Boom.clientTimeout = function (message) {
  100. return new internals.Boom(408, message);
  101. };
  102. internals.Boom.serverTimeout = function (message) {
  103. return new internals.Boom(503, message);
  104. };
  105. internals.Boom.forbidden = function (message) {
  106. return new internals.Boom(403, message);
  107. };
  108. internals.Boom.notFound = function (message) {
  109. return new internals.Boom(404, message);
  110. };
  111. internals.Boom.internal = function (message, data) {
  112. var err = new internals.Boom(500, message);
  113. if (data && data.stack) {
  114. err.trace = data.stack.split('\n');
  115. err.outterTrace = Hoek.displayStack(1);
  116. }
  117. else {
  118. err.trace = Hoek.displayStack(1);
  119. }
  120. err.data = data;
  121. err.response.payload.message = 'An internal server error occurred'; // Hide actual error from user
  122. return err;
  123. };
  124. internals.Boom.passThrough = function (code, payload, contentType, headers) {
  125. var err = new internals.Boom(500, 'Pass-through'); // 500 code is only used to initialize
  126. err.data = {
  127. code: code,
  128. payload: payload,
  129. type: contentType
  130. };
  131. err.response.code = code;
  132. err.response.type = contentType;
  133. err.response.headers = headers;
  134. err.response.payload = payload;
  135. return err;
  136. };