app.coffee 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. Metrics = require("metrics-sharelatex")
  2. Settings = require "settings-sharelatex"
  3. Metrics.initialize(Settings.appName or "real-time")
  4. logger = require "logger-sharelatex"
  5. logger.initialize("real-time-sharelatex")
  6. Metrics.event_loop.monitor(logger)
  7. express = require("express")
  8. session = require("express-session")
  9. redis = require("redis-sharelatex")
  10. if Settings.sentry?.dsn?
  11. logger.initializeErrorReporting(Settings.sentry.dsn)
  12. sessionRedisClient = redis.createClient(Settings.redis.websessions)
  13. RedisStore = require('connect-redis')(session)
  14. SessionSockets = require('session.socket.io')
  15. CookieParser = require("cookie-parser")
  16. DrainManager = require("./app/js/DrainManager")
  17. # Set up socket.io server
  18. app = express()
  19. Metrics.injectMetricsRoute(app)
  20. server = require('http').createServer(app)
  21. io = require('socket.io').listen(server)
  22. # Bind to sessions
  23. sessionStore = new RedisStore(client: sessionRedisClient)
  24. cookieParser = CookieParser(Settings.security.sessionSecret)
  25. sessionSockets = new SessionSockets(io, sessionStore, cookieParser, Settings.cookieName)
  26. io.configure ->
  27. io.enable('browser client minification')
  28. io.enable('browser client etag')
  29. # Fix for Safari 5 error of "Error during WebSocket handshake: location mismatch"
  30. # See http://answers.dotcloud.com/question/578/problem-with-websocket-over-ssl-in-safari-with
  31. io.set('match origin protocol', true)
  32. # gzip uses a Node 0.8.x method of calling the gzip program which
  33. # doesn't work with 0.6.x
  34. #io.enable('browser client gzip')
  35. io.set('transports', ['websocket', 'flashsocket', 'htmlfile', 'xhr-polling', 'jsonp-polling'])
  36. io.set('log level', 1)
  37. app.get "/", (req, res, next) ->
  38. res.send "real-time-sharelatex is alive"
  39. app.get "/status", (req, res, next) ->
  40. res.send "real-time-sharelatex is alive"
  41. rclient = require("redis-sharelatex").createClient(Settings.redis.realtime)
  42. app.get "/health_check/redis", (req, res, next) ->
  43. rclient.healthCheck (error) ->
  44. if error?
  45. logger.err {err: error}, "failed redis health check"
  46. res.sendStatus 500
  47. else
  48. res.sendStatus 200
  49. Metrics.injectMetricsRoute(app)
  50. Router = require "./app/js/Router"
  51. Router.configure(app, io, sessionSockets)
  52. WebsocketLoadBalancer = require "./app/js/WebsocketLoadBalancer"
  53. WebsocketLoadBalancer.listenForEditorEvents(io)
  54. DocumentUpdaterController = require "./app/js/DocumentUpdaterController"
  55. DocumentUpdaterController.listenForUpdatesFromDocumentUpdater(io)
  56. port = Settings.internal.realTime.port
  57. host = Settings.internal.realTime.host
  58. server.listen port, host, (error) ->
  59. throw error if error?
  60. logger.info "realtime starting up, listening on #{host}:#{port}"
  61. # Stop huge stack traces in logs from all the socket.io parsing steps.
  62. Error.stackTraceLimit = 10
  63. shutdownCleanly = (signal) ->
  64. connectedClients = io.sockets.clients()?.length
  65. if connectedClients == 0
  66. logger.log("no clients connected, exiting")
  67. process.exit()
  68. else
  69. logger.log {connectedClients}, "clients still connected, not shutting down yet"
  70. setTimeout () ->
  71. shutdownCleanly(signal)
  72. , 10000
  73. forceDrain = ->
  74. logger.log {delay_ms:Settings.forceDrainMsDelay}, "starting force drain after timeout"
  75. setTimeout ()->
  76. logger.log "starting drain"
  77. DrainManager.startDrain(io, 4)
  78. , Settings.forceDrainMsDelay
  79. if Settings.forceDrainMsDelay?
  80. Settings.forceDrainMsDelay = parseInt(Settings.forceDrainMsDelay, 10)
  81. logger.log forceDrainMsDelay: Settings.forceDrainMsDelay,"forceDrainMsDelay enabled"
  82. for signal in ['SIGINT', 'SIGHUP', 'SIGQUIT', 'SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGABRT']
  83. process.on signal, ->
  84. logger.log signal: signal, "received interrupt, cleaning up"
  85. shutdownCleanly(signal)
  86. forceDrain()