async-websocket 0.14.0 → 0.15.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/websocket/adapters/rack.rb +1 -1
  3. data/lib/async/websocket/client.rb +7 -1
  4. data/lib/async/websocket/version.rb +1 -1
  5. metadata +60 -84
  6. data/.editorconfig +0 -6
  7. data/.gitignore +0 -13
  8. data/.rspec +0 -3
  9. data/.travis.yml +0 -21
  10. data/Gemfile +0 -7
  11. data/README.md +0 -143
  12. data/async-websocket.gemspec +0 -29
  13. data/examples/chat/.env +0 -3
  14. data/examples/chat/README.md +0 -147
  15. data/examples/chat/client.rb +0 -32
  16. data/examples/chat/config.ru +0 -150
  17. data/examples/chat/multi-client.rb +0 -79
  18. data/examples/mud/client.rb +0 -34
  19. data/examples/mud/config.ru +0 -142
  20. data/examples/rack/client.rb +0 -19
  21. data/examples/rack/config.ru +0 -12
  22. data/examples/utopia/.bowerrc +0 -4
  23. data/examples/utopia/.gitignore +0 -9
  24. data/examples/utopia/.rspec +0 -4
  25. data/examples/utopia/Gemfile +0 -33
  26. data/examples/utopia/Guardfile +0 -29
  27. data/examples/utopia/README.md +0 -16
  28. data/examples/utopia/Rakefile +0 -8
  29. data/examples/utopia/config.ru +0 -47
  30. data/examples/utopia/config/README.md +0 -7
  31. data/examples/utopia/config/environment.rb +0 -8
  32. data/examples/utopia/lib/readme.txt +0 -1
  33. data/examples/utopia/pages/_heading.xnode +0 -2
  34. data/examples/utopia/pages/_page.xnode +0 -30
  35. data/examples/utopia/pages/client/client.js +0 -28
  36. data/examples/utopia/pages/client/index.xnode +0 -8
  37. data/examples/utopia/pages/errors/exception.xnode +0 -5
  38. data/examples/utopia/pages/errors/file-not-found.xnode +0 -5
  39. data/examples/utopia/pages/links.yaml +0 -2
  40. data/examples/utopia/pages/server/controller.rb +0 -26
  41. data/examples/utopia/public/_static/icon.png +0 -0
  42. data/examples/utopia/public/_static/site.css +0 -205
  43. data/examples/utopia/public/_static/utopia-background.svg +0 -1
  44. data/examples/utopia/public/_static/utopia.svg +0 -1
  45. data/examples/utopia/public/readme.txt +0 -1
  46. data/examples/utopia/spec/spec_helper.rb +0 -31
  47. data/examples/utopia/spec/website_context.rb +0 -11
  48. data/examples/utopia/spec/website_spec.rb +0 -56
  49. data/examples/utopia/tasks/bower.rake +0 -45
  50. data/examples/utopia/tasks/deploy.rake +0 -13
  51. data/examples/utopia/tasks/development.rake +0 -34
  52. data/examples/utopia/tasks/environment.rake +0 -17
  53. data/examples/utopia/tasks/log.rake +0 -17
  54. data/examples/utopia/tasks/static.rake +0 -43
  55. data/spec/async/websocket/adapters/rack/client.rb +0 -38
  56. data/spec/async/websocket/adapters/rack/config.ru +0 -23
  57. data/spec/async/websocket/adapters/rack_spec.rb +0 -84
  58. data/spec/async/websocket/connection_spec.rb +0 -34
  59. data/spec/async/websocket/server_examples.rb +0 -117
  60. data/spec/async/websocket/server_spec.rb +0 -31
  61. data/spec/async/websocket/upgrade.rb +0 -43
  62. data/spec/spec_helper.rb +0 -16
@@ -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
@@ -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
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'async'
4
- require 'async/semaphore'
5
- require 'async/clock'
6
- require 'async/io/stream'
7
- require 'async/http/endpoint'
8
- require_relative '../../lib/async/websocket/client'
9
-
10
- require 'samovar'
11
-
12
- GC.disable
13
-
14
- class Command < Samovar::Command
15
- options do
16
- option "-c/--count <integer>", "The total number of connections to make.", default: 1000, type: Integer
17
- option "--bind <address>", "The local address to bind to before making a connection."
18
- option "--connect <string>", "The remote server to connect to.", default: "https://localhost:8080"
19
-
20
- option "-s/--semaphore <integer>", "The number of simultaneous connections to perform."
21
- end
22
-
23
- def local_address
24
- if bind = @options[:bind]
25
- Async::IO::Address.tcp(bind, 0)
26
- end
27
- end
28
-
29
- def call
30
- endpoint = Async::HTTP::Endpoint.parse(@options[:connect])
31
- # endpoint = endpoint.each.first
32
-
33
- count = @options[:count]
34
-
35
- connections = Async::Queue.new
36
-
37
- Async do |task|
38
- task.logger.info!
39
-
40
- task.async do |subtask|
41
- while connection = connections.dequeue
42
- subtask.async(connection) do |subtask, connection|
43
- while message = connection.read
44
- puts "> #{message.inspect}"
45
- end
46
- ensure
47
- connection.close
48
- end
49
- end
50
-
51
- GC.start
52
- end
53
-
54
- client = Async::WebSocket::Client.open(endpoint)
55
- start_time = Async::Clock.now
56
-
57
- count.times do |i|
58
- connections.enqueue(client.connect(endpoint.path))
59
-
60
- if (i % 10000).zero?
61
- count = i+1
62
- duration = Async::Clock.now - start_time
63
- Async.logger.info(self) {"Made #{count} connections: #{(count/duration).round(2)} connections/second..."}
64
- end
65
-
66
- if (i % 10000).zero?
67
- duration = Async::Clock.measure{GC.start(full_mark: false, immediate_sweep: false)}
68
- Async.logger.info(self) {"GC.start duration=#{duration.round(2)}s GC.count=#{GC.count}"}
69
- end
70
- end
71
-
72
- connections.enqueue(nil)
73
-
74
- Async.logger.info(self) {"Finished top level connection loop..."}
75
- end
76
- end
77
- end
78
-
79
- Command.call
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'async'
4
- require 'async/io/stream'
5
- require 'async/http/endpoint'
6
- require 'async/websocket/client'
7
-
8
- USER = ARGV.pop || "anonymous"
9
- URL = ARGV.pop || "http://127.0.0.1:7070"
10
-
11
- Async do |task|
12
- stdin = Async::IO::Stream.new(
13
- Async::IO::Generic.new($stdin)
14
- )
15
-
16
- endpoint = Async::HTTP::Endpoint.parse(URL)
17
-
18
- Async::WebSocket::Client.connect(endpoint) do |connection|
19
- task.async do
20
- $stdout.write "> "
21
-
22
- while line = stdin.read_until("\n")
23
- connection.write({input: line})
24
- connection.flush
25
-
26
- $stdout.write "> "
27
- end
28
- end
29
-
30
- while message = connection.read
31
- $stdout.puts message
32
- end
33
- end
34
- end