async-websocket 0.14.0 → 0.15.0

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