-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* new error handling, still broken * one suite to go * all test passing * unskip test * linted * 100% code coverage * Cleanup * Added docs * Update docs/Errors.md Co-authored-by: James Sumners <james@sumners.email> * typo * Fixed typos Co-authored-by: James Sumners <james@sumners.email>
- Loading branch information
Showing
17 changed files
with
310 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
'use strict' | ||
|
||
const FJS = require('fast-json-stringify') | ||
const statusCodes = require('http').STATUS_CODES | ||
const wrapThenable = require('./wrapThenable') | ||
const { | ||
kReplyHeaders, kReplyNextErrorHandler, kReplyIsRunningOnErrorHook, kReplySent, kReplyHasStatusCode | ||
} = require('./symbols.js') | ||
|
||
const { getSchemaSerializer } = require('./schemas') | ||
|
||
const serializeError = FJS({ | ||
type: 'object', | ||
properties: { | ||
statusCode: { type: 'number' }, | ||
code: { type: 'string' }, | ||
error: { type: 'string' }, | ||
message: { type: 'string' } | ||
} | ||
}) | ||
|
||
const rootErrorHandler = { | ||
func: defaultErrorHandler, | ||
toJSON () { | ||
return this.func.name.toString() + '()' | ||
} | ||
} | ||
|
||
function handleError (reply, error, cb) { | ||
reply[kReplyIsRunningOnErrorHook] = false | ||
|
||
const context = reply.context | ||
if (reply[kReplyNextErrorHandler] === false) { | ||
fallbackErrorHandler(error, reply, function (reply, payload) { | ||
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders]) | ||
reply.raw.end(payload) | ||
}) | ||
return | ||
} | ||
const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler | ||
|
||
// In case the error handler throws, we set the next errorHandler so we can error again | ||
reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler) | ||
|
||
reply[kReplyHeaders]['content-length'] = undefined | ||
|
||
const func = errorHandler.func | ||
|
||
if (!func) { | ||
reply[kReplyNextErrorHandler] = false | ||
fallbackErrorHandler(error, reply, cb) | ||
return | ||
} | ||
|
||
const result = func(error, reply.request, reply) | ||
if (result !== undefined) { | ||
if (result !== null && typeof result.then === 'function') { | ||
wrapThenable(result, reply) | ||
} else { | ||
reply.send(result) | ||
} | ||
} | ||
} | ||
|
||
function defaultErrorHandler (error, request, reply) { | ||
setErrorHeaders(error, reply) | ||
if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) { | ||
const statusCode = error.statusCode || error.status | ||
reply.code(statusCode >= 400 ? statusCode : 500) | ||
} | ||
if (reply.statusCode < 500) { | ||
reply.log.info( | ||
{ res: reply, err: error }, | ||
error && error.message | ||
) | ||
} else { | ||
reply.log.error( | ||
{ req: request, res: reply, err: error }, | ||
error && error.message | ||
) | ||
} | ||
reply.send(error) | ||
} | ||
|
||
function fallbackErrorHandler (error, reply, cb) { | ||
const res = reply.raw | ||
const statusCode = reply.statusCode | ||
let payload | ||
try { | ||
const serializerFn = getSchemaSerializer(reply.context, statusCode) | ||
payload = (serializerFn === false) | ||
? serializeError({ | ||
error: statusCodes[statusCode + ''], | ||
code: error.code, | ||
message: error.message, | ||
statusCode: statusCode | ||
}) | ||
: serializerFn(Object.create(error, { | ||
error: { value: statusCodes[statusCode + ''] }, | ||
message: { value: error.message }, | ||
statusCode: { value: statusCode } | ||
})) | ||
} catch (err) { | ||
// error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization | ||
reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed') | ||
reply.code(500) | ||
payload = serializeError({ | ||
error: statusCodes['500'], | ||
message: err.message, | ||
statusCode: 500 | ||
}) | ||
} | ||
|
||
reply[kReplyHeaders]['content-type'] = 'application/json; charset=utf-8' | ||
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload) | ||
|
||
reply[kReplySent] = true | ||
|
||
cb(reply, payload) | ||
} | ||
|
||
function buildErrorHandler (parent = rootErrorHandler, func) { | ||
if (!func) { | ||
return parent | ||
} | ||
|
||
const errorHandler = Object.create(parent) | ||
errorHandler.func = func | ||
return errorHandler | ||
} | ||
|
||
function setErrorHeaders (error, reply) { | ||
const res = reply.raw | ||
let statusCode = res.statusCode | ||
statusCode = (statusCode >= 400) ? statusCode : 500 | ||
// treat undefined and null as same | ||
if (error != null) { | ||
if (error.headers !== undefined) { | ||
reply.headers(error.headers) | ||
} | ||
if (error.status >= 400) { | ||
statusCode = error.status | ||
} else if (error.statusCode >= 400) { | ||
statusCode = error.statusCode | ||
} | ||
} | ||
res.statusCode = statusCode | ||
} | ||
|
||
module.exports = { | ||
buildErrorHandler, | ||
handleError | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.