iodine 0.7.59 → 0.8.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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -20
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -146
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/iodine_caller.h
CHANGED
|
@@ -1,27 +1,236 @@
|
|
|
1
|
-
#ifndef
|
|
2
|
-
#define
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
#ifndef H___IODINE_CALLER___H
|
|
2
|
+
#define H___IODINE_CALLER___H
|
|
3
|
+
#include "iodine.h"
|
|
4
|
+
|
|
5
|
+
/* *****************************************************************************
|
|
6
|
+
Ruby Caller and Thread (GVL) Helper
|
|
7
|
+
|
|
8
|
+
From within the GVL call Ruby functions so:
|
|
9
|
+
|
|
10
|
+
iodine_caller_result_s r = iodine_ruby_call_inside(recv, mid,
|
|
11
|
+
argc, argv, proc);
|
|
12
|
+
|
|
13
|
+
From outside the GVL call Ruby functions so:
|
|
14
|
+
|
|
15
|
+
iodine_caller_result_s r = iodine_ruby_call_outside(recv, mid, argc, argv);
|
|
16
|
+
|
|
17
|
+
When GVL state is ambiguous (may or may not hold it) call Ruby functions so:
|
|
18
|
+
|
|
19
|
+
iodine_caller_result_s r = iodine_ruby_call_anywhere(recv, mid, argc, argv);
|
|
20
|
+
|
|
21
|
+
***************************************************************************** */
|
|
22
|
+
|
|
23
|
+
/* printout backtrace in case of exceptions */
|
|
24
|
+
static void *iodine_handle_exception(void *ignr) {
|
|
25
|
+
(void)ignr;
|
|
26
|
+
FIO_LOG_ERROR("(%d) iodine catching an exposed exception", fio_io_pid());
|
|
27
|
+
VALUE exc = rb_errinfo();
|
|
28
|
+
if (exc != Qnil && rb_respond_to(exc, rb_intern("message")) &&
|
|
29
|
+
rb_respond_to(exc, rb_intern("backtrace"))) {
|
|
30
|
+
VALUE msg = rb_funcallv(exc, rb_intern("message"), 0, NULL);
|
|
31
|
+
VALUE exc_class = rb_class_name(CLASS_OF(exc));
|
|
32
|
+
VALUE bt = rb_funcallv(exc, rb_intern("backtrace"), 0, NULL);
|
|
33
|
+
if (msg == Qnil)
|
|
34
|
+
msg = rb_str_new("Error message unavailable", 25);
|
|
35
|
+
if (exc_class == Qnil)
|
|
36
|
+
exc_class = rb_str_new("unknown exception class", 23);
|
|
37
|
+
if (TYPE(bt) == RUBY_T_ARRAY) {
|
|
38
|
+
bt = rb_ary_join(bt, rb_str_new_literal("\n"));
|
|
39
|
+
FIO_LOG_ERROR("exposed exception message: %.*s: %.*s\n %s ",
|
|
40
|
+
(int)RSTRING_LEN(exc_class),
|
|
41
|
+
RSTRING_PTR(exc_class),
|
|
42
|
+
(int)RSTRING_LEN(msg),
|
|
43
|
+
RSTRING_PTR(msg),
|
|
44
|
+
StringValueCStr(bt));
|
|
45
|
+
} else if (TYPE(bt) == RUBY_T_STRING) {
|
|
46
|
+
FIO_LOG_ERROR("exposed exception message: %.*s: %.*s\n"
|
|
47
|
+
"No backtrace available.\n",
|
|
48
|
+
(int)RSTRING_LEN(exc_class),
|
|
49
|
+
RSTRING_PTR(exc_class),
|
|
50
|
+
(int)RSTRING_LEN(msg),
|
|
51
|
+
RSTRING_PTR(msg));
|
|
52
|
+
} else {
|
|
53
|
+
FIO_LOG_ERROR("exposed exception message: %.*s: %.*s\n %s\n"
|
|
54
|
+
"BACKTRACE UNAVAILABLE!\n",
|
|
55
|
+
(int)RSTRING_LEN(exc_class),
|
|
56
|
+
RSTRING_PTR(exc_class),
|
|
57
|
+
(int)RSTRING_LEN(msg),
|
|
58
|
+
RSTRING_PTR(msg));
|
|
59
|
+
FIO_LOG_ERROR("Backtrace missing.");
|
|
60
|
+
}
|
|
61
|
+
rb_backtrace();
|
|
62
|
+
FIO_LOG_ERROR("\n");
|
|
63
|
+
rb_set_errinfo(Qnil);
|
|
64
|
+
} else if (exc != Qnil) {
|
|
65
|
+
FIO_LOG_ERROR("exposed exception message: NO MESSAGE / DATA AVAILABLE");
|
|
66
|
+
}
|
|
67
|
+
return (void *)Qnil;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
typedef struct {
|
|
71
|
+
VALUE recv;
|
|
72
|
+
ID mid;
|
|
73
|
+
int argc;
|
|
74
|
+
VALUE *argv;
|
|
75
|
+
VALUE proc;
|
|
76
|
+
int ignore_exceptions;
|
|
77
|
+
} iodine_caller_args_s;
|
|
78
|
+
|
|
79
|
+
typedef struct {
|
|
80
|
+
VALUE result;
|
|
81
|
+
int exception;
|
|
82
|
+
} iodine_caller_result_s;
|
|
83
|
+
|
|
84
|
+
typedef struct {
|
|
85
|
+
iodine_caller_args_s in;
|
|
86
|
+
iodine_caller_result_s out;
|
|
87
|
+
} iodine___caller_s;
|
|
88
|
+
|
|
89
|
+
static VALUE iodine___func_caller_task(VALUE args_) {
|
|
90
|
+
iodine_caller_args_s *args = (iodine_caller_args_s *)args_;
|
|
91
|
+
return rb_funcallv(args->recv, args->mid, args->argc, args->argv);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static VALUE iodine___func_caller_task_proc(VALUE args_) {
|
|
95
|
+
iodine_caller_args_s *args = (iodine_caller_args_s *)args_;
|
|
96
|
+
return rb_funcall_with_block(args->recv,
|
|
97
|
+
args->mid,
|
|
98
|
+
args->argc,
|
|
99
|
+
args->argv,
|
|
100
|
+
args->proc);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static void *iodine_ruby____outside_task_proc(void *c_) {
|
|
104
|
+
iodine___caller_s *c = (iodine___caller_s *)c_;
|
|
105
|
+
c->out.result = rb_protect(iodine___func_caller_task_proc,
|
|
106
|
+
(VALUE)&c->in,
|
|
107
|
+
&c->out.exception);
|
|
108
|
+
if (c->out.exception && !c->in.ignore_exceptions)
|
|
109
|
+
iodine_handle_exception(NULL);
|
|
110
|
+
return NULL;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static void *iodine_ruby____outside_task(void *c_) {
|
|
114
|
+
iodine___caller_s *c = (iodine___caller_s *)c_;
|
|
115
|
+
c->out.result =
|
|
116
|
+
rb_protect(iodine___func_caller_task, (VALUE)&c->in, &c->out.exception);
|
|
117
|
+
if (c->out.exception && !c->in.ignore_exceptions)
|
|
118
|
+
iodine_handle_exception(NULL);
|
|
119
|
+
return NULL;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* *****************************************************************************
|
|
123
|
+
Ruby Caller and Thread (GVL) - Helper Implementation
|
|
124
|
+
***************************************************************************** */
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
Calls a function from inside the GVL.
|
|
128
|
+
|
|
129
|
+
Accepts the following, possibly named, arguments:
|
|
130
|
+
|
|
131
|
+
(VALUE recv, ID mid, int argc,
|
|
132
|
+
VALUE *argv, VALUE proc, int ignore_exceptions)
|
|
133
|
+
|
|
134
|
+
*/
|
|
135
|
+
inline static iodine_caller_result_s iodine_ruby_call_inside(
|
|
136
|
+
iodine_caller_args_s args) {
|
|
137
|
+
iodine_caller_result_s r = {0};
|
|
138
|
+
FIO_ASSERT_DEBUG(args.recv && args.mid,
|
|
139
|
+
"iodine_ruby_call requires an object and method name");
|
|
140
|
+
VALUE stub[1] = {Qnil};
|
|
141
|
+
if (!args.argv)
|
|
142
|
+
args.argv = stub;
|
|
143
|
+
r.result = rb_protect(
|
|
144
|
+
(args.proc ? iodine___func_caller_task_proc : iodine___func_caller_task),
|
|
145
|
+
(VALUE)&args,
|
|
146
|
+
&r.exception);
|
|
147
|
+
if (r.exception)
|
|
148
|
+
iodine_handle_exception(NULL);
|
|
149
|
+
return r;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
Calls a function from outside the GVL, acquiring the lock and then releasing it.
|
|
154
|
+
|
|
155
|
+
All call sites are from IO/worker threads that do NOT hold the GVL.
|
|
156
|
+
For ambiguous GVL state, use iodine_ruby_call_anywhere instead.
|
|
157
|
+
|
|
158
|
+
Accepts the following, possibly named, arguments:
|
|
159
|
+
|
|
160
|
+
(VALUE recv, ID mid, int argc,
|
|
161
|
+
VALUE *argv, VALUE proc, int ignore_exceptions)
|
|
162
|
+
|
|
163
|
+
*/
|
|
164
|
+
inline static iodine_caller_result_s iodine_ruby_call_outside(
|
|
165
|
+
iodine_caller_args_s args) {
|
|
166
|
+
VALUE stub[1] = {Qnil};
|
|
167
|
+
if (!args.argv)
|
|
168
|
+
args.argv = stub;
|
|
169
|
+
iodine___caller_s r = {args, {0}};
|
|
170
|
+
FIO_ASSERT_DEBUG(args.recv && args.mid,
|
|
171
|
+
"iodine_ruby_call requires an object and method name");
|
|
172
|
+
void *(*fn)(void *) = args.proc ? iodine_ruby____outside_task_proc
|
|
173
|
+
: iodine_ruby____outside_task;
|
|
174
|
+
rb_thread_call_with_gvl(fn, &r);
|
|
175
|
+
return r.out;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
Calls a function from inside the GVL.
|
|
180
|
+
|
|
181
|
+
Accepts the following, possibly named, arguments:
|
|
182
|
+
|
|
183
|
+
(VALUE recv, ID mid, int argc,
|
|
184
|
+
VALUE *argv, VALUE proc, int ignore_exceptions)
|
|
185
|
+
|
|
186
|
+
*/
|
|
187
|
+
#define iodine_ruby_call_inside(...) \
|
|
188
|
+
iodine_ruby_call_inside((iodine_caller_args_s){__VA_ARGS__})
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
Calls a function from outside the GVL, aquiring the lock and then releasing it.
|
|
192
|
+
|
|
193
|
+
Accepts the following, possibly named, arguments:
|
|
194
|
+
|
|
195
|
+
(VALUE recv, ID mid, int argc,
|
|
196
|
+
VALUE *argv, VALUE proc, int ignore_exceptions)
|
|
197
|
+
|
|
198
|
+
*/
|
|
199
|
+
#define iodine_ruby_call_outside(...) \
|
|
200
|
+
iodine_ruby_call_outside((iodine_caller_args_s){__VA_ARGS__})
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
For code paths where the calling thread may or may not hold the GVL.
|
|
204
|
+
|
|
205
|
+
On Ruby 3.x and 4.x (current path): checks ruby_thread_has_gvl_p() at runtime.
|
|
206
|
+
|
|
207
|
+
NOTE: Ruby Feature #20750 (merged Dec 2025) made rb_thread_call_with_gvl()
|
|
208
|
+
GVL-lenient on Ruby 4.x — safe to call whether or not GVL is held.
|
|
209
|
+
The `&& 0` gate below preserves the safe runtime-check path on all versions
|
|
210
|
+
until the lenient behaviour is confirmed present in every supported Ruby 4.x
|
|
211
|
+
release used in CI. To activate the simplified Ruby 4 fast path, remove
|
|
212
|
+
`&& 0` once Ruby 4.0.1+ GVL-lenience is verified in CI.
|
|
213
|
+
*/
|
|
214
|
+
#if RUBY_API_VERSION_MAJOR >= 4 && 0
|
|
215
|
+
#define iodine_ruby_call_anywhere(...) \
|
|
216
|
+
iodine_ruby_call_outside((iodine_caller_args_s){__VA_ARGS__})
|
|
217
|
+
#define iodine_c_call_with(fn, args) rb_thread_call_with_gvl(fn, args)
|
|
218
|
+
#define iodine_c_call_without(fn, args) \
|
|
219
|
+
rb_thread_call_without_gvl(fn, args, NULL, NULL)
|
|
220
|
+
|
|
221
|
+
#else
|
|
222
|
+
#define iodine_ruby_call_anywhere(...) \
|
|
223
|
+
(ruby_thread_has_gvl_p() \
|
|
224
|
+
? iodine_ruby_call_inside \
|
|
225
|
+
: iodine_ruby_call_outside)((iodine_caller_args_s){__VA_ARGS__})
|
|
226
|
+
|
|
227
|
+
#define iodine_c_call_with(fn, args) \
|
|
228
|
+
(ruby_thread_has_gvl_p() ? (fn)(args) : rb_thread_call_with_gvl(fn, args))
|
|
229
|
+
|
|
230
|
+
#define iodine_c_call_without(fn, args) \
|
|
231
|
+
(ruby_thread_has_gvl_p() ? rb_thread_call_without_gvl(fn, args, NULL, NULL) \
|
|
232
|
+
: (fn)(args))
|
|
26
233
|
|
|
27
234
|
#endif
|
|
235
|
+
|
|
236
|
+
#endif /* H___IODINE_CALLER___H */
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
#ifndef H___IODINE_CLI___H
|
|
2
|
+
#define H___IODINE_CLI___H
|
|
3
|
+
#include "iodine.h"
|
|
4
|
+
|
|
5
|
+
/* *****************************************************************************
|
|
6
|
+
Iodine CLI - Command Line Interface Parsing
|
|
7
|
+
|
|
8
|
+
This module provides the Iodine::Base::CLI Ruby module for parsing and
|
|
9
|
+
managing command-line arguments. It wraps facil.io's CLI parser with
|
|
10
|
+
Ruby-friendly access.
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Parses ARGV with support for named and positional arguments
|
|
14
|
+
- Supports various argument types: strings, integers, booleans
|
|
15
|
+
- Provides default values from environment variables (THREADS, WORKERS, etc.)
|
|
16
|
+
- Configures TLS, RESP3, clustering, and HTTP settings
|
|
17
|
+
|
|
18
|
+
CLI Options (parsed from command line):
|
|
19
|
+
- Address: -b/--bind, -p/--port
|
|
20
|
+
- Concurrency: -t/--threads, -w/--workers
|
|
21
|
+
- HTTP: --public, --max-line, --max-header, --max-body, --keep-alive, --log
|
|
22
|
+
- WebSocket/SSE: --ws-max-msg, --timeout
|
|
23
|
+
- TLS: --tls-self, --tls-name, --tls-cert, --tls-key, --tls-password
|
|
24
|
+
- Clustering: --broadcast, --secret
|
|
25
|
+
- RESP3: --redis, --redis-ping
|
|
26
|
+
- Hot Restart: --hot-restart, --config, --preload
|
|
27
|
+
- Misc: --verbose, --rack, --pid, --contained
|
|
28
|
+
|
|
29
|
+
Ruby API (Iodine::Base::CLI):
|
|
30
|
+
- CLI.parse(required) - Parse ARGV, returns Hash of options
|
|
31
|
+
- CLI[key] - Get CLI option value
|
|
32
|
+
- CLI[key] = value - Set CLI option value (before start only)
|
|
33
|
+
|
|
34
|
+
Environment Variables:
|
|
35
|
+
- THREADS, WORKERS - Default thread/worker counts
|
|
36
|
+
- ADDRESS, PORT - Default binding address/port
|
|
37
|
+
- SECRET - Cluster secret key
|
|
38
|
+
- IODINE_MTLS - Enable embedded TLS 1.3
|
|
39
|
+
***************************************************************************** */
|
|
40
|
+
|
|
41
|
+
/** Maximum number of CLI arguments supported */
|
|
42
|
+
#define IODINE_CLI_LIMIT 256
|
|
43
|
+
|
|
44
|
+
static int iodine_cli_task(fio_buf_info_s name,
|
|
45
|
+
fio_buf_info_s val,
|
|
46
|
+
fio_cli_arg_e t,
|
|
47
|
+
void *h_) {
|
|
48
|
+
static long at = 0;
|
|
49
|
+
VALUE h = (VALUE)h_;
|
|
50
|
+
VALUE tmp = Qnil;
|
|
51
|
+
VALUE n = Qnil;
|
|
52
|
+
VALUE v = Qnil;
|
|
53
|
+
switch (t) {
|
|
54
|
+
case FIO_CLI_ARG_BOOL: v = Qtrue; break;
|
|
55
|
+
case FIO_CLI_ARG_INT: v = RB_LL2NUM(fio_atol(&val.buf)); break;
|
|
56
|
+
default: v = rb_str_new(val.buf, val.len); break;
|
|
57
|
+
}
|
|
58
|
+
STORE.cache(v);
|
|
59
|
+
if (name.buf) {
|
|
60
|
+
at = 0;
|
|
61
|
+
while (name.buf[0] == '-') {
|
|
62
|
+
++name.buf;
|
|
63
|
+
--name.len;
|
|
64
|
+
}
|
|
65
|
+
n = rb_str_new(name.buf, name.len);
|
|
66
|
+
STORE.cache(n);
|
|
67
|
+
} else {
|
|
68
|
+
n = RB_INT2FIX(at);
|
|
69
|
+
++at;
|
|
70
|
+
}
|
|
71
|
+
rb_hash_aset(h, n, v);
|
|
72
|
+
if (RB_TYPE_P(n, RUBY_T_STRING)) {
|
|
73
|
+
tmp = rb_str_intern(n);
|
|
74
|
+
STORE.cache((n = tmp));
|
|
75
|
+
rb_hash_aset(h, n, v);
|
|
76
|
+
}
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* *****************************************************************************
|
|
81
|
+
Ruby Public API.
|
|
82
|
+
***************************************************************************** */
|
|
83
|
+
/* used here, defined later */
|
|
84
|
+
static VALUE iodine_tls_default_set(VALUE klass, VALUE backend);
|
|
85
|
+
|
|
86
|
+
FIO_IFUNC bool iodine___mtls_env(void) {
|
|
87
|
+
bool ret = 0;
|
|
88
|
+
char *e = fio_sys_env("IODINE_MTLS");
|
|
89
|
+
if (!e)
|
|
90
|
+
return ret;
|
|
91
|
+
fio_str_info_s s = FIO_STR_INFO1(e);
|
|
92
|
+
fio_str_info_s untrue = FIO_STR_INFO1((char *)"false");
|
|
93
|
+
fio_str_info_s untrue2 = FIO_STR_INFO1((char *)"0");
|
|
94
|
+
ret = (bool)(1UL ^ ((unsigned)(FIO_STR_INFO_IS_EQ(s, untrue)) |
|
|
95
|
+
FIO_STR_INFO_IS_EQ(s, untrue2)));
|
|
96
|
+
return ret;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Read CLI as required data. */
|
|
100
|
+
static VALUE iodine_cli_parse(VALUE self, VALUE required) {
|
|
101
|
+
if (!RB_TYPE_P(rb_argv, RUBY_T_ARRAY))
|
|
102
|
+
rb_raise(rb_eException, "ARGV should be an Array!");
|
|
103
|
+
FIO_STR_INFO_TMP_VAR(desc, 2048);
|
|
104
|
+
FIO_STR_INFO_TMP_VAR(threads, 128);
|
|
105
|
+
FIO_STR_INFO_TMP_VAR(workers, 128);
|
|
106
|
+
const char *argv[IODINE_CLI_LIMIT];
|
|
107
|
+
const char *threads_env = NULL;
|
|
108
|
+
const char *workers_env = NULL;
|
|
109
|
+
long len = 0;
|
|
110
|
+
VALUE iodine_version = rb_const_get(iodine_rb_IODINE, rb_intern("VERSION"));
|
|
111
|
+
|
|
112
|
+
/* I don't know if Ruby promises a NUL separator, but fio_bstr does. */
|
|
113
|
+
if (rb_argv0 && RB_TYPE_P(rb_argv0, RUBY_T_STRING)) {
|
|
114
|
+
argv[len++] =
|
|
115
|
+
fio_bstr_write(NULL, RSTRING_PTR(rb_argv0), RSTRING_LEN(rb_argv0));
|
|
116
|
+
} else
|
|
117
|
+
argv[len++] = (const char *)"iodine";
|
|
118
|
+
for (long i = 0; len < IODINE_CLI_LIMIT && i < rb_array_len(rb_argv); ++i) {
|
|
119
|
+
VALUE o = rb_ary_entry(rb_argv, i);
|
|
120
|
+
if (!RB_TYPE_P(o, RUBY_T_STRING)) {
|
|
121
|
+
FIO_LOG_WARNING("ARGV member skipped - not a String!");
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
argv[len++] = fio_bstr_write(NULL, RSTRING_PTR(o), RSTRING_LEN(o));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* in case of `-h` or error, make sure we clean up */
|
|
128
|
+
for (long i = 0; i < len; ++i)
|
|
129
|
+
fio_state_callback_add(FIO_CALL_AT_EXIT,
|
|
130
|
+
(void (*)(void *))fio_bstr_free,
|
|
131
|
+
(void *)argv[i]);
|
|
132
|
+
|
|
133
|
+
threads_env = fio_sys_env("THREADS");
|
|
134
|
+
workers_env = fio_sys_env("WORKERS");
|
|
135
|
+
|
|
136
|
+
fio_string_write2(
|
|
137
|
+
&threads,
|
|
138
|
+
NULL,
|
|
139
|
+
FIO_STRING_WRITE_STR1("--threads -t ("),
|
|
140
|
+
(threads_env ? FIO_STRING_WRITE_STR1(threads_env)
|
|
141
|
+
: FIO_STRING_WRITE_STR1("-4")),
|
|
142
|
+
FIO_STRING_WRITE_STR1(") number of worker threads to use."));
|
|
143
|
+
fio_string_write2(
|
|
144
|
+
&workers,
|
|
145
|
+
NULL,
|
|
146
|
+
FIO_STRING_WRITE_STR1("--workers -w ("),
|
|
147
|
+
(workers_env ? FIO_STRING_WRITE_STR1(workers_env)
|
|
148
|
+
: FIO_STRING_WRITE_STR1("-2")),
|
|
149
|
+
FIO_STRING_WRITE_STR1(") number of worker processes to use."));
|
|
150
|
+
|
|
151
|
+
fio_string_write2(
|
|
152
|
+
&desc,
|
|
153
|
+
NULL,
|
|
154
|
+
FIO_STRING_WRITE_STR1("Iodine's (" FIO_POLL_ENGINE_STR
|
|
155
|
+
") HTTP/WebSocket server version "),
|
|
156
|
+
FIO_STRING_WRITE_STR2(RSTRING_PTR(iodine_version),
|
|
157
|
+
(size_t)RSTRING_LEN(iodine_version)),
|
|
158
|
+
FIO_STRING_WRITE_STR1(
|
|
159
|
+
"\r\n\r\nUse:\r\n iodine <options> <filename>\r\n\r\n"
|
|
160
|
+
"Both <options> and <filename> are optional. i.e.,:\r\n"
|
|
161
|
+
" iodine -p 0 -b /tmp/my_unix_sock\r\n"
|
|
162
|
+
" iodine -p 8080 path/to/app/config.ru\r\n"
|
|
163
|
+
" iodine -p 8080 path/to/app/config.nru\r\n"
|
|
164
|
+
" iodine -p 8080 -w 4 -t 16\r\n"
|
|
165
|
+
" iodine -w -1 -t 4 -r redis://usr:pass@localhost:6379/\r\n\r\n"
|
|
166
|
+
"Default <filename> if unspecified: `config.nru` and `config.ru`"));
|
|
167
|
+
|
|
168
|
+
fio_cli_end();
|
|
169
|
+
fio_cli_start(
|
|
170
|
+
(int)len,
|
|
171
|
+
(const char **)argv,
|
|
172
|
+
0,
|
|
173
|
+
((required == Qnil || required == Qfalse) ? -1 : 1),
|
|
174
|
+
desc.buf,
|
|
175
|
+
FIO_CLI_PRINT_HEADER("Address Binding"),
|
|
176
|
+
FIO_CLI_PRINT_LINE(
|
|
177
|
+
"NOTE: also controlled by the ADDRESS or PORT environment vars."),
|
|
178
|
+
FIO_CLI_STRING(
|
|
179
|
+
"-bind -b address to listen to in URL format (MAY include PORT)."),
|
|
180
|
+
FIO_CLI_PRINT(
|
|
181
|
+
"It's possible to add TLS/SSL data to the binding URL. i.e.:"),
|
|
182
|
+
FIO_CLI_PRINT("\t iodine -b https://0.0.0.0/?tls=./cert_path/"),
|
|
183
|
+
FIO_CLI_PRINT(
|
|
184
|
+
"\t iodine -b https://0.0.0.0/?key=./key.pem&cert=./cert.pem"),
|
|
185
|
+
FIO_CLI_PRINT("TLS client authentication / trust is available. i.e.:"),
|
|
186
|
+
FIO_CLI_PRINT("\t iodine -b https://0.0.0.0/?trust=ca.pem"),
|
|
187
|
+
FIO_CLI_PRINT("\t iodine -b https://0.0.0.0/?trust=system"),
|
|
188
|
+
FIO_CLI_INT("-port -p default port number to listen to."),
|
|
189
|
+
FIO_CLI_PRINT(
|
|
190
|
+
"Note: these are optional and supersede previous instructions."),
|
|
191
|
+
|
|
192
|
+
FIO_CLI_PRINT_HEADER("Concurrency"),
|
|
193
|
+
FIO_CLI_INT(threads.buf),
|
|
194
|
+
FIO_CLI_INT(workers.buf),
|
|
195
|
+
|
|
196
|
+
FIO_CLI_PRINT_HEADER("HTTP"),
|
|
197
|
+
FIO_CLI_STRING("--public -www public folder for static file service."),
|
|
198
|
+
FIO_CLI_INT("--max-line -maxln (" FIO_MACRO2STR(
|
|
199
|
+
FIO_HTTP_DEFAULT_MAX_LINE_LEN) ") per-header line limit, in bytes."),
|
|
200
|
+
FIO_CLI_INT("--max-header -maxhd (" FIO_MACRO2STR(
|
|
201
|
+
FIO_HTTP_DEFAULT_MAX_HEADER_SIZE) ") total header limit per request, "
|
|
202
|
+
"in bytes."),
|
|
203
|
+
FIO_CLI_INT("--max-body -maxbd (" FIO_MACRO2STR(
|
|
204
|
+
FIO_HTTP_DEFAULT_MAX_BODY_SIZE) ") total message payload limit per "
|
|
205
|
+
"request, in bytes."),
|
|
206
|
+
FIO_CLI_INT("--keep-alive -k (" FIO_MACRO2STR(
|
|
207
|
+
FIO_HTTP_DEFAULT_TIMEOUT) ") HTTP keep-alive timeout in seconds "
|
|
208
|
+
"(0..255)"),
|
|
209
|
+
FIO_CLI_INT("--max-age -maxage (3600) default Max-Age header value for "
|
|
210
|
+
"static files."),
|
|
211
|
+
FIO_CLI_BOOL("--no-dynamic-deflate -no-dynd Disable dynamic deflation of "
|
|
212
|
+
"HTTP replies."),
|
|
213
|
+
FIO_CLI_BOOL("--log -v log HTTP messages."),
|
|
214
|
+
|
|
215
|
+
FIO_CLI_PRINT_HEADER("WebSocket / SSE"),
|
|
216
|
+
FIO_CLI_BOOL("--no-websocket-deflate -no-wsd Disable WebSocket "
|
|
217
|
+
"permessage-deflate."),
|
|
218
|
+
FIO_CLI_INT("--ws-max-msg -maxms (" FIO_MACRO2STR(
|
|
219
|
+
FIO_HTTP_DEFAULT_WS_MAX_MSG_SIZE) ") incoming WebSocket message "
|
|
220
|
+
"limit, in bytes."),
|
|
221
|
+
FIO_CLI_INT("--timeout -ping (" FIO_MACRO2STR(
|
|
222
|
+
FIO_HTTP_DEFAULT_TIMEOUT_LONG) ") WebSocket / SSE timeout, in "
|
|
223
|
+
"seconds."),
|
|
224
|
+
|
|
225
|
+
FIO_CLI_PRINT_HEADER("TLS / SSL"),
|
|
226
|
+
FIO_CLI_PRINT(
|
|
227
|
+
"NOTE: may crash if no crypto implementation is available."),
|
|
228
|
+
FIO_CLI_BOOL(
|
|
229
|
+
"--tls-self -tls uses SSL/TLS with a self signed certificate."),
|
|
230
|
+
FIO_CLI_STRING("--tls-name -name The host name for the SSL/TLS "
|
|
231
|
+
"certificate (if any)."),
|
|
232
|
+
FIO_CLI_STRING("--tls-cert -cert The SSL/TLS certificate .pem file."),
|
|
233
|
+
FIO_CLI_STRING("--tls-key -key The SSL/TLS private key .pem file."),
|
|
234
|
+
FIO_CLI_STRING(
|
|
235
|
+
"--tls-password -tls-pass The SSL/TLS password for the private key."),
|
|
236
|
+
FIO_CLI_BOOL("--tls-embedded -mtls uses facil.io's minimal TLS 1.3 "
|
|
237
|
+
"implementation."),
|
|
238
|
+
FIO_CLI_PRINT(
|
|
239
|
+
"NOTE: facil.io's TLS 1.3 was never audited, but could be useful."),
|
|
240
|
+
|
|
241
|
+
FIO_CLI_PRINT_HEADER("Clustering Pub/Sub"),
|
|
242
|
+
FIO_CLI_INT("--broadcast -bp Cluster Broadcast Port."),
|
|
243
|
+
FIO_CLI_STRING("--secret -scrt Cluster Secret."),
|
|
244
|
+
FIO_CLI_PRINT("NOTE: also controlled by the SECRET and SECRET_LENGTH "
|
|
245
|
+
"environment vars."),
|
|
246
|
+
|
|
247
|
+
FIO_CLI_PRINT_HEADER("Connecting Iodine to a RESP3 Database:"),
|
|
248
|
+
FIO_CLI_STRING(
|
|
249
|
+
"--redis -r an optional RESP3 database URL (e.g., Valkey or Redis)."),
|
|
250
|
+
FIO_CLI_INT("--redis-ping -rp RESP3 ping interval in seconds."),
|
|
251
|
+
|
|
252
|
+
FIO_CLI_PRINT_HEADER("Hot Application Swapping / Restarts"),
|
|
253
|
+
FIO_CLI_PRINT_LINE(
|
|
254
|
+
"(requires cluster mode, app code runs only in workers)"),
|
|
255
|
+
FIO_CLI_INT(
|
|
256
|
+
"--hot-restart -hr restarts all worker processes every n seconds."),
|
|
257
|
+
FIO_CLI_STRING(
|
|
258
|
+
"--config -C (config/boot.rb) configuration file to be loaded."),
|
|
259
|
+
FIO_CLI_PRINT("NOTE: the code in the --config file won't be updated."),
|
|
260
|
+
FIO_CLI_BOOL("--preload -preload loads the app code before forking "
|
|
261
|
+
"(cluster only)."),
|
|
262
|
+
FIO_CLI_PRINT(
|
|
263
|
+
"NOTE: otherwise only the --config file will run in root process."),
|
|
264
|
+
|
|
265
|
+
FIO_CLI_PRINT_HEADER("Misc"),
|
|
266
|
+
FIO_CLI_BOOL("--verbose -V -d print out debugging messages."),
|
|
267
|
+
FIO_CLI_BOOL("--rack -R -rack prefer Rack::Builder over NeoRack."),
|
|
268
|
+
FIO_CLI_STRING(
|
|
269
|
+
"--pid -pidfile -pid name for the pid file to be created."),
|
|
270
|
+
FIO_CLI_BOOL(
|
|
271
|
+
"--contained attempts to handle possible container restrictions."),
|
|
272
|
+
FIO_CLI_PRINT(
|
|
273
|
+
"Containers sometimes impose file-system restrictions, i.e.,"),
|
|
274
|
+
FIO_CLI_PRINT("the IPC Unix Socket might need to be placed in `/tmp`."));
|
|
275
|
+
|
|
276
|
+
/* review CLI for logging */
|
|
277
|
+
if (fio_cli_get_bool("-V")) {
|
|
278
|
+
FIO_LOG_LEVEL = FIO_LOG_LEVEL_DEBUG;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (fio_cli_get_bool("-mtls") || iodine___mtls_env()) {
|
|
282
|
+
fio_io_functions_s io_fn;
|
|
283
|
+
io_fn = fio_tls13_io_functions();
|
|
284
|
+
fio_io_tls_default_functions(&io_fn);
|
|
285
|
+
iodine_tls_default_set(Qnil, ID2SYM(rb_intern("iodine")));
|
|
286
|
+
fio_cli_set("-tls", "1");
|
|
287
|
+
FIO_LOG_DEBUG2("TLS 1.3 registered as default TLS implementation");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (fio_cli_get_bool("--contained")) { /* container - IPC url in tmp */
|
|
291
|
+
char *u = (char *)fio_ipc_url();
|
|
292
|
+
memcpy((void *)(u + 7), "/tmp/", 5);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* Secret Detection */
|
|
296
|
+
if (fio_cli_get("-scrt")) {
|
|
297
|
+
fio_buf_info_s scrt = FIO_BUF_INFO1((char *)fio_cli_get("-scrt"));
|
|
298
|
+
fio_secret_set(scrt.buf, scrt.len, 0);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Clustering */
|
|
302
|
+
if (!fio_cli_get_i("-bp") && fio_sys_env("PUBSUB_PORT")) {
|
|
303
|
+
char *bp_s = fio_sys_env("PUBSUB_PORT");
|
|
304
|
+
int64_t bp = fio_atol(&bp_s);
|
|
305
|
+
if (bp > 0 && bp < 0xFFFFLL)
|
|
306
|
+
fio_cli_set("-bp", fio_sys_env("PUBSUB_PORT"));
|
|
307
|
+
}
|
|
308
|
+
if (fio_cli_get_i("-bp") > 0) {
|
|
309
|
+
fio_ipc_cluster_listen((uint16_t)fio_cli_get_i("-bp"));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* support -b and -p for when a URL isn't provided */
|
|
313
|
+
if (fio_cli_get("-p")) {
|
|
314
|
+
fio_buf_info_s tmp = fio_cli_get_str("-b");
|
|
315
|
+
if (tmp.buf) {
|
|
316
|
+
FIO_STR_INFO_TMP_VAR(url, 2048);
|
|
317
|
+
FIO_ASSERT(tmp.len < 2000, "binding address / url too long.");
|
|
318
|
+
fio_url_s u = fio_url_parse(tmp.buf, tmp.len);
|
|
319
|
+
tmp.buf = (char *)fio_cli_get("-p");
|
|
320
|
+
tmp.len = strlen(tmp.buf);
|
|
321
|
+
FIO_ASSERT(tmp.len < 6, "port number too long.");
|
|
322
|
+
fio_string_write2(&url,
|
|
323
|
+
NULL,
|
|
324
|
+
FIO_STRING_WRITE_STR2(u.scheme.buf, u.scheme.len),
|
|
325
|
+
(u.scheme.len ? FIO_STRING_WRITE_STR2("://", 3)
|
|
326
|
+
: FIO_STRING_WRITE_STR2(NULL, 0)),
|
|
327
|
+
FIO_STRING_WRITE_STR2(u.host.buf, u.host.len),
|
|
328
|
+
FIO_STRING_WRITE_STR2(":", 1),
|
|
329
|
+
FIO_STRING_WRITE_STR2(tmp.buf, tmp.len),
|
|
330
|
+
(u.query.len ? FIO_STRING_WRITE_STR2("?", 1)
|
|
331
|
+
: FIO_STRING_WRITE_STR2(NULL, 0)),
|
|
332
|
+
FIO_STRING_WRITE_STR2(u.query.buf, u.query.len));
|
|
333
|
+
fio_cli_set("-b", url.buf);
|
|
334
|
+
} else {
|
|
335
|
+
fio_sys_env_set("PORT", fio_cli_get("-p"), 1);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Save data to Hash and return it... why? I don't know. */
|
|
340
|
+
VALUE h = rb_hash_new();
|
|
341
|
+
STORE.hold(h);
|
|
342
|
+
fio_cli_each(iodine_cli_task, (void *)h);
|
|
343
|
+
/* cleanup */
|
|
344
|
+
for (long i = 0; i < len; ++i) {
|
|
345
|
+
fio_state_callback_remove(FIO_CALL_AT_EXIT,
|
|
346
|
+
(void (*)(void *))fio_bstr_free,
|
|
347
|
+
(void *)argv[i]);
|
|
348
|
+
fio_bstr_free((char *)argv[i]);
|
|
349
|
+
}
|
|
350
|
+
STORE.release(h);
|
|
351
|
+
return h;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static VALUE iodine_cli_get(VALUE self, VALUE key) {
|
|
355
|
+
VALUE r = Qnil;
|
|
356
|
+
fio_buf_info_s val;
|
|
357
|
+
int64_t ival = 0;
|
|
358
|
+
char *tmp;
|
|
359
|
+
if (RB_INTEGER_TYPE_P(key)) {
|
|
360
|
+
val = fio_cli_unnamed_str(NUM2UINT(key));
|
|
361
|
+
if (val.buf)
|
|
362
|
+
r = rb_str_new(val.buf, val.len);
|
|
363
|
+
return r;
|
|
364
|
+
}
|
|
365
|
+
if (RB_TYPE_P(key, RUBY_T_SYMBOL))
|
|
366
|
+
key = rb_sym2str(key);
|
|
367
|
+
if (!RB_TYPE_P(key, RUBY_T_STRING))
|
|
368
|
+
rb_raise(rb_eArgError,
|
|
369
|
+
"key should be either an Integer, a String or a Symbol");
|
|
370
|
+
val = fio_cli_get_str(RSTRING_PTR(key));
|
|
371
|
+
if (!val.len)
|
|
372
|
+
return r;
|
|
373
|
+
tmp = val.buf;
|
|
374
|
+
ival = fio_atol(&tmp);
|
|
375
|
+
if (tmp == val.buf + val.len)
|
|
376
|
+
r = LL2NUM(ival);
|
|
377
|
+
else
|
|
378
|
+
r = rb_str_new(val.buf, val.len);
|
|
379
|
+
return r;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
static VALUE iodine_cli_set(VALUE self, VALUE key, VALUE value) {
|
|
383
|
+
if (fio_io_is_running() || !fio_io_is_master())
|
|
384
|
+
rb_raise(rb_eException,
|
|
385
|
+
"Setting CLI arguments can only be performed before Iodine.start "
|
|
386
|
+
"and in the master process.");
|
|
387
|
+
if (RB_INTEGER_TYPE_P(key)) {
|
|
388
|
+
if (!RB_TYPE_P(value, RUBY_T_STRING))
|
|
389
|
+
rb_raise(rb_eArgError,
|
|
390
|
+
"value for an indexed CLI argument should be a String");
|
|
391
|
+
fio_cli_set_unnamed(NUM2UINT(key), RSTRING_PTR(value));
|
|
392
|
+
return value;
|
|
393
|
+
}
|
|
394
|
+
if (RB_TYPE_P(key, RUBY_T_SYMBOL))
|
|
395
|
+
key = rb_sym2str(key);
|
|
396
|
+
if (!RB_TYPE_P(key, RUBY_T_STRING))
|
|
397
|
+
rb_raise(rb_eArgError,
|
|
398
|
+
"key should be either an Integer, a String or a Symbol");
|
|
399
|
+
fio_cli_arg_e t = fio_cli_type(RSTRING_PTR(key));
|
|
400
|
+
if (t == FIO_CLI_ARG_INT) { /* Number */
|
|
401
|
+
if (!RB_INTEGER_TYPE_P(value))
|
|
402
|
+
rb_raise(rb_eArgError,
|
|
403
|
+
"value for %s should be an Integer",
|
|
404
|
+
RSTRING_PTR(key));
|
|
405
|
+
fio_cli_set_i(RSTRING_PTR(key), NUM2LL(value));
|
|
406
|
+
} else if (t == FIO_CLI_ARG_BOOL) { /* Boolean */
|
|
407
|
+
if (value != Qtrue && value != Qfalse)
|
|
408
|
+
rb_raise(rb_eArgError,
|
|
409
|
+
"value for %s should be a Boolean",
|
|
410
|
+
RSTRING_PTR(key));
|
|
411
|
+
fio_cli_set_i(RSTRING_PTR(key), NUM2LL((value == Qtrue)));
|
|
412
|
+
|
|
413
|
+
} else { /* must be String */
|
|
414
|
+
if (!RB_TYPE_P(value, RUBY_T_STRING))
|
|
415
|
+
rb_raise(rb_eArgError,
|
|
416
|
+
"value for %s should be a String",
|
|
417
|
+
RSTRING_PTR(key));
|
|
418
|
+
fio_cli_set(RSTRING_PTR(key), RSTRING_PTR(value));
|
|
419
|
+
}
|
|
420
|
+
return value;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Initialize Iodine::Base::CLI */
|
|
424
|
+
/**
|
|
425
|
+
* The Iodine::Base::CLI module is used internally to manage CLI options.
|
|
426
|
+
*/
|
|
427
|
+
static void Init_Iodine_Base_CLI(void) {
|
|
428
|
+
VALUE cli = rb_define_module_under(iodine_rb_IODINE_BASE, "CLI");
|
|
429
|
+
rb_define_singleton_method(cli, "parse", iodine_cli_parse, 1);
|
|
430
|
+
rb_define_singleton_method(cli, "[]", iodine_cli_get, 1);
|
|
431
|
+
rb_define_singleton_method(cli, "[]=", iodine_cli_set, 2);
|
|
432
|
+
iodine_cli_parse(cli, Qfalse);
|
|
433
|
+
}
|
|
434
|
+
#endif /* H___IODINE_CLI___H */
|