message_bus 4.4.1 → 4.5.2
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/.github/workflows/ci.yml +35 -12
- data/.gitignore +1 -2
- data/CHANGELOG +20 -0
- data/README.md +5 -3
- data/Rakefile +2 -2
- data/docker-compose.yml +1 -1
- data/examples/bench/config.ru +1 -1
- data/examples/chat/Gemfile +1 -1
- data/examples/chat/docker_container/chat.yml +3 -3
- data/examples/minimal/config.ru +1 -1
- data/gemfiles/rack2.gemfile +5 -0
- data/gemfiles/rack3.gemfile +5 -0
- data/lib/message_bus/client.rb +7 -7
- data/lib/message_bus/connection_manager.rb +7 -2
- data/lib/message_bus/rack/middleware.rb +9 -9
- data/lib/message_bus/version.rb +1 -1
- data/message_bus.gemspec +5 -6
- data/package.json +8 -1
- data/pnpm-lock.yaml +1526 -0
- data/spec/fixtures/test/config.ru +1 -1
- data/spec/integration/http_client_spec.rb +1 -1
- data/spec/lib/message_bus/client_spec.rb +4 -2
- data/spec/lib/message_bus/connection_manager_spec.rb +23 -0
- data/spec/lib/message_bus/rack/middleware_spec.rb +1 -1
- metadata +32 -11
- data/DEV.md +0 -7
- data/package-lock.json +0 -3781
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0f8dcac413e4d7ebfd5518440b1bae9311dfee8395d13b9c9a137aed8048689
|
|
4
|
+
data.tar.gz: dd16dc24af70834d8d62ecbc5f570e7790e00c78f876eee3c3b35483e7e576ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a48f71540d916776ee00ea49d651b1e5463ce15c1f4c97b7188550896908a6d67cb90d0e165ea6a607b8e5ef6909f3aa53aa0b06772233b80f1656c79f750f18
|
|
7
|
+
data.tar.gz: a32fb12a213171d4c1bc40c2d55e34f8572bb97ed44b9fccbcf23c0f7844114039175f4ba122ea9d240280e21e57c3edc7da85d82c69012ef98c8575a0b7c457
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -19,27 +19,30 @@ jobs:
|
|
|
19
19
|
ruby-version: "3.3"
|
|
20
20
|
bundler-cache: true
|
|
21
21
|
|
|
22
|
-
-
|
|
23
|
-
|
|
22
|
+
- uses: pnpm/action-setup@v4
|
|
23
|
+
|
|
24
|
+
- uses: actions/setup-node@v6
|
|
24
25
|
with:
|
|
25
|
-
node-version:
|
|
26
|
-
cache:
|
|
26
|
+
node-version: "24"
|
|
27
|
+
cache: "pnpm"
|
|
27
28
|
|
|
28
|
-
- name:
|
|
29
|
-
run:
|
|
29
|
+
- name: Pnpm install
|
|
30
|
+
run: pnpm install
|
|
30
31
|
|
|
31
32
|
- name: Rubocop
|
|
32
33
|
run: bundle exec rubocop
|
|
33
34
|
|
|
34
35
|
- name: ESLint
|
|
35
|
-
|
|
36
|
+
if: ${{ !cancelled() }}
|
|
37
|
+
run: pnpm lint
|
|
36
38
|
|
|
37
39
|
test:
|
|
38
40
|
runs-on: ubuntu-latest
|
|
39
|
-
name: Ruby ${{ matrix.ruby }} (${{ matrix.redis }})
|
|
41
|
+
name: Ruby ${{ matrix.ruby }} (${{ matrix.redis }}, ${{ matrix.rack }})
|
|
40
42
|
timeout-minutes: 10
|
|
41
43
|
|
|
42
44
|
env:
|
|
45
|
+
BUNDLE_GEMFILE: gemfiles/${{ matrix.rack }}.gemfile
|
|
43
46
|
PGHOST: localhost
|
|
44
47
|
PGPASSWORD: postgres
|
|
45
48
|
PGUSER: postgres
|
|
@@ -48,7 +51,8 @@ jobs:
|
|
|
48
51
|
fail-fast: false
|
|
49
52
|
matrix:
|
|
50
53
|
ruby: ["3.2", "3.3", "3.4"]
|
|
51
|
-
|
|
54
|
+
rack: ["rack2", "rack3"]
|
|
55
|
+
redis: ["redis:7.4", "valkey/valkey"]
|
|
52
56
|
|
|
53
57
|
services:
|
|
54
58
|
postgres:
|
|
@@ -77,6 +81,16 @@ jobs:
|
|
|
77
81
|
ruby-version: ${{ matrix.ruby }}
|
|
78
82
|
bundler-cache: true
|
|
79
83
|
|
|
84
|
+
- uses: pnpm/action-setup@v4
|
|
85
|
+
|
|
86
|
+
- uses: actions/setup-node@v6
|
|
87
|
+
with:
|
|
88
|
+
node-version: "24"
|
|
89
|
+
cache: "pnpm"
|
|
90
|
+
|
|
91
|
+
- name: Pnpm install
|
|
92
|
+
run: pnpm install
|
|
93
|
+
|
|
80
94
|
- name: Tests
|
|
81
95
|
env:
|
|
82
96
|
TESTOPTS: --verbose
|
|
@@ -87,6 +101,10 @@ jobs:
|
|
|
87
101
|
needs: [lint, test]
|
|
88
102
|
runs-on: ubuntu-latest
|
|
89
103
|
|
|
104
|
+
permissions:
|
|
105
|
+
id-token: write
|
|
106
|
+
contents: write
|
|
107
|
+
|
|
90
108
|
steps:
|
|
91
109
|
- uses: actions/checkout@v4
|
|
92
110
|
|
|
@@ -108,8 +126,13 @@ jobs:
|
|
|
108
126
|
git add package.json
|
|
109
127
|
git commit -m 'bump'
|
|
110
128
|
|
|
129
|
+
- uses: pnpm/action-setup@v4
|
|
130
|
+
|
|
131
|
+
- uses: actions/setup-node@v6
|
|
132
|
+
with:
|
|
133
|
+
node-version: "24"
|
|
134
|
+
cache: "pnpm"
|
|
135
|
+
|
|
111
136
|
- name: Publish package
|
|
112
|
-
uses: JS-DevTools/npm-publish@v3
|
|
113
137
|
if: steps.publish-gem.outputs.new_version == 'true'
|
|
114
|
-
|
|
115
|
-
token: ${{ secrets.NPM_TOKEN }}
|
|
138
|
+
run: pnpm publish -r
|
data/.gitignore
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
.config
|
|
5
5
|
.yardoc
|
|
6
6
|
Gemfile.lock
|
|
7
|
+
gemfiles/*.gemfile.lock
|
|
7
8
|
InstalledFiles
|
|
8
9
|
_yardoc
|
|
9
10
|
coverage
|
|
@@ -16,7 +17,5 @@ test/tmp
|
|
|
16
17
|
test/version_tmp
|
|
17
18
|
tmp
|
|
18
19
|
*.swp
|
|
19
|
-
.rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml
|
|
20
20
|
.byebug_history
|
|
21
21
|
node_modules/
|
|
22
|
-
yarn.lock
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
13-02-2026
|
|
2
|
+
|
|
3
|
+
- Version 4.5.2
|
|
4
|
+
|
|
5
|
+
- FIX: Use consistent casing for headers
|
|
6
|
+
- DEV: Test against both Rack 2 and Rack 3 in CI
|
|
7
|
+
|
|
8
|
+
10-02-2026
|
|
9
|
+
|
|
10
|
+
- Version 4.5.1
|
|
11
|
+
|
|
12
|
+
- DEV: Allow 2.x rack dependency
|
|
13
|
+
|
|
14
|
+
10-02-2026
|
|
15
|
+
|
|
16
|
+
- Version 4.5.0
|
|
17
|
+
|
|
18
|
+
- Support chunked encoding for Puma 7 on Rack 3.1
|
|
19
|
+
- FIX: Prune empty subscriptions in `ConnectionManager#remove_client`
|
|
20
|
+
|
|
1
21
|
20-03-2025
|
|
2
22
|
|
|
3
23
|
- Version 4.4.1
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A reliable, robust messaging bus for Ruby processes and web clients.
|
|
|
4
4
|
|
|
5
5
|
MessageBus implements a Server to Server channel based protocol and Server to Web Client protocol (using polling, long-polling or long-polling + streaming)
|
|
6
6
|
|
|
7
|
-
Since long-polling is implemented using Rack Hijack and Thin::Async, all common Ruby web servers (
|
|
7
|
+
Since long-polling is implemented using Rack Hijack and Thin::Async, all common Ruby web servers (Pitchfork/Unicorn, Puma, Thin, Passenger) can run MessageBus and handle a large number of concurrent connections that wait on messages.
|
|
8
8
|
|
|
9
9
|
MessageBus is implemented as Rack middleware and can be used by any Rails / Sinatra or pure Rack application.
|
|
10
10
|
|
|
@@ -381,7 +381,9 @@ MessageBus.configure(keepalive_interval: 60)
|
|
|
381
381
|
message_bus supports using Redis as a storage backend, and you can configure message_bus to use redis in `config/initializers/message_bus.rb`, like so:
|
|
382
382
|
|
|
383
383
|
```ruby
|
|
384
|
-
MessageBus.configure(backend: :redis,
|
|
384
|
+
MessageBus.configure(backend: :redis, redis_config: {
|
|
385
|
+
url: "redis://:p4ssw0rd@10.0.1.1:6380/15"
|
|
386
|
+
})
|
|
385
387
|
```
|
|
386
388
|
|
|
387
389
|
The redis client message_bus uses is [redis-rb](https://github.com/redis/redis-rb), so you can visit it's repo to see what other options you can pass besides a `url`.
|
|
@@ -502,7 +504,7 @@ on_worker_boot do
|
|
|
502
504
|
end
|
|
503
505
|
```
|
|
504
506
|
|
|
505
|
-
#### Unicorn
|
|
507
|
+
#### Pitchfork/Unicorn
|
|
506
508
|
|
|
507
509
|
```ruby
|
|
508
510
|
# path/to/your/config/unicorn.rb
|
data/Rakefile
CHANGED
|
@@ -35,7 +35,7 @@ end
|
|
|
35
35
|
namespace :jasmine do
|
|
36
36
|
desc "Run Jasmine tests in headless mode"
|
|
37
37
|
task 'ci' do
|
|
38
|
-
if !system("
|
|
38
|
+
if !system("pnpm jasmine-browser-runner runSpecs")
|
|
39
39
|
exit 1
|
|
40
40
|
end
|
|
41
41
|
end
|
|
@@ -71,7 +71,7 @@ namespace :spec do
|
|
|
71
71
|
|
|
72
72
|
begin
|
|
73
73
|
ENV['MESSAGE_BUS_BACKEND'] = 'memory'
|
|
74
|
-
pid = spawn("bundle exec
|
|
74
|
+
pid = spawn("bundle exec pitchfork -p 9292 spec/fixtures/test/config.ru")
|
|
75
75
|
sleep 1 while port_available?(9292)
|
|
76
76
|
Rake::TestTask.new(:integration) do |t|
|
|
77
77
|
t.test_files = INTEGRATION_FILES
|
data/docker-compose.yml
CHANGED
|
@@ -36,7 +36,7 @@ services:
|
|
|
36
36
|
example:
|
|
37
37
|
build:
|
|
38
38
|
context: .
|
|
39
|
-
command: bash -c "cd examples/chat && bundle install && bundle exec rackup --server
|
|
39
|
+
command: bash -c "cd examples/chat && bundle install && bundle exec rackup --server pitchfork --host 0.0.0.0"
|
|
40
40
|
environment:
|
|
41
41
|
BUNDLE_TO: /usr/local/bundle
|
|
42
42
|
REDISURL: redis://redis:6379
|
data/examples/bench/config.ru
CHANGED
|
@@ -30,4 +30,4 @@ end
|
|
|
30
30
|
MessageBus.long_polling_interval = 1000 * 2
|
|
31
31
|
MessageBus.max_active_clients = 10000
|
|
32
32
|
use MessageBus::Rack::Middleware
|
|
33
|
-
run lambda { |_env| [200, { "
|
|
33
|
+
run lambda { |_env| [200, { "content-type" => "text/html" }, ["Howdy"]] }
|
data/examples/chat/Gemfile
CHANGED
|
@@ -33,18 +33,18 @@ hooks:
|
|
|
33
33
|
- exec: cd /var/www && git clone --depth 1 https://github.com/SamSaffron/message_bus.git
|
|
34
34
|
- exec:
|
|
35
35
|
cmd:
|
|
36
|
-
- gem install
|
|
36
|
+
- gem install pitchfork
|
|
37
37
|
- gem install redis
|
|
38
38
|
- gem install sinatra
|
|
39
39
|
- file:
|
|
40
|
-
path: /etc/service/
|
|
40
|
+
path: /etc/service/pitchfork/run
|
|
41
41
|
chmod: "+x"
|
|
42
42
|
contents: |
|
|
43
43
|
#!/bin/bash
|
|
44
44
|
exec 2>&1
|
|
45
45
|
# redis
|
|
46
46
|
cd $home/examples/chat
|
|
47
|
-
exec sudo -E -u chat LD_PRELOAD=/usr/lib/libjemalloc.so.1
|
|
47
|
+
exec sudo -E -u chat LD_PRELOAD=/usr/lib/libjemalloc.so.1 pitchfork -p 8080 -e production
|
|
48
48
|
- exec: rm /etc/nginx/sites-enabled/default
|
|
49
49
|
- replace:
|
|
50
50
|
filename: /etc/nginx/nginx.conf
|
data/examples/minimal/config.ru
CHANGED
data/lib/message_bus/client.rb
CHANGED
|
@@ -228,18 +228,18 @@ class MessageBus::Client
|
|
|
228
228
|
NEWLINE = "\r\n".freeze
|
|
229
229
|
COLON_SPACE = ": ".freeze
|
|
230
230
|
HTTP_11 = "HTTP/1.1 200 OK\r\n".freeze
|
|
231
|
-
CONTENT_LENGTH = "
|
|
232
|
-
CONNECTION_CLOSE = "
|
|
233
|
-
CHUNKED_ENCODING = "
|
|
234
|
-
NO_SNIFF = "
|
|
231
|
+
CONTENT_LENGTH = "content-length: ".freeze
|
|
232
|
+
CONNECTION_CLOSE = "connection: close\r\n".freeze
|
|
233
|
+
CHUNKED_ENCODING = "transfer-encoding: chunked\r\n".freeze
|
|
234
|
+
NO_SNIFF = "x-content-type-options: nosniff\r\n".freeze
|
|
235
235
|
|
|
236
|
-
TYPE_TEXT = "
|
|
237
|
-
TYPE_JSON = "
|
|
236
|
+
TYPE_TEXT = "content-type: text/plain; charset=utf-8\r\n".freeze
|
|
237
|
+
TYPE_JSON = "content-type: application/json; charset=utf-8\r\n".freeze
|
|
238
238
|
|
|
239
239
|
def write_headers
|
|
240
240
|
@io.write(HTTP_11)
|
|
241
241
|
@headers.each do |k, v|
|
|
242
|
-
next if k == "
|
|
242
|
+
next if k == "content-type"
|
|
243
243
|
|
|
244
244
|
@io.write(k)
|
|
245
245
|
@io.write(COLON_SPACE)
|
|
@@ -76,8 +76,13 @@ class MessageBus::ConnectionManager
|
|
|
76
76
|
def remove_client(c)
|
|
77
77
|
synchronize do
|
|
78
78
|
@clients.delete c.client_id
|
|
79
|
-
@subscriptions[c.site_id]
|
|
80
|
-
|
|
79
|
+
site_subs = @subscriptions[c.site_id]
|
|
80
|
+
if site_subs
|
|
81
|
+
site_subs.delete_if do |_k, set|
|
|
82
|
+
set.delete c.client_id
|
|
83
|
+
set.empty?
|
|
84
|
+
end
|
|
85
|
+
@subscriptions.delete(c.site_id) if site_subs.empty?
|
|
81
86
|
end
|
|
82
87
|
if c.cleanup_timer
|
|
83
88
|
# concurrency may cause this to fail
|
|
@@ -75,17 +75,17 @@ class MessageBus::Rack::Middleware
|
|
|
75
75
|
if @bus.allow_broadcast? && env['PATH_INFO'] == @broadcast_route
|
|
76
76
|
parsed = Rack::Request.new(env)
|
|
77
77
|
@bus.publish parsed["channel"], parsed["data"]
|
|
78
|
-
return [200, { "
|
|
78
|
+
return [200, { "content-type" => "text/html" }, ["sent"]]
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
client_id = env['PATH_INFO'][@base_route_length..-1].split("/")[0]
|
|
82
82
|
return [404, {}, ["not found"]] unless client_id
|
|
83
83
|
|
|
84
84
|
headers = {}
|
|
85
|
-
headers["
|
|
86
|
-
headers["
|
|
87
|
-
headers["
|
|
88
|
-
headers["
|
|
85
|
+
headers["cache-control"] = "must-revalidate, private, max-age=0"
|
|
86
|
+
headers["content-type"] = "application/json; charset=utf-8"
|
|
87
|
+
headers["pragma"] = "no-cache"
|
|
88
|
+
headers["expires"] = "0"
|
|
89
89
|
|
|
90
90
|
if @bus.extra_response_headers_lookup
|
|
91
91
|
@bus.extra_response_headers_lookup.call(env).each do |k, v|
|
|
@@ -131,7 +131,7 @@ class MessageBus::Rack::Middleware
|
|
|
131
131
|
env['QUERY_STRING'] !~ /dlp=t/ &&
|
|
132
132
|
@connection_manager.client_count < @bus.max_active_clients
|
|
133
133
|
|
|
134
|
-
allow_chunked = env['HTTP_VERSION'] == 'HTTP/1.1'
|
|
134
|
+
allow_chunked = env['SERVER_PROTOCOL'] == 'HTTP/1.1' || env['HTTP_VERSION'] == 'HTTP/1.1'
|
|
135
135
|
allow_chunked &&= !env['HTTP_DONT_CHUNK']
|
|
136
136
|
allow_chunked &&= @bus.chunked_encoding_enabled?
|
|
137
137
|
|
|
@@ -169,9 +169,9 @@ class MessageBus::Rack::Middleware
|
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
if allow_chunked
|
|
172
|
-
response.headers["
|
|
173
|
-
response.headers["
|
|
174
|
-
response.headers["
|
|
172
|
+
response.headers["x-content-type-options"] = "nosniff"
|
|
173
|
+
response.headers["transfer-encoding"] = "chunked"
|
|
174
|
+
response.headers["content-type"] = "text/plain; charset=utf-8"
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
response.status = 200
|
data/lib/message_bus/version.rb
CHANGED
data/message_bus.gemspec
CHANGED
|
@@ -14,9 +14,9 @@ Gem::Specification.new do |gem|
|
|
|
14
14
|
gem.name = "message_bus"
|
|
15
15
|
gem.require_paths = ["lib"]
|
|
16
16
|
gem.version = MessageBus::VERSION
|
|
17
|
-
gem.required_ruby_version = ">= 2.
|
|
17
|
+
gem.required_ruby_version = ">= 3.2.0"
|
|
18
18
|
|
|
19
|
-
gem.add_runtime_dependency 'rack', '
|
|
19
|
+
gem.add_runtime_dependency 'rack', '> 2', '< 4'
|
|
20
20
|
|
|
21
21
|
# Optional runtime dependencies
|
|
22
22
|
gem.add_development_dependency 'redis'
|
|
@@ -30,13 +30,12 @@ Gem::Specification.new do |gem|
|
|
|
30
30
|
gem.add_development_dependency 'http_parser.rb'
|
|
31
31
|
gem.add_development_dependency 'thin'
|
|
32
32
|
gem.add_development_dependency 'rack-test'
|
|
33
|
-
gem.add_development_dependency '
|
|
33
|
+
gem.add_development_dependency 'pitchfork'
|
|
34
34
|
gem.add_development_dependency 'm'
|
|
35
35
|
gem.add_development_dependency 'byebug'
|
|
36
|
+
gem.add_development_dependency 'method_source'
|
|
36
37
|
gem.add_development_dependency 'oj'
|
|
37
38
|
gem.add_development_dependency 'yard'
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
gem.add_development_dependency 'rubocop-discourse', '3.8.1'
|
|
41
|
-
end
|
|
40
|
+
gem.add_development_dependency 'rubocop-discourse', '3.8.1'
|
|
42
41
|
end
|
data/package.json
CHANGED
|
@@ -19,9 +19,16 @@
|
|
|
19
19
|
"url": "https://github.com/discourse/message_bus/issues"
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/discourse/message_bus#readme",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"lint": "eslint ."
|
|
27
|
+
},
|
|
22
28
|
"devDependencies": {
|
|
23
29
|
"eslint": "^8.31.0",
|
|
24
30
|
"jasmine-browser-runner": "^0.10.0",
|
|
25
31
|
"jasmine-core": "^3.10.1"
|
|
26
|
-
}
|
|
32
|
+
},
|
|
33
|
+
"packageManager": "pnpm@9.15.5"
|
|
27
34
|
}
|