firehose 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/assets/javascripts/firehose/consumer.js.coffee +4 -1
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +22 -20
- data/lib/assets/javascripts/firehose/transport.js.coffee +7 -3
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +23 -9
- data/lib/firehose/rack/consumer_app.rb +47 -52
- data/lib/firehose/version.rb +2 -2
- metadata +2 -2
@@ -22,22 +22,25 @@ class Firehose.Consumer
|
|
22
22
|
this
|
23
23
|
|
24
24
|
connect: (delay=0) =>
|
25
|
+
@config.connectionVerified = @_upgradeTransport
|
25
26
|
if Firehose.WebSocket.supported()
|
26
27
|
@upgradeTimeout = setTimeout =>
|
27
|
-
@config.connectionVerified = @_upgradeTransport
|
28
28
|
ws = new Firehose.WebSocket @config
|
29
29
|
ws.connect delay
|
30
30
|
, 500
|
31
31
|
@transport = new Firehose.LongPoll @config
|
32
32
|
@transport.connect delay
|
33
|
+
return
|
33
34
|
|
34
35
|
stop: =>
|
35
36
|
if @upgradeTimeout?
|
36
37
|
clearTimeout @upgradeTimeout
|
37
38
|
@upgradeTimeout = null
|
38
39
|
@transport.stop()
|
40
|
+
return
|
39
41
|
|
40
42
|
_upgradeTransport: (ws) =>
|
41
43
|
@transport.stop()
|
42
44
|
ws.sendStartingMessageSequence @transport.getLastMessageSequence()
|
43
45
|
@transport = ws
|
46
|
+
return
|
@@ -26,18 +26,8 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
26
26
|
@_lagTime = 5000
|
27
27
|
@_timeout = @config.longPoll.timeout + @_lagTime
|
28
28
|
@_okInterval = 0
|
29
|
-
@_isConnected = false
|
30
29
|
@_stopRequestLoop = false
|
31
30
|
|
32
|
-
connect: (delay = 0) =>
|
33
|
-
unless @_isConnected
|
34
|
-
@_isConnected = true
|
35
|
-
@config.connected()
|
36
|
-
super(delay)
|
37
|
-
|
38
|
-
getLastMessageSequence: =>
|
39
|
-
@_lastMessageSequence or 0
|
40
|
-
|
41
31
|
_request: =>
|
42
32
|
return if @_stopRequestLoop
|
43
33
|
# Set the Last Message Sequence in a query string.
|
@@ -47,7 +37,7 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
47
37
|
data.last_message_sequence = @_lastMessageSequence
|
48
38
|
# TODO: Some of these options will be deprecated in jQuery 1.8
|
49
39
|
# See: http://api.jquery.com/jQuery.ajax/#jqXHR
|
50
|
-
$.ajax
|
40
|
+
@_lastRequest = $.ajax
|
51
41
|
url: @config.longPoll.url
|
52
42
|
crossDomain: true
|
53
43
|
data: data
|
@@ -58,9 +48,17 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
58
48
|
|
59
49
|
stop: =>
|
60
50
|
@_stopRequestLoop = true
|
51
|
+
if @_lastRequest?
|
52
|
+
try @_lastRequest.abort() catch e
|
53
|
+
delete @_lastRequest
|
54
|
+
if @_lastPingRequest?
|
55
|
+
try @_lastPingRequest.abort() catch e
|
56
|
+
delete @_lastPingRequest
|
61
57
|
|
62
58
|
_success: (data, status, jqXhr) =>
|
63
|
-
@
|
59
|
+
if @_needToNotifyOfReconnect or not @_succeeded
|
60
|
+
@_needToNotifyOfReconnect = false
|
61
|
+
@_open data
|
64
62
|
return if @_stopRequestLoop
|
65
63
|
if jqXhr.status is 200
|
66
64
|
# Of course, IE's XDomainRequest doesn't support non-200 success codes.
|
@@ -75,18 +73,22 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
75
73
|
# Ping long poll server to verify internet connectivity
|
76
74
|
# jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
|
77
75
|
# directly so we can't manually set a timeout.
|
78
|
-
$.ajax
|
79
|
-
url:
|
80
|
-
method:
|
81
|
-
crossDomain:
|
82
|
-
data:
|
83
|
-
success:
|
76
|
+
@_lastPingRequest = $.ajax
|
77
|
+
url: @config.longPoll.url
|
78
|
+
method: 'HEAD'
|
79
|
+
crossDomain: true
|
80
|
+
data: @config.params
|
81
|
+
success: =>
|
82
|
+
if @_needToNotifyOfReconnect
|
83
|
+
@_needToNotifyOfReconnect = false
|
84
|
+
@config.connected @
|
84
85
|
|
85
86
|
# We need this custom handler to have the connection status
|
86
87
|
# properly displayed
|
87
88
|
_error: (jqXhr, status, error) =>
|
88
|
-
@
|
89
|
-
|
89
|
+
unless @_needToNotifyOfReconnect or @_stopRequestLoop
|
90
|
+
@_needToNotifyOfReconnect = true
|
91
|
+
@config.disconnected()
|
90
92
|
unless @_stopRequestLoop
|
91
93
|
# Ping the server to make sure this isn't a network connectivity error
|
92
94
|
setTimeout @_ping, @_retryDelay + @_lagTime
|
@@ -6,8 +6,8 @@ class Firehose.Transport
|
|
6
6
|
false
|
7
7
|
|
8
8
|
constructor: (config={}) ->
|
9
|
-
@config
|
10
|
-
@_retryDelay =
|
9
|
+
@config = config
|
10
|
+
@_retryDelay = 3000
|
11
11
|
|
12
12
|
# Lets rock'n'roll! Connect to the server.
|
13
13
|
connect: (delay = 0) =>
|
@@ -34,4 +34,8 @@ class Firehose.Transport
|
|
34
34
|
|
35
35
|
# Default connection closed handler
|
36
36
|
_close: (event) =>
|
37
|
-
@config.disconnected()
|
37
|
+
@config.disconnected()
|
38
|
+
|
39
|
+
# Useful for reconnecting after any networking hiccups
|
40
|
+
getLastMessageSequence: =>
|
41
|
+
@_lastMessageSequence or 0
|
@@ -27,25 +27,35 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
27
27
|
@socket.onerror = @_error
|
28
28
|
@socket.onmessage = @_lookForInitialPong
|
29
29
|
|
30
|
+
_open: =>
|
31
|
+
sendPing @socket
|
32
|
+
|
30
33
|
_lookForInitialPong: (event) =>
|
34
|
+
@_restartKeepAlive()
|
31
35
|
if isPong(try JSON.parse event.data catch e then {})
|
32
|
-
|
36
|
+
if @_lastMessageSequence?
|
37
|
+
# don't callback to connectionVerified on subsequent reconnects
|
38
|
+
@sendStartingMessageSequence @_lastMessageSequence
|
39
|
+
else @config.webSocket.connectionVerified @
|
33
40
|
|
34
41
|
sendStartingMessageSequence: (message_sequence) =>
|
35
|
-
@
|
42
|
+
@_lastMessageSequence = message_sequence
|
43
|
+
@socket.onmessage = @_message
|
36
44
|
@socket.send JSON.stringify {message_sequence}
|
45
|
+
@_needToNotifyOfDisconnect = true
|
46
|
+
Firehose.Transport::_open.call @
|
37
47
|
|
38
48
|
stop: =>
|
39
49
|
@_cleanUp()
|
40
50
|
|
41
|
-
_open: =>
|
42
|
-
sendPing @socket
|
43
|
-
super
|
44
|
-
|
45
51
|
_message: (event) =>
|
52
|
+
frame = @config.parse event.data
|
46
53
|
@_restartKeepAlive()
|
47
|
-
|
48
|
-
|
54
|
+
unless isPong frame
|
55
|
+
try
|
56
|
+
@_lastMessageSequence = frame.last_sequence
|
57
|
+
@config.message @config.parse frame.message
|
58
|
+
catch e
|
49
59
|
|
50
60
|
_close: (event) =>
|
51
61
|
if event?.wasClean then @_cleanUp()
|
@@ -53,7 +63,11 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
53
63
|
|
54
64
|
_error: (event) =>
|
55
65
|
@_cleanUp()
|
56
|
-
|
66
|
+
if @_needToNotifyOfDisconnect
|
67
|
+
@_needToNotifyOfDisconnect = false
|
68
|
+
@config.disconnected()
|
69
|
+
if @_succeeded then @connect @_retryDelay
|
70
|
+
else @config.failed @
|
57
71
|
|
58
72
|
_cleanUp: =>
|
59
73
|
@_clearKeepalive()
|
@@ -93,74 +93,69 @@ module Firehose
|
|
93
93
|
end
|
94
94
|
|
95
95
|
|
96
|
-
# It _may_ be more memory efficient if we used the same instance of
|
97
|
-
# class (or
|
98
|
-
# request/connection. However, we couldn't use instance variables, and
|
99
|
-
# so I'd need to confirm that local variables would be accessible from
|
100
|
-
# the callback blocks.
|
96
|
+
# It _may_ be more memory efficient if we used the same instance of
|
97
|
+
# this class (or if we even just used a lambda) for every connection.
|
101
98
|
class WebSocket
|
102
99
|
def call(env)
|
103
|
-
req
|
104
|
-
ws
|
100
|
+
req = ::Rack::Request.new env
|
101
|
+
@ws = Faye::WebSocket.new env
|
105
102
|
@path = req.path
|
103
|
+
@ws.onopen = method :handle_open
|
104
|
+
@ws.onclose = method :handle_close
|
105
|
+
@ws.onerror = method :handle_error
|
106
|
+
@ws.onmessage = method :handle_message
|
107
|
+
return @ws.rack_response
|
108
|
+
end
|
106
109
|
|
107
|
-
|
108
|
-
@channel = Channel.new(@path)
|
109
|
-
@deferrable = @channel.next_message(last_sequence).callback do |message, sequence|
|
110
|
-
Firehose.logger.debug "WS sent `#{message}` to `#{@path}` with sequence `#{sequence}`"
|
111
|
-
ws.send message
|
112
|
-
subscribe.call(sequence)
|
113
|
-
end.errback { |e| EM.next_tick { raise e.inspect } unless e == :disconnect }
|
114
|
-
end
|
110
|
+
private
|
115
111
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
112
|
+
def subscribe(last_sequence)
|
113
|
+
@subscribed = true
|
114
|
+
@channel = Channel.new @path
|
115
|
+
@deferrable = @channel.next_message last_sequence
|
116
|
+
@deferrable.callback do |message, sequence|
|
117
|
+
Firehose.logger.debug "WS sent `#{message}` to `#{@path}` with sequence `#{sequence}`"
|
118
|
+
@ws.send self.class.wrap_frame(message, last_sequence)
|
119
|
+
subscribe sequence
|
122
120
|
end
|
123
|
-
|
124
|
-
|
125
|
-
msg = JSON.parse(event.data, :symbolize_names => true) rescue {}
|
126
|
-
seq = msg[:message_sequence]
|
127
|
-
if seq.kind_of? Integer
|
128
|
-
Firehose.logger.debug "Subscribing at message_sequence #{seq}"
|
129
|
-
subscribe.call seq
|
130
|
-
ws.onmessage = handle_ping
|
131
|
-
end
|
121
|
+
@deferrable.errback do |e|
|
122
|
+
EM.next_tick { raise e.inspect } unless e == :disconnect
|
132
123
|
end
|
124
|
+
end
|
133
125
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
126
|
+
def handle_message(event)
|
127
|
+
msg = JSON.parse(event.data, :symbolize_names => true) rescue {}
|
128
|
+
seq = msg[:message_sequence]
|
129
|
+
if msg[:ping] == 'PING'
|
130
|
+
Firehose.logger.debug "WS ping received, sending pong"
|
131
|
+
@ws.send JSON.generate :pong => 'PONG'
|
132
|
+
elsif !@subscribed && seq.kind_of?(Integer)
|
133
|
+
Firehose.logger.debug "Subscribing at message_sequence #{seq}"
|
134
|
+
subscribe seq
|
141
135
|
end
|
136
|
+
end
|
142
137
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
end
|
138
|
+
def handle_open(event)
|
139
|
+
Firehose.logger.debug "WebSocket subscribed to `#{@path}`. Waiting for message_sequence..."
|
140
|
+
end
|
147
141
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
Firehose.logger.debug "WS connection `#{@path}` closing. Code: #{event.code.inspect}; Reason #{event.reason.inspect}"
|
142
|
+
def handle_close(event)
|
143
|
+
if @deferrable
|
144
|
+
@deferrable.fail :disconnect
|
145
|
+
@channel.unsubscribe(@deferrable) if @channel
|
154
146
|
end
|
147
|
+
Firehose.logger.debug "WS connection `#{@path}` closing. Code: #{event.code.inspect}; Reason #{event.reason.inspect}"
|
148
|
+
end
|
155
149
|
|
156
|
-
|
157
|
-
|
158
|
-
|
150
|
+
def handle_error(event)
|
151
|
+
Firehose.logger.error "WS connection `#{@path}` error `#{error}`: #{error.backtrace}"
|
152
|
+
end
|
159
153
|
|
160
|
-
|
161
|
-
|
154
|
+
def self.wrap_frame(message, last_sequence)
|
155
|
+
JSON.generate :message => message, :last_sequence => last_sequence
|
162
156
|
end
|
163
157
|
end
|
158
|
+
|
164
159
|
end
|
165
160
|
end
|
166
161
|
end
|
data/lib/firehose/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firehose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2012-11-
|
15
|
+
date: 2012-11-21 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: eventmachine
|