isomorfeus-iodine 0.8.0 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +782 -782
- data/Rakefile +0 -1
- data/examples/config.ru +59 -56
- data/examples/rack3.ru +12 -0
- data/exe/iodine +64 -64
- data/ext/iodine_ext/extconf.rb +1 -0
- data/ext/iodine_ext/fio.c +12 -10
- data/ext/iodine_ext/fio.h +5 -3
- data/ext/iodine_ext/http.c +1 -1
- data/ext/iodine_ext/http1_parser.h +1065 -103
- data/ext/iodine_ext/iodine_connection.c +10 -6
- data/ext/iodine_ext/iodine_http.c +23 -11
- data/ext/iodine_ext/redis_engine.c +1 -1
- data/ext/iodine_ext/websocket_parser.h +1 -0
- data/ext/iodine_ext/websockets.c +28 -11
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +35 -35
- metadata +5 -5
- data/.travis.yml +0 -32
data/Rakefile
CHANGED
data/examples/config.ru
CHANGED
@@ -1,56 +1,59 @@
|
|
1
|
-
# This is a WebSocket / SSE notification broadcasting application.
|
2
|
-
#
|
3
|
-
# Running this application from the command line is easy with:
|
4
|
-
#
|
5
|
-
# iodine
|
6
|
-
#
|
7
|
-
# Or, in single thread and single process:
|
8
|
-
#
|
9
|
-
# iodine -t 1 -w 1
|
10
|
-
#
|
11
|
-
# Benchmark with `ab` or `wrk` (a 5 seconds benchmark with 2000 concurrent clients):
|
12
|
-
#
|
13
|
-
# ab -c 2000 -t 5 -n 1000000 -k http://127.0.0.1:3000/
|
14
|
-
# wrk -c2000 -d5 -t12 http://localhost:3000/
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
1
|
+
# This is a WebSocket / SSE notification broadcasting application.
|
2
|
+
#
|
3
|
+
# Running this application from the command line is easy with:
|
4
|
+
#
|
5
|
+
# iodine
|
6
|
+
#
|
7
|
+
# Or, in single thread and single process:
|
8
|
+
#
|
9
|
+
# iodine -t 1 -w 1
|
10
|
+
#
|
11
|
+
# Benchmark with `ab` or `wrk` (a 5 seconds benchmark with 2000 concurrent clients):
|
12
|
+
#
|
13
|
+
# ab -c 2000 -t 5 -n 1000000 -k http://127.0.0.1:3000/
|
14
|
+
# wrk -c2000 -d5 -t12 http://localhost:3000/
|
15
|
+
#
|
16
|
+
# Test websocket echo using the browser. For example:
|
17
|
+
# 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");};
|
18
|
+
|
19
|
+
|
20
|
+
# A simple router - Checks for Websocket Upgrade and answers HTTP.
|
21
|
+
module MyHTTPRouter
|
22
|
+
# This is the HTTP response object according to the Rack specification.
|
23
|
+
HTTP_RESPONSE = [200, { 'Content-Type' => 'text/html',
|
24
|
+
'Content-Length' => '77' },
|
25
|
+
['Please connect using WebSockets or SSE (send messages only using WebSockets).']]
|
26
|
+
|
27
|
+
WS_RESPONSE = [0, {}, []].freeze
|
28
|
+
|
29
|
+
# this is function will be called by the Rack server (iodine) for every request.
|
30
|
+
def self.call env
|
31
|
+
# check if this is an upgrade request (WebsSocket / SSE).
|
32
|
+
if(env['rack.upgrade?'.freeze])
|
33
|
+
env['rack.upgrade'.freeze] = BroadcastClient
|
34
|
+
return WS_RESPONSE
|
35
|
+
end
|
36
|
+
# simply return the RESPONSE object, no matter what request was received.
|
37
|
+
HTTP_RESPONSE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# A simple Websocket Callback Object.
|
42
|
+
module BroadcastClient
|
43
|
+
# seng a message to new clients.
|
44
|
+
def on_open client
|
45
|
+
client.subscribe :broadcast
|
46
|
+
end
|
47
|
+
# send a message, letting the client know the server is suggunt down.
|
48
|
+
def on_shutdown client
|
49
|
+
client.write "Server shutting down. Goodbye."
|
50
|
+
end
|
51
|
+
# perform the echo
|
52
|
+
def on_message client, data
|
53
|
+
client.publish :broadcast, data
|
54
|
+
end
|
55
|
+
extend self
|
56
|
+
end
|
57
|
+
|
58
|
+
# this function call links our HelloWorld application with Rack
|
59
|
+
run MyHTTPRouter
|
data/examples/rack3.ru
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# adjusted from the code suggested by @taku0 (GitHub issue #131)
|
4
|
+
require 'rack'
|
5
|
+
require 'iodine'
|
6
|
+
|
7
|
+
run { |env|
|
8
|
+
response = Rack::Response.new(nil, 204)
|
9
|
+
response.set_cookie('aaa', 'aaa')
|
10
|
+
response.set_cookie('bbb', 'bbb')
|
11
|
+
response.finish
|
12
|
+
}
|
data/exe/iodine
CHANGED
@@ -1,64 +1,64 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
IODINE_PARSE_CLI = true
|
4
|
-
require 'iodine'
|
5
|
-
|
6
|
-
# Load Rack if available (assume it will be used)
|
7
|
-
|
8
|
-
require 'rack'
|
9
|
-
|
10
|
-
module Iodine
|
11
|
-
# The Iodine::Base namespace is reserved for internal use and is NOT part of the public API.
|
12
|
-
module Base
|
13
|
-
# Command line interface. The Ruby CLI might be changed in future versions.
|
14
|
-
module CLI
|
15
|
-
|
16
|
-
def self.try_file filename
|
17
|
-
return nil unless File.exist? filename
|
18
|
-
::Rack::Builder.parse_file filename
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.get_app
|
22
|
-
app = nil
|
23
|
-
filename = Iodine::DEFAULT_SETTINGS[:filename_]
|
24
|
-
if filename
|
25
|
-
app = try_file filename
|
26
|
-
app = try_file "#{filename}.ru" unless app
|
27
|
-
unless app
|
28
|
-
puts "* Couldn't find #{filename}\n testing for config.ru\n"
|
29
|
-
app = try_file "config.ru"
|
30
|
-
end
|
31
|
-
else
|
32
|
-
app = try_file "config.ru";
|
33
|
-
end
|
34
|
-
app
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.perform_warmup(app)
|
38
|
-
# load anything marked with `autoload`, since autoload is niether thread safe nor fork friendly.
|
39
|
-
Iodine.on_state(:on_start) do
|
40
|
-
Module.constants.each do |n|
|
41
|
-
begin
|
42
|
-
Object.const_get(n)
|
43
|
-
rescue StandardError => _e
|
44
|
-
end
|
45
|
-
end
|
46
|
-
::Rack::Builder.new(app) do |r|
|
47
|
-
r.warmup do |a|
|
48
|
-
client = ::Rack::MockRequest.new(a)
|
49
|
-
client.get('/')
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.call
|
56
|
-
app = get_app
|
57
|
-
perform_warmup(app) if Iodine::DEFAULT_SETTINGS[:warmup_]
|
58
|
-
Iodine::Rack.run(app)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
Iodine::Base::CLI.call
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
IODINE_PARSE_CLI = true
|
4
|
+
require 'iodine'
|
5
|
+
|
6
|
+
# Load Rack if available (assume it will be used)
|
7
|
+
|
8
|
+
require 'rack'
|
9
|
+
|
10
|
+
module Iodine
|
11
|
+
# The Iodine::Base namespace is reserved for internal use and is NOT part of the public API.
|
12
|
+
module Base
|
13
|
+
# Command line interface. The Ruby CLI might be changed in future versions.
|
14
|
+
module CLI
|
15
|
+
|
16
|
+
def self.try_file filename
|
17
|
+
return nil unless File.exist? filename
|
18
|
+
::Rack::Builder.parse_file filename
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.get_app
|
22
|
+
app = nil
|
23
|
+
filename = Iodine::DEFAULT_SETTINGS[:filename_]
|
24
|
+
if filename
|
25
|
+
app = try_file filename
|
26
|
+
app = try_file "#{filename}.ru" unless app
|
27
|
+
unless app
|
28
|
+
puts "* Couldn't find #{filename}\n testing for config.ru\n"
|
29
|
+
app = try_file "config.ru"
|
30
|
+
end
|
31
|
+
else
|
32
|
+
app = try_file "config.ru";
|
33
|
+
end
|
34
|
+
app
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.perform_warmup(app)
|
38
|
+
# load anything marked with `autoload`, since autoload is niether thread safe nor fork friendly.
|
39
|
+
Iodine.on_state(:on_start) do
|
40
|
+
Module.constants.each do |n|
|
41
|
+
begin
|
42
|
+
Object.const_get(n)
|
43
|
+
rescue StandardError => _e
|
44
|
+
end
|
45
|
+
end
|
46
|
+
::Rack::Builder.new(app) do |r|
|
47
|
+
r.warmup do |a|
|
48
|
+
client = ::Rack::MockRequest.new(a)
|
49
|
+
client.get('/')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.call
|
56
|
+
app = get_app
|
57
|
+
perform_warmup(app) if Iodine::DEFAULT_SETTINGS[:warmup_]
|
58
|
+
Iodine::Rack.run(app)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Iodine::Base::CLI.call
|
data/ext/iodine_ext/extconf.rb
CHANGED
data/ext/iodine_ext/fio.c
CHANGED
@@ -155,7 +155,7 @@ int ioctl (int fd, u_long request, int* argp) {
|
|
155
155
|
else { return 0; }
|
156
156
|
}
|
157
157
|
|
158
|
-
int
|
158
|
+
int fio_kill(int pid, int sig) {
|
159
159
|
/* Credit to Jan Biedermann (GitHub: @janbiedermann) */
|
160
160
|
HANDLE handle;
|
161
161
|
DWORD status;
|
@@ -987,7 +987,7 @@ FIO_FUNC void fio_thread_signal(void) {
|
|
987
987
|
(void)r;
|
988
988
|
} else if (fd == -1) {
|
989
989
|
/* hardly the best way, but there's a thread sleeping on air */
|
990
|
-
|
990
|
+
fio_kill(getpid(), SIGCONT);
|
991
991
|
}
|
992
992
|
#endif
|
993
993
|
}
|
@@ -1025,7 +1025,7 @@ static void fio_defer_thread_wait(void) {
|
|
1025
1025
|
if (fio_defer_has_queue())
|
1026
1026
|
pthread_setspecific(static_throttle_key, (void *)1);
|
1027
1027
|
else if (static_throttle < FIO_DEFER_THROTTLE_LIMIT)
|
1028
|
-
pthread_setspecific(static_throttle_key, (void *)(static_throttle << 1));
|
1028
|
+
pthread_setspecific(static_throttle_key, (void *)((static_throttle << 1) | 1));
|
1029
1029
|
}
|
1030
1030
|
}
|
1031
1031
|
|
@@ -1654,7 +1654,7 @@ void fio_reap_children(void) {
|
|
1654
1654
|
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
1655
1655
|
if (sigaction(SIGCHLD, &sa, &fio_old_sig_chld) == -1) {
|
1656
1656
|
perror("Child reaping initialization failed");
|
1657
|
-
|
1657
|
+
fio_kill(0, SIGINT);
|
1658
1658
|
exit(errno);
|
1659
1659
|
}
|
1660
1660
|
}
|
@@ -3689,7 +3689,8 @@ closed:
|
|
3689
3689
|
return -1;
|
3690
3690
|
|
3691
3691
|
flush_rw_hook:
|
3692
|
-
|
3692
|
+
if(uuid_data(uuid).rw_hooks)
|
3693
|
+
flushed = uuid_data(uuid).rw_hooks->flush(uuid, uuid_data(uuid).rw_udata);
|
3693
3694
|
fio_unlock(&uuid_data(uuid).sock_lock);
|
3694
3695
|
if (!flushed)
|
3695
3696
|
return 0;
|
@@ -3749,7 +3750,8 @@ size_t fio_flush_all(void) {
|
|
3749
3750
|
return 0;
|
3750
3751
|
size_t count = 0;
|
3751
3752
|
for (uintptr_t i = 0; i <= fio_data->max_protocol_fd; ++i) {
|
3752
|
-
if ((fd_data(i).open || fd_data(i).packet) &&
|
3753
|
+
if ((fd_data(i).open || fd_data(i).packet) &&
|
3754
|
+
fd_data(i).rw_hooks && fio_flush(fd2uuid(i)) > 0)
|
3753
3755
|
++count;
|
3754
3756
|
}
|
3755
3757
|
return count;
|
@@ -4603,7 +4605,7 @@ static void *fio_sentinel_worker_thread(void *arg) {
|
|
4603
4605
|
if (child == -1) {
|
4604
4606
|
FIO_LOG_FATAL("couldn't spawn worker.");
|
4605
4607
|
perror("\n errno");
|
4606
|
-
|
4608
|
+
fio_kill(fio_parent_pid(), SIGINT);
|
4607
4609
|
fio_stop();
|
4608
4610
|
return NULL;
|
4609
4611
|
} else if (child) {
|
@@ -4617,7 +4619,7 @@ static void *fio_sentinel_worker_thread(void *arg) {
|
|
4617
4619
|
} else {
|
4618
4620
|
FIO_LOG_FATAL("Child worker (%d) shutdown. Stopping services.", child);
|
4619
4621
|
}
|
4620
|
-
|
4622
|
+
fio_kill(0, SIGINT);
|
4621
4623
|
}
|
4622
4624
|
#else
|
4623
4625
|
if (fio_data->active) {
|
@@ -6879,7 +6881,7 @@ static void fio_cluster_on_close(intptr_t uuid, fio_protocol_s *pr_) {
|
|
6879
6881
|
#ifndef __MINGW32__
|
6880
6882
|
fio_cluster_data_cleanup(1);
|
6881
6883
|
#endif
|
6882
|
-
|
6884
|
+
fio_kill(getpid(), SIGINT);
|
6883
6885
|
}
|
6884
6886
|
}
|
6885
6887
|
if (c->msg)
|
@@ -7155,7 +7157,7 @@ static void fio_cluster_on_connect(intptr_t uuid, void *udata) {
|
|
7155
7157
|
static void fio_cluster_on_fail(intptr_t uuid, void *udata) {
|
7156
7158
|
FIO_LOG_FATAL("(facil.io) unknown cluster connection error");
|
7157
7159
|
perror(" errno");
|
7158
|
-
|
7160
|
+
fio_kill(fio_parent_pid(), SIGINT);
|
7159
7161
|
fio_stop();
|
7160
7162
|
// exit(errno ? errno : 1);
|
7161
7163
|
(void)udata;
|
data/ext/iodine_ext/fio.h
CHANGED
@@ -263,10 +263,12 @@ Version and helper macros
|
|
263
263
|
#define pipe(fds) _pipe(fds, 65536, _O_BINARY)
|
264
264
|
|
265
265
|
int fork(void);
|
266
|
-
int
|
266
|
+
int fio_kill(int, int);
|
267
267
|
ssize_t pread(int, void*, size_t, off_t);
|
268
268
|
ssize_t pwrite(int, const void *, size_t, off_t);
|
269
269
|
int fio_osffd4fd(unsigned int);
|
270
|
+
#else
|
271
|
+
#define fio_kill kill
|
270
272
|
#endif
|
271
273
|
|
272
274
|
/* *****************************************************************************
|
@@ -523,7 +525,7 @@ FIO_LOG2STDERR(const char *format, ...) {
|
|
523
525
|
if (!(ptr)) { \
|
524
526
|
FIO_LOG_FATAL("memory allocation error "__FILE__ \
|
525
527
|
":" FIO_MACRO2STR(__LINE__)); \
|
526
|
-
|
528
|
+
fio_kill(0, SIGINT); \
|
527
529
|
exit(errno); \
|
528
530
|
}
|
529
531
|
#endif
|
@@ -6048,7 +6050,7 @@ FIO_NAME(_insert_or_overwrite_)(FIO_NAME(s) * set, FIO_SET_HASH_TYPE hash_value,
|
|
6048
6050
|
pos->hash = hash_value;
|
6049
6051
|
pos->pos->hash = hash_value;
|
6050
6052
|
FIO_SET_COPY(pos->pos->obj, obj);
|
6051
|
-
|
6053
|
+
|
6052
6054
|
return pos->pos->obj;
|
6053
6055
|
}
|
6054
6056
|
|
data/ext/iodine_ext/http.c
CHANGED
@@ -922,7 +922,7 @@ intptr_t http_listen(const char *port, const char *binding,
|
|
922
922
|
if (arg_settings.on_request == NULL) {
|
923
923
|
FIO_LOG_ERROR("http_listen requires the .on_request parameter "
|
924
924
|
"to be set\n");
|
925
|
-
|
925
|
+
fio_kill(0, SIGINT);
|
926
926
|
exit(11);
|
927
927
|
}
|
928
928
|
|