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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -20
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -146
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
data/ext/iodine/iodine.c CHANGED
@@ -1,1430 +1,250 @@
1
+ /* core include */
1
2
  #include "iodine.h"
2
3
 
3
- #include <ruby/version.h>
4
-
5
- #define FIO_INCLUDE_LINKED_LIST
6
- #include "fio.h"
7
- #include "fio_cli.h"
8
4
  /* *****************************************************************************
9
- OS specific patches
5
+ Deprecation Warnings
10
6
  ***************************************************************************** */
11
7
 
12
- #ifdef __APPLE__
13
- #include <dlfcn.h>
14
- #endif
15
-
16
- /** Any patches required by the running environment for consistent behavior */
17
- static void patch_env(void) {
18
- #ifdef __APPLE__
19
- /* patch for dealing with the High Sierra `fork` limitations */
20
- void *obj_c_runtime = dlopen("Foundation.framework/Foundation", RTLD_LAZY);
21
- (void)obj_c_runtime;
22
- #endif
23
- }
8
+ /** @deprecated use {Iodine::TLS.add_cert}. */
9
+ static VALUE iodine_tls_cert_add_old_name(int argc, VALUE *argv, VALUE self);
24
10
 
25
11
  /* *****************************************************************************
26
- Constants and State
12
+ Initialize module
27
13
  ***************************************************************************** */
28
14
 
