blob: 0cc675fa94c6e001b3021c8da0c21c353c90f624 (
plain) (
tree)
|
|
# 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))
|