message_bus 2.2.3 → 3.3.1
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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -1
- data/.travis.yml +1 -1
- data/CHANGELOG +39 -0
- data/Gemfile +8 -3
- data/Guardfile +1 -0
- data/README.md +62 -11
- data/Rakefile +5 -0
- data/assets/message-bus.js +64 -78
- data/examples/bench/config.ru +1 -0
- data/examples/bench/puma.rb +1 -0
- data/examples/bench/unicorn.conf.rb +1 -0
- data/examples/chat/Gemfile +1 -0
- data/examples/chat/chat.rb +2 -0
- data/examples/chat/config.ru +2 -0
- data/examples/diagnostics/Gemfile +1 -0
- data/examples/diagnostics/config.ru +1 -0
- data/examples/minimal/Gemfile +1 -0
- data/examples/minimal/config.ru +1 -0
- data/lib/message_bus.rb +33 -0
- data/lib/message_bus/backends/redis.rb +11 -8
- data/lib/message_bus/client.rb +36 -8
- data/lib/message_bus/diagnostics.rb +1 -1
- data/lib/message_bus/em_ext.rb +1 -0
- data/lib/message_bus/http_client.rb +2 -1
- data/lib/message_bus/http_client/channel.rb +1 -0
- data/lib/message_bus/rack/diagnostics.rb +5 -4
- data/lib/message_bus/rack/middleware.rb +8 -4
- data/lib/message_bus/rails/railtie.rb +15 -13
- data/lib/message_bus/version.rb +1 -1
- data/package.json +20 -0
- data/spec/assets/message-bus.spec.js +0 -9
- data/spec/assets/support/jasmine_helper.rb +1 -0
- data/spec/fixtures/test/Gemfile +1 -0
- data/spec/fixtures/test/config.ru +1 -0
- data/spec/helpers.rb +1 -0
- data/spec/integration/http_client_spec.rb +2 -0
- data/spec/lib/fake_async_middleware.rb +3 -2
- data/spec/lib/message_bus/assets/asset_encoding_spec.rb +1 -0
- data/spec/lib/message_bus/backend_spec.rb +2 -0
- data/spec/lib/message_bus/client_spec.rb +208 -23
- data/spec/lib/message_bus/connection_manager_spec.rb +3 -1
- data/spec/lib/message_bus/distributed_cache_spec.rb +2 -0
- data/spec/lib/message_bus/multi_process_spec.rb +2 -0
- data/spec/lib/message_bus/rack/middleware_spec.rb +63 -0
- data/spec/lib/message_bus/timer_thread_spec.rb +2 -0
- data/spec/lib/message_bus_spec.rb +34 -0
- data/spec/performance/publish.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- metadata +4 -3
data/examples/bench/config.ru
CHANGED
data/examples/bench/puma.rb
CHANGED
data/examples/chat/Gemfile
CHANGED
data/examples/chat/chat.rb
CHANGED
data/examples/chat/config.ru
CHANGED
data/examples/minimal/Gemfile
CHANGED
data/examples/minimal/config.ru
CHANGED
data/lib/message_bus.rb
CHANGED
@@ -149,6 +149,19 @@ module MessageBus::Implementation
|
|
149
149
|
@config[:long_polling_interval] || 25 * 1000
|
150
150
|
end
|
151
151
|
|
152
|
+
# @param [String] route Message bus will listen to requests on this route.
|
153
|
+
# @return [void]
|
154
|
+
def base_route=(route)
|
155
|
+
configure(base_route: route.gsub(Regexp.new('\A(?!/)|(?<!/)\Z|//+'), "/"))
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [String] the route that message bus will respond to. If not
|
159
|
+
# explicitly set, defaults to "/". Requests to "#{base_route}message-bus/*" will be handled
|
160
|
+
# by the message bus server.
|
161
|
+
def base_route
|
162
|
+
@config[:base_route] || "/"
|
163
|
+
end
|
164
|
+
|
152
165
|
# @return [Boolean] whether the bus is disabled or not
|
153
166
|
def off?
|
154
167
|
@off
|
@@ -554,6 +567,26 @@ module MessageBus::Implementation
|
|
554
567
|
@config[:keepalive_interval] || 60
|
555
568
|
end
|
556
569
|
|
570
|
+
# Registers a client message filter that allows messages to be filtered from the client.
|
571
|
+
#
|
572
|
+
# @param [String,Regexp] channel_prefix channel prefix to match against a message's channel
|
573
|
+
#
|
574
|
+
# @yieldparam [MessageBus::Message] message published to the channel that matched the prefix provided
|
575
|
+
# @yieldreturn [Boolean] whether the message should be published to the client
|
576
|
+
# @return [void]
|
577
|
+
def register_client_message_filter(channel_prefix, &blk)
|
578
|
+
if blk
|
579
|
+
configure(client_message_filters: []) if !@config[:client_message_filters]
|
580
|
+
@config[:client_message_filters] << [channel_prefix, blk]
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# @return [Array] returns a hash of message filters that have been registered
|
585
|
+
def client_message_filters
|
586
|
+
configure(client_message_filters: []) if !@config[:client_message_filters]
|
587
|
+
@config[:client_message_filters]
|
588
|
+
end
|
589
|
+
|
557
590
|
private
|
558
591
|
|
559
592
|
ENCODE_SITE_TOKEN = "$|$"
|
@@ -251,11 +251,11 @@ LUA
|
|
251
251
|
|
252
252
|
# (see Base#global_unsubscribe)
|
253
253
|
def global_unsubscribe
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
254
|
+
begin
|
255
|
+
new_redis = new_redis_connection
|
256
|
+
new_redis.publish(redis_channel_name, UNSUB_MESSAGE)
|
257
|
+
ensure
|
258
|
+
new_redis&.disconnect!
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
@@ -278,13 +278,13 @@ LUA
|
|
278
278
|
end
|
279
279
|
|
280
280
|
begin
|
281
|
-
|
281
|
+
global_redis = new_redis_connection
|
282
282
|
|
283
283
|
if highest_id
|
284
284
|
clear_backlog.call(&blk)
|
285
285
|
end
|
286
286
|
|
287
|
-
|
287
|
+
global_redis.subscribe(redis_channel_name) do |on|
|
288
288
|
on.subscribe do
|
289
289
|
if highest_id
|
290
290
|
clear_backlog.call(&blk)
|
@@ -298,7 +298,7 @@ LUA
|
|
298
298
|
|
299
299
|
on.message do |_c, m|
|
300
300
|
if m == UNSUB_MESSAGE
|
301
|
-
|
301
|
+
global_redis.unsubscribe
|
302
302
|
return
|
303
303
|
end
|
304
304
|
m = MessageBus::Message.decode m
|
@@ -320,7 +320,10 @@ LUA
|
|
320
320
|
rescue => error
|
321
321
|
@logger.warn "#{error} subscribe failed, reconnecting in 1 second. Call stack #{error.backtrace}"
|
322
322
|
sleep 1
|
323
|
+
global_redis&.disconnect!
|
323
324
|
retry
|
325
|
+
ensure
|
326
|
+
global_redis&.disconnect!
|
324
327
|
end
|
325
328
|
end
|
326
329
|
|
data/lib/message_bus/client.rb
CHANGED
@@ -128,15 +128,43 @@ class MessageBus::Client
|
|
128
128
|
# @return [Boolean] whether or not the client has permission to receive the
|
129
129
|
# passed message
|
130
130
|
def allowed?(msg)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
131
|
+
client_allowed = !msg.client_ids || msg.client_ids.length == 0 || msg.client_ids.include?(self.client_id)
|
132
|
+
|
133
|
+
user_allowed = false
|
134
|
+
group_allowed = false
|
135
|
+
|
136
|
+
# this is an inconsistency we should fix anyway, publishing `user_ids: nil` should work same as groups
|
137
|
+
has_users = msg.user_ids && msg.user_ids.length > 0
|
138
|
+
has_groups = msg.group_ids && msg.group_ids.length > 0
|
139
|
+
|
140
|
+
if has_users
|
141
|
+
user_allowed = msg.user_ids.include?(self.user_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
if has_groups
|
145
|
+
group_allowed = (
|
146
|
+
msg.group_ids - (self.group_ids || [])
|
138
147
|
).length < msg.group_ids.length
|
139
|
-
|
148
|
+
end
|
149
|
+
|
150
|
+
has_permission = client_allowed && (user_allowed || group_allowed || (!has_users && !has_groups))
|
151
|
+
|
152
|
+
return has_permission if !has_permission
|
153
|
+
|
154
|
+
filters_allowed = true
|
155
|
+
|
156
|
+
len = @bus.client_message_filters.length
|
157
|
+
while len > 0
|
158
|
+
len -= 1
|
159
|
+
channel_prefix, blk = @bus.client_message_filters[len]
|
160
|
+
|
161
|
+
if msg.channel.start_with?(channel_prefix)
|
162
|
+
filters_allowed = blk.call(msg)
|
163
|
+
break if !filters_allowed
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
filters_allowed
|
140
168
|
end
|
141
169
|
|
142
170
|
# @return [Array<MessageBus::Message>] the set of messages the client is due
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# MessageBus diagnostics are used for troubleshooting the bus and optimising its configuration
|
2
3
|
# @see MessageBus::Rack::Diagnostics
|
3
4
|
class MessageBus::Diagnostics
|
@@ -14,7 +15,6 @@ class MessageBus::Diagnostics
|
|
14
15
|
# process to process comms
|
15
16
|
bus.subscribe('/_diagnostics/hup') do |msg|
|
16
17
|
if Process.pid == msg.data["pid"] && hostname == msg.data["hostname"]
|
17
|
-
$shutdown = true
|
18
18
|
sleep 4
|
19
19
|
Process.kill("HUP", $$)
|
20
20
|
end
|
data/lib/message_bus/em_ext.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'securerandom'
|
2
3
|
require 'net/http'
|
3
4
|
require 'json'
|
@@ -245,7 +246,7 @@ module MessageBus
|
|
245
246
|
request.body = poll_payload
|
246
247
|
|
247
248
|
if @enable_long_polling
|
248
|
-
buffer =
|
249
|
+
buffer = +""
|
249
250
|
|
250
251
|
http.request(request) do |response|
|
251
252
|
response.read_body do |chunk|
|
@@ -16,9 +16,9 @@ class MessageBus::Rack::Diagnostics
|
|
16
16
|
# Process an HTTP request from a subscriber client
|
17
17
|
# @param [Rack::Request::Env] env the request environment
|
18
18
|
def call(env)
|
19
|
-
return @app.call(env) unless env['PATH_INFO'].start_with?
|
19
|
+
return @app.call(env) unless env['PATH_INFO'].start_with? "#{@bus.base_route}message-bus/_diagnostics"
|
20
20
|
|
21
|
-
route = env['PATH_INFO'].split(
|
21
|
+
route = env['PATH_INFO'].split("#{@bus.base_route}message-bus/_diagnostics")[1]
|
22
22
|
|
23
23
|
if @bus.is_admin_lookup.nil? || !@bus.is_admin_lookup.call(env)
|
24
24
|
return [403, {}, ['not allowed']]
|
@@ -45,7 +45,7 @@ class MessageBus::Rack::Diagnostics
|
|
45
45
|
return [200, { 'Content-Type' => 'application/javascript;charset=UTF-8' }, [content]]
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
[404, {}, ['not found']]
|
49
49
|
end
|
50
50
|
|
51
51
|
private
|
@@ -92,6 +92,7 @@ class MessageBus::Rack::Diagnostics
|
|
92
92
|
</body>
|
93
93
|
</html>
|
94
94
|
HTML
|
95
|
-
|
95
|
+
|
96
|
+
[200, { "content-type" => "text/html;" }, [html]]
|
96
97
|
end
|
97
98
|
end
|
@@ -39,6 +39,10 @@ class MessageBus::Rack::Middleware
|
|
39
39
|
@bus = config[:message_bus] || MessageBus
|
40
40
|
@connection_manager = MessageBus::ConnectionManager.new(@bus)
|
41
41
|
@started_listener = false
|
42
|
+
@base_route = "#{@bus.base_route}message-bus/"
|
43
|
+
@base_route_length = @base_route.length
|
44
|
+
@diagnostics_route = "#{@base_route}_diagnostics"
|
45
|
+
@broadcast_route = "#{@base_route}broadcast"
|
42
46
|
start_listener unless @bus.off?
|
43
47
|
end
|
44
48
|
|
@@ -54,7 +58,7 @@ class MessageBus::Rack::Middleware
|
|
54
58
|
# Process an HTTP request from a subscriber client
|
55
59
|
# @param [Rack::Request::Env] env the request environment
|
56
60
|
def call(env)
|
57
|
-
return @app.call(env) unless env['PATH_INFO']
|
61
|
+
return @app.call(env) unless env['PATH_INFO'].start_with? @base_route
|
58
62
|
|
59
63
|
handle_request(env)
|
60
64
|
end
|
@@ -63,18 +67,18 @@ class MessageBus::Rack::Middleware
|
|
63
67
|
|
64
68
|
def handle_request(env)
|
65
69
|
# special debug/test route
|
66
|
-
if @bus.allow_broadcast? && env['PATH_INFO'] ==
|
70
|
+
if @bus.allow_broadcast? && env['PATH_INFO'] == @broadcast_route
|
67
71
|
parsed = Rack::Request.new(env)
|
68
72
|
@bus.publish parsed["channel"], parsed["data"]
|
69
73
|
return [200, { "Content-Type" => "text/html" }, ["sent"]]
|
70
74
|
end
|
71
75
|
|
72
|
-
if env['PATH_INFO'].start_with?
|
76
|
+
if env['PATH_INFO'].start_with? @diagnostics_route
|
73
77
|
diags = MessageBus::Rack::Diagnostics.new(@app, message_bus: @bus)
|
74
78
|
return diags.call(env)
|
75
79
|
end
|
76
80
|
|
77
|
-
client_id = env['PATH_INFO'].split("/")[
|
81
|
+
client_id = env['PATH_INFO'][@base_route_length..-1].split("/")[0]
|
78
82
|
return [404, {}, ["not found"]] unless client_id
|
79
83
|
|
80
84
|
user_id = @bus.user_id_lookup.call(env) if @bus.user_id_lookup
|
@@ -11,27 +11,29 @@ class MessageBus::Rails::Railtie < ::Rails::Railtie
|
|
11
11
|
# the Rails app is configured that might be ActionDispatch::Session::CookieStore, or potentially
|
12
12
|
# ActionDispatch::Session::ActiveRecordStore.
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
# given https://github.com/rails/rails/commit/fedde239dcee256b417dc9bcfe5fef603bf0d952#diff-533a9a9cc17a8a899cb830626089e5f9
|
15
|
+
# there is no way of walking the stack for operations
|
16
|
+
if !skip_middleware?(app.config)
|
17
|
+
if api_only?(app.config)
|
18
|
+
app.middleware.use(MessageBus::Rack::Middleware)
|
19
|
+
else
|
20
|
+
app.middleware.insert_before(ActionDispatch::Flash, MessageBus::Rack::Middleware)
|
21
|
+
end
|
22
22
|
end
|
23
23
|
|
24
24
|
MessageBus.logger = Rails.logger
|
25
25
|
end
|
26
26
|
|
27
|
+
def skip_middleware?(config)
|
28
|
+
return false if !config.respond_to?(:skip_message_bus_middleware)
|
29
|
+
|
30
|
+
config.skip_message_bus_middleware
|
31
|
+
end
|
32
|
+
|
27
33
|
def api_only?(config)
|
28
|
-
return false
|
34
|
+
return false if !config.respond_to?(:api_only)
|
29
35
|
|
30
36
|
config.api_only
|
31
37
|
end
|
32
38
|
|
33
|
-
def flash_middleware_deleted?(middleware)
|
34
|
-
ops = middleware.instance_variable_get(:@operations)
|
35
|
-
ops.any? { |m| m[0] == :delete && m[1].include?(ActionDispatch::Flash) }
|
36
|
-
end
|
37
39
|
end
|
data/lib/message_bus/version.rb
CHANGED
data/package.json
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"name": "message-bus-client",
|
3
|
+
"version": "3.3.0",
|
4
|
+
"description": "A message bus client in Javascript",
|
5
|
+
"main": "assets/message-bus.js",
|
6
|
+
"keywords": "es6, modules",
|
7
|
+
"files": ["assets/message-bus.js"],
|
8
|
+
"jsnext:main": "assets/message-bus.js",
|
9
|
+
"module": "assets/message-bus.js",
|
10
|
+
"repository": {
|
11
|
+
"type": "git",
|
12
|
+
"url": "git+https://github.com/discourse/message_bus.git"
|
13
|
+
},
|
14
|
+
"author": "Sam Saffron, Robin Ward",
|
15
|
+
"license": "MIT",
|
16
|
+
"bugs": {
|
17
|
+
"url": "https://github.com/discourse/message_bus/issues"
|
18
|
+
},
|
19
|
+
"homepage": "https://github.com/discourse/message_bus#readme"
|
20
|
+
}
|
@@ -75,15 +75,6 @@ describe("Messagebus", function() {
|
|
75
75
|
})
|
76
76
|
});
|
77
77
|
|
78
|
-
it('removes itself from root namespace when noConflict is called', function(){
|
79
|
-
expect(window.MessageBus).not.toBeUndefined();
|
80
|
-
var mb = window.MessageBus;
|
81
|
-
expect(mb).toEqual(window.MessageBus.noConflict());
|
82
|
-
expect(window.MessageBus).toBeUndefined();
|
83
|
-
// reset it so afterEach has something to work on
|
84
|
-
window.MessageBus = mb;
|
85
|
-
});
|
86
|
-
|
87
78
|
it('respects minPollInterval setting with defaults', function(){
|
88
79
|
expect(MessageBus.minPollInterval).toEqual(100);
|
89
80
|
MessageBus.minPollInterval = 1000;
|
data/spec/fixtures/test/Gemfile
CHANGED
data/spec/helpers.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'http/parser'
|
2
3
|
class FakeAsyncMiddleware
|
3
4
|
def initialize(app, config = {})
|
@@ -53,7 +54,7 @@ class FakeAsyncMiddleware
|
|
53
54
|
|
54
55
|
def translate_io_result(io)
|
55
56
|
data = io.string
|
56
|
-
body = ""
|
57
|
+
body = +""
|
57
58
|
|
58
59
|
parser = Http::Parser.new
|
59
60
|
parser.on_body = proc { |chunk| body << chunk }
|
@@ -112,7 +113,7 @@ class FakeAsyncMiddleware
|
|
112
113
|
# more judo with deferrable body, at this point we just have headers
|
113
114
|
r[2].callback do
|
114
115
|
# even more judo cause rack test does not call each like the spec says
|
115
|
-
body = ""
|
116
|
+
body = +""
|
116
117
|
r[2].each do |m|
|
117
118
|
body << m
|
118
119
|
end
|