29
- VALUE IodineModule;
30
- VALUE IodineBaseModule;
31
-
32
- /** Default connection settings for {Iodine.listen} and {Iodine.connect}. */
33
- VALUE iodine_default_args;
34
-
35
- ID iodine_call_id;
36
- ID iodine_to_s_id;
37
-
38
- static VALUE address_sym;
39
- static VALUE app_sym;
40
- static VALUE body_sym;
41
- static VALUE cookies_sym;
42
- static VALUE handler_sym;
43
- static VALUE headers_sym;
44
- static VALUE log_sym;
45
- static VALUE max_body_sym;
46
- static VALUE max_clients_sym;
47
- static VALUE max_headers_sym;
48
- static VALUE max_msg_sym;
49
- static VALUE method_sym;
50
- static VALUE path_sym;
51
- static VALUE ping_sym;
52
- static VALUE port_sym;
53
- static VALUE public_sym;
54
- static VALUE service_sym;
55
- static VALUE timeout_sym;
56
- static VALUE tls_sym;
57
- static VALUE url_sym;
58
-
59
- /* *****************************************************************************
60
- Idling
61
- ***************************************************************************** */
62
-
63
- /* performs a Ruby state callback and clears the Ruby object's memory */
64
- static void iodine_perform_on_idle_callback(void *blk_) {
65
- VALUE blk = (VALUE)blk_;
66
- IodineCaller.call(blk, iodine_call_id);
67
- IodineStore.remove(blk);
68
- fio_state_callback_remove(FIO_CALL_ON_IDLE, iodine_perform_on_idle_callback,
69
- blk_);
70
- }
71
-
72
- /**
73
- Schedules a single occuring event for the next idle cycle.
74
-
75
- To schedule a reoccuring event, reschedule the event at the end of it's
76
- run.
77
-
78
- i.e.
79
-
80
- IDLE_PROC = Proc.new { puts "idle"; Iodine.on_idle &IDLE_PROC }
81
- Iodine.on_idle &IDLE_PROC
82
- */
83
- static VALUE iodine_sched_on_idle(VALUE self) {
84
- // clang-format on
85
- rb_need_block();
86
- VALUE block = rb_block_proc();
87
- IodineStore.add(block);
88
- fio_state_callback_add(FIO_CALL_ON_IDLE, iodine_perform_on_idle_callback,
89
- (void *)block);
90
- return block;
91
- (void)self;
15
+ static void Init_Iodine(void) {
16
+ rb_define_singleton_method(iodine_rb_IODINE, "start", iodine_start, 0);
17
+ rb_define_singleton_method(iodine_rb_IODINE, "stop", iodine_stop, 0);
18
+
19
+ rb_define_singleton_method(iodine_rb_IODINE,
20
+ "running?",
21
+ iodine_is_running,
22
+ 0);
23
+ rb_define_singleton_method(iodine_rb_IODINE, "master?", iodine_is_master, 0);
24
+ rb_define_singleton_method(iodine_rb_IODINE, "worker?", iodine_is_worker, 0);
25
+
26
+ rb_define_singleton_method(iodine_rb_IODINE, "workers", iodine_workers, 0);
27
+ rb_define_singleton_method(iodine_rb_IODINE,
28
+ "workers=",
29
+ iodine_workers_set,
30
+ 1);
31
+
32
+ rb_define_singleton_method(iodine_rb_IODINE, "threads", iodine_threads, 0);
33
+ rb_define_singleton_method(iodine_rb_IODINE,
34
+ "threads=",
35
+ iodine_threads_set,
36
+ 1);
37
+
38
+ rb_define_singleton_method(iodine_rb_IODINE, "secret", iodine_secret, 0);
39
+ rb_define_singleton_method(iodine_rb_IODINE, "secret=", iodine_secret_set, 1);
40
+
41
+ rb_define_singleton_method(iodine_rb_IODINE,
42
+ "shutdown_timeout",
43
+ iodine_shutdown_timeout,
44
+ 0);
45
+ rb_define_singleton_method(iodine_rb_IODINE,
46
+ "shutdown_timeout=",
47
+ iodine_shutdown_timeout_set,
48
+ 1);
49
+
50
+ rb_define_module_function(iodine_rb_IODINE, "run", iodine_defer_run, 0);
51
+ rb_define_module_function(iodine_rb_IODINE,
52
+ "async",
53
+ iodine_defer_run_async,
54
+ 0);
55
+
56
+ rb_define_module_function(iodine_rb_IODINE,
57
+ "run_after",
58
+ iodine_defer_run_after,
59
+ -1);
60
+ rb_define_module_function(iodine_rb_IODINE, "on_state", iodine_on_state, 1);
61
+
62
+ rb_define_singleton_method(iodine_rb_IODINE, "listen", iodine_listen_rb, -1);
92
63
  }
93
64
 
94
65
  /* *****************************************************************************
95
- Running Iodine
66
+ Cleanup
96
67
  ***************************************************************************** */
97
68
 
98
- typedef struct {
99
- int16_t threads;
100
- int16_t workers;
101
- } iodine_start_params_s;
102
-
103
- static void *iodine_run_outside_GVL(void *params_) {
104
- iodine_start_params_s *params = params_;
105
- fio_start(.threads = params->threads, .workers = params->workers);
69
+ FIO_SFUNC void *iodine___perform_exit_outside_gvl(void *ignr_) {
70
+ (void)ignr_;
71
+ fio_queue_perform_all(fio_io_queue());
72
+ fio_cli_end();
73
+ fio_state_callback_force(FIO_CALL_AT_EXIT);
74
+ fio_state_callback_clear(FIO_CALL_AT_EXIT);
75
+ STORE.destroy();
106
76
  return NULL;
107
77
  }
108
78
 
109
- /* *****************************************************************************
110
- Core API
111
- ***************************************************************************** */
112
-
113
- /**
114
- * Returns the number of worker threads that will be used when {Iodine.start}
115
- * is called.
116
- *
117
- * Negative numbers are translated as fractions of the number of CPU cores.
118
- * i.e., -2 == half the number of detected CPU cores.
119
- *
120
- * Zero values promise nothing (iodine will decide what to do with them).
121
- *
122
- * @return [FixNum] Thread Count
123
- */
124
- static VALUE iodine_threads_get(VALUE self) {
125
- VALUE i = rb_ivar_get(self, rb_intern2("@threads", 8));
126
- if (i == Qnil)
127
- i = INT2NUM(0);
128
- return i;
129
- }
130
-
131
- /**
132
- * Sets the number of worker threads that will be used when {Iodine.start}
133
- * is called.
134
- *
135
- * Negative numbers are translated as fractions of the number of CPU cores.
136
- * i.e., -2 == half the number of detected CPU cores.
137
- *
138
- * Zero values promise nothing (iodine will decide what to do with them).
139
- *
140
- * @param thread_count [FixNum] The number of worker threads to use
141
- */
142
- static VALUE iodine_threads_set(VALUE self, VALUE val) {
143
- Check_Type(val, T_FIXNUM);
144
- if (NUM2SSIZET(val) >= (1 << 12)) {
145
- rb_raise(rb_eRangeError, "requsted thread count is out of range.");
146
- }
147
- rb_ivar_set(self, rb_intern2("@threads", 8), val);
148
- return val;
149
- }
150
-
151
- /**
152
- * Gets the logging level used for Iodine messages.
153
- *
154
- * Levels range from 0-5, where:
155
- *
156
- * 0 == Quite (no messages)
157
- * 1 == Fatal Errors only.
158
- * 2 == Errors only (including fatal errors).
159
- * 3 == Warnings and errors only.
160
- * 4 == Informational messages, warnings and errors (default).
161
- * 5 == Everything, including debug information.
162
- *
163
- * Logging is always performed to the process's STDERR and can be piped away.
164
- *
165
- * @return [FixNum] Logging Level
166
- *
167
- * NOTE: this does NOT effect HTTP logging.
168
- */
169
- static VALUE iodine_logging_get(VALUE self) {
170
- return INT2FIX(FIO_LOG_LEVEL);
171
- (void)self;
79
+ /* Workers end with the C STL's `exit(0)`, which skips Ruby's VM cleanup (the
80
+ * step that frees every remaining TypedData object). Without it, objects that
81
+ * are already unreferenced but not yet collected look like leaks. ON_STOP runs
82
+ * after all IO is closed and the thread pool stopped, so collect them here. */
83
+ FIO_SFUNC void *iodine___worker_gc_in_gvl(void *ignr_) {
84
+ rb_gc();
85
+ return ignr_;
172
86
  }
173
87
 
174
- /**
175
- * Gets the logging level used for Iodine messages.
176
- *
177
- * Levels range from 0-5, where:
178
- *
179
- * 0 == Quite (no messages)
180
- * 1 == Fatal Errors only.
181
- * 2 == Errors only (including fatal errors).
182
- * 3 == Warnings and errors only.
183
- * 4 == Informational messages, warnings and errors (default).
184
- * 5 == Everything, including debug information.
185
- *
186
- * Logging is always performed to the process's STDERR and can be piped away.
187
- *
188
- * @param log_level [FixNum] Sets the logging level
189
- *
190
- * NOTE: this does NOT effect HTTP logging.
191
- */
192
- static VALUE iodine_logging_set(VALUE self, VALUE val) {
193
- Check_Type(val, T_FIXNUM);
194
- FIO_LOG_LEVEL = FIX2INT(val);
195
- return self;
196
- }
197
-
198
- /**
199
- * Returns the number of worker processes that will be used when {Iodine.start}
200
- * is called.
201
- *
202
- * Negative numbers are translated as fractions of the number of CPU cores.
203
- * i.e., -2 == half the number of detected CPU cores.
204
- *
205
- * Zero values promise nothing (iodine will decide what to do with them).
206
- *
207
- * 1 == single process mode, the msater process acts as a worker process.
208
- *
209
- * @return [FixNum] Worker Count
210
- */
211
- static VALUE iodine_workers_get(VALUE self) {
212
- VALUE i = rb_ivar_get(self, rb_intern2("@workers", 8));
213
- if (i == Qnil)
214
- i = INT2NUM(0);
215
- return i;
216
- }
217
-
218
- /**
219
- * Sets the number of worker processes that will be used when {Iodine.start}
220
- * is called.
221
- *
222
- * Negative numbers are translated as fractions of the number of CPU cores.
223
- * i.e., -2 == half the number of detected CPU cores.
224
- *
225
- * Zero values promise nothing (iodine will decide what to do with them).
226
- *
227
- * 1 == single process mode, the msater process acts as a worker process.
228
- *
229
- * @param worker_count [FixNum] Number of worker processes
230
- */
231
- static VALUE iodine_workers_set(VALUE self, VALUE val) {
232
- Check_Type(val, T_FIXNUM);
233
- if (NUM2SSIZET(val) >= (1 << 9)) {
234
- rb_raise(rb_eRangeError, "requsted worker process count is out of range.");
235
- }
236
- rb_ivar_set(self, rb_intern2("@workers", 8), val);
237
- return val;
238
- }
239
-
240
- /** Logs the Iodine startup message */
241
- static void iodine_print_startup_message(iodine_start_params_s params) {
242
- VALUE iodine_version = rb_const_get(IodineModule, rb_intern("VERSION"));
243
- VALUE ruby_version = rb_const_get(IodineModule, rb_intern("RUBY_VERSION"));
244
- fio_expected_concurrency(&params.threads, &params.workers);
245
- FIO_LOG_INFO("Starting up Iodine:\n"
246
- " * Iodine %s\n * Ruby %s\n"
247
- " * facil.io " FIO_VERSION_STRING " (%s)\n"
248
- " * %d Workers X %d Threads per worker.\n"
249
- " * Maximum %zu open files / sockets per worker.\n"
250
- " * Master (root) process: %d.\n",
251
- StringValueCStr(iodine_version), StringValueCStr(ruby_version),
252
- fio_engine(), params.workers, params.threads, fio_capa(),
253
- fio_parent_pid());
254
- (void)params;
255
- }
256
-
257
- /**
258
- * This will block the calling (main) thread and start the Iodine reactor.
259
- *
260
- * When using cluster mode (2 or more worker processes), it is important that no
261
- * other threads are active.
262
- *
263
- * For many reasons, `fork` should NOT be called while multi-threading, so
264
- * cluster mode must always be initiated from the main thread in a single thread
265
- * environment.
266
- *
267
- * For information about why forking in multi-threaded environments should be
268
- * avoided, see (for example):
269
- * http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them
270
- *
271
- */
272
- static VALUE iodine_start(VALUE self) {
273
- if (fio_is_running()) {
274
- rb_raise(rb_eRuntimeError, "Iodine already running!");
275
- }
276
- IodineCaller.set_GVL(1);
277
- VALUE threads_rb = iodine_threads_get(self);
278
- VALUE workers_rb = iodine_workers_get(self);
279
- iodine_start_params_s params = {
280
- .threads = NUM2SHORT(threads_rb),
281
- .workers = NUM2SHORT(workers_rb),
282
- };
283
- iodine_print_startup_message(params);
284
- IodineCaller.leaveGVL(iodine_run_outside_GVL, &params);
285
- return self;
286
- }
287
-
288
- /**
289
- * This will stop the iodine server, shutting it down.
290
- *
291
- * If called within a worker process (rather than the root/master process), this
292
- * will cause a hot-restart for the worker.
293
- */
294
- static VALUE iodine_stop(VALUE self) {
295
- fio_stop();
296
- return self;
297
- }
298
-
299
- /**
300
- * Returns `true` if this process is the master / root process, `false`
301
- * otherwise.
302
- *
303
- * Note that the master process might be a worker process as well, when running
304
- * in single process mode (see {Iodine.workers}).
305
- */
306
- static VALUE iodine_master_is(VALUE self) {
307
- return fio_is_master() ? Qtrue : Qfalse;
308
- }
309
-
310
- /**
311
- * Returns `true` if this process is a worker process or if iodine is running in
312
- * a single process mode (the master is also a worker), `false` otherwise.
313
- */
314
- static VALUE iodine_worker_is(VALUE self) {
315
- return fio_is_worker() ? Qtrue : Qfalse;
316
- }
317
-
318
- /**
319
- * Returns `true` if Iodine is currently running a server
320
- */
321
- static VALUE iodine_running(VALUE self) {
322
- if (fio_is_running()) {
323
- return Qtrue;
324
- } else {
325
- return Qfalse;
326
- }
88
+ FIO_SFUNC void iodine___worker_gc_on_stop(void *ignr_) {
89
+ if (fio_io_is_master()) /* the master process exits through Ruby */
90
+ return;
91
+ iodine_c_call_with(iodine___worker_gc_in_gvl, ignr_);
327
92
  }
328
93
 
329
- /* *****************************************************************************
330
- CLI parser (Ruby's OptParser is more limiting than I knew...)
331
- ***************************************************************************** */
332
-
333
- /**
334
- * Parses the CLI argnumnents, returning the Rack filename (if provided).
335
- *
336
- * Unknown arguments are ignored.
337
- *
338
- * @params [String] desc a String containg the iodine server's description.
339
- */
340
- static VALUE iodine_cli_parse(VALUE self) {
341
- (void)self;
342
- VALUE ARGV = rb_get_argv();
343
- VALUE ret = Qtrue;
344
- VALUE defaults = iodine_default_args;
345
- VALUE iodine_version = rb_const_get(IodineModule, rb_intern("VERSION"));
346
- char desc[1024];
347
- if (!defaults || !ARGV || TYPE(ARGV) != T_ARRAY || TYPE(defaults) != T_HASH ||
348
- TYPE(iodine_version) != T_STRING || RSTRING_LEN(iodine_version) > 512) {
349
- FIO_LOG_ERROR("CLI parsing initialization error "
350
- "ARGV=%p, Array?(%d), defaults == %p (%d)",
351
- (void *)ARGV, (int)(TYPE(ARGV) == T_ARRAY), (void *)defaults,
352
- (int)(TYPE(defaults) == T_HASH));
353
- return Qnil;
354
- }
355
- /* Copy the Ruby ARGV to a C valid ARGV */
356
- int argc = (int)RARRAY_LEN(ARGV) + 1;
357
- if (argc <= 1) {
358
- FIO_LOG_DEBUG("CLI: No arguments to parse...\n");
359
- return Qnil;
360
- } else {
361
- FIO_LOG_DEBUG("Iodine CLI parsing %d arguments", argc);
362
- }
363
- char **argv = calloc(argc, sizeof(*argv));
364
- FIO_ASSERT_ALLOC(argv);
365
- argv[0] = (char *)"iodine";
366
- for (int i = 1; i < argc; ++i) {
367
- VALUE tmp = rb_ary_entry(ARGV, (long)(i - 1));
368
- if (TYPE(tmp) != T_STRING) {
369
- FIO_LOG_ERROR("ARGV Array contains a non-String object.");
370
- ret = Qnil;
371
- goto finish;
372
- }
373
- fio_str_info_s s = IODINE_RSTRINFO(tmp);
374
- argv[i] = malloc(s.len + 1);
375
- FIO_ASSERT_ALLOC(argv[i]);
376
- memcpy(argv[i], s.data, s.len);
377
- argv[i][s.len] = 0;
378
- }
379
- /* Levarage the facil.io CLI library */
380
- memcpy(desc, "Iodine's HTTP/WebSocket server version ", 39);
381
- memcpy(desc + 39, StringValueCStr(iodine_version),
382
- RSTRING_LEN(iodine_version));
383
- memcpy(desc + 39 + RSTRING_LEN(iodine_version),
384
- "\r\n\r\nUse:\r\n iodine <options> <filename>\r\n\r\n"
385
- "Both <options> and <filename> are optional. i.e.,:\r\n"
386
- " iodine -p 0 -b /tmp/my_unix_sock\r\n"
387
- " iodine -p 8080 path/to/app/conf.ru\r\n"
388
- " iodine -p 8080 -w 4 -t 16\r\n"
389
- " iodine -w -1 -t 4 -r redis://usr:pass@localhost:6379/",
390
- 263);
391
- desc[39 + 263 + RSTRING_LEN(iodine_version)] = 0;
392
- fio_cli_start(
393
- argc, (const char **)argv, 0, -1, desc,
394
- FIO_CLI_PRINT_HEADER("Address Binding:"),
395
- "-bind -b -address address to listen to. defaults to any available.",
396
- FIO_CLI_INT("-port -p port number to listen to. defaults port 3000"),
397
- FIO_CLI_PRINT("\t\t\x1B[4mNote\x1B[0m: to bind to a Unix socket, set "
398
- "\x1B[1mport\x1B[0m to 0."),
399
- FIO_CLI_PRINT_HEADER("Concurrency:"),
400
- FIO_CLI_INT("-threads -t number of threads per process."),
401
- FIO_CLI_INT("-workers -w number of processes to use."),
402
- FIO_CLI_PRINT("Negative concurrency values "
403
- "map to fractions of available CPU cores."),
404
- FIO_CLI_PRINT_HEADER("HTTP Settings:"),
405
- FIO_CLI_STRING("-public -www public folder, for static file service."),
406
- FIO_CLI_INT("-keep-alive -k -tout HTTP keep-alive timeout in seconds "
407
- "(0..255). Default: 40s"),
408
- FIO_CLI_BOOL("-log -v HTTP request logging."),
409
- FIO_CLI_INT(
410
- "-max-body -maxbd HTTP upload limit in Mega-Bytes. Default: 50Mb"),
411
- FIO_CLI_INT("-max-header -maxhd header limit per HTTP request in Kb. "
412
- "Default: 32Kb."),
413
- FIO_CLI_PRINT_HEADER("WebSocket Settings:"),
414
- FIO_CLI_INT("-max-msg -maxms incoming WebSocket message limit in Kb. "
415
- "Default: 250Kb"),
416
- FIO_CLI_INT("-ping websocket ping interval (1..255). Default: 40s"),
417
- FIO_CLI_PRINT_HEADER("SSL/TLS:"),
418
- FIO_CLI_BOOL("-tls enable SSL/TLS using a self-signed certificate."),
419
- FIO_CLI_STRING(
420
- "-tls-cert -cert the SSL/TLS public certificate file name."),
421
- FIO_CLI_STRING("-tls-key -key the SSL/TLS private key file name."),
422
- FIO_CLI_STRING(
423
- "-tls-pass -tls-password the password (if any) protecting the "
424
- "private key file."),
425
- FIO_CLI_PRINT("\t\t\x1B[1m-tls-password\x1B[0m is deprecated, use "
426
- "\x1B[1m-tls-pass\x1B[0m"),
427
- FIO_CLI_PRINT_HEADER("Connecting Iodine to Redis:"),
428
- FIO_CLI_STRING(
429
- "-redis -r an optional Redis URL server address. Default: none."),
430
- FIO_CLI_INT(
431
- "-redis-ping -rp websocket ping interval (0..255). Default: 300s"),
432
- FIO_CLI_PRINT_HEADER("Misc:"),
433
- FIO_CLI_STRING("-config -C configuration file to be loaded."),
434
- FIO_CLI_STRING("-pid -pidfile name for the pid file to be created."),
435
- FIO_CLI_INT("-verbosity -V 0..5 server verbosity level. Default: 4"),
436
- FIO_CLI_BOOL(
437
- "-warmup --preload warm up the application. CAREFUL! with workers."));
438
-
439
- /* copy values from CLI library to iodine */
440
- if (fio_cli_get("-V")) {
441
- int level = fio_cli_get_i("-V");
442
- if (level >= 0 && level < 100)
443
- FIO_LOG_LEVEL = level;
444
- }
445
- if (!fio_cli_get("-w") && getenv("WEB_CONCURRENCY")) {
446
- fio_cli_set("-w", getenv("WEB_CONCURRENCY"));
447
- }
448
- if (!fio_cli_get("-w") && getenv("WORKERS")) {
449
- fio_cli_set("-w", getenv("WORKERS"));
450
- }
451
- if (fio_cli_get("-w")) {
452
- iodine_workers_set(IodineModule, INT2NUM(fio_cli_get_i("-w")));
453
- }
454
- if (!fio_cli_get("-t") && getenv("THREADS")) {
455
- fio_cli_set("-t", getenv("THREADS"));
456
- }
457
- if (fio_cli_get("-t")) {
458
- iodine_threads_set(IodineModule, INT2NUM(fio_cli_get_i("-t")));
459
- }
460
- if (fio_cli_get_bool("-v")) {
461
- rb_hash_aset(defaults, log_sym, Qtrue);
462
- }
463
- if (fio_cli_get_bool("-warmup")) {
464
- rb_hash_aset(defaults, ID2SYM(rb_intern("warmup_")), Qtrue);
465
- }
466
- // if (!fio_cli_get("-b") && getenv("ADDRESS")) {
467
- // fio_cli_set("-b", getenv("ADDRESS"));
468
- // }
469
- if (fio_cli_get("-b")) {
470
- if (fio_cli_get("-b")[0] == '/' ||
471
- (fio_cli_get("-b")[0] == '.' && fio_cli_get("-b")[1] == '/')) {
472
- if (fio_cli_get("-p") &&
473
- (fio_cli_get("-p")[0] != '0' || fio_cli_get("-p")[1])) {
474
- FIO_LOG_WARNING(
475
- "Detected a Unix socket binding (-b) conflicting with port.\n"
476
- " Port settings (-p %s) are ignored",
477
- fio_cli_get("-p"));
478
- }
479
- fio_cli_set("-p", "0");
480
- } else {
481
- // if (!fio_cli_get("-p") && getenv("PORT")) {
482
- // fio_cli_set("-p", getenv("PORT"));
483
- // }
484
- }
485
- rb_hash_aset(defaults, address_sym, rb_str_new_cstr(fio_cli_get("-b")));
486
- }
487
- if (fio_cli_get("-p")) {
488
- rb_hash_aset(defaults, port_sym, rb_str_new_cstr(fio_cli_get("-p")));
489
- }
490
- if (fio_cli_get("-www")) {
491
- rb_hash_aset(defaults, public_sym, rb_str_new_cstr(fio_cli_get("-www")));
492
- }
493
- if (!fio_cli_get("-redis") && getenv("IODINE_REDIS_URL")) {
494
- fio_cli_set("-redis", getenv("IODINE_REDIS_URL"));
495
- }
496
- if (fio_cli_get("-redis")) {
497
- rb_hash_aset(defaults, ID2SYM(rb_intern("redis_")),
498
- rb_str_new_cstr(fio_cli_get("-redis")));
499
- }
500
- if (fio_cli_get("-k")) {
501
- rb_hash_aset(defaults, timeout_sym, INT2NUM(fio_cli_get_i("-k")));
502
- }
503
- if (fio_cli_get("-ping")) {
504
- rb_hash_aset(defaults, ping_sym, INT2NUM(fio_cli_get_i("-ping")));
505
- }
506
- if (fio_cli_get("-redis-ping")) {
507
- rb_hash_aset(defaults, ID2SYM(rb_intern("redis_ping_")),
508
- INT2NUM(fio_cli_get_i("-redis-ping")));
509
- }
510
- if (fio_cli_get("-max-body")) {
511
- rb_hash_aset(defaults, max_body_sym,
512
- INT2NUM((fio_cli_get_i("-max-body") /* * 1024 * 1024 */)));
513
- }
514
- if (fio_cli_get("-maxms")) {
515
- rb_hash_aset(defaults, max_msg_sym,
516
- INT2NUM((fio_cli_get_i("-maxms") /* * 1024 */)));
517
- }
518
- if (fio_cli_get("-maxhd")) {
519
- rb_hash_aset(defaults, max_headers_sym,
520
- INT2NUM((fio_cli_get_i("-maxhd") /* * 1024 */)));
521
- }
522
- #ifndef __MINGW32__
523
- if (fio_cli_get_bool("-tls") || fio_cli_get("-key") || fio_cli_get("-cert")) {
524
- VALUE rbtls = IodineCaller.call(IodineTLSClass, rb_intern2("new", 3));
525
- if (rbtls == Qnil) {
526
- FIO_LOG_FATAL("Iodine internal error, Ruby TLS object is nil.");
527
- exit(-1);
528
- }
529
- fio_tls_s *tls = iodine_tls2c(rbtls);
530
- if (!tls) {
531
- FIO_LOG_FATAL("Iodine internal error, TLS object NULL.");
532
- exit(-1);
533
- }
534
- if (fio_cli_get("-tls-key") && fio_cli_get("-tls-cert")) {
535
- fio_tls_cert_add(tls, NULL, fio_cli_get("-tls-cert"),
536
- fio_cli_get("-tls-key"), fio_cli_get("-tls-pass"));
537
- } else {
538
- if (!fio_cli_get_bool("-tls"))
539
- FIO_LOG_ERROR("TLS support requires both key and certificate."
540
- "\r\n\t\tfalling back on a self signed certificate.");
541
- char name[1024];
542
- fio_local_addr(name, 1024);
543
- fio_tls_cert_add(tls, name, NULL, NULL, NULL);
544
- }
545
- rb_hash_aset(defaults, tls_sym, rbtls);
546
- }
547
- #endif
548
- if (fio_cli_unnamed_count()) {
549
- rb_hash_aset(defaults, ID2SYM(rb_intern("filename_")),
550
- rb_str_new_cstr(fio_cli_unnamed(0)));
551
- }
552
- if (fio_cli_get("-pid")) {
553
- VALUE pid_filename = rb_str_new_cstr(fio_cli_get("-pid"));
554
- rb_hash_aset(defaults, ID2SYM(rb_intern("pid_")), pid_filename);
555
- rb_hash_aset(defaults, ID2SYM(rb_intern("pid")), pid_filename);
556
- }
557
- if (fio_cli_get("-config")) {
558
- VALUE conf_filename = rb_str_new_cstr(fio_cli_get("-config"));
559
- rb_hash_aset(defaults, ID2SYM(rb_intern("conf_")), conf_filename);
560
- }
561
-
562
- /* create `filename` String, cleanup and return */
563
- fio_cli_end();
564
- finish:
565
- for (int i = 1; i < argc; ++i) {
566
- free(argv[i]);
567
- }
568
- free(argv);
569
- return ret;
94
+ FIO_SFUNC void iodine___perform_exit(VALUE ignr_) {
95
+ (void)ignr_;
96
+ // rb_gc();
97
+ /* iodine runs outside of GVL, but at_exit runs in GVL.. so... */
98
+ rb_thread_call_without_gvl(iodine___perform_exit_outside_gvl,
99
+ NULL,
100
+ NULL,
101
+ NULL);
570
102
  }
571
103
 
572
104
  /* *****************************************************************************
573
- Argument support for `connect` / `listen`
105
+ OS specific patches
574
106
  ***************************************************************************** */
575
107
 
576
- static int for_each_header_value(VALUE key, VALUE val, VALUE h_) {
577
- FIOBJ h = h_;
578
- if (RB_TYPE_P(key, T_SYMBOL))
579
- key = rb_sym2str(key);
580
- if (!RB_TYPE_P(key, T_STRING)) {
581
- FIO_LOG_WARNING("invalid key type in header hash, ignored.");
582
- return ST_CONTINUE;
583
- }
584
- if (RB_TYPE_P(val, T_SYMBOL))
585
- val = rb_sym2str(val);
586
- if (RB_TYPE_P(val, T_STRING)) {
587
- FIOBJ k = fiobj_str_new(RSTRING_PTR(key), RSTRING_LEN(key));
588
- fiobj_hash_set(h, k, fiobj_str_new(RSTRING_PTR(val), RSTRING_LEN(val)));
589
- fiobj_free(k);
590
- } else if (RB_TYPE_P(val, T_ARRAY)) {
591
- FIOBJ k = fiobj_str_new(RSTRING_PTR(key), RSTRING_LEN(key));
592
- size_t len = rb_array_len(val);
593
- FIOBJ v = fiobj_ary_new2(len);
594
- fiobj_hash_set(h, k, v);
595
- fiobj_free(k);
596
- for (size_t i = 0; i < len; ++i) {
597
- VALUE tmp = rb_ary_entry(val, i);
598
- if (RB_TYPE_P(tmp, T_SYMBOL))
599
- tmp = rb_sym2str(tmp);
600
- if (RB_TYPE_P(tmp, T_STRING))
601
- fiobj_ary_push(v, fiobj_str_new(RSTRING_PTR(tmp), RSTRING_LEN(tmp)));
602
- }
603
- } else {
604
- FIO_LOG_WARNING("invalid header value type, ignored.");
605
- }
606
- return ST_CONTINUE;
607
- }
608
-
609
- static int for_each_cookie(VALUE key, VALUE val, VALUE h_) {
610
- FIOBJ h = h_;
611
- if (RB_TYPE_P(key, T_SYMBOL))
612
- key = rb_sym2str(key);
613
- if (!RB_TYPE_P(key, T_STRING)) {
614
- FIO_LOG_WARNING("invalid key type in cookie hash, ignored.");
615
- return ST_CONTINUE;
616
- }
617
- if (RB_TYPE_P(val, T_SYMBOL))
618
- val = rb_sym2str(val);
619
- if (RB_TYPE_P(val, T_STRING)) {
620
- FIOBJ k = fiobj_str_new(RSTRING_PTR(key), RSTRING_LEN(key));
621
- fiobj_hash_set(h, k, fiobj_str_new(RSTRING_PTR(val), RSTRING_LEN(val)));
622
- fiobj_free(k);
623
- } else {
624
- FIO_LOG_WARNING("invalid cookie value type, ignored.");
625
- }
626
- return ST_CONTINUE;
627
- }
628
-
629
- /* cleans up any resources used by the argument list processing */
630
- FIO_FUNC void iodine_connect_args_cleanup(iodine_connection_args_s *s) {
631
- if (!s)
632
- return;
633
- fiobj_free(s->cookies);
634
- fiobj_free(s->headers);
635
- if (s->port.capa)
636
- fio_free(s->port.data);
637
- if (s->address.capa)
638
- fio_free(s->address.data);
639
- #ifndef __MINGW32__
640
- if (s->tls)
641
- fio_tls_destroy(s->tls);
642
- #endif
643
- }
644
-
645
- /*
646
- Accepts:
647
-
648
- func(settings)
649
-
650
- Supported Settigs:
651
- - `:url`
652
- - `:handler` (deprecated: `app`)
653
- - `:service` (raw / ws / wss / http / https )
654
- - `:address`
655
- - `:port`
656
- - `:path` (HTTP/WebSocket client)
657
- - `:method` (HTTP client)
658
- - `:headers` (HTTP/WebSocket client)
659
- - `:cookies` (HTTP/WebSocket client)
660
- - `:body` (HTTP client)
661
- - `:tls`
662
- - `:log` (HTTP only)
663
- - `:public` (public folder, HTTP server only)
664
- - `:timeout` (HTTP only)
665
- - `:ping` (`:raw` clients and WebSockets only)
666
- - `:max_headers` (HTTP only)
667
- - `:max_body` (HTTP only)
668
- - `:max_msg` (WebSockets only)
669
-
670
- */
671
- FIO_FUNC iodine_connection_args_s iodine_connect_args(VALUE s, uint8_t is_srv) {
672
- Check_Type(s, T_HASH);
673
- iodine_connection_args_s r = {.ping = 0}; /* set all to 0 */
674
- /* Collect argument values */
675
- VALUE address = rb_hash_aref(s, address_sym);
676
- VALUE app = rb_hash_aref(s, app_sym);
677
- VALUE body = rb_hash_aref(s, body_sym);
678
- VALUE cookies = rb_hash_aref(s, cookies_sym);
679
- VALUE handler = rb_hash_aref(s, handler_sym);
680
- VALUE headers = rb_hash_aref(s, headers_sym);
681
- VALUE log = rb_hash_aref(s, log_sym);
682
- VALUE max_body = rb_hash_aref(s, max_body_sym);
683
- VALUE max_clients = rb_hash_aref(s, max_clients_sym);
684
- VALUE max_headers = rb_hash_aref(s, max_headers_sym);
685
- VALUE max_msg = rb_hash_aref(s, max_msg_sym);
686
- VALUE method = rb_hash_aref(s, method_sym);
687
- VALUE path = rb_hash_aref(s, path_sym);
688
- VALUE ping = rb_hash_aref(s, ping_sym);
689
- VALUE port = rb_hash_aref(s, port_sym);
690
- VALUE r_public = rb_hash_aref(s, public_sym);
691
- VALUE service = rb_hash_aref(s, service_sym);
692
- VALUE timeout = rb_hash_aref(s, timeout_sym);
693
- #ifndef __MINGW32__
694
- VALUE tls = rb_hash_aref(s, tls_sym);
695
- #endif
696
- VALUE r_url = rb_hash_aref(s, url_sym);
697
- fio_str_info_s service_str = {.data = NULL};
698
-
699
- /* Complete using default values */
700
- if (address == Qnil)
701
- address = rb_hash_aref(iodine_default_args, address_sym);
702
- if (app == Qnil)
703
- app = rb_hash_aref(iodine_default_args, app_sym);
704
- if (cookies == Qnil)
705
- cookies = rb_hash_aref(iodine_default_args, cookies_sym);
706
- if (handler == Qnil)
707
- handler = rb_hash_aref(iodine_default_args, handler_sym);
708
- if (headers == Qnil)
709
- headers = rb_hash_aref(iodine_default_args, headers_sym);
710
- if (log == Qnil)
711
- log = rb_hash_aref(iodine_default_args, log_sym);
712
- if (max_body == Qnil)
713
- max_body = rb_hash_aref(iodine_default_args, max_body_sym);
714
- if (max_clients == Qnil)
715
- max_clients = rb_hash_aref(iodine_default_args, max_clients_sym);
716
- if (max_headers == Qnil)
717
- max_headers = rb_hash_aref(iodine_default_args, max_headers_sym);
718
- if (max_msg == Qnil)
719
- max_msg = rb_hash_aref(iodine_default_args, max_msg_sym);
720
- if (method == Qnil)
721
- method = rb_hash_aref(iodine_default_args, method_sym);
722
- if (path == Qnil)
723
- path = rb_hash_aref(iodine_default_args, path_sym);
724
- if (ping == Qnil)
725
- ping = rb_hash_aref(iodine_default_args, ping_sym);
726
- if (port == Qnil)
727
- port = rb_hash_aref(iodine_default_args, port_sym);
728
- if (r_public == Qnil) {
729
- r_public = rb_hash_aref(iodine_default_args, public_sym);
730
- }
731
- // if (service == Qnil) // not supported by default settings...
732
- // service = rb_hash_aref(iodine_default_args, service_sym);
733
- if (timeout == Qnil)
734
- timeout = rb_hash_aref(iodine_default_args, timeout_sym);
735
- #ifndef __MINGW32__
736
- if (tls == Qnil)
737
- tls = rb_hash_aref(iodine_default_args, tls_sym);
108
+ #ifdef __APPLE__
109
+ #include <dlfcn.h>
738
110
  #endif
739
111
 
740
- /* TODO: deprecation */
741
- if (handler == Qnil) {
742
- handler = rb_hash_aref(s, app_sym);
743
- if (handler != Qnil)
744
- FIO_LOG_WARNING(":app is deprecated in Iodine.listen and Iodine.connect. "
745
- "Use :handler");
746
- }
747
-
748
- /* specific for HTTP */
749
- if (is_srv && handler == Qnil && rb_block_given_p()) {
750
- handler = rb_block_proc();
751
- }
752
-
753
- /* Raise exceptions on errors (last chance) */
754
- if (handler == Qnil) {
755
- rb_raise(rb_eArgError, "a :handler is required.");
756
- }
757
-
758
- /* Set existing values */
759
- if (handler != Qnil) {
760
- r.handler = handler;
761
- }
762
- if (address != Qnil && RB_TYPE_P(address, T_STRING)) {
763
- r.address = IODINE_RSTRINFO(address);
764
- }
765
- if (body != Qnil && RB_TYPE_P(body, T_STRING)) {
766
- r.body = IODINE_RSTRINFO(body);
767
- }
768
- if (cookies != Qnil && RB_TYPE_P(cookies, T_HASH)) {
769
- r.cookies = fiobj_hash_new2(rb_hash_size(cookies));
770
- rb_hash_foreach(cookies, for_each_cookie, r.cookies);
771
- }
772
- if (headers != Qnil && RB_TYPE_P(headers, T_HASH)) {
773
- r.headers = fiobj_hash_new2(rb_hash_size(headers));
774
- rb_hash_foreach(headers, for_each_header_value, r.headers);
775
- }
776
- if (log != Qnil && log != Qfalse) {
777
- r.log = 1;
778
- }
779
- if (max_body != Qnil && RB_TYPE_P(max_body, T_FIXNUM)) {
780
- r.max_body = FIX2ULONG(max_body) * 1024 * 1024;
781
- }
782
- if (max_clients != Qnil && RB_TYPE_P(max_clients, T_FIXNUM)) {
783
- r.max_clients = FIX2ULONG(max_clients);
784
- }
785
- if (max_headers != Qnil && RB_TYPE_P(max_headers, T_FIXNUM)) {
786
- r.max_headers = FIX2ULONG(max_headers) * 1024;
787
- }
788
- if (max_msg != Qnil && RB_TYPE_P(max_msg, T_FIXNUM)) {
789
- r.max_msg = FIX2ULONG(max_msg) * 1024;
790
- }
791
- if (method != Qnil && RB_TYPE_P(method, T_STRING)) {
792
- r.method = IODINE_RSTRINFO(method);
793
- }
794
- if (path != Qnil && RB_TYPE_P(path, T_STRING)) {
795
- r.path = IODINE_RSTRINFO(path);
796
- }
797
- if (ping != Qnil && RB_TYPE_P(ping, T_FIXNUM)) {
798
- if (FIX2ULONG(ping) > 255)
799
- FIO_LOG_WARNING(":ping value over 255 will be silently ignored.");
800
- else
801
- r.ping = FIX2ULONG(ping);
802
- }
803
- if (port != Qnil) {
804
- if (RB_TYPE_P(port, T_STRING)) {
805
- char *tmp = RSTRING_PTR(port);
806
- if (fio_atol(&tmp))
807
- r.port = IODINE_RSTRINFO(port);
808
- } else if (RB_TYPE_P(port, T_FIXNUM) && FIX2UINT(port)) {
809
- if (FIX2UINT(port) >= 65536) {
810
- FIO_LOG_WARNING("Port number %u is too high, quietly ignored.",
811
- FIX2UINT(port));
812
- } else {
813
- r.port = (fio_str_info_s){.data = fio_malloc(16), .len = 0, .capa = 1};
814
- r.port.len = fio_ltoa(r.port.data, FIX2INT(port), 10);
815
- r.port.data[r.port.len] = 0;
816
- }
817
- }
818
- }
819
-
820
- if (r_public != Qnil && RB_TYPE_P(r_public, T_STRING)) {
821
- r.public = IODINE_RSTRINFO(r_public);
822
- }
823
- if (service != Qnil && RB_TYPE_P(service, T_STRING)) {
824
- service_str = IODINE_RSTRINFO(service);
825
- } else if (service != Qnil && RB_TYPE_P(service, T_SYMBOL)) {
826
- service = rb_sym2str(service);
827
- service_str = IODINE_RSTRINFO(service);
828
- }
829
- if (timeout != Qnil && RB_TYPE_P(ping, T_FIXNUM)) {
830
- if (FIX2ULONG(timeout) > 255)
831
- FIO_LOG_WARNING(":timeout value over 255 will be silently ignored.");
832
- else
833
- r.timeout = FIX2ULONG(timeout);
834
- }
835
- #ifndef __MINGW32__
836
- if (tls != Qnil) {
837
- r.tls = iodine_tls2c(tls);
838
- if (r.tls)
839
- fio_tls_dup(r.tls);
840
- }
841
- #endif
842
- /* URL parsing */
843
- if (r_url != Qnil && RB_TYPE_P(r_url, T_STRING)) {
844
- r.url = IODINE_RSTRINFO(r_url);
845
- fio_url_s u = fio_url_parse(r.url.data, r.url.len);
846
- /* set service string */
847
- if (u.scheme.data) {
848
- service_str = u.scheme;
849
- }
850
- /* copy port number */
851
- if (u.port.data) {
852
- char *tmp = u.port.data;
853
- if (fio_atol(&tmp) == 0) {
854
- if (r.port.capa)
855
- fio_free(r.port.data);
856
- r.port = (fio_str_info_s){.data = NULL};
857
- } else {
858
- if (u.port.len > 5)
859
- FIO_LOG_WARNING("Port number error (%.*s too long to be valid).",
860
- (int)u.port.len, u.port.data);
861
- if (r.port.capa && u.port.len >= 16) {
862
- fio_free(r.port.data);
863
- r.port = (fio_str_info_s){.data = NULL};
864
- }
865
- if (!r.port.capa)
866
- r.port = (fio_str_info_s){
867
- .data = fio_malloc(u.port.len + 1), .len = u.port.len, .capa = 1};
868
- memcpy(r.port.data, u.port.data, u.port.len);
869
- r.port.len = u.port.len;
870
- r.port.data[r.port.len] = 0;
871
- }
872
- } else {
873
- if (r.port.capa)
874
- fio_free(r.port.data);
875
- r.port = (fio_str_info_s){.data = NULL};
876
- }
877
- /* copy host / address */
878
- if (u.host.data) {
879
- r.address = (fio_str_info_s){
880
- .data = fio_malloc(u.host.len + 1), .len = u.host.len, .capa = 1};
881
- memcpy(r.address.data, u.host.data, u.host.len);
882
- r.address.len = u.host.len;
883
- r.address.data[r.address.len] = 0;
884
- } else {
885
- if (r.address.capa)
886
- fio_free(r.address.data);
887
- r.address = (fio_str_info_s){.data = NULL};
888
- }
889
- /* set path */
890
- if (u.path.data) {
891
- /* support possible Unix address as "raw://:0/my/sock.sock" */
892
- if (r.address.data || r.port.data)
893
- r.path = u.path;
894
- else
895
- r.address = u.path;
896
- }
897
- }
898
- /* test/set service type */
899
- r.service = IODINE_SERVICE_RAW;
900
- if (service_str.data) {
901
- #ifdef __MINGW32__
902
- switch (service_str.data[0]) {
903
- case 't': /* overflow */
904
- /* tcp or tls */
905
- if (service_str.data[1] == 'l') {
906
- char *local = NULL;
907
- char buf[1024];
908
- buf[1023] = 0;
909
- if (is_srv) {
910
- local = buf;
911
- if (fio_local_addr(buf, 1023) >= 1022)
912
- local = NULL;
913
- }
914
- }
915
- /* overflow */
916
- case 'u': /* overflow */
917
- /* unix */
918
- case 'r':
919
- /* raw */
920
- r.service = IODINE_SERVICE_RAW;
921
- break;
922
- case 'h':
923
- /* http(s) */
924
- r.service = IODINE_SERVICE_HTTP;
925
- if (service_str.len == 5) {
926
- char *local = NULL;
927
- char buf[1024];
928
- buf[1023] = 0;
929
- if (is_srv) {
930
- local = buf;
931
- if (fio_local_addr(buf, 1023) >= 1022)
932
- local = NULL;
933
- }
934
- }
935
- case 'w':
936
- /* ws(s) */
937
- r.service = IODINE_SERVICE_WS;
938
- if (service_str.len == 3) {
939
- char *local = NULL;
940
- char buf[1024];
941
- buf[1023] = 0;
942
- if (is_srv) {
943
- local = buf;
944
- if (fio_local_addr(buf, 1023) >= 1022)
945
- local = NULL;
946
- }
947
- }
948
- break;
949
- }
950
- #else
951
- switch (service_str.data[0]) {
952
- case 't': /* overflow */
953
- /* tcp or tls */
954
- if (service_str.data[1] == 'l' && !r.tls) {
955
- char *local = NULL;
956
- char buf[1024];
957
- buf[1023] = 0;
958
- if (is_srv) {
959
- local = buf;
960
- if (fio_local_addr(buf, 1023) >= 1022)
961
- local = NULL;
962
- }
963
- r.tls = fio_tls_new(local, NULL, NULL, NULL);
964
- }
965
- /* overflow */
966
- case 'u': /* overflow */
967
- /* unix */
968
- case 'r':
969
- /* raw */
970
- r.service = IODINE_SERVICE_RAW;
971
- break;
972
- case 'h':
973
- /* http(s) */
974
- r.service = IODINE_SERVICE_HTTP;
975
- if (service_str.len == 5 && !r.tls) {
976
- char *local = NULL;
977
- char buf[1024];
978
- buf[1023] = 0;
979
- if (is_srv) {
980
- local = buf;
981
- if (fio_local_addr(buf, 1023) >= 1022)
982
- local = NULL;
983
- }
984
- r.tls = fio_tls_new(local, NULL, NULL, NULL);
985
- }
986
- case 'w':
987
- /* ws(s) */
988
- r.service = IODINE_SERVICE_WS;
989
- if (service_str.len == 3 && !r.tls) {
990
- char *local = NULL;
991
- char buf[1024];
992
- buf[1023] = 0;
993
- if (is_srv) {
994
- local = buf;
995
- if (fio_local_addr(buf, 1023) >= 1022)
996
- local = NULL;
997
- }
998
- r.tls = fio_tls_new(local, NULL, NULL, NULL);
999
- }
1000
- break;
1001
- }
112
+ /** Any patches required by the running environment for consistent behavior */
113
+ static void patch_env(void) {
114
+ #ifdef __APPLE__
115
+ /* patch for dealing with the High Sierra `fork` limitations */
116
+ void *obj_c_runtime = dlopen("Foundation.framework/Foundation", RTLD_LAZY);
117
+ (void)obj_c_runtime;
1002
118
  #endif
1003
- }
1004
- return r;
1005
- }
1006
-
1007
- /* *****************************************************************************
1008
- Listen function routing
1009
- ***************************************************************************** */
1010
-
1011
- // clang-format off
1012
- /*
1013
- {Iodine.listen} can be used to listen to any incoming connections, including HTTP and raw (tcp/ip and unix sockets) connections.
1014
-
1015
- Iodine.listen(settings)
1016
-
1017
- Supported Settigs:
1018
-
1019
- | | |
1020
- |---|---|
1021
- | `:url` | URL indicating service type, host name and port. Path will be parsed as a Unix socket. |
1022
- | `:handler` | (deprecated: `:app`) see details below. |
1023
- | `:address` | an IP address or a unix socket address. Only relevant if `:url` is missing. |
1024
- | `:log` | (HTTP only) request logging. For global verbosity see {Iodine.verbosity} |
1025
- | `:max_body` | (HTTP only) maximum upload size allowed per request before disconnection (in Mb). |
1026
- | `:max_headers` | (HTTP only) maximum total header length allowed per request (in Kb). |
1027
- | `:max_msg` | (WebSockets only) maximum message size pre message (in Kb). |
1028
- | `:ping` | (`:raw` clients and WebSockets only) ping interval (in seconds). Up to 255 seconds. |
1029
- | `:port` | port number to listen to either a String or Number) |
1030
- | `:public` | (HTTP server only) public folder for static file service. |
1031
- | `:service` | (`:raw` / `:tls` / `:ws` / `:wss` / `:http` / `:https` ) a supported service this socket will listen to. |
1032
- | `:timeout` | (HTTP only) keep-alive timeout in seconds. Up to 255 seconds. |
1033
- | `:tls` | an {Iodine::TLS} context object for encrypted connections. |
1034
-
1035
- Some connection settings are only valid when listening to HTTP / WebSocket connections.
1036
-
1037
- If `:url` is provided, it will overwrite the `:address` and `:port` settings (if provided).
1038
-
1039
- For HTTP connections, the `:handler` **must** be a valid Rack application object (answers `.call(env)`).
1040
-
1041
- Here's an example for an HTTP hello world application:
1042
-
1043
- require 'iodine'
1044
- # a handler can be a block
1045
- Iodine.listen(service: :http, port: "3000") {|env| [200, {"Content-Length" => "12"}, ["Hello World!"]] }
1046
- # start the service
1047
- Iodine.threads = 1
1048
- Iodine.start
1049
-
1050
-
1051
- Here's another example, using a Unix Socket instead of a TCP/IP socket for an HTTP hello world application.
1052
-
1053
- This example shows how the `:url` option can be used, but the `:address` settings could have been used for the same effect (with `port: 0`).
1054
-
1055
- require 'iodine'
1056
- # note that unix sockets in URL form use an absolute path.
1057
- Iodine.listen(url: "http://:0/tmp/sock.sock") {|env| [200, {"Content-Length" => "12"}, ["Hello World!"]] }
1058
- # start the service
1059
- Iodine.threads = 1
1060
- Iodine.start
1061
-
1062
-
1063
- For raw connections, the `:handler` object should be an object that answer `.call` and returns a valid callback object that supports the following callbacks (see also {Iodine::Connection}):
1064
-
1065
- | | |
1066
- |---|---|
1067
- | `on_open(client)` | called after a connection was established |
1068
- | `on_message(client,data)` | called when incoming data is available. Data may be fragmented. |
1069
- | `on_drained(client)` | called after pending `client.write` events have been processed (see {Iodine::Connection#pending}). |
1070
- | `ping(client)` | called whenever a timeout has occured (see {Iodine::Connection#timeout=}). |
1071
- | `on_shutdown(client)` | called if the server is shutting down. This is called before the connection is closed. |
1072
- | `on_close(client)` | called when the connection with the client was closed. |
1073
-
1074
- The `client` argument passed to the `:handler` callbacks is an {Iodine::Connection} instance that represents the connection / the client.
1075
-
1076
- Here's an example for a telnet based chat-room example:
1077
-
1078
- require 'iodine'
1079
- # define the protocol for our service
1080
- module ChatHandler
1081
- def self.on_open(client)
1082
- # Set a connection timeout
1083
- client.timeout = 10
1084
- # subscribe to the chat channel.
1085
- client.subscribe :chat
1086
- # Write a welcome message
1087
- client.publish :chat, "new member entered the chat\r\n"
1088
- end
1089
- # this is called for incoming data - note data might be fragmented.
1090
- def self.on_message(client, data)
1091
- # publish the data we received
1092
- client.publish :chat, data
1093
- # close the connection when the time comes
1094
- client.close if data =~ /^bye[\n\r]/
1095
- end
1096
- # called whenever timeout occurs.
1097
- def self.ping(client)
1098
- client.write "System: quite, isn't it...?\r\n"
1099
- end
1100
- # called if the connection is still open and the server is shutting down.
1101
- def self.on_shutdown(client)
1102
- # write the data we received
1103
- client.write "Chat server going away. Try again later.\r\n"
1104
- end
1105
- # returns the callback object (self).
1106
- def self.call
1107
- self
1108
- end
1109
- end
1110
- # we use can both the `handler` keyword or a block, anything that answers #call.
1111
- Iodine.listen(service: :raw, port: "3000", handler: ChatHandler)
1112
- # we can listen to more than a single socket at a time.
1113
- Iodine.listen(url: "raw://:3030", handler: ChatHandler)
1114
- # start the service
1115
- Iodine.threads = 1
1116
- Iodine.start
1117
-
1118
-
1119
-
1120
- Returns the handler object used.
1121
- */
1122
- static VALUE iodine_listen(VALUE self, VALUE args) {
1123
- // clang-format on
1124
- iodine_connection_args_s s = iodine_connect_args(args, 1);
1125
- intptr_t uuid = -1;
1126
- switch (s.service) {
1127
- case IODINE_SERVICE_RAW:
1128
- uuid = iodine_tcp_listen(s);
1129
- break;
1130
- case IODINE_SERVICE_HTTP: /* overflow */
1131
- case IODINE_SERVICE_WS:
1132
- uuid = iodine_http_listen(s);
1133
- break;
1134
- }
1135
- iodine_connect_args_cleanup(&s);
1136
- if (uuid == -1)
1137
- rb_raise(rb_eRuntimeError, "Couldn't open listening socket.");
1138
- return s.handler;
1139
- (void)self;
1140
- }
1141
-
1142
- /* *****************************************************************************
1143
- Connect function routing
1144
- ***************************************************************************** */
1145
-
1146
- // clang-format off
1147
- /*
1148
-
1149
- The {connect} method instructs iodine to connect to a server using either TCP/IP or Unix sockets.
1150
-
1151
- Iodine.connect(settings)
1152
-
1153
- Supported Settigs:
1154
-
1155
-
1156
- | | |
1157
- |---|---|
1158
- | `:url` | URL indicating service type, host name, port and optional path. |
1159
- | `:handler` | see details below. |
1160
- | `:address` | an IP address or a unix socket address. Only relevant if `:url` is missing. |
1161
- | `:body` | (HTTP client) the body to be sent. |
1162
- | `:cookies` | (HTTP/WebSocket client) cookie data. |
1163
- | `:headers` | (HTTP/WebSocket client) custom headers. |
1164
- | `:log` | (HTTP only) - logging the requests. |
1165
- | `:max_body` | (HTTP only) - limits HTTP body in the response, see {listen}. |
1166
- | `:max_headers` | (HTTP only) - limits the header length in the response, see {listen}. |
1167
- | `:max_msg` | (WebSockets only) maximum incoming message size pre message (in Kb). |
1168
- | `:method` | (HTTP client) a String such as "GET" or "POST". |
1169
- | `:path` |HTTP/WebSocket client) the HTTP path to be used. |
1170
- | `:ping` | ping interval (in seconds). Up to 255 seconds. |
1171
- | `:port` | port number to listen to either a String or Number) |
1172
- | `:public` | (public folder, HTTP server only) |
1173
- | `:service` | (`:raw` / `:tls` / `:ws` / `:wss` ) |
1174
- | `:timeout` | (HTTP only) keep-alive timeout in seconds. Up to 255 seconds. |
1175
- | `:tls` | an {Iodine::TLS} context object for encrypted connections. |
1176
-
1177
- Some connection settings are only valid for HTTP / WebSocket connections.
1178
-
1179
- If `:url` is provided, it will overwrite the `:address`, `:port` and `:path` settings (if provided).
1180
-
1181
- Unlike {Iodine.listen}, a block can't be used and a `:handler` object **must** be provided.
1182
-
1183
- If the connection fails, only the `on_close` callback will be called (with a `nil` client).
1184
-
1185
- Here's an example TCP/IP client that sends a simple HTTP GET request:
1186
-
1187
- # use a secure connection?
1188
- USE_TLS = false
1189
-
1190
- # remote server details
1191
- $port = USE_TLS ? 443 : 80
1192
- $address = "google.com"
1193
-
1194
-
1195
- # require iodine
1196
- require 'iodine'
1197
-
1198
- # Iodine runtime settings
1199
- Iodine.threads = 1
1200
- Iodine.workers = 1
1201
- Iodine.verbosity = 3 # warnings only
1202
-
1203
-
1204
- # a client callback handler
1205
- module Client
1206
-
1207
- def self.on_open(connection)
1208
- # Set a connection timeout
1209
- connection.timeout = 10
1210
- # subscribe to the chat channel.
1211
- puts "* Sending request..."
1212
- connection.write "GET / HTTP/1.1\r\nHost: #{$address}\r\n\r\n"
1213
- end
1214
-
1215
- def self.on_message(connection, data)
1216
- # publish the data we received
1217
- STDOUT.write data
1218
- # close the connection after a second... we're not really parsing anything, so it's a guess.
1219
- Iodine.run_after(1000) { connection.close }
1220
- end
1221
-
1222
- def self.on_close(connection)
1223
- # stop iodine
1224
- Iodine.stop
1225
- puts "Done."
1226
- end
1227
-
1228
- # returns the callback object (self).
1229
- def self.call
1230
- self
1231
- end
1232
- end
1233
-
1234
-
1235
-
1236
- if(USE_TLS)
1237
- tls = Iodine::TLS.new
1238
- # ALPN blocks should return a valid calback object
1239
- tls.on_protocol("http/1.1") { Client }
1240
- end
1241
-
1242
- Iodine.connect(address: $address, port: $port, handler: Client, tls: tls)
1243
-
1244
- # start the iodine reactor
1245
- Iodine.start
1246
-
1247
- Iodine also supports WebSocket client connections, using either the `url` property or the `ws` and `wss` service names.
1248
-
1249
- The following example establishes a secure (TLS) connects to the WebSocket echo testing server at `wss://echo.websocket.org`:
1250
-
1251
- # require iodine
1252
- require 'iodine'
1253
-
1254
- # The client class
1255
- class EchoClient
1256
-
1257
- def on_open(connection)
1258
- @messages = [ "Hello World!",
1259
- "I'm alive and sending messages",
1260
- "I also receive messages",
1261
- "now that we all know this...",
1262
- "I can stop.",
1263
- "Goodbye." ]
1264
- send_one_message(connection)
1265
- end
1266
-
1267
- def on_message(connection, message)
1268
- puts "Received: #{message}"
1269
- send_one_message(connection)
1270
- end
1271
-
1272
- def on_close(connection)
1273
- # in this example, we stop iodine once the client is closed
1274
- puts "* Client closed."
1275
- Iodine.stop
1276
- end
1277
-
1278
- # We use this method to pop messages from the queue and send them
1279
- #
1280
- # When the queue is empty, we disconnect the client.
1281
- def send_one_message(connection)
1282
- msg = @messages.shift
1283
- if(msg)
1284
- connection.write msg
1285
- else
1286
- connection.close
1287
- end
1288
- end
1289
- end
1290
-
1291
- Iodine.threads = 1
1292
- Iodine.connect url: "wss://echo.websocket.org", handler: EchoClient.new, ping: 40
1293
- Iodine.start
1294
-
1295
- **Note**: the `on_close` callback is always called, even if a connection couldn't be established.
1296
-
1297
- Returns the handler object used.
1298
- */
1299
- static VALUE iodine_connect(VALUE self, VALUE args) {
1300
- // clang-format on
1301
- iodine_connection_args_s s = iodine_connect_args(args, 0);
1302
- intptr_t uuid = -1;
1303
- switch (s.service) {
1304
- case IODINE_SERVICE_RAW:
1305
- uuid = iodine_tcp_connect(s);
1306
- break;
1307
- case IODINE_SERVICE_HTTP:
1308
- iodine_connect_args_cleanup(&s);
1309
- rb_raise(rb_eRuntimeError, "HTTP client connections aren't supported yet.");
1310
- return Qnil;
1311
- break;
1312
- case IODINE_SERVICE_WS:
1313
- uuid = iodine_ws_connect(s);
1314
- break;
1315
- }
1316
- iodine_connect_args_cleanup(&s);
1317
- if (uuid == -1)
1318
- rb_raise(rb_eRuntimeError, "Couldn't open client socket.");
1319
- return self;
1320
119
  }
1321
120
 
1322
121
  /* *****************************************************************************
1323
- Ruby loads the library and invokes the Init_<lib_name> function...
1324
-
1325
- Here we connect all the C code to the Ruby interface, completing the bridge
1326
- between Lib-Server and Ruby.
122
+ Initialize Extension
1327
123
  ***************************************************************************** */
1328
- void Init_iodine_ext(void) {
1329
- /* common Symbol objects in use by Iodine */
1330
- #define IODINE_MAKE_SYM(name) \
1331
- do { \
1332
- name##_sym = rb_id2sym(rb_intern(#name)); \
1333
- rb_global_variable(&name##_sym); \
1334
- } while (0)
1335
- IODINE_MAKE_SYM(address);
1336
- IODINE_MAKE_SYM(app);
1337
- IODINE_MAKE_SYM(body);
1338
- IODINE_MAKE_SYM(cookies);
1339
- IODINE_MAKE_SYM(handler);
1340
- IODINE_MAKE_SYM(headers);
1341
- IODINE_MAKE_SYM(log);
1342
- IODINE_MAKE_SYM(max_body);
1343
- IODINE_MAKE_SYM(max_clients);
1344
- IODINE_MAKE_SYM(max_headers);
1345
- IODINE_MAKE_SYM(max_msg);
1346
- IODINE_MAKE_SYM(method);
1347
- IODINE_MAKE_SYM(path);
1348
- IODINE_MAKE_SYM(ping);
1349
- IODINE_MAKE_SYM(port);
1350
- IODINE_MAKE_SYM(public);
1351
- IODINE_MAKE_SYM(service);
1352
- IODINE_MAKE_SYM(timeout);
1353
- IODINE_MAKE_SYM(tls);
1354
- IODINE_MAKE_SYM(url);
1355
124
 
1356
- // load any environment specific patches
125
+ void Init_iodine(void) {
1357
126
  patch_env();
1358
-
1359
- // force the GVL state for the main thread
1360
- IodineCaller.set_GVL(1);
1361
-
1362
- // Create the Iodine module (namespace)
1363
- IodineModule = rb_define_module("Iodine");
1364
- IodineBaseModule = rb_define_module_under(IodineModule, "Base");
1365
- VALUE IodineCLIModule = rb_define_module_under(IodineBaseModule, "CLI");
1366
- iodine_call_id = rb_intern2("call", 4);
1367
- iodine_to_s_id = rb_intern("to_s");
1368
-
1369
- // register core methods
1370
- rb_define_module_function(IodineModule, "threads", iodine_threads_get, 0);
1371
- rb_define_module_function(IodineModule, "threads=", iodine_threads_set, 1);
1372
- rb_define_module_function(IodineModule, "verbosity", iodine_logging_get, 0);
1373
- rb_define_module_function(IodineModule, "verbosity=", iodine_logging_set, 1);
1374
- rb_define_module_function(IodineModule, "workers", iodine_workers_get, 0);
1375
- rb_define_module_function(IodineModule, "workers=", iodine_workers_set, 1);
1376
- rb_define_module_function(IodineModule, "start", iodine_start, 0);
1377
- rb_define_module_function(IodineModule, "stop", iodine_stop, 0);
1378
- rb_define_module_function(IodineModule, "on_idle", iodine_sched_on_idle, 0);
1379
- rb_define_module_function(IodineModule, "master?", iodine_master_is, 0);
1380
- rb_define_module_function(IodineModule, "worker?", iodine_worker_is, 0);
1381
- rb_define_module_function(IodineModule, "running?", iodine_running, 0);
1382
- rb_define_module_function(IodineModule, "listen", iodine_listen, 1);
1383
- rb_define_module_function(IodineModule, "connect", iodine_connect, 1);
1384
-
1385
- // register CLI methods
1386
- rb_define_module_function(IodineCLIModule, "parse", iodine_cli_parse, 0);
1387
-
1388
- /** Default connection settings for {listen} and {connect}. */
1389
- iodine_default_args = rb_hash_new();
1390
- /** Default connection settings for {listen} and {connect}. */
1391
- rb_const_set(IodineModule, rb_intern("DEFAULT_SETTINGS"),
1392
- iodine_default_args);
1393
-
1394
- /** Depracated, use {Iodine::DEFAULT_SETTINGS}. */
1395
- rb_const_set(IodineModule, rb_intern("DEFAULT_HTTP_ARGS"),
1396
- iodine_default_args);
1397
-
1398
- // initialize Object storage for GC protection
1399
- iodine_storage_init();
1400
-
1401
- // initialize concurrency related methods
1402
- iodine_defer_initialize();
1403
-
1404
- // initialize the connection class
1405
- iodine_connection_init();
1406
-
1407
- // intialize the TCP/IP related module
1408
- iodine_init_tcp_connections();
1409
-
1410
- // initialize the HTTP module
1411
- iodine_init_http();
1412
-
1413
- #ifndef __MINGW32__
1414
- // initialize SSL/TLS support module
1415
- iodine_init_tls();
127
+ IODINE_THREAD_POOL = (fio_io_async_s)FIO_IO_ASYN_INIT;
128
+ fio_state_callback_force(FIO_CALL_ON_INITIALIZE);
129
+
130
+ IodineUTF8Encoding = rb_enc_find("UTF-8");
131
+ IodineBinaryEncoding = rb_enc_find("binary");
132
+
133
+ /** The Iodine module is where it all happens. */
134
+ iodine_rb_IODINE = rb_define_module("Iodine");
135
+ STORE.hold(iodine_rb_IODINE);
136
+ /** The PubSub module contains Pub/Sub related classes / data. */
137
+ iodine_rb_IODINE_PUBSUB = rb_define_module_under(iodine_rb_IODINE, "PubSub");
138
+ STORE.hold(iodine_rb_IODINE_PUBSUB);
139
+ /** The Iodine::Base module is for internal concerns. */
140
+ iodine_rb_IODINE_BASE =
141
+ rb_define_class_under(iodine_rb_IODINE, "Base", rb_cObject);
142
+ rb_undef_alloc_func(iodine_rb_IODINE_BASE);
143
+ STORE.hold(iodine_rb_IODINE_BASE);
144
+ /** The Iodine::Base::App404 module is for static file only. */
145
+ iodine_rb_IODINE_BASE_APP404 =
146
+ rb_define_module_under(iodine_rb_IODINE_BASE, "App404");
147
+ STORE.hold(iodine_rb_IODINE_BASE_APP404);
148
+ { /** Initialize `STORE` and object reference counting. */
149
+ iodine_setup_value_reference_counter(iodine_rb_IODINE_BASE);
150
+ rb_define_singleton_method(iodine_rb_IODINE_BASE,
151
+ "print_debug",
152
+ iodine_store___print_debug,
153
+ 0);
154
+ rb_define_singleton_method(iodine_rb_IODINE_BASE,
155
+ "store_size",
156
+ iodine_store___held_count,
157
+ 0);
158
+ }
159
+
160
+ IODINE_CONST_ID_STORE(IODINE_CALL_ID, "call");
161
+ IODINE_CONST_ID_STORE(IODINE_CLOSE_ID, "close");
162
+ IODINE_CONST_ID_STORE(IODINE_EACH_ID, "each");
163
+ IODINE_CONST_ID_STORE(IODINE_FILENO_ID, "fileno");
164
+ IODINE_CONST_ID_STORE(IODINE_NEW_ID, "new");
165
+ IODINE_CONST_ID_STORE(IODINE_CURRENT_ID, "current");
166
+ IODINE_CONST_ID_STORE(IODINE_JOIN_ID, "join");
167
+ IODINE_CONST_ID_STORE(IODINE_TO_PATH_ID, "to_path");
168
+ IODINE_CONST_ID_STORE(IODINE_TO_S_ID, "to_s");
169
+ IODINE_CONST_ID_STORE(IODINE_TO_JSON_ID, "to_json");
170
+
171
+ IODINE_CONST_ID_STORE(IODINE_INDEX_ID, "index");
172
+ IODINE_CONST_ID_STORE(IODINE_SHOW_ID, "show");
173
+ IODINE_CONST_ID_STORE(IODINE_EDIT_ID, "edit");
174
+ IODINE_CONST_ID_STORE(IODINE_CREATE_ID, "create");
175
+ IODINE_CONST_ID_STORE(IODINE_UPDATE_ID, "update");
176
+ IODINE_CONST_ID_STORE(IODINE_DELETE_ID, "delete");
177
+
178
+ IODINE_CONST_ID_STORE(IODINE_RACK_HIJACK_ID, "rack_hijack");
179
+ IODINE_CONST_ID_STORE(IODINE_ON_AUTHENTICATE_ID, "on_authenticate");
180
+ IODINE_CONST_ID_STORE(IODINE_ON_AUTHENTICATE_SSE_ID, "on_authenticate_sse");
181
+ IODINE_CONST_ID_STORE(IODINE_ON_AUTHENTICATE_WEBSOCKET_ID,
182
+ "on_authenticate_websocket");
183
+ IODINE_CONST_ID_STORE(IODINE_ON_CLOSE_ID, "on_close");
184
+ IODINE_CONST_ID_STORE(IODINE_ON_DATA_ID, "on_data");
185
+ IODINE_CONST_ID_STORE(IODINE_ON_DRAINED_ID, "on_drained");
186
+ IODINE_CONST_ID_STORE(IODINE_ON_EVENTSOURCE_ID, "on_eventsource");
187
+ IODINE_CONST_ID_STORE(IODINE_ON_EVENTSOURCE_RECONNECT_ID,
188
+ "on_eventsource_reconnect");
189
+ IODINE_CONST_ID_STORE(IODINE_ON_FINISH_ID, "on_finish");
190
+ IODINE_CONST_ID_STORE(IODINE_ON_HTTP_ID, "on_http");
191
+ IODINE_CONST_ID_STORE(IODINE_ON_MESSAGE_ID, "on_message");
192
+ IODINE_CONST_ID_STORE(IODINE_ON_OPEN_ID, "on_open");
193
+ IODINE_CONST_ID_STORE(IODINE_ON_SHUTDOWN_ID, "on_shutdown");
194
+ IODINE_CONST_ID_STORE(IODINE_ON_TIMEOUT_ID, "on_timeout");
195
+ IODINE_CONST_ID_STORE(IODINE_QUERY_ID, "query");
196
+
197
+ IODINE_CONST_ID_STORE(IODINE_STATE_PRE_START, "pre_start");
198
+ IODINE_CONST_ID_STORE(IODINE_STATE_BEFORE_FORK, "before_fork");
199
+ IODINE_CONST_ID_STORE(IODINE_STATE_AFTER_FORK, "after_fork");
200
+ IODINE_CONST_ID_STORE(IODINE_STATE_ENTER_CHILD, "enter_child");
201
+ IODINE_CONST_ID_STORE(IODINE_STATE_ENTER_MASTER, "enter_master");
202
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_START, "start");
203
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_PARENT_CRUSH, "parent_crush");
204
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_CHILD_CRUSH, "child_crush");
205
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_SHUTDOWN, "shutdown");
206
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_STOP, "stop");
207
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_IDLE, "idle");
208
+ IODINE_CONST_ID_STORE(IODINE_STATE_ON_EXIT, "exit");
209
+
210
+ STORE.hold(IODINE_RACK_HIJACK_ID_SYM = rb_id2sym(IODINE_RACK_HIJACK_ID));
211
+ STORE.hold(IODINE_RACK_HIJACK_SYM = rb_id2sym(rb_intern("rack.hijack")));
212
+ STORE.hold(IODINE_RACK_HIJACK_STR = rb_sym_to_s(IODINE_RACK_HIJACK_SYM));
213
+
214
+ IODINE_RACK_PROTOCOL_STR =
215
+ STORE.frozen_str(FIO_STR_INFO1((char *)"rack.protocol"));
216
+ IODINE_RACK_UPGRADE_STR =
217
+ STORE.frozen_str(FIO_STR_INFO1((char *)"rack.upgrade"));
218
+ IODINE_RACK_UPGRADE_Q_STR =
219
+ STORE.frozen_str(FIO_STR_INFO1((char *)"rack.upgrade?"));
220
+ STORE.hold(IODINE_RACK_UPGRADE_WS_SYM = rb_id2sym(rb_intern("websocket")));
221
+ STORE.hold(IODINE_RACK_UPGRADE_SSE_SYM = rb_id2sym(rb_intern("sse")));
222
+ STORE.hold(IODINE_RACK_AFTER_RPLY_STR =
223
+ rb_str_new_static("rack.after_reply", 16));
224
+
225
+ Init_Iodine();
226
+ Init_Iodine_Base_CLI();
227
+ Init_Iodine_Compression();
228
+ Init_Iodine_Crypto();
229
+ Init_Iodine_Mustache();
230
+ Init_Iodine_Utils();
231
+ Init_Iodine_JSON();
232
+ Init_Iodine_Listener();
233
+ Init_Iodine_Logger();
234
+ Init_Iodine_MiniMap();
235
+ Init_Iodine_PubSub_Engine();
236
+ Init_Iodine_PubSub_Subscription();
237
+ Init_Iodine_PubSub_History();
238
+ Init_Iodine_RESP3();
239
+ Init_Iodine_PubSub_Message();
240
+ Init_Iodine_TLS();
241
+ Init_Iodine_Connection();
242
+ /* make sure the Iodine.run / async methods are available pre-start */
243
+ fio_io_async_attach(&IODINE_THREAD_POOL, (uint32_t)fio_cli_get_i("-t"));
244
+ #ifdef SIGUSR1
245
+ /* support hot restarting of workers */
246
+ fio_io_restart_on_signal(SIGUSR1);
1416
247
  #endif
1417
-
1418
- // initialize JSON helpers
1419
- iodine_init_json();
1420
-
1421
- // initialize Mustache engine
1422
- iodine_init_mustache();
1423
-
1424
- // initialize Rack helpers and IO
1425
- iodine_init_helpers();
1426
- IodineRackIO.init();
1427
-
1428
- // initialize Pub/Sub extension (for Engines)
1429
- iodine_pubsub_init();
248
+ fio_state_callback_add(FIO_CALL_ON_STOP, iodine___worker_gc_on_stop, NULL);
249
+ rb_set_end_proc(iodine___perform_exit, (VALUE)Qnil);
1430
250
  }