Last active
September 19, 2019 12:52
-
-
Save zemccartney/4dd02ce584b444b043b82bbc3b5964bc to your computer and use it in GitHub Desktop.
hapi crash-only
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
'use strict'; | |
/* | |
Inspired by ideas in: | |
- https://www.joyent.com/node-js/production/design/errors | |
- https://www.usenix.org/legacy/events/hotos03/tech/full_papers/candea/candea.pdf | |
*/ | |
const Boom = require('@hapi/boom'); | |
// https://github.com/kanongil/exiting | |
// Not strictly necessary, but used | |
// 1.) to try out what looks like an interesting tool | |
// 2.) abstracts away the variety of exit triggers, hopefully simplifies graceful shutdown | |
const Exiting = require('exiting'); | |
const Glue = require('@hapi/glue'); | |
const Manifest = require('./manifest'); | |
exports.deployment = async (start) => { | |
const manifest = Manifest.get('/'); | |
const server = await Glue.compose(manifest, { relativeTo: __dirname }); | |
await server.initialize(); | |
if (!start) { | |
return server; | |
} | |
server.events.on('stop', () => { | |
console.log('Server stopped.'); | |
}); | |
const manager = Exiting.createManager(server); | |
server.ext('onPreResponse', async (request, h) => { | |
// We're trying to express: if the current response represents | |
// an encountered bug, then (gracefully) shutdown the server | |
// In production, we'd rely on pm2 (or some other tooling) | |
// to reboot the server | |
// We crash to: | |
// - guarantee that any hanging/leaked resources stemming from the bug | |
// are closed | |
// - hopefully simplify server's fault model to simplify debugging: | |
// -- crashing = bug (programmer's responsibility to fix) | |
// -- responding = success or operational error | |
// no ambiguity in channels; unrecoverable errors never sent back to the requesting client | |
// crashing hopefully provides a simpler hook to capture and somehow report | |
// bugs to maintainers | |
if (request.response.isBoom && request.response.isDeveloperError) { | |
console.error('Programmer Error detected; crashing server'); | |
console.error(request.response); | |
await manager.stop(); | |
// TODO What does this mean for the web client? | |
// https://www.tjvantoll.com/2015/09/13/fetch-and-errors/ | |
// At least for fetch, would get to catch block | |
// Not sure about axios or others | |
} | |
// TODO If some sort of 5xx error, configure Retry-After headers? | |
return h.continue; | |
}); | |
server.route({ | |
method: 'get', | |
path: '/test/{trigger?}', | |
options: { | |
handler: (request, h) => { | |
const { trigger } = request.params; | |
switch (trigger) { | |
case 'throw': | |
console.log('throwing will not crash; desired behavior'); | |
// hapi treats these as plain errors (just calls boomify) | |
// I assume so hapi doesn't assume anything about user's error handling | |
// conventions; user might intend to throw Errors i.e. not use Boom | |
// see: https://github.com/hapijs/hapi/blob/7a5d2820ccf7f2b82785982cb35a5d0de9a5831e/lib/toolkit.js#L47-L57 | |
throw new Error('regular throw'); | |
case 'boom': | |
console.log('regular boom error will not crash; desired behavior'); | |
// returning or throwing yield same result | |
throw Boom.notFound('Not found'); | |
case 'internal': | |
console.log('standard internal error will not crash; ambiguous?'); | |
// ambiguous b/c Boom.internal does NOT set isDeveloperError, | |
// even though Boom docs refer to internal as an alias of | |
// badImplementation, which does set err.isDeveloperError | |
// Why? | |
throw Boom.internal('BAD!'); | |
case 'impl': | |
console.log('standard bad implementation error will crash; desired behavior'); | |
throw Boom.badImplementation(''); | |
case 'why': | |
console.log('throwing nonerror will crash the server'); | |
// https://github.com/hapijs/hapi/blob/7a5d2820ccf7f2b82785982cb35a5d0de9a5831e/lib/toolkit.js#L51-L52 | |
// Illustrates that programmer errors can be system errors (as defined by Boom: https://github.com/hapijs/bounce#issystemerrs) | |
// or misuse of hapi; either case represents an unrecoverable error (bug) caused by the programmer | |
throw 'thx cinco'; | |
case 'bug': | |
console.log('programmer error will crash; desired behavior'); | |
return request.duggs.nuggs; | |
default: | |
return 'success'; | |
} | |
} | |
} | |
}); | |
//await server.start(); | |
await manager.start(); | |
console.log(`Server started at ${server.info.uri}`); | |
//return server; | |
return manager; | |
}; | |
if (!module.parent) { | |
exports.deployment(true); | |
/* | |
Commented out b/c Exiting sets its own unhandledRejection handler | |
process.on('unhandledRejection', (err) => { | |
throw err; | |
}); | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Issues not addressed here: