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.
- checksums.yaml +4 -4
- data/.gitignore +3 -2
- data/.travis.yml +23 -2
- data/CHANGELOG.md +9 -2
- data/README.md +232 -179
- data/Rakefile +13 -1
- data/bin/config.ru +63 -0
- data/bin/console +6 -0
- data/bin/echo +42 -32
- data/bin/http-hello +62 -0
- data/bin/http-playground +124 -0
- data/bin/playground +62 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/bin/raw-rbhttp +35 -0
- data/bin/raw_broadcast +66 -0
- data/bin/test_with_faye +40 -0
- data/bin/ws-broadcast +108 -0
- data/bin/ws-echo +108 -0
- data/exe/iodine +59 -0
- data/ext/iodine/base64.c +264 -0
- data/ext/iodine/base64.h +72 -0
- data/ext/iodine/bscrypt-common.h +109 -0
- data/ext/iodine/bscrypt.h +49 -0
- data/ext/iodine/extconf.rb +41 -0
- data/ext/iodine/hex.c +123 -0
- data/ext/iodine/hex.h +70 -0
- data/ext/iodine/http.c +200 -0
- data/ext/iodine/http.h +128 -0
- data/ext/iodine/http1.c +402 -0
- data/ext/iodine/http1.h +56 -0
- data/ext/iodine/http1_simple_parser.c +473 -0
- data/ext/iodine/http1_simple_parser.h +59 -0
- data/ext/iodine/http_request.h +128 -0
- data/ext/iodine/http_response.c +1606 -0
- data/ext/iodine/http_response.h +393 -0
- data/ext/iodine/http_response_http1.h +374 -0
- data/ext/iodine/iodine_core.c +641 -0
- data/ext/iodine/iodine_core.h +70 -0
- data/ext/iodine/iodine_http.c +615 -0
- data/ext/iodine/iodine_http.h +19 -0
- data/ext/iodine/iodine_websocket.c +430 -0
- data/ext/iodine/iodine_websocket.h +21 -0
- data/ext/iodine/libasync.c +552 -0
- data/ext/iodine/libasync.h +117 -0
- data/ext/iodine/libreact.c +347 -0
- data/ext/iodine/libreact.h +244 -0
- data/ext/iodine/libserver.c +912 -0
- data/ext/iodine/libserver.h +435 -0
- data/ext/iodine/libsock.c +950 -0
- data/ext/iodine/libsock.h +478 -0
- data/ext/iodine/misc.c +181 -0
- data/ext/iodine/misc.h +76 -0
- data/ext/iodine/random.c +193 -0
- data/ext/iodine/random.h +48 -0
- data/ext/iodine/rb-call.c +127 -0
- data/ext/iodine/rb-call.h +60 -0
- data/ext/iodine/rb-libasync.h +79 -0
- data/ext/iodine/rb-rack-io.c +389 -0
- data/ext/iodine/rb-rack-io.h +17 -0
- data/ext/iodine/rb-registry.c +213 -0
- data/ext/iodine/rb-registry.h +33 -0
- data/ext/iodine/sha1.c +359 -0
- data/ext/iodine/sha1.h +85 -0
- data/ext/iodine/sha2.c +825 -0
- data/ext/iodine/sha2.h +138 -0
- data/ext/iodine/siphash.c +136 -0
- data/ext/iodine/siphash.h +15 -0
- data/ext/iodine/spnlock.h +235 -0
- data/ext/iodine/websockets.c +696 -0
- data/ext/iodine/websockets.h +120 -0
- data/ext/iodine/xor-crypt.c +189 -0
- data/ext/iodine/xor-crypt.h +107 -0
- data/iodine.gemspec +25 -18
- data/lib/iodine.rb +57 -58
- data/lib/iodine/http.rb +0 -189
- data/lib/iodine/protocol.rb +36 -245
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +145 -2
- metadata +115 -37
- data/bin/core_http_test +0 -51
- data/bin/em playground +0 -56
- data/bin/hello_world +0 -75
- data/bin/setup +0 -7
- data/lib/iodine/client.rb +0 -5
- data/lib/iodine/core.rb +0 -102
- data/lib/iodine/core_init.rb +0 -143
- data/lib/iodine/http/hpack.rb +0 -553
- data/lib/iodine/http/http1.rb +0 -251
- data/lib/iodine/http/http2.rb +0 -507
- data/lib/iodine/http/rack_support.rb +0 -108
- data/lib/iodine/http/request.rb +0 -462
- data/lib/iodine/http/response.rb +0 -474
- data/lib/iodine/http/session.rb +0 -143
- data/lib/iodine/http/websocket_client.rb +0 -335
- data/lib/iodine/http/websocket_handler.rb +0 -101
- data/lib/iodine/http/websockets.rb +0 -336
- data/lib/iodine/io.rb +0 -56
- data/lib/iodine/logging.rb +0 -46
- data/lib/iodine/settings.rb +0 -158
- data/lib/iodine/ssl_connector.rb +0 -48
- 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
|
data/bin/test_with_faye
ADDED
@@ -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
|
+
# };
|