firehose 1.2.20 → 1.3.6
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +29 -0
- data/.dockerignore +2 -0
- data/.gitignore +3 -1
- data/.rubocop.yml +1156 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -7
- data/CHANGELOG.md +15 -0
- data/Dockerfile +11 -0
- data/Gemfile +4 -2
- data/Procfile.dev +0 -1
- data/README.md +66 -8
- data/Rakefile +43 -32
- data/coffeelint.json +129 -0
- data/docker-compose.yml +17 -0
- data/firehose.gemspec +5 -9
- data/karma.config.coffee +89 -0
- data/lib/assets/javascripts/firehose.js.coffee +1 -2
- data/lib/assets/javascripts/firehose/consumer.js.coffee +18 -2
- data/lib/assets/javascripts/firehose/core.js.coffee +2 -1
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +69 -8
- data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +74 -0
- data/lib/assets/javascripts/firehose/transport.js.coffee +4 -2
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +51 -5
- data/lib/firehose/cli.rb +2 -1
- data/lib/firehose/client/producer.rb +10 -4
- data/lib/firehose/rack/consumer.rb +39 -0
- data/lib/firehose/rack/consumer/http_long_poll.rb +118 -45
- data/lib/firehose/rack/consumer/web_socket.rb +133 -28
- data/lib/firehose/rack/ping.rb +1 -1
- data/lib/firehose/rack/publisher.rb +10 -4
- data/lib/firehose/server.rb +9 -9
- data/lib/firehose/server/channel.rb +23 -31
- data/lib/firehose/server/message_buffer.rb +59 -0
- data/lib/firehose/server/publisher.rb +16 -17
- data/lib/firehose/server/redis.rb +32 -0
- data/lib/firehose/server/subscriber.rb +7 -7
- data/lib/firehose/version.rb +2 -2
- data/package.json +14 -2
- data/spec/integrations/shared_examples.rb +89 -7
- data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +72 -0
- data/spec/javascripts/firehose/transport_spec.coffee +0 -2
- data/spec/javascripts/firehose/websocket_spec.coffee +2 -0
- data/spec/javascripts/helpers/spec_helper.js +1 -0
- data/spec/javascripts/support/jquery-1.11.1.js +10308 -0
- data/{lib/assets/javascripts/vendor → spec/javascripts/support}/json2.js +0 -0
- data/spec/javascripts/support/spec_helper.coffee +3 -0
- data/spec/lib/assets_spec.rb +8 -8
- data/spec/lib/client/producer_spec.rb +14 -14
- data/spec/lib/firehose_spec.rb +2 -2
- data/spec/lib/rack/consumer/http_long_poll_spec.rb +21 -3
- data/spec/lib/rack/consumer_spec.rb +4 -4
- data/spec/lib/rack/ping_spec.rb +4 -4
- data/spec/lib/rack/publisher_spec.rb +5 -5
- data/spec/lib/server/app_spec.rb +2 -2
- data/spec/lib/server/channel_spec.rb +58 -44
- data/spec/lib/server/message_buffer_spec.rb +148 -0
- data/spec/lib/server/publisher_spec.rb +29 -22
- data/spec/lib/server/redis_spec.rb +13 -0
- data/spec/lib/server/subscriber_spec.rb +14 -13
- data/spec/spec_helper.rb +8 -1
- metadata +34 -95
- data/.rbenv-version +0 -1
- data/Guardfile +0 -31
- data/config/evergreen.rb +0 -9
data/karma.config.coffee
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Karma configuration
|
2
|
+
# Generated on Mon Jul 07 2014 12:32:37 GMT-0700 (PDT)
|
3
|
+
module.exports = (config) ->
|
4
|
+
config.set
|
5
|
+
# base path that will be used to resolve all patterns (eg. files, exclude)
|
6
|
+
basePath: ""
|
7
|
+
|
8
|
+
# frameworks to use
|
9
|
+
# available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
10
|
+
frameworks: [
|
11
|
+
"sprockets-mincer"
|
12
|
+
"jasmine"
|
13
|
+
]
|
14
|
+
|
15
|
+
# list of files / patterns to load in the browser
|
16
|
+
files: []
|
17
|
+
|
18
|
+
# list of files to exclude
|
19
|
+
exclude: []
|
20
|
+
|
21
|
+
# preprocess matching files before serving them to the browser
|
22
|
+
# available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
23
|
+
preprocessors:
|
24
|
+
"**/*.coffee": ["coffee"]
|
25
|
+
|
26
|
+
# test results reporter to use
|
27
|
+
# possible values: 'dots', 'progress'
|
28
|
+
# available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
29
|
+
reporters: ["progress", "dots"]
|
30
|
+
|
31
|
+
# web server port
|
32
|
+
port: 9876
|
33
|
+
|
34
|
+
# enable / disable colors in the output (reporters and logs)
|
35
|
+
colors: true
|
36
|
+
|
37
|
+
# level of logging
|
38
|
+
# possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
39
|
+
logLevel: config.LOG_INFO
|
40
|
+
|
41
|
+
# enable / disable watching file and executing tests whenever any file changes
|
42
|
+
autoWatch: true
|
43
|
+
|
44
|
+
# start these browsers
|
45
|
+
# available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
46
|
+
browsers: ["Chrome", "PhantomJS"]
|
47
|
+
|
48
|
+
# Continuous Integration mode
|
49
|
+
# if true, Karma captures browsers, runs the tests and exits
|
50
|
+
singleRun: false
|
51
|
+
|
52
|
+
plugins: [
|
53
|
+
"karma-jasmine"
|
54
|
+
"karma-chrome-launcher"
|
55
|
+
"karma-coffee-preprocessor"
|
56
|
+
"karma-sprockets-mincer"
|
57
|
+
"karma-phantomjs-launcher"
|
58
|
+
"karma-junit-reporter"
|
59
|
+
]
|
60
|
+
|
61
|
+
sprocketsPaths: [
|
62
|
+
"lib/assets/javascripts"
|
63
|
+
"spec/javascripts"
|
64
|
+
]
|
65
|
+
|
66
|
+
sprocketsBundles: [
|
67
|
+
"support/spec_helper.coffee"
|
68
|
+
"firehose.js.coffee"
|
69
|
+
]
|
70
|
+
|
71
|
+
# The spec files to run and watch changes for
|
72
|
+
files: [
|
73
|
+
'spec/javascripts/**/*_spec.coffee'
|
74
|
+
]
|
75
|
+
|
76
|
+
junitReporter: {
|
77
|
+
outputFile: 'tmp/spec-results.xml',
|
78
|
+
suite: ''
|
79
|
+
}
|
80
|
+
|
81
|
+
coffeePreprocessor:
|
82
|
+
# options passed to the coffee compiler
|
83
|
+
options:
|
84
|
+
bare: true
|
85
|
+
sourceMap: false
|
86
|
+
|
87
|
+
# transforming the filenames
|
88
|
+
transformPath: (path) ->
|
89
|
+
path.replace /\.js\.coffee$/, ".js"
|
@@ -18,19 +18,35 @@ class Firehose.Consumer
|
|
18
18
|
# Do stuff before we send the message into config.message. The sensible
|
19
19
|
# default on the webs is to parse JSON.
|
20
20
|
@config.parse ||= JSON.parse
|
21
|
+
|
22
|
+
@_isConnected = false
|
23
|
+
origConnected = @config.connected
|
24
|
+
@config.connected = =>
|
25
|
+
@_isConnected = true
|
26
|
+
origConnected()
|
27
|
+
|
21
28
|
# Make sure we return ourself out of the constructor so we can chain.
|
22
29
|
this
|
23
30
|
|
31
|
+
connected: =>
|
32
|
+
@_isConnected
|
33
|
+
|
34
|
+
websocketTransport: (config) =>
|
35
|
+
new Firehose.WebSocket(config)
|
36
|
+
|
37
|
+
longpollTransport: (config) =>
|
38
|
+
new Firehose.LongPoll(config)
|
39
|
+
|
24
40
|
connect: (delay=0) =>
|
25
41
|
promise = @_connectPromise()
|
26
42
|
|
27
43
|
@config.connectionVerified = @_upgradeTransport
|
28
44
|
if Firehose.WebSocket.supported()
|
29
45
|
@upgradeTimeout = setTimeout =>
|
30
|
-
ws =
|
46
|
+
ws = @websocketTransport(@config)
|
31
47
|
ws.connect delay
|
32
48
|
, 500
|
33
|
-
@transport =
|
49
|
+
@transport = @longpollTransport(@config)
|
34
50
|
@transport.connect delay
|
35
51
|
|
36
52
|
promise
|
@@ -25,10 +25,11 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
25
25
|
@config.longPoll.timeout ||= 25000
|
26
26
|
# TODO - What is @_lagTime for? Can't we just use the @_timeout value?
|
27
27
|
# We use the lag time to make the client live longer than the server.
|
28
|
-
@_lagTime
|
29
|
-
@_timeout
|
30
|
-
@_okInterval
|
31
|
-
@_stopRequestLoop
|
28
|
+
@_lagTime = 5000
|
29
|
+
@_timeout = @config.longPoll.timeout + @_lagTime
|
30
|
+
@_okInterval = @config.okInterval || 0
|
31
|
+
@_stopRequestLoop = false
|
32
|
+
@_lastMessageSequence = 0
|
32
33
|
|
33
34
|
# Protocol schema we should use for talking to firehose server.
|
34
35
|
_protocol: =>
|
@@ -39,7 +40,7 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
39
40
|
# Set the Last Message Sequence in a query string.
|
40
41
|
# Ideally we'd use an HTTP header, but android devices don't let us
|
41
42
|
# set any HTTP headers for CORS requests.
|
42
|
-
data = @
|
43
|
+
data = @_requestParams()
|
43
44
|
data.last_message_sequence = @_lastMessageSequence
|
44
45
|
# TODO: Some of these options will be deprecated in jQuery 1.8
|
45
46
|
# See: http://api.jquery.com/jQuery.ajax/#jqXHR
|
@@ -53,6 +54,9 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
53
54
|
error: @_error
|
54
55
|
cache: false
|
55
56
|
|
57
|
+
_requestParams: =>
|
58
|
+
@config.params
|
59
|
+
|
56
60
|
stop: =>
|
57
61
|
@_stopRequestLoop = true
|
58
62
|
if @_lastRequest?
|
@@ -71,7 +75,7 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
71
75
|
# Of course, IE's XDomainRequest doesn't support non-200 success codes.
|
72
76
|
try
|
73
77
|
{message, last_sequence} = JSON.parse jqXhr.responseText
|
74
|
-
@_lastMessageSequence = last_sequence
|
78
|
+
@_lastMessageSequence = last_sequence || 0
|
75
79
|
@config.message @config.parse message
|
76
80
|
catch e
|
77
81
|
@connect @_okInterval
|
@@ -81,11 +85,11 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
81
85
|
# jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
|
82
86
|
# directly so we can't manually set a timeout.
|
83
87
|
@_lastPingRequest = $.ajax
|
84
|
-
url: @config.
|
88
|
+
url: @config.uri
|
85
89
|
method: 'HEAD'
|
86
90
|
crossDomain: true
|
87
91
|
firehose: true
|
88
|
-
data: @
|
92
|
+
data: @_requestParams()
|
89
93
|
success: =>
|
90
94
|
if @_needToNotifyOfReconnect
|
91
95
|
@_needToNotifyOfReconnect = false
|
@@ -147,3 +151,60 @@ if $?.browser?.msie and parseInt($.browser.version, 10) in [8, 9]
|
|
147
151
|
xdr.onerror = jQuery.noop()
|
148
152
|
xdr.abort()
|
149
153
|
}
|
154
|
+
|
155
|
+
class Firehose.MultiplexedLongPoll extends Firehose.LongPoll
|
156
|
+
constructor: (args) ->
|
157
|
+
super args
|
158
|
+
@_lastMessageSequence = {}
|
159
|
+
|
160
|
+
subscribe: (channel, opts) =>
|
161
|
+
# nothing to be done
|
162
|
+
|
163
|
+
unsubscribe: (channelNames...) =>
|
164
|
+
# same here
|
165
|
+
|
166
|
+
_request: =>
|
167
|
+
return if @_stopRequestLoop
|
168
|
+
data = @_subscriptions()
|
169
|
+
|
170
|
+
@_lastRequest = $.ajax
|
171
|
+
url: @config.uri
|
172
|
+
firehose: true
|
173
|
+
crossDomain: true
|
174
|
+
method: "POST"
|
175
|
+
data: data
|
176
|
+
dataType: "json"
|
177
|
+
timeout: @_timeout
|
178
|
+
success: @_success
|
179
|
+
error: @_error
|
180
|
+
cache: false
|
181
|
+
|
182
|
+
_updateLastMessageSequences: =>
|
183
|
+
for channel, opts of @config.channels
|
184
|
+
if seq = @_lastMessageSequence[channel]
|
185
|
+
opts.last_sequence = seq
|
186
|
+
else
|
187
|
+
unless opts.last_sequence
|
188
|
+
opts.last_sequence = 0
|
189
|
+
|
190
|
+
_subscriptions: =>
|
191
|
+
@_updateLastMessageSequences()
|
192
|
+
subs = {}
|
193
|
+
for channel, opts of @config.channels
|
194
|
+
subs[channel] = opts.last_sequence || 0
|
195
|
+
JSON.stringify(subs)
|
196
|
+
|
197
|
+
_success: (data, status, jqXhr) =>
|
198
|
+
if @_needToNotifyOfReconnect or not @_succeeded
|
199
|
+
@_needToNotifyOfReconnect = false
|
200
|
+
@_open data
|
201
|
+
return if @_stopRequestLoop
|
202
|
+
if jqXhr.status is 200
|
203
|
+
# Of course, IE's XDomainRequest doesn't support non-200 success codes.
|
204
|
+
try
|
205
|
+
message = JSON.parse jqXhr.responseText
|
206
|
+
@_lastMessageSequence ||= {}
|
207
|
+
@_lastMessageSequence[message.channel] = message.last_sequence
|
208
|
+
@config.message message
|
209
|
+
catch e
|
210
|
+
@connect @_okInterval
|
@@ -0,0 +1,74 @@
|
|
1
|
+
Firehose.multiplexChannel = "channels@firehose"
|
2
|
+
|
3
|
+
class Firehose.MultiplexedConsumer extends Firehose.Consumer
|
4
|
+
@subscriptionQuery: (config) ->
|
5
|
+
{
|
6
|
+
subscribe: [
|
7
|
+
"#{channel}!#{opts.last_sequence || 0}" for channel, opts of config.channels
|
8
|
+
].join(",")
|
9
|
+
}
|
10
|
+
|
11
|
+
@normalizeChannels: (config) ->
|
12
|
+
for chan, opts of config.channels
|
13
|
+
if chan[0] != "/"
|
14
|
+
delete config.channels[chan]
|
15
|
+
config.channels["/" + chan] = opts
|
16
|
+
|
17
|
+
@normalizeChannel: (channel) ->
|
18
|
+
if channel[0] != "/"
|
19
|
+
return "/" + channel
|
20
|
+
else
|
21
|
+
return channel
|
22
|
+
|
23
|
+
constructor: (@config = {}) ->
|
24
|
+
@messageHandlers = {}
|
25
|
+
@config.message ||= @message
|
26
|
+
@config.channels ||= {}
|
27
|
+
@config.uri += Firehose.multiplexChannel
|
28
|
+
|
29
|
+
@_updateSubscriptions()
|
30
|
+
|
31
|
+
for channel, opts of @config.channels
|
32
|
+
@_addSubscriptionHandler(channel, opts)
|
33
|
+
|
34
|
+
super(@config)
|
35
|
+
|
36
|
+
websocketTransport: (config) =>
|
37
|
+
new Firehose.MultiplexedWebSocket(config)
|
38
|
+
|
39
|
+
longpollTransport: (config) =>
|
40
|
+
new Firehose.MultiplexedLongPoll(config)
|
41
|
+
|
42
|
+
message: (msg) =>
|
43
|
+
if handler = @messageHandlers[msg.channel]
|
44
|
+
handler(@config.parse msg.message)
|
45
|
+
|
46
|
+
_addSubscriptionHandler: (channel, opts) =>
|
47
|
+
if opts.message
|
48
|
+
@messageHandlers[channel] = opts.message
|
49
|
+
|
50
|
+
_removeSubscriptionHandler: (channelNames...) =>
|
51
|
+
for chan in channelNames
|
52
|
+
delete @messageHandlers[chan]
|
53
|
+
|
54
|
+
_updateSubscriptions: =>
|
55
|
+
Firehose.MultiplexedConsumer.normalizeChannels(@config)
|
56
|
+
|
57
|
+
subscribe: (channel, opts = {}) =>
|
58
|
+
channel = Firehose.MultiplexedConsumer.normalizeChannel(channel)
|
59
|
+
@config.channels[channel] = opts
|
60
|
+
|
61
|
+
@_updateSubscriptions()
|
62
|
+
@_addSubscriptionHandler(channel, opts)
|
63
|
+
@transport.subscribe(channel, opts)
|
64
|
+
|
65
|
+
unsubscribe: (channelNames...) =>
|
66
|
+
return unless @connected()
|
67
|
+
|
68
|
+
for channel in channelNames
|
69
|
+
channel = Firehose.MultiplexedConsumer.normalizeChannel(channel)
|
70
|
+
delete @config.channels[channel]
|
71
|
+
|
72
|
+
@_updateSubscriptions()
|
73
|
+
@_removeSubscriptionHandler(channelNames...)
|
74
|
+
@transport.unsubscribe(channelNames...)
|
@@ -1,12 +1,14 @@
|
|
1
1
|
class Firehose.Transport
|
2
2
|
# Class method to determine whether transport is supported by the current browser. Note that while
|
3
|
-
# the transport may be supported by the browser, its possible that the network connection won't
|
3
|
+
# the transport may be supported by the browser, its possible that the network connection won't
|
4
4
|
# succeed. That should be accounted for during the initial connecting to the server.
|
5
5
|
@supported: =>
|
6
6
|
false
|
7
7
|
|
8
8
|
constructor: (config={}) ->
|
9
|
-
@config
|
9
|
+
@config = config
|
10
|
+
@config.params ||= {}
|
11
|
+
|
10
12
|
@_retryDelay = 3000
|
11
13
|
@
|
12
14
|
|
@@ -13,11 +13,14 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
13
13
|
@config.webSocket ||= {}
|
14
14
|
@config.webSocket.connectionVerified = @config.connectionVerified
|
15
15
|
|
16
|
+
_sendMessage: (message) =>
|
17
|
+
@socket?.send(JSON.stringify message)
|
18
|
+
|
16
19
|
_request: =>
|
17
|
-
# Run this
|
20
|
+
# Run this in a try/catch block because IE10 inside of a .NET control
|
18
21
|
# complains about security zones.
|
19
22
|
try
|
20
|
-
@socket = new (if module?.exports? then global else window).WebSocket "#{@_protocol()}:#{@config.uri}?#{$.param @
|
23
|
+
@socket = new (if module?.exports? then global else window).WebSocket "#{@_protocol()}:#{@config.uri}?#{$.param @_requestParams()}"
|
21
24
|
@socket.onopen = @_open
|
22
25
|
@socket.onclose = @_close
|
23
26
|
@socket.onerror = @_error
|
@@ -29,6 +32,9 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
29
32
|
_protocol: =>
|
30
33
|
if @config.ssl then "wss" else "ws"
|
31
34
|
|
35
|
+
_requestParams: =>
|
36
|
+
@config.params
|
37
|
+
|
32
38
|
_open: =>
|
33
39
|
sendPing @socket
|
34
40
|
|
@@ -43,7 +49,7 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
43
49
|
sendStartingMessageSequence: (message_sequence) =>
|
44
50
|
@_lastMessageSequence = message_sequence
|
45
51
|
@socket.onmessage = @_message
|
46
|
-
@
|
52
|
+
@_sendMessage({message_sequence})
|
47
53
|
@_needToNotifyOfDisconnect = true
|
48
54
|
Firehose.Transport::_open.call @
|
49
55
|
|
@@ -68,8 +74,10 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
68
74
|
if @_needToNotifyOfDisconnect
|
69
75
|
@_needToNotifyOfDisconnect = false
|
70
76
|
@config.disconnected()
|
71
|
-
if @_succeeded
|
72
|
-
|
77
|
+
if @_succeeded
|
78
|
+
@connect @_retryDelay
|
79
|
+
else if @config.failed
|
80
|
+
@config.failed @
|
73
81
|
|
74
82
|
_cleanUp: =>
|
75
83
|
@_clearKeepalive()
|
@@ -95,6 +103,44 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
95
103
|
clearTimeout @keepaliveTimeout
|
96
104
|
@keepaliveTimeout = null
|
97
105
|
|
106
|
+
class Firehose.MultiplexedWebSocket extends Firehose.WebSocket
|
107
|
+
constructor: (args) ->
|
108
|
+
super args
|
109
|
+
|
110
|
+
subscribe: (channel, opts) =>
|
111
|
+
@_sendMessage
|
112
|
+
multiplex_subscribe:
|
113
|
+
channel: channel
|
114
|
+
message_sequence: opts.last_sequence
|
115
|
+
|
116
|
+
unsubscribe: (channelNames...) =>
|
117
|
+
@_sendMessage
|
118
|
+
multiplex_unsubscribe: channelNames
|
119
|
+
|
120
|
+
getLastMessageSequence: =>
|
121
|
+
@_lastMessageSequence or {}
|
122
|
+
|
123
|
+
_open: =>
|
124
|
+
super()
|
125
|
+
for channel, opts of @config.channels
|
126
|
+
if @_lastMessageSequence
|
127
|
+
opts.last_sequence = @_lastMessageSequence[channel]
|
128
|
+
|
129
|
+
unless opts.last_sequence
|
130
|
+
opts.last_sequence = 0
|
131
|
+
|
132
|
+
@subscribe channel, opts
|
133
|
+
|
134
|
+
_message: (event) =>
|
135
|
+
frame = @config.parse event.data
|
136
|
+
@_restartKeepAlive()
|
137
|
+
unless isPong frame
|
138
|
+
try
|
139
|
+
@_lastMessageSequence ||= {}
|
140
|
+
@_lastMessageSequence[frame.channel] = frame.last_sequence
|
141
|
+
@config.message frame
|
142
|
+
catch e
|
143
|
+
|
98
144
|
sendPing = (socket) ->
|
99
145
|
socket.send JSON.stringify ping: 'PING'
|
100
146
|
|
data/lib/firehose/cli.rb
CHANGED
@@ -16,7 +16,8 @@ module Firehose
|
|
16
16
|
|
17
17
|
desc "javascript", "Compile the Firehose JavaScript."
|
18
18
|
def javascript
|
19
|
-
puts Firehose
|
19
|
+
$stderr.puts "DEPRECATION WARNING: Firehose JS assets have been moved to https://github.com/firehoseio/js_client"
|
20
|
+
$stdout.puts Firehose::Assets::Sprockets.javascript
|
20
21
|
end
|
21
22
|
|
22
23
|
desc "version", "Display the current version."
|