async-websocket 0.14.0 → 0.19.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/websocket/adapters/http.rb +64 -0
  3. data/lib/async/websocket/adapters/rack.rb +11 -28
  4. data/{spec/async/websocket/upgrade.rb → lib/async/websocket/adapters/rails.rb} +20 -19
  5. data/lib/async/websocket/client.rb +19 -13
  6. data/lib/async/websocket/connect_request.rb +4 -0
  7. data/lib/async/websocket/connection.rb +1 -0
  8. data/lib/async/websocket/version.rb +1 -1
  9. metadata +37 -101
  10. data/.editorconfig +0 -6
  11. data/.gitignore +0 -13
  12. data/.rspec +0 -3
  13. data/.travis.yml +0 -21
  14. data/Gemfile +0 -7
  15. data/README.md +0 -143
  16. data/async-websocket.gemspec +0 -29
  17. data/examples/chat/.env +0 -3
  18. data/examples/chat/README.md +0 -147
  19. data/examples/chat/client.rb +0 -32
  20. data/examples/chat/config.ru +0 -150
  21. data/examples/chat/multi-client.rb +0 -79
  22. data/examples/mud/client.rb +0 -34
  23. data/examples/mud/config.ru +0 -142
  24. data/examples/rack/client.rb +0 -19
  25. data/examples/rack/config.ru +0 -12
  26. data/examples/utopia/.bowerrc +0 -4
  27. data/examples/utopia/.gitignore +0 -9
  28. data/examples/utopia/.rspec +0 -4
  29. data/examples/utopia/Gemfile +0 -33
  30. data/examples/utopia/Guardfile +0 -29
  31. data/examples/utopia/README.md +0 -16
  32. data/examples/utopia/Rakefile +0 -8
  33. data/examples/utopia/config.ru +0 -47
  34. data/examples/utopia/config/README.md +0 -7
  35. data/examples/utopia/config/environment.rb +0 -8
  36. data/examples/utopia/lib/readme.txt +0 -1
  37. data/examples/utopia/pages/_heading.xnode +0 -2
  38. data/examples/utopia/pages/_page.xnode +0 -30
  39. data/examples/utopia/pages/client/client.js +0 -28
  40. data/examples/utopia/pages/client/index.xnode +0 -8
  41. data/examples/utopia/pages/errors/exception.xnode +0 -5
  42. data/examples/utopia/pages/errors/file-not-found.xnode +0 -5
  43. data/examples/utopia/pages/links.yaml +0 -2
  44. data/examples/utopia/pages/server/controller.rb +0 -26
  45. data/examples/utopia/public/_static/icon.png +0 -0
  46. data/examples/utopia/public/_static/site.css +0 -205
  47. data/examples/utopia/public/_static/utopia-background.svg +0 -1
  48. data/examples/utopia/public/_static/utopia.svg +0 -1
  49. data/examples/utopia/public/readme.txt +0 -1
  50. data/examples/utopia/spec/spec_helper.rb +0 -31
  51. data/examples/utopia/spec/website_context.rb +0 -11
  52. data/examples/utopia/spec/website_spec.rb +0 -56
  53. data/examples/utopia/tasks/bower.rake +0 -45
  54. data/examples/utopia/tasks/deploy.rake +0 -13
  55. data/examples/utopia/tasks/development.rake +0 -34
  56. data/examples/utopia/tasks/environment.rake +0 -17
  57. data/examples/utopia/tasks/log.rake +0 -17
  58. data/examples/utopia/tasks/static.rake +0 -43
  59. data/spec/async/websocket/adapters/rack/client.rb +0 -38
  60. data/spec/async/websocket/adapters/rack/config.ru +0 -23
  61. data/spec/async/websocket/adapters/rack_spec.rb +0 -84
  62. data/spec/async/websocket/connection_spec.rb +0 -34
  63. data/spec/async/websocket/server_examples.rb +0 -117
  64. data/spec/async/websocket/server_spec.rb +0 -31
  65. data/spec/spec_helper.rb +0 -16
