aboutsummaryrefslogblamecommitdiffstats
path: root/actioncable/app/assets/javascripts/action_cable/connection_monitor.coffee
blob: 740e86643e30f7cf74a77f551eb472386173981d (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                                                                                        
                                   

                

           

                                                                                
                             
            



                     
                          
                                                  
 
                  
                           
 
          


                     
                          
                                 





                      
                                                                       
                                                                                     
 

                      
                                                                          
                                                

          
                 



                           
 
                 

                                                   
                                    
 

                           
                                                                                                               
                          
                                
                                                                                                                
          
                                                      
                                     
 
                       



                                                                                   
 



                                                                    
                                                                                                                               


                                       

                        





                                        
# 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: (@consumer) ->
    @start()

  connected: ->
    @reset()
    @pingedAt = now()
    delete @disconnectedAt
    ActionCable.log("ConnectionMonitor connected")

  disconnected: ->
    @disconnectedAt = now()

  ping: ->
    @pingedAt = now()

  reset: ->
    @reconnectAttempts = 0
    @consumer.connection.isOpen()

  start: ->
    @reset()
    delete @stoppedAt
    @startedAt = now()
    @poll()
    document.addEventListener("visibilitychange", @visibilityDidChange)
    ActionCable.log("ConnectionMonitor started, pollInterval is #{@getInterval()}ms")

  stop: ->
    @stoppedAt = now()
    document.removeEventListener("visibilitychange", @visibilityDidChange)
    ActionCable.log("ConnectionMonitor stopped")

  poll: ->
    setTimeout =>
      unless @stoppedAt
        @reconnectIfStale()
        @poll()
    , @getInterval()

  getInterval: ->
    {min, max} = @constructor.pollInterval
    interval = 5 * Math.log(@reconnectAttempts + 1)
    clamp(interval, min, max) * 1000

  reconnectIfStale: ->
    if @connectionIsStale()
      ActionCable.log("ConnectionMonitor detected stale connection, reconnectAttempts = #{@reconnectAttempts}")
      @reconnectAttempts++
      if @disconnectedRecently()
        ActionCable.log("ConnectionMonitor skipping reopen because recently disconnected at #{@disconnectedAt}")
      else
        ActionCable.log("ConnectionMonitor reopening")
        @consumer.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 @consumer.connection.isOpen()
          ActionCable.log("ConnectionMonitor reopening stale connection after visibilitychange to #{document.visibilityState}")
          @consumer.connection.reopen()
      , 200

  now = ->
    new Date().getTime()

  secondsSince = (time) ->
    (now() - time) / 1000

  clamp = (number, min, max) ->
    Math.max(min, Math.min(max, number))