lib/firehose/rack/ping.rb
require 'em-hiredis'
module Firehose
module Rack
# Allows the Firehose client to periodically "ping" the server
# so that the connection isn't timed out by browsers or proxies from
# inactivity.
class Ping
attr_reader :redis
def initialize(redis=nil)
@redis = redis
end
def call(env)
PingCheck.new(env, redis).call
ASYNC_RESPONSE
end
# Encapsulate this in a class so we aren't passing a bunch of variables around
class PingCheck
include Firehose::Rack::Helpers
attr_reader :req, :env, :key, :redis
TEST_VALUE = 'Firehose Healthcheck Test Value'
SECONDS_TO_EXPIRE = 60
def self.redis
@redis ||= Firehose::Server.redis.connection
end
def initialize(env, redis=nil)
@redis = redis || self.class.redis
@env = env
@req = env['parsed_request'] ||= ::Rack::Request.new(env)
@key = "/firehose/ping/#{Time.now.to_i}/#{rand}"
end
def call
log req, 'started'
test_redis
end
private
def log(req, msg)
Firehose.logger.debug "HTTP PING request for path '#{req.path}': #{msg}"
end
def test_redis
redis.set(key, TEST_VALUE).
callback { expire_key }.
callback { read_and_respond }.
errback do |e|
log req, "failed with write value to redis: #{e.inspect}"
env['async.callback'].call response(500)
end
end
def expire_key
redis.expire(key, SECONDS_TO_EXPIRE).
errback do
log req, "failed to expire key #{key.inspect}. If this key is not manually deleted, it may cause a memory leak."
end
end
def read_and_respond
redis.get(key).
callback do |val|
if val == TEST_VALUE
log req, 'succeeded'
env['async.callback'].call response(200)
else
log req, "failed with unexpected value retrieved from redis: #{val.inspect}"
env['async.callback'].call response(500)
end
end.
errback do |e|
log req, "failed with read value from redis: #{e.inspect}"
env['async.callback'].call response(500)
end
end
end
end
end
end