firehose 1.3.6 → 2.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +31 -10
- data/{LICENSE.txt → LICENSE} +6 -6
- data/README.md +379 -187
- data/app/assets/javascripts/firehose.js +258 -0
- data/config/importmap.rb +1 -0
- data/db/migrate/20260214185244_create_firehose_events.rb +23 -0
- data/lib/commands/firehose_command.rb +119 -0
- data/lib/firehose/channel.rb +40 -0
- data/lib/firehose/cleanup_job.rb +21 -0
- data/lib/firehose/engine.rb +36 -0
- data/lib/firehose/helper.rb +40 -0
- data/lib/firehose/models/channel.rb +12 -0
- data/lib/firehose/models/message.rb +11 -0
- data/lib/firehose/queue.rb +44 -0
- data/lib/firehose/server.rb +930 -17
- data/lib/firehose/sse.rb +187 -0
- data/lib/firehose/stream.rb +32 -0
- data/lib/firehose/streamable.rb +25 -0
- data/lib/firehose/websocket.rb +242 -0
- data/lib/firehose.rb +67 -15
- data/lib/generators/firehose/install/install_generator.rb +43 -0
- data/lib/generators/firehose/install/templates/firehose_controller.rb +16 -0
- metadata +39 -366
- data/.codeclimate.yml +0 -29
- data/.dockerignore +0 -2
- data/.env.sample +0 -17
- data/.gitignore +0 -14
- data/.rspec +0 -1
- data/.rubocop.yml +0 -1156
- data/.ruby-version +0 -1
- data/.travis.yml +0 -12
- data/Dockerfile +0 -11
- data/Gemfile +0 -6
- data/Procfile +0 -1
- data/Procfile.dev +0 -3
- data/Rakefile +0 -64
- data/bin/firehose +0 -9
- data/coffeelint.json +0 -129
- data/config/rainbows.rb +0 -17
- data/docker-compose.yml +0 -17
- data/firehose.gemspec +0 -45
- data/karma.config.coffee +0 -89
- data/lib/assets/javascripts/firehose/base.js.coffee +0 -4
- data/lib/assets/javascripts/firehose/consumer.js.coffee +0 -86
- data/lib/assets/javascripts/firehose/core.js.coffee +0 -6
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +0 -210
- data/lib/assets/javascripts/firehose/multiplexed_consumer.js.coffee +0 -74
- data/lib/assets/javascripts/firehose/transport.js.coffee +0 -44
- data/lib/assets/javascripts/firehose/version.js.coffee.erb +0 -2
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +0 -148
- data/lib/assets/javascripts/firehose.js.coffee +0 -2
- data/lib/firehose/assets.rb +0 -56
- data/lib/firehose/cli.rb +0 -79
- data/lib/firehose/client/consumer.rb +0 -94
- data/lib/firehose/client/producer.rb +0 -114
- data/lib/firehose/client.rb +0 -10
- data/lib/firehose/logging.rb +0 -32
- data/lib/firehose/rack/app.rb +0 -49
- data/lib/firehose/rack/consumer/http_long_poll.rb +0 -159
- data/lib/firehose/rack/consumer/web_socket.rb +0 -200
- data/lib/firehose/rack/consumer.rb +0 -78
- data/lib/firehose/rack/ping.rb +0 -88
- data/lib/firehose/rack/publisher.rb +0 -59
- data/lib/firehose/rack.rb +0 -32
- data/lib/firehose/rails.rb +0 -7
- data/lib/firehose/server/app.rb +0 -52
- data/lib/firehose/server/channel.rb +0 -72
- data/lib/firehose/server/message_buffer.rb +0 -59
- data/lib/firehose/server/publisher.rb +0 -133
- data/lib/firehose/server/redis.rb +0 -32
- data/lib/firehose/server/subscriber.rb +0 -51
- data/lib/firehose/version.rb +0 -4
- data/package.json +0 -17
- data/spec/integrations/integration_test_helper.rb +0 -22
- data/spec/integrations/rainbows_spec.rb +0 -7
- data/spec/integrations/shared_examples.rb +0 -202
- data/spec/integrations/thin_spec.rb +0 -7
- data/spec/javascripts/firehose/base_spec.coffee +0 -2
- data/spec/javascripts/firehose/consumer_spec.coffee +0 -45
- data/spec/javascripts/firehose/long_poll_spec.coffee +0 -44
- data/spec/javascripts/firehose/multiplexed_consumer_spec.coffee +0 -72
- data/spec/javascripts/firehose/transport_spec.coffee +0 -30
- data/spec/javascripts/firehose/websocket_spec.coffee +0 -27
- data/spec/javascripts/helpers/spec_helper.js +0 -31
- data/spec/javascripts/support/jquery-1.11.1.js +0 -10308
- data/spec/javascripts/support/json2.js +0 -487
- data/spec/javascripts/support/sinon-1.7.3.js +0 -4290
- data/spec/javascripts/support/spec_helper.coffee +0 -3
- data/spec/lib/assets_spec.rb +0 -35
- data/spec/lib/client/consumer_spec.rb +0 -13
- data/spec/lib/client/producer_spec.rb +0 -71
- data/spec/lib/firehose_spec.rb +0 -7
- data/spec/lib/rack/consumer/http_long_poll_spec.rb +0 -30
- data/spec/lib/rack/consumer_spec.rb +0 -19
- data/spec/lib/rack/ping_spec.rb +0 -28
- data/spec/lib/rack/publisher_spec.rb +0 -34
- data/spec/lib/server/app_spec.rb +0 -15
- data/spec/lib/server/channel_spec.rb +0 -178
- data/spec/lib/server/message_buffer_spec.rb +0 -148
- data/spec/lib/server/publisher_spec.rb +0 -91
- data/spec/lib/server/redis_spec.rb +0 -13
- data/spec/lib/server/subscriber_spec.rb +0 -70
- data/spec/spec_helper.rb +0 -84
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
class Firehose.LongPoll extends Firehose.Transport
|
|
2
|
-
messageSequenceHeader: 'Pragma'
|
|
3
|
-
name: -> 'LongPoll'
|
|
4
|
-
|
|
5
|
-
# CORS is kinda supported in IE8+ except that its implementation cannot
|
|
6
|
-
# access "simple request" response headers. This means we don't yet have a
|
|
7
|
-
# plan to support IE<10 (when it gets a real XHR2 implementation). Sucks...
|
|
8
|
-
# $.browser.msie and parseInt($.browser.version) >= 8 # DEPRECATED
|
|
9
|
-
@ieSupported: -> (document.documentMode || 10) >= 8
|
|
10
|
-
|
|
11
|
-
@supported: ->
|
|
12
|
-
# IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
|
|
13
|
-
if xhr = $.ajaxSettings.xhr()
|
|
14
|
-
"withCredentials" of xhr || Firehose.LongPoll.ieSupported()
|
|
15
|
-
|
|
16
|
-
constructor: (args) ->
|
|
17
|
-
super args
|
|
18
|
-
|
|
19
|
-
@config.ssl ?= false
|
|
20
|
-
|
|
21
|
-
# Configrations specifically for long polling
|
|
22
|
-
@config.longPoll ||= {}
|
|
23
|
-
@config.longPoll.url ||= "#{@_protocol()}:#{@config.uri}"
|
|
24
|
-
# How many ms should we wait before timing out the AJAX connection?
|
|
25
|
-
@config.longPoll.timeout ||= 25000
|
|
26
|
-
# TODO - What is @_lagTime for? Can't we just use the @_timeout value?
|
|
27
|
-
# We use the lag time to make the client live longer than the server.
|
|
28
|
-
@_lagTime = 5000
|
|
29
|
-
@_timeout = @config.longPoll.timeout + @_lagTime
|
|
30
|
-
@_okInterval = @config.okInterval || 0
|
|
31
|
-
@_stopRequestLoop = false
|
|
32
|
-
@_lastMessageSequence = 0
|
|
33
|
-
|
|
34
|
-
# Protocol schema we should use for talking to firehose server.
|
|
35
|
-
_protocol: =>
|
|
36
|
-
if @config.ssl then "https" else "http"
|
|
37
|
-
|
|
38
|
-
_request: =>
|
|
39
|
-
return if @_stopRequestLoop
|
|
40
|
-
# Set the Last Message Sequence in a query string.
|
|
41
|
-
# Ideally we'd use an HTTP header, but android devices don't let us
|
|
42
|
-
# set any HTTP headers for CORS requests.
|
|
43
|
-
data = @_requestParams()
|
|
44
|
-
data.last_message_sequence = @_lastMessageSequence
|
|
45
|
-
# TODO: Some of these options will be deprecated in jQuery 1.8
|
|
46
|
-
# See: http://api.jquery.com/jQuery.ajax/#jqXHR
|
|
47
|
-
@_lastRequest = $.ajax
|
|
48
|
-
url: @config.longPoll.url
|
|
49
|
-
firehose: true
|
|
50
|
-
crossDomain: true
|
|
51
|
-
data: data
|
|
52
|
-
timeout: @_timeout
|
|
53
|
-
success: @_success
|
|
54
|
-
error: @_error
|
|
55
|
-
cache: false
|
|
56
|
-
|
|
57
|
-
_requestParams: =>
|
|
58
|
-
@config.params
|
|
59
|
-
|
|
60
|
-
stop: =>
|
|
61
|
-
@_stopRequestLoop = true
|
|
62
|
-
if @_lastRequest?
|
|
63
|
-
try @_lastRequest.abort() catch e
|
|
64
|
-
delete @_lastRequest
|
|
65
|
-
if @_lastPingRequest?
|
|
66
|
-
try @_lastPingRequest.abort() catch e
|
|
67
|
-
delete @_lastPingRequest
|
|
68
|
-
|
|
69
|
-
_success: (data, status, jqXhr) =>
|
|
70
|
-
if @_needToNotifyOfReconnect or not @_succeeded
|
|
71
|
-
@_needToNotifyOfReconnect = false
|
|
72
|
-
@_open data
|
|
73
|
-
return if @_stopRequestLoop
|
|
74
|
-
if jqXhr.status is 200
|
|
75
|
-
# Of course, IE's XDomainRequest doesn't support non-200 success codes.
|
|
76
|
-
try
|
|
77
|
-
{message, last_sequence} = JSON.parse jqXhr.responseText
|
|
78
|
-
@_lastMessageSequence = last_sequence || 0
|
|
79
|
-
@config.message @config.parse message
|
|
80
|
-
catch e
|
|
81
|
-
@connect @_okInterval
|
|
82
|
-
|
|
83
|
-
_ping: =>
|
|
84
|
-
# Ping long poll server to verify internet connectivity
|
|
85
|
-
# jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
|
|
86
|
-
# directly so we can't manually set a timeout.
|
|
87
|
-
@_lastPingRequest = $.ajax
|
|
88
|
-
url: @config.uri
|
|
89
|
-
method: 'HEAD'
|
|
90
|
-
crossDomain: true
|
|
91
|
-
firehose: true
|
|
92
|
-
data: @_requestParams()
|
|
93
|
-
success: =>
|
|
94
|
-
if @_needToNotifyOfReconnect
|
|
95
|
-
@_needToNotifyOfReconnect = false
|
|
96
|
-
@config.connected @
|
|
97
|
-
|
|
98
|
-
# We need this custom handler to have the connection status
|
|
99
|
-
# properly displayed
|
|
100
|
-
_error: (jqXhr, status, error) =>
|
|
101
|
-
unless @_needToNotifyOfReconnect or @_stopRequestLoop
|
|
102
|
-
@_needToNotifyOfReconnect = true
|
|
103
|
-
@config.disconnected()
|
|
104
|
-
unless @_stopRequestLoop
|
|
105
|
-
# Ping the server to make sure this isn't a network connectivity error
|
|
106
|
-
setTimeout @_ping, @_retryDelay + @_lagTime
|
|
107
|
-
# Reconnect with delay
|
|
108
|
-
setTimeout @_request, @_retryDelay
|
|
109
|
-
|
|
110
|
-
# Let's try to hack in support for IE8-9 via the XDomainRequest object!
|
|
111
|
-
# This was adapted from code shamelessly stolen from:
|
|
112
|
-
# https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
|
|
113
|
-
if $?.browser?.msie and parseInt($.browser.version, 10) in [8, 9]
|
|
114
|
-
jQuery.ajaxTransport (s) ->
|
|
115
|
-
if s.crossDomain and s.async and s.firehose
|
|
116
|
-
if s.timeout
|
|
117
|
-
s.xdrTimeout = s.timeout
|
|
118
|
-
delete s.timeout
|
|
119
|
-
xdr = undefined
|
|
120
|
-
return {
|
|
121
|
-
send: (_, complete) ->
|
|
122
|
-
callback = (status, statusText, responses, responseHeaders) ->
|
|
123
|
-
xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
|
|
124
|
-
xdr = undefined
|
|
125
|
-
complete status, statusText, responses, responseHeaders
|
|
126
|
-
|
|
127
|
-
xdr = new XDomainRequest()
|
|
128
|
-
|
|
129
|
-
xdr.open s.type, s.url
|
|
130
|
-
|
|
131
|
-
xdr.onload = ->
|
|
132
|
-
headers = "Content-Type: #{xdr.contentType}"
|
|
133
|
-
callback 200, "OK", {text: xdr.responseText}, headers
|
|
134
|
-
|
|
135
|
-
xdr.onerror = -> callback 404, "Not Found"
|
|
136
|
-
|
|
137
|
-
# This is critical for long poll to work in IE9.
|
|
138
|
-
# Without it, the initial request will work but
|
|
139
|
-
# subsequent requests will fail silently.
|
|
140
|
-
# http://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
|
|
141
|
-
xdr.onprogress = -> {}
|
|
142
|
-
|
|
143
|
-
if s.xdrTimeout?
|
|
144
|
-
xdr.ontimeout = -> callback 0, "timeout"
|
|
145
|
-
xdr.timeout = s.xdrTimeout
|
|
146
|
-
|
|
147
|
-
xdr.send (s.hasContent and s.data) or null
|
|
148
|
-
|
|
149
|
-
abort: ->
|
|
150
|
-
if xdr?
|
|
151
|
-
xdr.onerror = jQuery.noop()
|
|
152
|
-
xdr.abort()
|
|
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
|
|
@@ -1,74 +0,0 @@
|
|
|
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,44 +0,0 @@
|
|
|
1
|
-
class Firehose.Transport
|
|
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
|
|
4
|
-
# succeed. That should be accounted for during the initial connecting to the server.
|
|
5
|
-
@supported: =>
|
|
6
|
-
false
|
|
7
|
-
|
|
8
|
-
constructor: (config={}) ->
|
|
9
|
-
@config = config
|
|
10
|
-
@config.params ||= {}
|
|
11
|
-
|
|
12
|
-
@_retryDelay = 3000
|
|
13
|
-
@
|
|
14
|
-
|
|
15
|
-
# Lets rock'n'roll! Connect to the server.
|
|
16
|
-
connect: (delay = 0) =>
|
|
17
|
-
setTimeout @_request, delay
|
|
18
|
-
this
|
|
19
|
-
|
|
20
|
-
# Hey subclasses:
|
|
21
|
-
name: -> throw 'not implemented in base Transport' # implement this to identify transport type
|
|
22
|
-
stop: -> throw 'not implemented in base Transport' # implement this to stop receiving messages
|
|
23
|
-
_request: -> throw 'not implemented in base Transport' # implement this to handle requests
|
|
24
|
-
|
|
25
|
-
# Default error handler
|
|
26
|
-
_error: (event) =>
|
|
27
|
-
if @_succeeded
|
|
28
|
-
# Lets try to connect again with delay
|
|
29
|
-
@config.disconnected()
|
|
30
|
-
@connect(@_retryDelay)
|
|
31
|
-
else @config.failed @
|
|
32
|
-
|
|
33
|
-
# Default connection established handler
|
|
34
|
-
_open: (event) =>
|
|
35
|
-
@_succeeded = true
|
|
36
|
-
@config.connected(@)
|
|
37
|
-
|
|
38
|
-
# Default connection closed handler
|
|
39
|
-
_close: (event) =>
|
|
40
|
-
@config.disconnected()
|
|
41
|
-
|
|
42
|
-
# Useful for reconnecting after any networking hiccups
|
|
43
|
-
getLastMessageSequence: =>
|
|
44
|
-
@_lastMessageSequence or 0
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
INITIAL_PING_TIMEOUT = 2000
|
|
2
|
-
KEEPALIVE_PING_TIMEOUT = 20000
|
|
3
|
-
|
|
4
|
-
class Firehose.WebSocket extends Firehose.Transport
|
|
5
|
-
name: -> 'WebSocket'
|
|
6
|
-
|
|
7
|
-
@ieSupported:-> (document.documentMode || 10) > 9
|
|
8
|
-
@supported :-> (if module?.exports? then global else window).WebSocket? # Check if WebSocket is an object in the window.
|
|
9
|
-
|
|
10
|
-
constructor: (args) ->
|
|
11
|
-
super args
|
|
12
|
-
# Configrations specifically for web sockets
|
|
13
|
-
@config.webSocket ||= {}
|
|
14
|
-
@config.webSocket.connectionVerified = @config.connectionVerified
|
|
15
|
-
|
|
16
|
-
_sendMessage: (message) =>
|
|
17
|
-
@socket?.send(JSON.stringify message)
|
|
18
|
-
|
|
19
|
-
_request: =>
|
|
20
|
-
# Run this in a try/catch block because IE10 inside of a .NET control
|
|
21
|
-
# complains about security zones.
|
|
22
|
-
try
|
|
23
|
-
@socket = new (if module?.exports? then global else window).WebSocket "#{@_protocol()}:#{@config.uri}?#{$.param @_requestParams()}"
|
|
24
|
-
@socket.onopen = @_open
|
|
25
|
-
@socket.onclose = @_close
|
|
26
|
-
@socket.onerror = @_error
|
|
27
|
-
@socket.onmessage = @_lookForInitialPong
|
|
28
|
-
catch err
|
|
29
|
-
console?.log(err)
|
|
30
|
-
|
|
31
|
-
# Protocol schema we should use for talking to firehose server.
|
|
32
|
-
_protocol: =>
|
|
33
|
-
if @config.ssl then "wss" else "ws"
|
|
34
|
-
|
|
35
|
-
_requestParams: =>
|
|
36
|
-
@config.params
|
|
37
|
-
|
|
38
|
-
_open: =>
|
|
39
|
-
sendPing @socket
|
|
40
|
-
|
|
41
|
-
_lookForInitialPong: (event) =>
|
|
42
|
-
@_restartKeepAlive()
|
|
43
|
-
if isPong(try JSON.parse event.data catch e then {})
|
|
44
|
-
if @_lastMessageSequence?
|
|
45
|
-
# don't callback to connectionVerified on subsequent reconnects
|
|
46
|
-
@sendStartingMessageSequence @_lastMessageSequence
|
|
47
|
-
else @config.webSocket.connectionVerified @
|
|
48
|
-
|
|
49
|
-
sendStartingMessageSequence: (message_sequence) =>
|
|
50
|
-
@_lastMessageSequence = message_sequence
|
|
51
|
-
@socket.onmessage = @_message
|
|
52
|
-
@_sendMessage({message_sequence})
|
|
53
|
-
@_needToNotifyOfDisconnect = true
|
|
54
|
-
Firehose.Transport::_open.call @
|
|
55
|
-
|
|
56
|
-
stop: =>
|
|
57
|
-
@_cleanUp()
|
|
58
|
-
|
|
59
|
-
_message: (event) =>
|
|
60
|
-
frame = @config.parse event.data
|
|
61
|
-
@_restartKeepAlive()
|
|
62
|
-
unless isPong frame
|
|
63
|
-
try
|
|
64
|
-
@_lastMessageSequence = frame.last_sequence
|
|
65
|
-
@config.message @config.parse frame.message
|
|
66
|
-
catch e
|
|
67
|
-
|
|
68
|
-
_close: (event) =>
|
|
69
|
-
if event?.wasClean then @_cleanUp()
|
|
70
|
-
else @_error event
|
|
71
|
-
|
|
72
|
-
_error: (event) =>
|
|
73
|
-
@_cleanUp()
|
|
74
|
-
if @_needToNotifyOfDisconnect
|
|
75
|
-
@_needToNotifyOfDisconnect = false
|
|
76
|
-
@config.disconnected()
|
|
77
|
-
if @_succeeded
|
|
78
|
-
@connect @_retryDelay
|
|
79
|
-
else if @config.failed
|
|
80
|
-
@config.failed @
|
|
81
|
-
|
|
82
|
-
_cleanUp: =>
|
|
83
|
-
@_clearKeepalive()
|
|
84
|
-
if @socket?
|
|
85
|
-
@socket.onopen = null
|
|
86
|
-
@socket.onclose = null
|
|
87
|
-
@socket.onerror = null
|
|
88
|
-
@socket.onmessage = null
|
|
89
|
-
@socket.close()
|
|
90
|
-
delete @socket
|
|
91
|
-
|
|
92
|
-
_restartKeepAlive: =>
|
|
93
|
-
doPing = =>
|
|
94
|
-
sendPing @socket
|
|
95
|
-
setNextKeepAlive()
|
|
96
|
-
setNextKeepAlive = =>
|
|
97
|
-
@keepaliveTimeout = setTimeout doPing, KEEPALIVE_PING_TIMEOUT
|
|
98
|
-
@_clearKeepalive()
|
|
99
|
-
setNextKeepAlive()
|
|
100
|
-
|
|
101
|
-
_clearKeepalive: =>
|
|
102
|
-
if @keepaliveTimeout?
|
|
103
|
-
clearTimeout @keepaliveTimeout
|
|
104
|
-
@keepaliveTimeout = null
|
|
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
|
-
|
|
144
|
-
sendPing = (socket) ->
|
|
145
|
-
socket.send JSON.stringify ping: 'PING'
|
|
146
|
-
|
|
147
|
-
isPong = (o) ->
|
|
148
|
-
o.pong is 'PONG'
|
data/lib/firehose/assets.rb
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
require 'sprockets'
|
|
2
|
-
|
|
3
|
-
module Firehose
|
|
4
|
-
# Deal with bundling Sprocket assets into environments (like Rails or Sprockets)
|
|
5
|
-
module Assets
|
|
6
|
-
def self.path(*segs)
|
|
7
|
-
File.join File.expand_path('../../assets', __FILE__), segs
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# Integrate Firehose ./lib/assets files into a sprocket-enabled environment.
|
|
11
|
-
module Sprockets
|
|
12
|
-
# Entry-point Javascript for Sprockets project.
|
|
13
|
-
JAVASCRIPT = "firehose.js"
|
|
14
|
-
|
|
15
|
-
# Drop flash and javascript paths to Firehose assets into a sprockets environment.
|
|
16
|
-
def self.configure(env)
|
|
17
|
-
env.append_path Firehose::Assets.path('javascripts')
|
|
18
|
-
env
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# The "main" javascript that folks should compile and use in their
|
|
22
|
-
# web applications.
|
|
23
|
-
def self.javascript
|
|
24
|
-
Firehose::Assets::Sprockets.environment[JAVASCRIPT].source
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Return a new sprockets environment configured with Firehose.
|
|
28
|
-
def self.environment
|
|
29
|
-
configure ::Sprockets::Environment.new
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Quick and dirty way for folks to compile the Firehose assets to a path
|
|
33
|
-
# from the CLI and use. These are usualy non-ruby (or non-sprockets) folks
|
|
34
|
-
# who want to run the firehose process and use the JS in a web app.
|
|
35
|
-
def self.manifest(directory)
|
|
36
|
-
::Sprockets::Manifest.new(environment, directory)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Try to automatically configure Sprockets if it's detected in the project.
|
|
40
|
-
def self.auto_detect
|
|
41
|
-
if defined? ::Sprockets and ::Sprockets.respond_to? :append_path
|
|
42
|
-
Firehose::Assets::Sprockets.configure ::Sprockets
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def self.manifest_paths
|
|
47
|
-
paths = []
|
|
48
|
-
paths << File.basename(Firehose::Assets.path('/javascripts/firehose/firehose.js.coffee'), '.coffee')
|
|
49
|
-
paths
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Detect if Sprockets is loaded. If it is, lets configure Firehose to use it!
|
|
56
|
-
Firehose::Assets::Sprockets.auto_detect
|
data/lib/firehose/cli.rb
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
require 'thor'
|
|
2
|
-
require 'eventmachine'
|
|
3
|
-
require 'uri'
|
|
4
|
-
|
|
5
|
-
# Enable native async-io libs
|
|
6
|
-
EM.kqueue if EM.kqueue?
|
|
7
|
-
EM.epoll if EM.epoll?
|
|
8
|
-
|
|
9
|
-
module Firehose
|
|
10
|
-
class CLI < Thor
|
|
11
|
-
def initialize(*args)
|
|
12
|
-
super
|
|
13
|
-
# Disable buffering to $stdio for Firehose.logger
|
|
14
|
-
$stdout.sync = true
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
desc "javascript", "Compile the Firehose JavaScript."
|
|
18
|
-
def javascript
|
|
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
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
desc "version", "Display the current version."
|
|
24
|
-
def version
|
|
25
|
-
puts %[Firehose #{Firehose::VERSION} "#{Firehose::CODENAME}"]
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
desc "server", "Start an instance of a server."
|
|
29
|
-
method_option :port, :type => :numeric, :default => ENV['PORT'] || Firehose::URI.port, :required => false, :aliases => '-p'
|
|
30
|
-
method_option :host, :type => :string, :default => ENV['HOST'] || Firehose::URI.host, :required => false, :aliases => '-h'
|
|
31
|
-
method_option :server, :type => :string, :default => ENV['SERVER'] ||'rainbows', :required => false, :aliases => '-s'
|
|
32
|
-
def server
|
|
33
|
-
begin
|
|
34
|
-
Firehose::Server::App.new(options).start
|
|
35
|
-
rescue => e
|
|
36
|
-
Firehose.logger.error "#{e.message}: #{e.backtrace}"
|
|
37
|
-
raise e
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
desc "consume URI", "Consume messages from a resource."
|
|
42
|
-
method_option :concurrency, :type => :numeric, :default => 1, :aliases => '-c'
|
|
43
|
-
def consume(uri)
|
|
44
|
-
EM.run do
|
|
45
|
-
options[:concurrency].times { Firehose::Client::Consumer.parse(uri).request }
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
desc "publish URI [PAYLOAD]", "Publish messages to a resource."
|
|
50
|
-
method_option :ttl, :type => :numeric, :aliases => '-t'
|
|
51
|
-
method_option :times, :type => :numeric, :aliases => '-n', :default => 1
|
|
52
|
-
method_option :interval, :type => :numeric, :aliases => '-i'
|
|
53
|
-
def publish(uri, payload=nil)
|
|
54
|
-
payload ||= $stdin.read
|
|
55
|
-
client = Firehose::Client::Producer::Http.new(uri)
|
|
56
|
-
path = ::URI.parse(uri).path
|
|
57
|
-
times = options[:times]
|
|
58
|
-
ttl = options[:ttl]
|
|
59
|
-
|
|
60
|
-
EM.run do
|
|
61
|
-
# TODO I think this can be cleaned up so the top-level if/else can be ditched.
|
|
62
|
-
if interval = options[:interval]
|
|
63
|
-
# Publish messages at a forced interval.
|
|
64
|
-
EM.add_periodic_timer interval do
|
|
65
|
-
client.publish(payload).to(path, :ttl => ttl)
|
|
66
|
-
EM.stop if times && (times-=1).zero?
|
|
67
|
-
end
|
|
68
|
-
else
|
|
69
|
-
# Publish messages as soon as the last message was published.
|
|
70
|
-
worker = Proc.new do
|
|
71
|
-
client.publish(payload).to(path, :ttl => ttl)
|
|
72
|
-
times && (times-=1).zero? ? EM.stop : worker.call
|
|
73
|
-
end
|
|
74
|
-
worker.call
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|