firehose 0.1.1 → 0.2.alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.env.sample +10 -0
- data/.gitignore +2 -0
- data/Procfile +1 -1
- data/README.md +117 -11
- data/config/rainbows.rb +20 -0
- data/firehose.gemspec +9 -6
- data/lib/assets/flash/firehose/WebSocketMain.swf +0 -0
- data/lib/assets/javascripts/firehose.js.coffee +4 -1
- data/lib/assets/javascripts/firehose/consumer.js.coffee +3 -11
- data/lib/assets/javascripts/firehose/lib/jquery.cors.headers.js.coffee +16 -0
- data/lib/assets/javascripts/firehose/lib/swfobject.js +4 -0
- data/lib/assets/javascripts/firehose/lib/web_socket.js +389 -0
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +42 -39
- data/lib/assets/javascripts/firehose/transport.js.coffee +1 -1
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +8 -14
- data/lib/firehose.rb +12 -17
- data/lib/firehose/channel.rb +84 -0
- data/lib/firehose/cli.rb +57 -13
- data/lib/firehose/client.rb +92 -0
- data/lib/firehose/default.rb +2 -2
- data/lib/firehose/logging.rb +35 -0
- data/lib/firehose/producer.rb +1 -0
- data/lib/firehose/publisher.rb +56 -4
- data/lib/firehose/rack.rb +37 -120
- data/lib/firehose/rack/consumer_app.rb +143 -0
- data/lib/firehose/rack/ping_app.rb +84 -0
- data/lib/firehose/rack/publisher_app.rb +40 -0
- data/lib/firehose/server.rb +48 -0
- data/lib/firehose/subscriber.rb +54 -0
- data/lib/firehose/swf_policy_request.rb +23 -0
- data/lib/firehose/version.rb +2 -2
- data/lib/rainbows_em_swf_policy.rb +33 -0
- data/lib/thin_em_swf_policy.rb +26 -0
- data/spec/integrations/integration_test_helper.rb +16 -0
- data/spec/integrations/rainbows_spec.rb +7 -0
- data/spec/integrations/shared_examples.rb +111 -0
- data/spec/integrations/thin_spec.rb +5 -79
- data/spec/lib/channel_spec.rb +164 -0
- data/spec/lib/client_spec.rb +9 -0
- data/spec/lib/default_spec.rb +2 -2
- data/spec/lib/publisher_spec.rb +82 -0
- data/spec/lib/rack/consumer_app_spec.rb +11 -0
- data/spec/lib/rack/ping_app_spec.rb +28 -0
- data/spec/lib/rack/publisher_app_spec.rb +26 -0
- data/spec/lib/subscriber_spec.rb +69 -0
- data/spec/spec_helper.rb +49 -8
- metadata +114 -45
- data/config.ru +0 -6
- data/lib/firehose/subscription.rb +0 -77
@@ -1,4 +1,6 @@
|
|
1
1
|
class Firehose.LongPoll extends Firehose.Transport
|
2
|
+
messageSequenceHeader: 'pragma'
|
3
|
+
|
2
4
|
# CORS is supported in IE 8+
|
3
5
|
@ieSupported: =>
|
4
6
|
$.browser.msie and parseInt($.browser.version) > 7 and window.XDomainRequest
|
@@ -15,56 +17,43 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
15
17
|
# Protocol schema we should use for talking to WS server.
|
16
18
|
@config.longPoll.url ||= "http:#{@config.uri}"
|
17
19
|
# How many ms should we wait before timing out the AJAX connection?
|
18
|
-
@config.longPoll.timeout ||=
|
20
|
+
@config.longPoll.timeout ||= 25000
|
19
21
|
|
20
22
|
# TODO - What is @_lagTime for? Can't we just use the @_timeout value?
|
21
23
|
# We use the lag time to make the client live longer than the server.
|
22
24
|
@_lagTime = 5000
|
23
25
|
@_timeout = @config.longPoll.timeout + @_lagTime
|
24
|
-
@_offlineTimer
|
25
26
|
@_okInterval = 0
|
26
27
|
|
27
|
-
@
|
28
|
-
|
29
|
-
registerIETransport: =>
|
30
|
-
if Firehose.LongPoll.ieSupported()
|
31
|
-
# TODO - Ask Steel what this is for. Looks like some kind of polygot fill, but I want
|
32
|
-
# to take the 'json' transport out and do that myself.
|
33
|
-
$.ajaxTransport 'json', (options, orignalOptions, jqXhr) ->
|
34
|
-
xdr = null
|
35
|
-
send: (_, callback) ->
|
36
|
-
xdr = new XDomainRequest()
|
37
|
-
xdr.onload = ->
|
38
|
-
statusCode = if xdr.responseText.length > 0 then 200 else 204
|
39
|
-
callback(statusCode, 'success', text: xdr.responseText)
|
40
|
-
|
41
|
-
xdr.onerror = xdr.ontimeout = ->
|
42
|
-
callback(400, 'failed', text: xdr.responseText)
|
43
|
-
|
44
|
-
xdr.open(options.type, options.url)
|
45
|
-
xdr.send(options.data)
|
46
|
-
|
47
|
-
abort: ->
|
48
|
-
if xdr
|
49
|
-
xdr.onerror = $.noop()
|
50
|
-
xdr.abort()
|
51
|
-
|
52
|
-
# also, override the support check
|
53
|
-
$.support.cors = true;
|
54
|
-
|
28
|
+
@_isConnected = false
|
29
|
+
|
55
30
|
connect: (delay = 0) =>
|
56
|
-
@
|
31
|
+
unless @_isConnected
|
32
|
+
@_isConnected = true
|
33
|
+
@config.connected()
|
57
34
|
super(delay)
|
58
35
|
|
59
36
|
_request: =>
|
37
|
+
# Set the Last Message Sequence in a query string.
|
38
|
+
# Ideally we'd use an HTTP header, but android devices don't let us
|
39
|
+
# set any HTTP headers for CORS requests.
|
40
|
+
data = @config.params
|
41
|
+
data.last_message_sequence = @_lastMessageSequence
|
42
|
+
# TODO: Some of these options will be deprecated in jQurey 1.8
|
43
|
+
# See: http://api.jquery.com/jQuery.ajax/#jqXHR
|
60
44
|
$.ajax @config.longPoll.url,
|
61
45
|
crossDomain: true
|
62
|
-
|
63
|
-
data: @config.params
|
46
|
+
data: data
|
64
47
|
timeout: @_timeout
|
65
48
|
success: @_success
|
66
49
|
error: @_error
|
67
|
-
|
50
|
+
complete: (jqXhr) =>
|
51
|
+
# Get the last sequence from the server if specified.
|
52
|
+
if jqXhr.status == 200
|
53
|
+
@_lastMessageSequence = jqXhr.getResponseHeader(@messageSequenceHeader)
|
54
|
+
if @_lastMessageSequence == null
|
55
|
+
console.log 'ERROR: Unable to get last message sequnce from header'
|
56
|
+
|
68
57
|
_success: (data, status, jqXhr) =>
|
69
58
|
# TODO we actually want to do this when the thing calls out... mmm right now it takes
|
70
59
|
# up to 30s before we can call this thing.
|
@@ -76,16 +65,30 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
76
65
|
#
|
77
66
|
# Why did we use a 204 and not a 408? Because FireFox is really stupid about 400 level error
|
78
67
|
# codes and would claims its a 0 error code, which we use for something else. Firefox is IE
|
79
|
-
# in
|
68
|
+
# in this case
|
80
69
|
@connect(@_okInterval)
|
81
70
|
else
|
82
|
-
@config.message(@config.parse(
|
71
|
+
@config.message(@config.parse(jqXhr.responseText))
|
83
72
|
@connect(@_okInterval)
|
84
73
|
|
74
|
+
_ping: =>
|
75
|
+
# Ping long poll server to verify internet connectivity
|
76
|
+
# jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
|
77
|
+
# directly so we can't manually set a timeout.
|
78
|
+
$.ajax @config.longPoll.url,
|
79
|
+
method: 'HEAD'
|
80
|
+
crossDomain: true
|
81
|
+
data: @config.params
|
82
|
+
success: @config.connected
|
83
|
+
|
85
84
|
# We need this custom handler to have the connection status
|
86
85
|
# properly displayed
|
87
86
|
_error: (jqXhr, status, error) =>
|
88
|
-
|
87
|
+
@_isConnected = false
|
89
88
|
@config.disconnected()
|
90
|
-
|
91
|
-
|
89
|
+
|
90
|
+
# Ping the server to make sure this isn't a network connectivity error
|
91
|
+
setTimeout @_ping, @_retryDelay + @_lagTime
|
92
|
+
|
93
|
+
# Reconnect with delay
|
94
|
+
setTimeout @_request, @_retryDelay
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# Unfortunately this needs to a global variable. The only other option is to
|
2
|
+
# hack into the internals of the web_socket.js plugin we are using.
|
3
|
+
window.WEB_SOCKET_SWF_LOCATION = '/assets/firehose/WebSocketMain.swf' if !window.WEB_SOCKET_SWF_LOCATION
|
4
4
|
|
5
|
+
class Firehose.WebSocket extends Firehose.Transport
|
5
6
|
@supported: =>
|
6
7
|
# Compatibility reference: http://caniuse.com/websockets
|
7
|
-
#
|
8
|
-
|
8
|
+
# We don't need to explicitly check for Flash web socket or MozWebSocket
|
9
|
+
# because web_socket.js has already handled that.
|
10
|
+
!!(window.WebSocket)
|
9
11
|
|
10
12
|
constructor: (args) ->
|
11
13
|
super args
|
@@ -14,14 +16,6 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
14
16
|
@config.webSocket ||= {}
|
15
17
|
# Protocol schema we should use for talking to WS server.
|
16
18
|
@config.webSocket.url ||= "ws:#{@config.uri}?#{$.param(@config.params)}"
|
17
|
-
# Path of the swf WebSocket that we use in non-WS flash browsers.
|
18
|
-
@config.webSocket.swf_path ||= "/flash/firehose/WebSocketMain.swf"
|
19
|
-
|
20
|
-
# Set flash socket path for the WS SWF polyfill.
|
21
|
-
WebSocket.__swfLocation = @config.webSocket.swf_path
|
22
|
-
|
23
|
-
# Mozilla decided to have their own implementation of Web Sockets so detect for that.
|
24
|
-
window.WebSocket = window.MozWebSocket if window["MozWebSocket"] and window.MozWebSocket
|
25
19
|
|
26
20
|
_request: =>
|
27
21
|
@socket = new window.WebSocket(@config.webSocket.url)
|
@@ -49,4 +43,4 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
49
43
|
@socket.close()
|
50
44
|
delete(@socket)
|
51
45
|
|
52
|
-
super
|
46
|
+
super
|
data/lib/firehose.rb
CHANGED
@@ -1,24 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'em-hiredis'
|
4
|
-
require 'logger'
|
1
|
+
ENV['RACK_ENV'] ||= 'development'
|
5
2
|
|
6
|
-
require 'firehose/
|
3
|
+
require 'firehose/version'
|
4
|
+
require 'em-hiredis' # TODO Move this into a Redis module so that we can auto-load it. Lots of the CLI tools don't need this.
|
5
|
+
require 'firehose/logging'
|
6
|
+
require 'firehose/rails' if defined?(::Rails::Engine) # TODO Detect Sprockets instead of the jankin Rails::Engine test.
|
7
7
|
|
8
8
|
module Firehose
|
9
|
-
autoload :
|
9
|
+
autoload :Subscriber, 'firehose/subscriber'
|
10
10
|
autoload :Publisher, 'firehose/publisher'
|
11
|
-
autoload :Producer, 'firehose/producer'
|
11
|
+
autoload :Producer, 'firehose/producer' # TODO Move this into the Firehose::Client namespace.
|
12
12
|
autoload :Default, 'firehose/default'
|
13
13
|
autoload :Rack, 'firehose/rack'
|
14
14
|
autoload :CLI, 'firehose/cli'
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def self.logger=(logger)
|
22
|
-
@logger = logger
|
23
|
-
end
|
24
|
-
end
|
15
|
+
autoload :Client, 'firehose/client'
|
16
|
+
autoload :Server, 'firehose/server'
|
17
|
+
autoload :Channel, 'firehose/channel'
|
18
|
+
autoload :SwfPolicyRequest, 'firehose/swf_policy_request'
|
19
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Firehose
|
2
|
+
class Channel
|
3
|
+
attr_reader :channel_key, :redis, :subscriber, :list_key, :sequence_key
|
4
|
+
|
5
|
+
def self.redis
|
6
|
+
@redis ||= EM::Hiredis.connect
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.subscriber
|
10
|
+
@subscriber ||= Subscriber.new(EM::Hiredis.connect)
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(channel_key, redis=self.class.redis, subscriber=self.class.subscriber)
|
15
|
+
@channel_key, @redis, @subscriber = channel_key, redis, subscriber
|
16
|
+
|
17
|
+
@list_key, @sequence_key = key(channel_key, :list), key(channel_key, :sequence)
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_message(last_sequence=nil, options={})
|
21
|
+
last_sequence = last_sequence.to_i
|
22
|
+
|
23
|
+
deferrable = EM::DefaultDeferrable.new
|
24
|
+
# TODO - Think this through a little harder... maybe some tests ol buddy!
|
25
|
+
deferrable.errback {|e| EM.next_tick { raise e } unless [:timeout, :disconnect].include?(e) }
|
26
|
+
|
27
|
+
|
28
|
+
# TODO: Use HSET so we don't have to pull 100 messages back every time.
|
29
|
+
redis.multi
|
30
|
+
redis.get(sequence_key).
|
31
|
+
errback {|e| deferrable.fail e }
|
32
|
+
redis.lrange(list_key, 0, Firehose::Publisher::MAX_MESSAGES).
|
33
|
+
errback {|e| deferrable.fail e }
|
34
|
+
redis.exec.callback do |(sequence, message_list)|
|
35
|
+
Firehose.logger.debug "exec returened: `#{sequence}` and `#{message_list.inspect}`"
|
36
|
+
sequence = sequence.to_i
|
37
|
+
|
38
|
+
if sequence.nil? || (diff = sequence - last_sequence) <= 0
|
39
|
+
Firehose.logger.debug "No message available yet, subscribing. sequence: `#{sequence}`"
|
40
|
+
# Either this resource has never been seen before or we are all caught up.
|
41
|
+
# Subscribe and hope something gets published to this end-point.
|
42
|
+
subscribe(deferrable, options[:timeout])
|
43
|
+
elsif last_sequence > 0 && diff < Firehose::Publisher::MAX_MESSAGES
|
44
|
+
# The client is kinda-sorta running behind, but has a chance to catch
|
45
|
+
# up. Catch them up FTW.
|
46
|
+
# But we won't "catch them up" if last_sequence was zero/nil because
|
47
|
+
# that implies the client is connecting for the 1st time.
|
48
|
+
message = message_list[diff-1]
|
49
|
+
Firehose.logger.debug "Sending old message `#{message}` and sequence `#{sequence}` to client directly. Client is `#{diff}` behind, at `#{last_sequence}`."
|
50
|
+
deferrable.succeed message, last_sequence + 1
|
51
|
+
else
|
52
|
+
# The client is hopelessly behind and underwater. Just reset
|
53
|
+
# their whole world with the lastest message.
|
54
|
+
message = message_list[0]
|
55
|
+
Firehose.logger.debug "Sending latest message `#{message}` and sequence `#{sequence}` to client directly."
|
56
|
+
deferrable.succeed message, sequence
|
57
|
+
end
|
58
|
+
end.errback {|e| deferrable.fail e }
|
59
|
+
|
60
|
+
deferrable
|
61
|
+
end
|
62
|
+
|
63
|
+
def unsubscribe(deferrable)
|
64
|
+
subscriber.unsubscribe channel_key, deferrable
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def key(*segments)
|
69
|
+
segments.unshift(:firehose).join(':')
|
70
|
+
end
|
71
|
+
|
72
|
+
def subscribe(deferrable, timeout=nil)
|
73
|
+
subscriber.subscribe(channel_key, deferrable)
|
74
|
+
if timeout
|
75
|
+
timer = EventMachine::Timer.new(timeout) do
|
76
|
+
deferrable.fail :timeout
|
77
|
+
unsubscribe deferrable
|
78
|
+
end
|
79
|
+
# Cancel the timer if when the deferrable succeeds
|
80
|
+
deferrable.callback { timer.cancel }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/firehose/cli.rb
CHANGED
@@ -1,28 +1,72 @@
|
|
1
1
|
require 'thor'
|
2
|
-
require '
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
# Enable native
|
6
|
+
EM.kqueue if EM.kqueue?
|
7
|
+
EM.epoll if EM.epoll?
|
3
8
|
|
4
9
|
module Firehose
|
5
10
|
class CLI < Thor
|
6
|
-
|
11
|
+
def initialize(*args)
|
12
|
+
super
|
13
|
+
# Disable buffering to $stdio for Firehose.logger
|
14
|
+
$stdout.sync = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "version", "Display the current version."
|
7
18
|
def version
|
8
19
|
puts %[Firehose #{Firehose::VERSION} "#{Firehose::CODENAME}"]
|
9
20
|
end
|
10
21
|
|
11
|
-
desc "server", "
|
12
|
-
method_option :port,
|
13
|
-
method_option :host,
|
14
|
-
|
22
|
+
desc "server", "Start an instance of a server."
|
23
|
+
method_option :port, :type => :numeric, :default => ENV['PORT'] || Firehose::Default::URI.port, :required => false, :aliases => '-p'
|
24
|
+
method_option :host, :type => :string, :default => ENV['HOST'] || Firehose::Default::URI.host, :required => false, :aliases => '-h'
|
25
|
+
method_option :server, :type => :string, :default => ENV['SERVER'] ||'rainbows', :required => false, :aliases => '-s'
|
15
26
|
def server
|
16
|
-
server = Thin::Server.new(options[:host], options[:port]) do
|
17
|
-
run Firehose::Rack::App.new
|
18
|
-
end
|
19
|
-
|
20
27
|
begin
|
21
|
-
|
22
|
-
rescue
|
28
|
+
Firehose::Server.new(options).start
|
29
|
+
rescue => e
|
23
30
|
Firehose.logger.error "#{e.message}: #{e.backtrace}"
|
24
|
-
raise
|
31
|
+
raise e
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "consume URI", "Consume messages from a resource."
|
36
|
+
method_option :concurrency, :type => :numeric, :default => 1, :aliases => '-c'
|
37
|
+
def consume(uri)
|
38
|
+
EM.run do
|
39
|
+
options[:concurrency].times { Firehose::Client::Consumer.parse(uri).request }
|
25
40
|
end
|
26
41
|
end
|
42
|
+
|
43
|
+
desc "publish URI [PAYLOAD]", "Publish messages to a resource."
|
44
|
+
method_option :interval, :type => :numeric, :aliases => '-i'
|
45
|
+
method_option :times, :type => :numeric, :aliases => '-n'
|
46
|
+
def publish(uri, payload=nil)
|
47
|
+
payload ||= $stdin.read
|
48
|
+
client = Firehose::Producer.new(uri)
|
49
|
+
path = URI.parse(uri).path
|
50
|
+
times = options[:times]
|
51
|
+
|
52
|
+
EM.run do
|
53
|
+
# TODO I think this can be cleaned up so the top-level if/else can be ditched.
|
54
|
+
if interval = options[:interval]
|
55
|
+
# Publish messages at a forced interval.
|
56
|
+
EM.add_periodic_timer interval do
|
57
|
+
client.publish(payload).to(path)
|
58
|
+
EM.stop if times && (times-=1).zero?
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# Publish messages as soon as the last message was published.
|
62
|
+
worker = Proc.new do
|
63
|
+
client.publish(payload).to(path)
|
64
|
+
times && (times-=1).zero? ? EM.stop : worker.call
|
65
|
+
end
|
66
|
+
worker.call
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
27
71
|
end
|
28
72
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'em-http'
|
2
|
+
require 'faye/websocket'
|
3
|
+
|
4
|
+
module Firehose
|
5
|
+
# Ruby clients that connect to Firehose to either publish or consume messages.
|
6
|
+
module Client
|
7
|
+
# TODO - Move the Firehose producer.rb file/class in here and rename to Firehose::Client::Producer::Http.new() ..
|
8
|
+
module Producer
|
9
|
+
end
|
10
|
+
|
11
|
+
# TODO - Test this libs. I had to throw these quickly into our app so that we could get
|
12
|
+
# some stress testing out of the way.
|
13
|
+
# TODO - Replace the integration test clients with these guys. You'll want to refactor each
|
14
|
+
# transport to use on(:message), on(:conncect), and on(:disconnect) callbacks.
|
15
|
+
module Consumer
|
16
|
+
TransportNotSupportedError = Class.new(RuntimeError)
|
17
|
+
|
18
|
+
# Build up a benchmark client based on a given URI. Accepts ws:// and http:// for now.
|
19
|
+
def self.parse(uri)
|
20
|
+
case transport = URI.parse(uri).scheme
|
21
|
+
when 'ws'
|
22
|
+
Consumer::WebSocket.new(uri)
|
23
|
+
when 'http'
|
24
|
+
Consumer::HttpLongPoll.new(uri)
|
25
|
+
else
|
26
|
+
raise TransportNotSupportedError.new("Transport #{transport.inspect} not supported.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Connect to Firehose via WebSockets and consume messages.
|
31
|
+
class WebSocket
|
32
|
+
attr_reader :url, :logger
|
33
|
+
|
34
|
+
def initialize(url, logger = Firehose.logger)
|
35
|
+
@url, @logger = url, logger
|
36
|
+
end
|
37
|
+
|
38
|
+
def request
|
39
|
+
ws = Faye::WebSocket::Client.new(url)
|
40
|
+
ws.onmessage = lambda do |event|
|
41
|
+
logger.info "WS | #{event.data[0...40].inspect}"
|
42
|
+
end
|
43
|
+
ws.onclose = lambda do |event|
|
44
|
+
logger.info "WS | Closed"
|
45
|
+
end
|
46
|
+
ws.onerror do
|
47
|
+
logger.error "WS | Failed"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Connect to Firehose via HTTP Long Polling and consume messages.
|
53
|
+
class HttpLongPoll
|
54
|
+
JITTER = 0.003
|
55
|
+
|
56
|
+
attr_reader :url, :logger
|
57
|
+
|
58
|
+
def initialize(url, logger = Firehose.logger)
|
59
|
+
@url, @logger = url, logger
|
60
|
+
end
|
61
|
+
|
62
|
+
def request(last_sequence=0)
|
63
|
+
http = EM::HttpRequest.new(url, :inactivity_timeout => 0).get(:query => {'last_message_sequence' => last_sequence})
|
64
|
+
http.callback do
|
65
|
+
case status = http.response_header.status
|
66
|
+
when 200
|
67
|
+
next_sequence = http.response_header['Pragma'].to_i
|
68
|
+
logger.info "HTTP 200 | Next Sequence: #{next_sequence} - #{http.response[0...40].inspect}"
|
69
|
+
EM::add_timer(jitter) { request next_sequence }
|
70
|
+
when 204
|
71
|
+
logger.info "HTTP 204 | Last Sequence #{last_sequence}"
|
72
|
+
EM::add_timer(jitter) { request last_sequence }
|
73
|
+
else
|
74
|
+
logger.error "HTTP #{status} | Failed"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
http.errback do
|
78
|
+
logger.error "Connection Failed"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
private
|
84
|
+
# Random jitter between long poll requests.
|
85
|
+
def jitter
|
86
|
+
rand*JITTER
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|