data/.editorconfig DELETED
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
12
- Gemfile.lock
13
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- language: ruby
2
- dist: xenial
3
- cache: bundler
4
-
5
- script: bundle exec rspec
6
-
7
- matrix:
8
- include:
9
- - rvm: 2.5
10
- - rvm: 2.6
11
- - rvm: 2.7
12
- - rvm: 2.6
13
- env: COVERAGE=PartialSummary,Coveralls
14
- - rvm: truffleruby
15
- - rvm: jruby-head
16
- env: JRUBY_OPTS="--debug -X+O"
17
- - rvm: ruby-head
18
- allow_failures:
19
- - rvm: ruby-head
20
- - rvm: jruby-head
21
- - rvm: truffleruby
data/Gemfile DELETED
@@ -1,7 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :test do
6
- gem 'rack-test'
7
- end
data/README.md DELETED
@@ -1,143 +0,0 @@
1
- # Async::WebSocket
2
-
3
- A simple asynchronous websocket client/server implementation for [HTTP/1](https://tools.ietf.org/html/rfc6455) and [HTTP/2](https://tools.ietf.org/html/rfc8441).
4
-
5
- [![Build Status](https://secure.travis-ci.org/socketry/async-websocket.svg)](http://travis-ci.org/socketry/async-websocket)
6
- [![Code Climate](https://codeclimate.com/github/socketry/async-websocket.svg)](https://codeclimate.com/github/socketry/async-websocket)
7
- [![Coverage Status](https://coveralls.io/repos/socketry/async-websocket/badge.svg)](https://coveralls.io/r/socketry/async-websocket)
8
-
9
- [![WebSocket Client & Server for Ruby](https://img.youtube.com/vi/aHop4Yyjs_o/0.jpg)](https://www.youtube.com/watch?v=aHop4Yyjs_o)
10
-
11
- ## Installation
12
-
13
- Add this line to your application's Gemfile:
14
-
15
- gem 'async-websocket'
16
-
17
- And then execute:
18
-
19
- $ bundle
20
-
21
- Or install it yourself as:
22
-
23
- $ gem install async-websocket
24
-
25
- ## Usage
26
-
27
- There are [examples](examples/) which include:
28
-
29
- - [A command line chat client/server](examples/chat) which can read input from `stdin` and send messages to the server.
30
- - [A small MUD client/server](examples/mud) which uses JSON for communation between client/server.
31
- - [A utopia-based web application](examples/utopia) which uses a JavaScript client to connect to a web application server.
32
-
33
- ### Client Side with Async
34
-
35
- ```ruby
36
- #!/usr/bin/env ruby
37
-
38
- require 'async'
39
- require 'async/io/stream'
40
- require 'async/http/endpoint'
41
- require 'async/websocket/client'
42
-
43
- USER = ARGV.pop || "anonymous"
44
- URL = ARGV.pop || "http://localhost:7070"
45
-
46
- Async do |task|
47
- stdin = Async::IO::Stream.new(
48
- Async::IO::Generic.new($stdin)
49
- )
50
-
51
- endpoint = Async::HTTP::Endpoint.parse(URL)
52
-
53
- Async::WebSocket::Client.connect(endpoint) do |connection|
54
- input_task = task.async do
55
- while line = stdin.read_until("\n")
56
- connection.write({user: USER, text: line})
57
- connection.flush
58
- end
59
- end
60
-
61
- connection.write({
62
- user: USER,
63
- status: "connected",
64
- })
65
-
66
- while message = connection.read
67
- puts message.inspect
68
- end
69
- ensure
70
- input_task&.stop
71
- end
72
- end
73
- ```
74
-
75
- ### Server Side with Rack & Falcon
76
-
77
- ```ruby
78
- #!/usr/bin/env -S falcon serve --bind http://localhost:7070 --count 1 -c
79
-
80
- require 'async/websocket/adapters/rack'
81
- require 'set'
82
-
83
- $connections = Set.new
84
-
85
- run lambda {|env|
86
- Async::WebSocket::Adapters::Rack.open(env, protocols: ['ws']) do |connection|
87
- $connections << connection
88
-
89
- while message = connection.read
90
- $connections.each do |connection|
91
- connection.write(message)
92
- connection.flush
93
- end
94
- end
95
- ensure
96
- $connections.delete(connection)
97
- end or [200, {}, ["Hello World"]]
98
- }
99
- ```
100
-
101
- ### Force HTTP/1 Connection
102
-
103
- This forces the endpoint to connect using `HTTP/1.1`.
104
-
105
- ```ruby
106
- endpoint = Async::HTTP::Endpoint.parse("https://remote-server.com", alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
107
-
108
- Async::WebSocket::Client.connect(endpoint) do ...
109
- ```
110
-
111
- You may want to use this if the server advertises `HTTP/2` but doesn't support `HTTP/2` for WebSocket connections.
112
-
113
- ## Contributing
114
-
115
- 1. Fork it
116
- 2. Create your feature branch (`git checkout -b my-new-feature`)
117
- 3. Commit your changes (`git commit -am 'Add some feature'`)
118
- 4. Push to the branch (`git push origin my-new-feature`)
119
- 5. Create new Pull Request
120
-
121
- ## License
122
-
123
- Released under the MIT license.
124
-
125
- Copyright, 2015, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
126
-
127
- Permission is hereby granted, free of charge, to any person obtaining a copy
128
- of this software and associated documentation files (the "Software"), to deal
129
- in the Software without restriction, including without limitation the rights
130
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
131
- copies of the Software, and to permit persons to whom the Software is
132
- furnished to do so, subject to the following conditions:
133
-
134
- The above copyright notice and this permission notice shall be included in
135
- all copies or substantial portions of the Software.
136
-
137
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
138
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
139
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
140
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
141
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
142
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
143
- THE SOFTWARE.
@@ -1,29 +0,0 @@
1
-
2
- require_relative 'lib/async/websocket/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "async-websocket"
6
- spec.version = Async::WebSocket::VERSION
7
- spec.authors = ["Samuel Williams"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
- spec.summary = %q{An async websocket library on top of websocket-driver.}
10
- spec.homepage = ""
11
- spec.license = "MIT"
12
-
13
- spec.files = `git ls-files -z`.split("\x0")
14
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
- spec.require_paths = ["lib"]
17
-
18
- spec.add_dependency "async-io", "~> 1.23"
19
- spec.add_dependency "async-http", "~> 0.51"
20
- spec.add_dependency "protocol-websocket", "~> 0.7.0"
21
-
22
- spec.add_development_dependency "async-rspec"
23
- spec.add_development_dependency "falcon", "~> 0.34"
24
-
25
- spec.add_development_dependency "covered"
26
- spec.add_development_dependency "bundler"
27
- spec.add_development_dependency "rspec", "~> 3.6"
28
- spec.add_development_dependency "bake-bundler"
29
- end
data/examples/chat/.env DELETED
@@ -1,3 +0,0 @@
1
- export RUBY_FIBER_VM_STACK_SIZE=0
2
- export RUBY_FIBER_MACHINE_STACK_SIZE=0
3
- export RUBY_SHARED_FIBER_POOL_FREE_STACKS=0
@@ -1,147 +0,0 @@
1
- # The Journey to One Million
2
-
3
- ## Allocations per Connection
4
-
5
- ```
6
- Array: 188498 allocations
7
- Hash: 137041 allocations
8
- String: 91387 allocations
9
- Proc: 81242 allocations
10
- Fiber: 30169 allocations
11
- Async::Task: 30168 allocations
12
- Async::IO::Buffer: 20904 allocations
13
- Protocol::HTTP2::Window: 20162 allocations
14
- Set: 20091 allocations
15
- Async::Queue: 20082 allocations
16
- Method: 20006 allocations
17
- Protocol::HTTP::Headers::Merged: 10100 allocations
18
- Protocol::HTTP::Headers: 10100 allocations
19
- Async::Condition: 10002 allocations
20
- Protocol::WebSocket::Framer: 10001 allocations
21
- Async::HTTP::Body::Stream: 10001 allocations
22
- Async::HTTP::Body::Hijack: 10001 allocations
23
- Async::WebSocket::ConnectResponse: 10001 allocations
24
- Async::WebSocket::Connection: 10001 allocations
25
- Async::HTTP::Body::Writable: 10001 allocations
26
- Async::HTTP::Protocol::HTTP2::Request::Stream: 10001 allocations
27
- Async::HTTP::Protocol::HTTP2::Request: 10001 allocations
28
- Falcon::Adapters::Input: 10001 allocations
29
- Protocol::HTTP::Headers::Split: 10001 allocations
30
- Async::HTTP::Protocol::HTTP2::Stream::Input: 10001 allocations
31
- Async::HTTP::Protocol::HTTP2::Stream::Output: 10001 allocations
32
- ** 80.98830116988302 objects per connection.
33
- ```
34
-
35
- ## System Limits
36
-
37
- ### Fiber Performance
38
-
39
- To improve fiber performance:
40
-
41
- export RUBY_FIBER_VM_STACK_SIZE=0
42
- export RUBY_FIBER_MACHINE_STACK_SIZE=0
43
- export RUBY_SHARED_FIBER_POOL_FREE_STACKS=0
44
-
45
- `RUBY_SHARED_FIBER_POOL_FREE_STACKS` is an experimental feature on `ruby-head`.
46
-
47
- ### FiberError: can't set a guard page: Cannot allocate memory
48
-
49
- This error occurs because the operating system has limited resources for allocating fiber stacks.
50
-
51
- You can find the current limit:
52
-
53
- % sysctl vm.max_map_count
54
- vm.max_map_count = 65530
55
-
56
- You can increase it:
57
-
58
- % sysctl -w vm.max_map_count=2500000
59
-
60
- ## Logs
61
-
62
- ### 2020
63
-
64
- ```
65
- koyoko% ./multi-client.rb -c 100000
66
- 0.15s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:39 +1300]
67
- | Made 1 connections: 202.88 connections/second...
68
- 0.15s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:39 +1300]
69
- | GC.start duration=0.0s GC.count=27
70
- 8.82s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:47 +1300]
71
- | Made 10001 connections: 1152.85 connections/second...
72
- 8.89s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:47 +1300]
73
- | GC.start duration=0.06s GC.count=28
74
- 17.7s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:56 +1300]
75
- | Made 20001 connections: 1139.39 connections/second...
76
- 17.84s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:11:56 +1300]
77
- | GC.start duration=0.14s GC.count=29
78
- 26.71s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:05 +1300]
79
- | Made 30001 connections: 1129.44 connections/second...
80
- 26.9s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:05 +1300]
81
- | GC.start duration=0.19s GC.count=30
82
- 35.97s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:14 +1300]
83
- | Made 40001 connections: 1116.59 connections/second...
84
- 36.22s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:15 +1300]
85
- | GC.start duration=0.25s GC.count=31
86
- 45.41s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:24 +1300]
87
- | Made 50001 connections: 1104.74 connections/second...
88
- 45.65s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:24 +1300]
89
- | GC.start duration=0.24s GC.count=32
90
- 54.94s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:33 +1300]
91
- | Made 60001 connections: 1095.18 connections/second...
92
- 55.3s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:34 +1300]
93
- | GC.start duration=0.37s GC.count=33
94
- 1m4s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:43 +1300]
95
- | Made 70001 connections: 1085.98 connections/second...
96
- 1m5s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:43 +1300]
97
- | GC.start duration=0.44s GC.count=34
98
- 1m14s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:53 +1300]
99
- | Made 80001 connections: 1074.6 connections/second...
100
- 1m14s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:12:53 +1300]
101
- | GC.start duration=0.36s GC.count=35
102
- 1m24s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:13:03 +1300]
103
- | Made 90001 connections: 1066.37 connections/second...
104
- 1m24s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:13:03 +1300]
105
- | GC.start duration=0.35s GC.count=36
106
- 1m34s info: Command [oid=0x2f8] [pid=820099] [2020-02-04 23:13:13 +1300]
107
- | Finished top level connection loop...
108
- ```
109
-
110
- ### 2019
111
-
112
- This report is affected by `tty-progressbar` bugs.
113
-
114
- ```
115
- koyoko% bundle exec ./multi-client.rb --count 100000
116
- 145.94 connection/s [ ] 1/100000 ( 6s/ 0s)
117
- 0.11s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:29:58 +1200]
118
- | GC.start -> 0.01s
119
- 591.10 connection/s [========== ] 10001/100000 ( 1m52s/12s)
120
- 12.92s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:30:11 +1200]
121
- | GC.start -> 0.3s
122
- 455.06 connection/s [==================== ] 20001/100000 ( 1m42s/25s)
123
- 26.17s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:30:24 +1200]
124
- | GC.start -> 0.45s
125
- 294.11 connection/s [============================= ] 30001/100000 ( 1m31s/39s)
126
- 39.95s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:30:38 +1200]
127
- | GC.start -> 0.68s
128
- 153.08 connection/s [======================================= ] 40001/100000 ( 1m19s/53s)
129
- 53.9s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:30:52 +1200]
130
- | GC.start -> 0.8s
131
- 23.03 connection/s [================================================ ] 50001/100000 ( 1m 7s/ 1m 7s)
132
- 1m8s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:31:07 +1200]
133
- | GC.start -> 0.95s
134
- 0.87552 connection/s [========================================================== ] 60001/100000 (55s/ 1m23s)
135
- 1m24s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:31:23 +1200]
136
- | GC.start -> 1.04s
137
- 0.74375 connection/s [==================================================================== ] 70001/100000 (43s/ 1m42s)
138
- 1m43s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:31:42 +1200]
139
- | GC.start -> 1.17s
140
- 0.64832 connection/s [============================================================================== ] 80001/100000 (30s/ 2m 2s)
141
- 2m4s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:32:02 +1200]
142
- | GC.start -> 1.29s
143
- 0.57842 connection/s [======================================================================================= ] 90001/100000 (16s/ 2m26s)
144
- 2m27s info: #<Command:0x000055de596579e8> [pid=14815] [2019-07-08 00:32:26 +1200]
145
- | GC.start -> 1.55s
146
- 435.05 connection/s [=================================================================================================] 100000/100000 ( 0s/ 2m50s)
147
- ```
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'async'
4
- require 'async/io/stream'
5
- require 'async/http/endpoint'
6
- require_relative '../../lib/async/websocket/client'
7
-
8
- USER = ARGV.pop || "anonymous"
9
- URL = ARGV.pop || "https://localhost:8080"
10
- ENDPOINT = Async::HTTP::Endpoint.parse(URL)
11
-
12
- Async do |task|
13
- stdin = Async::IO::Stream.new(
14
- Async::IO::Generic.new($stdin)
15
- )
16
-
17
- Async::WebSocket::Client.connect(ENDPOINT) do |connection|
18
- input_task = task.async do
19
- while line = stdin.read_until("\n")
20
- connection.write({text: line})
21
- connection.flush
22
- end
23
- end
24
-
25
- puts "Connected..."
26
- while message = connection.read
27
- puts "> #{message.inspect}"
28
- end
29
- ensure
30
- input_task&.stop
31
- end
32
- end
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env -S falcon serve --bind https://localhost:8080 --count 1 -c
2
-
3
- require_relative '../../lib/async/websocket/adapters/rack'
4
- require 'async/clock'
5
- require 'async/semaphore'
6
- require 'async/logger'
7
-
8
- require 'set'
9
-
10
- GC.disable
11
-
12
- class Room
13
- def initialize
14
- @connections = Set.new
15
- @semaphore = Async::Semaphore.new(512)
16
-
17
- @count = 0
18
- @profile = nil
19
- end
20
-
21
- def connect connection
22
- @connections << connection
23
-
24
- @count += 1
25
-
26
- if (@count % 10000).zero?
27
- # (full_mark: false, immediate_sweep: false)
28
- duration = Async::Clock.measure{GC.start}
29
- Async.logger.info(self) {"GC.start duration=#{duration.round(2)}s GC.count=#{GC.count} @connections.count=#{@connections.count}"}
30
- end
31
- end
32
-
33
- def disconnect connection
34
- @connections.delete(connection)
35
- end
36
-
37
- def each(&block)
38
- @connections.each(&block)
39
- end
40
-
41
- def allocations
42
- counts = Hash.new{|h,k| h[k] = 0}
43
-
44
- ObjectSpace.each_object do |object|
45
- counts[object.class] += 1
46
- end
47
-
48
- return counts
49
- end
50
-
51
- def show_allocations(key, limit = 1000)
52
- Async.logger.info(self) do |buffer|
53
- ObjectSpace.each_object(key).each do |object|
54
- buffer.puts object
55
- end
56
- end
57
- end
58
-
59
- def print_allocations(minimum = @connections.count)
60
- count = 0
61
-
62
- Async.logger.info(self) do |buffer|
63
- allocations.select{|k,v| v >= minimum}.sort_by{|k,v| -v}.each do |key, value|
64
- count += value
65
- buffer.puts "#{key}: #{value} allocations"
66
- end
67
-
68
- buffer.puts "** #{count.to_f / @connections.count} objects per connection."
69
- end
70
- end
71
-
72
- def start_profile
73
- require 'ruby-prof' unless defined?(RubyProf)
74
-
75
- return false if @profile
76
-
77
- @profile = RubyProf::Profile.new(merge_fibers: true)
78
- @profile.start
79
- end
80
-
81
- def stop_profile
82
- return false unless @profile
83
-
84
- result = @profile.stop
85
- printer = RubyProf::FlatPrinter.new(result)
86
- printer.print(STDOUT, min_percent: 0.5)
87
-
88
- # printer = RubyProf::GraphPrinter.new(result)
89
- # printer.print(STDOUT, min_percent: 0.5)
90
-
91
- @profile = nil
92
- end
93
-
94
- def command(code)
95
- Async.logger.warn self, "eval(#{code})"
96
-
97
- eval(code)
98
- end
99
-
100
- def broadcast(message)
101
- Async.logger.info "Broadcast: #{message.inspect}"
102
- start_time = Async::Clock.now
103
-
104
- @connections.each do |connection|
105
- @semaphore.async do
106
- connection.write(message)
107
- connection.flush
108
- end
109
- end
110
-
111
- end_time = Async::Clock.now
112
- Async.logger.info "Duration: #{(end_time - start_time).round(3)}s for #{@connections.count} connected clients."
113
- end
114
-
115
- def open(connection)
116
- self.connect(connection)
117
-
118
- if @connections.size == 1_000_000
119
- connection.write("Congratulations, you have completed the journey to one million! 🥳 👏👏👏🏼")
120
- end
121
-
122
- while message = connection.read
123
- if message[:text] =~ /^\/(.*?)$/
124
- begin
125
- result = self.command($1)
126
-
127
- if result.is_a? Hash
128
- connection.write(result)
129
- else
130
- connection.write({result: result.inspect})
131
- end
132
- rescue
133
- connection.write({error: $!.inspect})
134
- end
135
- else
136
- self.broadcast(message)
137
- end
138
- end
139
-
140
- connection.close
141
- ensure
142
- self.disconnect(connection)
143
- end
144
-
145
- def call(env)
146
- Async::WebSocket::Adapters::Rack.open(env, &self.method(:open))
147
- end
148
- end
149
-
150
- run Room.new