Last active
September 19, 2019 16:08
-
-
Save zemccartney/c53c839e231930542203693c3a955133 to your computer and use it in GitHub Desktop.
Very rough crash-only hapi server
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'; | |
// Adapted from Exiting README example: https://github.com/kanongil/exiting | |
const Boom = require('@hapi/boom'); | |
const Exiting = require('exiting'); | |
const Hapi = require('@hapi/hapi'); | |
const server = Hapi.Server(); | |
const manager = Exiting.createManager(server); | |
server.events.on('stop', () => { | |
console.log('Server stopped.'); | |
}); | |
const provision = async () => { | |
server.ext('onPreResponse', async (request, h) => { | |
// We're trying to express: if the current response represents a 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) | |
// TODO Should we stop gracefully on this condition? Or is crashing hard, regardless of other clients, | |
// the safest option (insofar as it mitigates the risk of other clients immediately encountering this bug) | |
if (request.response.isBoom && request.response.isDeveloperError) { | |
console.error('Programmer Error detected; crashing server'); | |
console.error(request.response); | |
// Reply with the error while stopping the server in the background | |
// TODO Does this create an unpredictable race, potentially leaving server in an undefined state? Should we await manager.stop(); | |
manager.stop(); | |
return h.continue; | |
// TODO What does this behavior mean for the web clients? | |
// https://www.tjvantoll.com/2015/09/13/fetch-and-errors/ | |
// At least for fetch, would get to catch block. Not sure about axios, request, etc. | |
} | |
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 yields 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? Am I just confused? | |
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': | |
// triggers a ReferenceError | |
console.log('programmer error will crash; desired behavior'); | |
return request.bugs.nugs; | |
default: | |
return 'success'; | |
} | |
} | |
} | |
}); | |
await manager.start(); | |
console.log('Server started at:', server.info.uri); | |
}; | |
provision(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment