iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
data/bin/poc/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # A proof of concept for Rack's `env['rack.websocket']`
2
+
3
+ This is a proof of concept for Rack based Websocket connections, showing how the Rack API can be adjusted to support server native real-time connections.
4
+
5
+ The chosen proof of concept was the ugliest chatroom I could find.
6
+
7
+ Although my hope is that Rack will adopt the concept and make `env['rack.websocket?']` and `env['rack.websocket']` part of it's standard, at the moment it's an Iodine specific feature, implemented using `env['iodine.websocket']`.
8
+
9
+ ## Install
10
+
11
+ Install required gems using:
12
+
13
+ ```sh
14
+ bundler install
15
+ ```
16
+
17
+ ## Run
18
+
19
+ Run this application single threaded:
20
+
21
+ ```sh
22
+ bundler exec iodine -- -www ./www
23
+ ```
24
+
25
+ Or both multi-threaded and forked (you'll notice that memory barriers for forked processes prevent websocket broadcasting from reaching websockets connected to a different process).
26
+
27
+ ```sh
28
+ bundler exec iodine -- -www ./www -t 16 -w 4
29
+ ```
30
+
31
+ ## Further reading
32
+
33
+ * https://github.com/rack/rack/issues/1093
34
+
35
+ * https://bowild.wordpress.com/2016/07/31/the-dark-side-of-the-rack
36
+
37
+ * https://github.com/boazsegev/iodine
data/bin/poc/config.ru ADDED
@@ -0,0 +1,66 @@
1
+ # The Rack Application container
2
+ module MyRackApplication
3
+ # Rack applications use the `call` callback to handle HTTP requests.
4
+ def self.call(env)
5
+ # if upgrading...
6
+ if env['HTTP_UPGRADE'.freeze] =~ /websocket/i
7
+ # We can assign a class or an instance that implements callbacks.
8
+ # We will assign an object, passing it the request information (`env`)
9
+ env['iodine.websocket'.freeze] = MyWebsocket.new(env)
10
+ # Rack responses must be a 3 item array
11
+ # [status, {http: :headers}, ["response body"]]
12
+ return [0, {}, []]
13
+ end
14
+ # a semi-regualr HTTP response
15
+ out = File.open File.expand_path('../www/index.html', __FILE__)
16
+ [200, { 'X-Sendfile' => File.expand_path('../www/index.html', __FILE__),
17
+ 'Content-Length' => out.size }, out]
18
+ end
19
+ end
20
+
21
+ # The Websocket Callback Object
22
+ class MyWebsocket
23
+ # this is optional, but I wanted the object to have the nickname provided in
24
+ # the HTTP request
25
+ def initialize(env)
26
+ # we need to change the ASCI Rack encoding to UTF-8,
27
+ # otherwise everything with the nickname will be a binary "blob" in the
28
+ # Javascript layer
29
+ @nickname = env['PATH_INFO'][1..-1].force_encoding 'UTF-8'
30
+ end
31
+
32
+ # A classic websocket callback, called when the connection is opened and
33
+ # linked to this object
34
+ def on_open
35
+ puts 'We have a websocket connection'
36
+ end
37
+
38
+ # A classic websocket callback, called when the connection is closed
39
+ # (after disconnection).
40
+ def on_close
41
+ puts "Bye Bye... #{count} connections left..."
42
+ end
43
+
44
+ # A server-side niceness, called when the server if shutting down,
45
+ # to gracefully disconnect (before disconnection).
46
+ def on_shutdown
47
+ write 'The server is shutting down, goodbye.'
48
+ end
49
+
50
+ def on_message(data)
51
+ puts "got message: #{data} encoded as #{data.encoding}"
52
+ # data is a temporary string, it's buffer cleared as soon as we return.
53
+ # So we make a copy with the desired format.
54
+ tmp = "#{@nickname}: #{data}"
55
+ # The `write` method was added by the server and writes to the current
56
+ # connection
57
+ write tmp
58
+ puts "broadcasting #{tmp.bytesize} bytes with encoding #{tmp.encoding}"
59
+ # `each` was added by the server and excludes this connection
60
+ # (each except self).
61
+ each { |h| h.write tmp }
62
+ end
63
+ end
64
+
65
+ # `run` is a Rack API command, telling Rack where the `call(env)` callback is located.
66
+ run MyRackApplication
data/bin/poc/gemfile ADDED
@@ -0,0 +1 @@
1
+ gem 'iodine', git: 'https://github.com/boazsegev/iodine.git'
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
5
+ <script>
6
+ ws = NaN
7
+ handle = ''
8
+ function onsubmit(e) {
9
+ e.preventDefault();
10
+ if($('#text')[0].value == '') {return false}
11
+ if(ws && ws.readyState == 1) {
12
+ ws.send($('#text')[0].value);
13
+ $('#text')[0].value = '';
14
+ } else {
15
+ handle = $('#text')[0].value
16
+ var url = (window.location.protocol.match(/https/) ? 'wss' : 'ws') +
17
+ '://' + window.document.location.host +
18
+ '/' + $('#text')[0].value
19
+ ws = new WebSocket(url)
20
+ ws.onopen = function(e) {
21
+ output("<b>Connected :-)</b>");
22
+ $('#text')[0].value = '';
23
+ $('#text')[0].placeholder = 'your message';
24
+ }
25
+ ws.onclose = function(e) {
26
+ output("<b>Disonnected :-/</b>")
27
+ $('#text')[0].value = '';
28
+ $('#text')[0].placeholder = 'nickname';
29
+ $('#text')[0].value = handle
30
+ }
31
+ ws.onmessage = function(e) {
32
+ output(e.data);
33
+ }
34
+ }
35
+ return false;
36
+ }
37
+ function output(data) {
38
+ $('#output').append("<li>" + data + "</li>")
39
+ $('#output').animate({ scrollTop:
40
+ $('#output')[0].scrollHeight }, "slow");
41
+ }
42
+ </script>
43
+ <style>
44
+ html, body {width:100%; height: 100%; background-color: #ddd; color: #111;}
45
+ h3, form {text-align: center;}
46
+ input {background-color: #fff; color: #111; padding: 0.3em;}
47
+ </style>
48
+ </head><body>
49
+ <h3>The Ugly Chatroom POC</h3>
50
+ <form id='form'>
51
+ <input type='text' id='text' name='text' placeholder='nickname'></input>
52
+ <input type='submit' value='send'></input>
53
+ </form>
54
+ <script> $('#form')[0].onsubmit = onsubmit </script>
55
+ <ul id='output'></ul>
56
+ </body>
57
+ </html>
data/bin/raw-rbhttp ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # this will compile Iodine and run a raw sockets server that emulates HTTP (without parsing any incoming requests).
4
+
5
+ # # test using:
6
+ # ab -n 100000 -c 200 -k http://127.0.0.1:3000/
7
+
8
+ Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
9
+ puts `rake clean`
10
+ puts `rake compile`
11
+
12
+ require 'benchmark'
13
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
14
+ require 'bundler/setup'
15
+ require 'iodine'
16
+
17
+ class HttpProtocol
18
+ @timeout = 5
19
+ # `on_message` is an optional alternative to the `on_data` callback.
20
+ # `on_message` has a 1Kb buffer that recycles itself for memory optimization.
21
+ def on_data
22
+ # writing will never block and will use a buffer written in C when needed.
23
+ data = nil
24
+ while (data = read)
25
+ write "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nKeep-Alive: timeout=1\r\nContent-Length: 12\r\n\r\nHello World!"
26
+ end
27
+ end
28
+ end
29
+
30
+ puts "thread #{Iodine.threads}"
31
+ # Listen
32
+ Iodine.listen '3000', HttpProtocol
33
+ Iodine.threads = 7
34
+ puts "now, thread #{Iodine.threads}"
35
+ Iodine.start
data/bin/raw_broadcast ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # this will compile Iodine and run a raw sockets broadcasting (chatroom) server.
4
+
5
+ # # test using:
6
+ # telnet localhost 3000
7
+
8
+ Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
9
+ puts `rake clean`
10
+ puts `rake compile`
11
+
12
+ require 'benchmark'
13
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
14
+ require 'bundler/setup'
15
+ require 'iodine'
16
+
17
+ # You can add fixtures and/or initialization code here to make experimenting
18
+ # with your gem easier. You can also use a different console, if you like.
19
+
20
+ # (If you use this, don't forget to add pry to your Gemfile!)
21
+ # require "pry"
22
+ # Pry.start
23
+
24
+ # test the upgrade protocol using the ShoooProtocol
25
+ class ShoooProtocol
26
+ @timeout = 10
27
+ def ping
28
+ write "Wha, ya' still here?! Go away!\n"
29
+ close
30
+ end
31
+
32
+ def on_message(buffer)
33
+ write "what do you mean - #{buffer.strip} ?!\nShoo, away with you!\n"
34
+ close
35
+ end
36
+ end
37
+
38
+ # the broadcasting protocol
39
+ class EchoProtocol
40
+ def self.on_start
41
+ Iodine.run_every(1000) { puts "#{Iodine.count} clients connected." }
42
+ puts "We have 1 timer, and #{Iodine.count} connections."
43
+ end
44
+
45
+ # `on_message` is an optional alternative to the `on_data` callback.
46
+ # `on_message` has a 1Kb buffer that recycles itself for memory optimization.
47
+ def on_message(buffer)
48
+ # writing will never block and will use a buffer written in C when needed.
49
+ write buffer
50
+ puts buffer.dump
51
+ # close will be performed only once all the data in the write buffer
52
+ # was sent. use `force_close` to close early.
53
+ close if buffer =~ /^bye[\r\n]/i
54
+ upgrade ShoooProtocol if buffer =~ /^shoo[\r\n]/i
55
+ # use buffer.dup to save the data from being recycled once we return.
56
+ data = "Someone said: #{buffer}"
57
+ # run asynchronous tasks with ease
58
+ each { |c| puts c; c.write data }
59
+ end
60
+ end
61
+
62
+ # create the server object and setup any settings we might need.
63
+ Iodine.listen 3000, EchoProtocol
64
+ Iodine.threads = 10
65
+ Iodine.processes = 1
66
+ Iodine.start
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
4
+ puts `rake clean`
5
+ puts `rake compile`
6
+
7
+ require 'benchmark'
8
+ require 'faye/websocket'
9
+ # require 'thin'
10
+ # Faye::WebSocket.load_adapter('thin')
11
+
12
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
13
+ require 'bundler/setup'
14
+ require 'iodine'
15
+
16
+ Iodine::Rack.app = lambda do |env|
17
+ if Faye::WebSocket.websocket?(env)
18
+ ws = Faye::WebSocket.new(env)
19
+
20
+ ws.on :message do |event|
21
+ ws.send(event.data.to_s)
22
+ end
23
+
24
+ ws.on :close do |event|
25
+ p [:close, event.code, event.reason]
26
+ ws = nil
27
+ end
28
+
29
+ # Return async Rack response
30
+ return ws.rack_response
31
+
32
+ else
33
+ # Normal HTTP request
34
+ [200, { 'Content-Type' => 'text/plain' }, ["Hello\n"]]
35
+ end
36
+ end
37
+
38
+ Iodine.start
39
+
40
+ # ws = new WebSocket("ws://localhost:3000"); ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data);}; ws.onclose = function(e) {console.log("closed")}; ws.onopen = function(e) {ws.send("hi");};
data/bin/ws-broadcast ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
4
+ puts `rake clean`
5
+ puts `rake compile`
6
+
7
+ require 'benchmark'
8
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
9
+ require 'bundler/setup'
10
+ require 'iodine'
11
+ require 'rack'
12
+
13
+ class WSEcho
14
+ def self.call(env)
15
+ if env['HTTP_UPGRADE'.freeze] =~ /websocket/i
16
+ env['iodine.websocket'.freeze] = WSEcho
17
+ return [0, {}, []]
18
+ end
19
+ out = "ENV:\r\n#{env.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n"}"
20
+ request = Rack::Request.new(env)
21
+ pr_raise if request.path_info =~ /raise[.]*/
22
+ out += "\nRequest Path: #{request.path_info}\nParams:\r\n#{request.params.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n"}" unless request.params.empty?
23
+ [200, { 'Content-Length' => out.length }, [out]]
24
+ end
25
+
26
+ def on_open
27
+ puts 'We have a websocket connection'
28
+ end
29
+
30
+ def on_close
31
+ puts "Bye Bye... only #{count} left..."
32
+ end
33
+
34
+ def on_shutdown
35
+ puts "I'm shutting down #{self}"
36
+ end
37
+
38
+ def on_message(data)
39
+ puts "got message: #{data}"
40
+ write data
41
+ data_cp = "Broadcast: #{data}"
42
+ # puts "broadcasting #{data_cp.bytesize} bytes"
43
+ each { |h| h.write data_cp }
44
+ end
45
+
46
+ def self.pr_raise
47
+ raise 'hell'
48
+ end
49
+ end
50
+
51
+ # create the server object and setup any settings we might need.
52
+ Iodine.threads ||= 4
53
+ Iodine.processes ||= 1
54
+ Iodine::Rack.public = nil
55
+ Iodine::Rack.log = 1
56
+ Iodine::Rack.app = WSEcho
57
+ # puts "Press enter to start (#{Process.pid})"
58
+ # gets
59
+ Iodine.start
60
+
61
+ # server.on_start do
62
+ # # server.run {puts "I'm running!"}
63
+ # # server.run_after(5000) {puts "5 seconds have passed."}
64
+ # server.run_every(1000) { puts "#{server.count} clients connected." }
65
+ # # server.run_every(10000) do
66
+ # # begin
67
+ # # puts "making a system call"
68
+ # # puts `ab -n 100000 -c 200 -k http://127.0.0.1:3000/`
69
+ # # rescue => e
70
+ # # l = Logger.new STDOUT
71
+ # # l.error e
72
+ # # end
73
+ # # end
74
+ # end
75
+ #
76
+ # Iodine.run_every(1000) {puts "#{server.connection_count} clients connected."}
77
+
78
+ # def nag
79
+ # puts `ab -n 200000 -c 2000 -k http://127.0.0.1:3000/`
80
+ # sleep 2
81
+ # end
82
+ #
83
+ # nag while true
84
+ #
85
+ # def nag
86
+ # puts `wrk -c2000 -d10 -t4 http://localhost:3000/`
87
+ # sleep 3
88
+ # end
89
+ #
90
+ # nag while true
91
+
92
+ # ab -n 100000 -c 200 -k http://127.0.0.1:3000/
93
+ # ab -n 100000 -c 4000 -k http://127.0.0.1:3000/
94
+ # ab -n 1000000 -c 20000 -k http://127.0.0.1:3000/
95
+ # ~/ruby/wrk/wrk -c400 -d10 -t12 http://localhost:3000/
96
+ # wrk -c200 -d4 -t12 http://localhost:3000/
97
+ # RACK_ENV="production" rackup -p 3000 -s iodine
98
+
99
+ # thor --amount 5000 ws://localhost:3000/echo
100
+ # thor --amount 5000 ws://localhost:3000/broadcast
101
+
102
+ # ws = new WebSocket("ws://localhost:3000"); ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data);}; ws.onclose = function(e) {console.log("closed")}; ws.onopen = function(e) {ws.send("hi");};
103
+ # for(i = 0; i< 256; i++) {
104
+ # ws = new WebSocket("ws://localhost:3000/");
105
+ # ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data); window.setTimeout(function(iws) {iws.send("leaving soon...");}, 3000, e.target); window.setTimeout(function(iws) {iws.close();}, 5000, e.target); };
106
+ # ws.onclose = function(e) {console.log("closed")};
107
+ # ws.onopen = function(e) {e.target.send("hi");};
108
+ # };
data/bin/ws-echo ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Dir.chdir(File.expand_path(File.join('..', '..'), __FILE__))
4
+ puts `rake clean`
5
+ puts `rake compile`
6
+
7
+ require 'benchmark'
8
+ $LOAD_PATH.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
9
+ require 'bundler/setup'
10
+ require 'iodine'
11
+ require 'rack'
12
+
13
+ class WSEcho
14
+ def self.call(env)
15
+ if env['upgrade.websocket?'.freeze] # && env['HTTP_UPGRADE'.freeze] =~ /websocket/i
16
+ env['upgrade.websocket'.freeze] = WSEcho
17
+ return [0, {}, []]
18
+ end
19
+ out = "ENV:\r\n#{env.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n"}\n"
20
+ request = Rack::Request.new(env)
21
+ out += "\nRequest Path: #{request.path_info}\nParams:\r\n#{request.params.to_a.map { |h| "#{h[0]}: #{h[1]}" } .join "\n"}\n" unless request.params.empty?
22
+ [200, { 'Content-Length' => out.length, 'Content-Type' => 'text/plain; charset=UTF-8;' }, [out]]
23
+ end
24
+
25
+ def on_open
26
+ puts 'We have a websocket connection'
27
+ Iodine::Websocket.defer(uuid) { |ws| puts "This websocket's uuid is #{ws.uuid}" }
28
+ end
29
+
30
+ def on_close
31
+ puts "Bye Bye... only #{count} left..."
32
+ end
33
+
34
+ def on_shutdown
35
+ puts "I'm shutting down #{self}"
36
+ end
37
+
38
+ def on_message(data)
39
+ puts "got message: #{data}"
40
+ write data
41
+ end
42
+
43
+ def echo(data)
44
+ write "echo: #{data}"
45
+ end
46
+ end
47
+
48
+ # create the server object and setup any settings we might need.
49
+ Iodine.threads ||= 4
50
+ Iodine.processes ||= 1
51
+ Iodine::Rack.public = nil
52
+ Iodine::Rack.log = 1
53
+ count = 2
54
+ Iodine::Rack.app = WSEcho
55
+ Iodine.start
56
+
57
+ # server.on_http= Proc.new do |env|
58
+ # # [200, {"Content-Length".freeze => "12".freeze}, ["Hello World!".freeze]];
59
+ # if env["HTTP_UPGRADE".freeze] =~ /websocket/i.freeze
60
+ # env['iodine.websocket'.freeze] = WSEcho.new
61
+ # [0,{}, []]
62
+ # else
63
+ # req = Rack::Request.new env
64
+ # res = Rack::Response.new
65
+ # res.write "Hello World!".freeze
66
+ # res.to_a
67
+ # end
68
+ # end
69
+
70
+ # server.on_start do
71
+ # server.run_every(1000) {puts "#{server.connection_count} clients connected."}
72
+ # end
73
+
74
+ # puts "Press enter to start (#{Process.pid})"
75
+ # gets
76
+
77
+ # def nag
78
+ # puts `ab -n 200000 -c 2000 -k http://127.0.0.1:3000/`
79
+ # sleep 2
80
+ # end
81
+ #
82
+ # nag while true
83
+ #
84
+ # def nag
85
+ # puts `wrk -c2000 -d10 -t4 http://localhost:3000/`
86
+ # sleep 3
87
+ # end
88
+ #
89
+ # nag while true
90
+
91
+ # ab -n 100000 -c 200 -k http://127.0.0.1:3000/
92
+ # ab -n 100000 -c 4000 -k http://127.0.0.1:3000/
93
+ # ab -n 1000000 -c 20000 -k http://127.0.0.1:3000/
94
+ # ~/ruby/wrk/wrk -c400 -d10 -t12 http://localhost:3000/
95
+ # wrk -c200 -d4 -t12 http://localhost:3000/
96
+ # ab -n 2000 -c 20 -H "Connection: close" http://127.0.0.1:3000/
97
+ # RACK_ENV="production" rackup -p 3000 -s iodine
98
+
99
+ # thor --amount 5000 ws://localhost:3000/echo
100
+ # thor --amount 5000 ws://localhost:3000/broadcast
101
+
102
+ # ws = new WebSocket("ws://localhost:3000"); ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data);}; ws.onclose = function(e) {console.log("closed")}; ws.onopen = function(e) {ws.send("hi");};
103
+ # for(i = 0; i< 256; i++) {
104
+ # ws = new WebSocket("ws://localhost:3000");
105
+ # ws.onmessage = function(e) {console.log("Got message!"); console.log(e.data); e.target.close(); };
106
+ # ws.onclose = function(e) {console.log("closed")};
107
+ # ws.onopen = function(e) {e.target.send("hi");};
108
+ # };