async-websocket 0.14.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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