ExceptionHandler.js 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. const Metrics = require('metrics-sharelatex')
  2. const logger = require('logger-sharelatex')
  3. // TODO: domain has been deprecated for some time - do we need it and is there a better way?
  4. // eslint-disable-next-line node/no-deprecated-api
  5. const domain = require('domain')
  6. const TWO_MINUTES = 120 * 1000
  7. class ExceptionHandler {
  8. constructor() {
  9. this._appIsOk = true
  10. }
  11. beginShutdown() {
  12. if (this._appIsOk) {
  13. this._appIsOk = false
  14. // hard-terminate this process if graceful shutdown fails
  15. const killTimer = setTimeout(() => process.exit(1), TWO_MINUTES)
  16. if (typeof killTimer.unref === 'function') {
  17. killTimer.unref()
  18. } // prevent timer from keeping process alive
  19. this.server.close(function() {
  20. logger.log('closed all connections')
  21. Metrics.close()
  22. if (typeof process.disconnect === 'function') {
  23. process.disconnect()
  24. }
  25. })
  26. logger.log('server will stop accepting connections')
  27. }
  28. }
  29. addMiddleware(app) {
  30. app.use(this.middleware.bind(this))
  31. }
  32. appIsOk() {
  33. return this._appIsOk
  34. }
  35. setNotOk() {
  36. this._appIsOk = false
  37. }
  38. middleware(req, res, next) {
  39. const rescueLogger = require('logger-sharelatex')
  40. const requestDomain = domain.create()
  41. requestDomain.add(req)
  42. requestDomain.add(res)
  43. requestDomain.on('error', err => {
  44. try {
  45. // request a shutdown to prevent memory leaks
  46. this.beginShutdown()
  47. if (!res.headerSent) {
  48. res.send('uncaught exception').status(500)
  49. }
  50. req = {
  51. body: req.body,
  52. headers: req.headers,
  53. url: req.url,
  54. key: req.key,
  55. statusCode: req.statusCode
  56. }
  57. err = {
  58. message: err.message,
  59. stack: err.stack,
  60. name: err.name,
  61. type: err.type,
  62. arguments: err.arguments
  63. }
  64. rescueLogger.err(
  65. { err, req, res },
  66. 'uncaught exception thrown on request'
  67. )
  68. } catch (exception) {
  69. rescueLogger.err(
  70. { err: exception },
  71. 'exception in request domain handler'
  72. )
  73. }
  74. })
  75. if (!this._appIsOk) {
  76. // when shutting down, close any HTTP keep-alive connections
  77. res.set('Connection', 'close')
  78. }
  79. requestDomain.run(next)
  80. }
  81. }
  82. module.exports = ExceptionHandler