firehose 1.0.0 → 1.1.0
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/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
|