Module: Strelka::WebSocketServer::Heartbeat

Extended by:
Loggability, Plugin
Includes:
Mongrel2::WebSocket::Constants, Constants
Defined in:
lib/strelka/websocketserver/heartbeat.rb

Overview

Heartbeat logic for Strelka WebSocketServers.

To ping connected clients, and disconnect them after 3 failed pings:

class ChatServer < Strelka::WebSocketServer
   plugin :heartbeat

   heartbeat_rate 5.0
   idle_timeout 15.0
end

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

DEFAULT_HEARTBEAT_RATE =

The default number of seconds between heartbeat events

5.0
DEFAULT_IDLE_TIMEOUT =

The default number of seconds between events before a client is disconnected

15.0

Instance Attribute Summary

Attributes included from Plugin

#pluggable, #successors

Instance Method Summary collapse

Methods included from Plugin

extended, plugin_name, run_inside, run_outside

Instance Method Details

#cull_idle_socketsObject

Disconnect any sockets that haven't sent any frames for at least SOCKET_IDLE_TIMEOUT seconds.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/strelka/websocketserver/heartbeat.rb', line 120

def cull_idle_sockets
  self.log.debug "Culling idle sockets."

  earliest = Time.now - self.class.idle_timeout

  self.connection_times.each do |sender_id, times|
    times.each do |conn_id, lastframe|
      next unless earliest > lastframe

      self.log.warn "Timing out connection %s:%d: last seen %0.3fs ago." %
        [ sender_id, conn_id, Time.now - lastframe ]

      # Make a CLOSE frame
      frame = Mongrel2::WebSocket::Frame.close
      frame.set_status( CLOSE_EXCEPTION )

      # Use the connection directly so we can send a frame and close the
      # connection
      self.conn.send( sender_id, conn_id, frame.to_s )
      self.conn.send_close( sender_id, conn_id )
    end
  end
end

#ping_all_socketsObject

Send a PING frame to all connected sockets.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/strelka/websocketserver/heartbeat.rb', line 146

def ping_all_sockets
  return if self.connections.empty?

  self.log.debug "Pinging %d connected sockets." % [ self.connections.length ]
  self.connections.each do |sender_id, conn_ids|
    frame = Mongrel2::WebSocket::Frame.new( sender_id, conn_id, '', {}, 'heartbeat' )
    frame.opcode = :ping
    frame.fin = true

    self.log.debug "  %s/%d: PING" % [ sender_id, conn_id ]
    self.conn.reply( frame )
  end

  self.log.debug "  done with pings."
end

#restartObject

Called by Mongrel2::Handler when the server is restarted. Overridden to restart the heartbeat thread.



86
87
88
89
90
# File 'lib/strelka/websocketserver/heartbeat.rb', line 86

def restart
  self.stop_heartbeat
  super
  self.start_heartbeat
end

#shutdownObject

Called by Mongrel2::Handler when the server is shut down. Overridden to stop the heartbeat thread.



95
96
97
98
# File 'lib/strelka/websocketserver/heartbeat.rb', line 95

def shutdown
  self.stop_heartbeat
  super
end

#start_accepting_requestsObject

Called by Mongrel2::Handler when it starts accepting requests. Overridden to start up the heartbeat thread.



78
79
80
81
# File 'lib/strelka/websocketserver/heartbeat.rb', line 78

def start_accepting_requests
  self.start_heartbeat
  super
end

#start_heartbeatObject

Start a thread that will periodically ping connected sockets and remove any connections that don't reply



103
104
105
106
107
108
109
# File 'lib/strelka/websocketserver/heartbeat.rb', line 103

def start_heartbeat
  self.log.info "Starting heartbeat timer."
  @heartbeat_timer = self.reactor.add_periodic_timer( self.class.heartbeat_rate ) do
    self.cull_idle_sockets
    self.ping_all_sockets
  end
end

#stop_heartbeatObject

Tell the heartbeat thread to exit.



113
114
115
# File 'lib/strelka/websocketserver/heartbeat.rb', line 113

def stop_heartbeat
  self.reactor.remove_timer( @heartbeat_timer )
end