mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
95 lines
2.9 KiB
CoffeeScript
95 lines
2.9 KiB
CoffeeScript
# Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting
|
|
# revival reconnections if things go astray. Internal class, not intended for direct user manipulation.
|
|
class ActionCable.ConnectionMonitor
|
|
@pollInterval:
|
|
min: 3
|
|
max: 30
|
|
|
|
@staleThreshold: 6 # Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
|
|
|
|
constructor: (@connection) ->
|
|
@reconnectAttempts = 0
|
|
|
|
start: ->
|
|
unless @isRunning()
|
|
@startedAt = now()
|
|
delete @stoppedAt
|
|
@startPolling()
|
|
document.addEventListener("visibilitychange", @visibilityDidChange)
|
|
ActionCable.log("ConnectionMonitor started. pollInterval = #{@getPollInterval()} ms")
|
|
|
|
stop: ->
|
|
if @isRunning()
|
|
@stoppedAt = now()
|
|
@stopPolling()
|
|
document.removeEventListener("visibilitychange", @visibilityDidChange)
|
|
ActionCable.log("ConnectionMonitor stopped")
|
|
|
|
isRunning: ->
|
|
@startedAt? and not @stoppedAt?
|
|
|
|
recordPing: ->
|
|
@pingedAt = now()
|
|
|
|
recordConnect: ->
|
|
@reconnectAttempts = 0
|
|
@recordPing()
|
|
delete @disconnectedAt
|
|
ActionCable.log("ConnectionMonitor recorded connect")
|
|
|
|
recordDisconnect: ->
|
|
@disconnectedAt = now()
|
|
ActionCable.log("ConnectionMonitor recorded disconnect")
|
|
|
|
# Private
|
|
|
|
startPolling: ->
|
|
@stopPolling()
|
|
@poll()
|
|
|
|
stopPolling: ->
|
|
clearTimeout(@pollTimeout)
|
|
|
|
poll: ->
|
|
@pollTimeout = setTimeout =>
|
|
@reconnectIfStale()
|
|
@poll()
|
|
, @getPollInterval()
|
|
|
|
getPollInterval: ->
|
|
{min, max} = @constructor.pollInterval
|
|
interval = 5 * Math.log(@reconnectAttempts + 1)
|
|
Math.round(clamp(interval, min, max) * 1000)
|
|
|
|
reconnectIfStale: ->
|
|
if @connectionIsStale()
|
|
ActionCable.log("ConnectionMonitor detected stale connection. reconnectAttempts = #{@reconnectAttempts}, pollInterval = #{@getPollInterval()} ms, time disconnected = #{secondsSince(@disconnectedAt)} s, stale threshold = #{@constructor.staleThreshold} s")
|
|
@reconnectAttempts++
|
|
if @disconnectedRecently()
|
|
ActionCable.log("ConnectionMonitor skipping reopening recent disconnect")
|
|
else
|
|
ActionCable.log("ConnectionMonitor reopening")
|
|
@connection.reopen()
|
|
|
|
connectionIsStale: ->
|
|
secondsSince(@pingedAt ? @startedAt) > @constructor.staleThreshold
|
|
|
|
disconnectedRecently: ->
|
|
@disconnectedAt and secondsSince(@disconnectedAt) < @constructor.staleThreshold
|
|
|
|
visibilityDidChange: =>
|
|
if document.visibilityState is "visible"
|
|
setTimeout =>
|
|
if @connectionIsStale() or not @connection.isOpen()
|
|
ActionCable.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = #{document.visibilityState}")
|
|
@connection.reopen()
|
|
, 200
|
|
|
|
now = ->
|
|
new Date().getTime()
|
|
|
|
secondsSince = (time) ->
|
|
(now() - time) / 1000
|
|
|
|
clamp = (number, min, max) ->
|
|
Math.max(min, Math.min(max, number))
|