iodine 0.1.21 → 0.2.0

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

Potentially problematic release.


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

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,641 @@
1
+ #include "iodine_core.h"
2
+ #include "iodine_http.h"
3
+ #include <ruby/io.h>
4
+
5
+ /* *****************************************************************************
6
+ Core data
7
+ */
8
+
9
+ /* these should be made globally accessible to any Iodine module */
10
+ rb_encoding *BinaryEncoding;
11
+ rb_encoding *UTF8Encoding;
12
+ int BinaryEncodingIndex;
13
+ int UTF8EncodingIndex;
14
+ VALUE Iodine;
15
+ VALUE IodineBase;
16
+ VALUE Iodine_Version;
17
+ const char *Iodine_Version_Str;
18
+ ID call_proc_id;
19
+ ID on_start_func_id;
20
+ ID on_finish_func_id;
21
+ ID new_func_id;
22
+ ID on_open_func_id;
23
+ ID on_message_func_id;
24
+ ID on_data_func_id;
25
+ ID on_ready_func_id;
26
+ ID on_shutdown_func_id;
27
+ ID on_close_func_id;
28
+ ID ping_func_id;
29
+ ID buff_var_id;
30
+ ID fd_var_id;
31
+ ID timeout_var_id;
32
+ ID to_s_method_id;
33
+
34
+ /* local core data variables */
35
+ static VALUE DynamicProtocol;
36
+ static VALUE DynamicProtocolClass;
37
+ /* *****************************************************************************
38
+ The Core dynamic Iodine protocol
39
+ */
40
+
41
+ static const char *iodine_protocol_service = "IodineDynamicProtocol";
42
+
43
+ /* *****************************************************************************
44
+ The Core dynamic Iodine protocol methods and helpers
45
+ */
46
+
47
+ /**
48
+ Reads `n` bytes from the network connection.
49
+ The number of bytes to be read (n) is:
50
+ - the number of bytes set in the optional `buffer_or_length` argument.
51
+ - the String capacity (not length) of the String passed as the optional
52
+ `buffer_or_length` argument.
53
+ - 1024 Bytes (1Kb) if the optional `buffer_or_length` is either missing or
54
+ contains a String who's capacity is less then 1Kb.
55
+ Returns a String (either the same one used as the buffer or a new one) on a
56
+ successful read. Returns `nil` if no data was available.
57
+ */
58
+ static VALUE dyn_read(int argc, VALUE *argv, VALUE self) {
59
+ if (argc > 1) {
60
+ rb_raise(
61
+ rb_eArgError,
62
+ "read accepts only one argument - a Fixnum (buffer length) or a String "
63
+ "(it's capacity - or 1Kb, whichever's the higher - will be used as "
64
+ "buffer's length).");
65
+ return Qnil;
66
+ }
67
+ VALUE buffer = (argc == 1 ? argv[0] : Qnil);
68
+ if (buffer != Qnil && TYPE(buffer) != T_FIXNUM && TYPE(buffer) != T_STRING) {
69
+ rb_raise(rb_eTypeError,
70
+ "buffer should either be a length (a new string will be created) "
71
+ "or a string (reading will be limited to the original string's "
72
+ "capacity or 1Kb - whichever the larger).");
73
+ return Qnil;
74
+ }
75
+ VALUE str;
76
+ long len;
77
+ intptr_t fd = iodine_get_fd(self);
78
+ if (buffer == Qnil) {
79
+ buffer = LONG2FIX(1024);
80
+ }
81
+ if (TYPE(buffer) == T_FIXNUM) {
82
+ len = FIX2LONG(buffer);
83
+ if (len <= 0)
84
+ len = 1024;
85
+ str = rb_str_buf_new(len);
86
+ // create a rb_String with X length and take it's pointer
87
+ // rb_str_resize(VALUE str, long len)
88
+ // RSTRING_PTR(str)
89
+ } else {
90
+ // take the string's pointer and length
91
+ len = rb_str_capacity(buffer);
92
+ // make sure the string is modifiable
93
+ rb_str_modify(buffer);
94
+ // resize te string if needed.
95
+ if (len < 1024)
96
+ rb_str_resize(buffer, (len = 1024));
97
+ str = buffer;
98
+ }
99
+ ssize_t in = sock_read(fd, RSTRING_PTR(str), len);
100
+ // make sure it's binary encoded
101
+ rb_enc_associate_index(str, BinaryEncodingIndex);
102
+ // set actual size....
103
+ if (in > 0)
104
+ rb_str_set_len(str, (long)in);
105
+ else {
106
+ rb_str_set_len(str, 0);
107
+ str = Qnil;
108
+ }
109
+ // return empty string? or fix above if to return Qnil?
110
+ return str;
111
+ }
112
+
113
+ /**
114
+ Writes data to the connection. Returns `false` on error and `self` on success.
115
+ */
116
+ static VALUE dyn_write(VALUE self, VALUE data) {
117
+ intptr_t fd = iodine_get_fd(self);
118
+ if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data)))
119
+ return Qfalse;
120
+ return self;
121
+ }
122
+
123
+ /**
124
+ Writes data to the connection. The data will be sent as soon as possible without
125
+ fragmantation of previously scheduled data.
126
+
127
+ Returns `false` on error and `self` on success.
128
+ */
129
+ static VALUE dyn_write_urgent(VALUE self, VALUE data) {
130
+ intptr_t fd = iodine_get_fd(self);
131
+ if (sock_write2(.fduuid = fd, .buffer = RSTRING(data),
132
+ .length = RSTRING_LEN(data), .urgent = 1))
133
+ return Qfalse;
134
+ return self;
135
+ }
136
+
137
+ /**
138
+ Closes a connection.
139
+
140
+ The connection will be closed only once all the data was sent.
141
+
142
+ Returns self.
143
+ */
144
+ static VALUE dyn_close(VALUE self) {
145
+ intptr_t fd = iodine_get_fd(self);
146
+ sock_close(fd);
147
+ return self;
148
+ }
149
+
150
+ /* *****************************************************************************
151
+ The Core dynamic Iodine protocol task implementation
152
+ */
153
+
154
+ static void dyn_perform_defer(intptr_t uuid, protocol_s *protocol, void *arg) {
155
+ RubyCaller.call((VALUE)arg, call_proc_id);
156
+ Registry.remove((VALUE)arg);
157
+ }
158
+ static void dyn_defer_fallback(intptr_t uuid, void *arg) {
159
+ Registry.remove((VALUE)arg);
160
+ };
161
+
162
+ /**
163
+ Runs the required block later (defers the blocks execution).
164
+
165
+ Unlike {Iodine#run}, the block will **not* run concurrently with any other
166
+ callback
167
+ for this object (except `ping` and `on_ready`).
168
+
169
+ Also, unlike {Iodine#run}, the block will **not** be called unless the
170
+ connection remains open at the time it's execution is scheduled.
171
+
172
+ Always returns `self`.
173
+ */
174
+ static VALUE dyn_defer(VALUE self) {
175
+ // requires a block to be passed
176
+ rb_need_block();
177
+ VALUE block = rb_block_proc();
178
+ if (block == Qnil)
179
+ return Qfalse;
180
+ Registry.add(block);
181
+ intptr_t fd = iodine_get_fd(self);
182
+ server_task(fd, dyn_perform_defer, (void *)block, dyn_defer_fallback);
183
+ return self;
184
+ }
185
+
186
+ static void dyn_perform_each_task(intptr_t fd, protocol_s *protocol,
187
+ void *data) {
188
+ RubyCaller.call2((VALUE)data, call_proc_id, 1,
189
+ &(dyn_prot(protocol)->handler));
190
+ }
191
+ static void dyn_finish_each_task(intptr_t fd, protocol_s *protocol,
192
+ void *data) {
193
+ Registry.remove((VALUE)data);
194
+ }
195
+
196
+ void iodine_run_each(intptr_t origin, const char *service, VALUE block) {
197
+ server_each(origin, service, dyn_perform_each_task, (void *)block,
198
+ dyn_finish_each_task);
199
+ }
200
+
201
+ /**
202
+ Runs the required block for each dynamic protocol connection except this one.
203
+
204
+ Tasks will be performed within each connections lock, so no connection will have
205
+ more then one task being performed at the same time (similar to {#defer}).
206
+
207
+ Also, unlike {Iodine.run}, the block will **not** be called unless the
208
+ connection remains open at the time it's execution is scheduled.
209
+
210
+ Always returns `self`.
211
+ */
212
+ static VALUE dyn_each(VALUE self) {
213
+ // requires a block to be passed
214
+ rb_need_block();
215
+ VALUE block = rb_block_proc();
216
+ if (block == Qnil)
217
+ return Qfalse;
218
+ Registry.add(block);
219
+ intptr_t fd = iodine_get_fd(self);
220
+ server_each(fd, iodine_protocol_service, dyn_perform_each_task, (void *)block,
221
+ dyn_finish_each_task);
222
+ return self;
223
+ }
224
+
225
+ /**
226
+ Runs the required block for each dynamic protocol connection.
227
+
228
+ Tasks will be performed within each connections lock, so no connection will have
229
+ more then one task being performed at the same time (similar to {#defer}).
230
+
231
+ Also, unlike {Iodine.run}, the block will **not** be called unless the
232
+ connection remains open at the time it's execution is scheduled.
233
+
234
+ Always returns `self`.
235
+ */
236
+ static VALUE dyn_class_each(VALUE self) {
237
+ // requires a block to be passed
238
+ rb_need_block();
239
+ VALUE block = rb_block_proc();
240
+ if (block == Qnil)
241
+ return Qfalse;
242
+ Registry.add(block);
243
+ server_each(-1, iodine_protocol_service, dyn_perform_each_task, (void *)block,
244
+ dyn_finish_each_task);
245
+ return self;
246
+ }
247
+
248
+ /* will be defined in detail later, after some more functions were written */
249
+ VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler);
250
+
251
+ VALUE dyn_upgrade(VALUE self, VALUE handler) {
252
+ return iodine_upgrade2basic(iodine_get_fd(self), handler);
253
+ }
254
+
255
+ /* *****************************************************************************
256
+ The Core dynamic Iodine protocol bridge
257
+ */
258
+
259
+ /** Implement this callback to handle the event. The default implementation will
260
+ * close the connection. */
261
+ static VALUE not_implemented_ping(VALUE self) {
262
+ sock_close(iodine_get_fd(self));
263
+ return Qnil;
264
+ }
265
+ /** implement this callback to handle the event. */
266
+ static VALUE not_implemented(VALUE self) { return Qnil; }
267
+ /** implement this callback to handle the event. */
268
+ static VALUE not_implemented2(VALUE self, VALUE data) { return Qnil; }
269
+
270
+ /**
271
+ A default on_data implementation will read up to 1Kb into a reusable buffer from
272
+ the socket and call the `on_message` callback.
273
+
274
+ It is recommended that you implement this callback if messages might require
275
+ more then 1Kb of space.
276
+ */
277
+ static VALUE default_on_data(VALUE self) {
278
+ VALUE buff = rb_ivar_get(self, buff_var_id);
279
+ if (buff == Qnil) {
280
+ rb_ivar_set(self, buff_var_id, (buff = rb_str_buf_new(1024)));
281
+ }
282
+ do {
283
+ dyn_read(1, &buff, self);
284
+ if (!RSTRING_LEN(buff))
285
+ return Qnil;
286
+ rb_funcall(self, on_message_func_id, 1, buff);
287
+ } while (RSTRING_LEN(buff) == rb_str_capacity(buff));
288
+ return Qnil;
289
+ }
290
+
291
+ /** called when a data is available, but will not run concurrently */
292
+ static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
293
+ RubyCaller.call(dyn_prot(protocol)->handler, on_data_func_id);
294
+ }
295
+ /** called when the socket is ready to be written to. */
296
+ static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
297
+ RubyCaller.call(dyn_prot(protocol)->handler, on_ready_func_id);
298
+ }
299
+ /** called when the server is shutting down,
300
+ * but before closing the connection. */
301
+ static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
302
+ RubyCaller.call(dyn_prot(protocol)->handler, on_shutdown_func_id);
303
+ }
304
+ /** called when the connection was closed, but will not run concurrently */
305
+ static void dyn_protocol_on_close(protocol_s *protocol) {
306
+ RubyCaller.call(dyn_prot(protocol)->handler, on_close_func_id);
307
+ Registry.remove(dyn_prot(protocol)->handler);
308
+ free(protocol);
309
+ }
310
+ /** called when a connection's timeout was reached */
311
+ static void dyn_protocol_ping(intptr_t fduuid, protocol_s *protocol) {
312
+ RubyCaller.call(dyn_prot(protocol)->handler, ping_func_id);
313
+ }
314
+
315
+ static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
316
+ uint8_t timeout) {
317
+ Registry.add(handler);
318
+ iodine_set_fd(handler, fduuid);
319
+ dyn_protocol_s *protocol = malloc(sizeof(*protocol));
320
+ if (protocol == NULL) {
321
+ Registry.remove(handler);
322
+ return NULL;
323
+ }
324
+ server_set_timeout(fduuid, timeout);
325
+ *protocol = (dyn_protocol_s){
326
+ .handler = handler,
327
+ .protocol.on_data = dyn_protocol_on_data,
328
+ .protocol.on_close = dyn_protocol_on_close,
329
+ .protocol.on_shutdown = dyn_protocol_on_shutdown,
330
+ .protocol.on_ready = dyn_protocol_on_ready,
331
+ .protocol.ping = dyn_protocol_ping,
332
+ .protocol.service = iodine_protocol_service,
333
+ };
334
+ RubyCaller.call(handler, on_open_func_id);
335
+ return (protocol_s *)protocol;
336
+ }
337
+ static protocol_s *on_open_dyn_protocol(intptr_t fduuid, void *udata) {
338
+ VALUE rb_tout = rb_ivar_get((VALUE)udata, timeout_var_id);
339
+ uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
340
+ VALUE handler = RubyCaller.call((VALUE)udata, new_func_id);
341
+ if (handler == Qnil)
342
+ return NULL;
343
+ return dyn_set_protocol(fduuid, handler, timeout);
344
+ }
345
+
346
+ /** called once, when Iodine starts running. */
347
+ static void on_server_start_for_handler(void *udata) {
348
+ RubyCaller.call((VALUE)udata, on_start_func_id);
349
+ }
350
+ /** called once, when Iodine stops running. */
351
+ static void on_server_on_finish_for_handler(void *udata) {
352
+ RubyCaller.call((VALUE)udata, on_finish_func_id);
353
+ }
354
+
355
+ void Init_DynamicProtocol(void) {
356
+ /**
357
+ The Protocol module is included in any object or class that handles an Iodine
358
+ connection using a custom / dynamic protocol (not the Websockets or HTTP
359
+ protocols that Iodine supports natively).
360
+ */
361
+ DynamicProtocolClass = rb_define_module_under(IodineBase, "ProtocolClass");
362
+ rb_define_method(DynamicProtocolClass, "on_start", not_implemented, 0);
363
+ rb_define_method(DynamicProtocolClass, "on_finish", not_implemented, 0);
364
+ rb_define_method(DynamicProtocolClass, "each", dyn_class_each, 0);
365
+
366
+ DynamicProtocol = rb_define_module_under(Iodine, "Protocol");
367
+ rb_define_method(DynamicProtocol, "on_open", not_implemented, 0);
368
+ rb_define_method(DynamicProtocol, "on_close", not_implemented, 0);
369
+ rb_define_method(DynamicProtocol, "on_message", not_implemented2, 1);
370
+ rb_define_method(DynamicProtocol, "on_data", default_on_data, 0);
371
+ rb_define_method(DynamicProtocol, "on_ready", not_implemented, 0);
372
+ rb_define_method(DynamicProtocol, "on_shutdown", not_implemented, 0);
373
+ rb_define_method(DynamicProtocol, "ping", not_implemented_ping, 0);
374
+
375
+ // helper methods
376
+ rb_define_method(DynamicProtocol, "read", dyn_read, -1);
377
+ rb_define_method(DynamicProtocol, "write", dyn_write, 1);
378
+ rb_define_method(DynamicProtocol, "write_urgent", dyn_write_urgent, 1);
379
+ rb_define_method(DynamicProtocol, "close", dyn_close, 0);
380
+ rb_define_method(DynamicProtocol, "defer", dyn_defer, 0);
381
+ rb_define_method(DynamicProtocol, "each", dyn_each, 0);
382
+ rb_define_method(DynamicProtocol, "upgrade", dyn_upgrade, 1);
383
+ }
384
+
385
+ /* *****************************************************************************
386
+ Iodine functions
387
+ */
388
+
389
+ /** Sets up a listenning socket. Conncetions received at the assigned port will
390
+ be handled by the assigned handler.
391
+
392
+ Multiple services (listenning sockets) can be registered before starting the
393
+ Iodine event loop. */
394
+ static VALUE iodine_listen_dyn_protocol(VALUE self, VALUE port, VALUE handler) {
395
+ // validate that the handler is a class and include the Iodine::Protocol
396
+ if (TYPE(handler) == T_CLASS) {
397
+ // include the Protocol module
398
+ // // do we neet to check?
399
+ // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
400
+ rb_include_module(handler, DynamicProtocol);
401
+ rb_extend_object(handler, DynamicProtocolClass);
402
+ } else {
403
+ rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
404
+ return Qnil;
405
+ }
406
+ if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
407
+ rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
408
+ if (TYPE(port) == T_FIXNUM)
409
+ port = rb_funcall2(port, to_s_method_id, 0, NULL);
410
+ // listen
411
+ server_listen(.port = StringValueCStr(port), .udata = (void *)handler,
412
+ .on_open = on_open_dyn_protocol,
413
+ .on_start = on_server_start_for_handler,
414
+ .on_finish = on_server_on_finish_for_handler);
415
+ return self;
416
+ }
417
+
418
+ VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler) {
419
+ uint8_t timeout;
420
+ if (TYPE(handler) == T_CLASS) {
421
+ // get the timeout
422
+ VALUE rb_tout = rb_ivar_get(handler, timeout_var_id);
423
+ timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
424
+ // include the Protocol module
425
+ // // do we neet to check?
426
+ // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
427
+ rb_include_module(handler, DynamicProtocol);
428
+ handler = RubyCaller.call(handler, new_func_id);
429
+ } else {
430
+ // include the Protocol module in the object's class
431
+ VALUE p_class = rb_obj_class(handler);
432
+ // // do we neet to check?
433
+ // if (rb_mod_include_p(p_class, rDynProtocol) == Qfalse)
434
+ rb_include_module(p_class, DynamicProtocol);
435
+ // get the timeout
436
+ VALUE rb_tout = rb_ivar_get(p_class, timeout_var_id);
437
+ if (rb_tout == Qnil)
438
+ rb_tout = rb_ivar_get(handler, timeout_var_id);
439
+ timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
440
+ }
441
+ protocol_s *protocol = dyn_set_protocol(fduuid, handler, timeout);
442
+ if (protocol) {
443
+ if (server_switch_protocol(fduuid, protocol))
444
+ dyn_protocol_on_close(protocol);
445
+ return handler;
446
+ }
447
+ return Qfalse;
448
+ }
449
+
450
+ /* *****************************************************************************
451
+ Iodine Task Management
452
+ */
453
+
454
+ static void iodine_run_once(void *block) {
455
+ RubyCaller.call((VALUE)block, call_proc_id);
456
+ Registry.remove((VALUE)block);
457
+ }
458
+
459
+ static void iodine_run_always(void *block) {
460
+ RubyCaller.call((VALUE)block, call_proc_id);
461
+ }
462
+
463
+ /**
464
+ Runs the required block later. The block might run concurrently with the
465
+ existing code (depending on the amount and availability of worker threads).
466
+
467
+ Returns the block object. The block will run only while Iodine is running (run
468
+ will be delayed until Iodine.start is called, unless Iodine's event loop is
469
+ active).
470
+ */
471
+ static VALUE iodine_run_async(VALUE self) {
472
+ // requires a block to be passed
473
+ rb_need_block();
474
+ VALUE block = rb_block_proc();
475
+ if (block == Qnil)
476
+ return Qfalse;
477
+ Registry.add(block);
478
+ if (async_run(iodine_run_once, (void *)block)) {
479
+ server_run_after(1, iodine_run_once, (void *)block);
480
+ ;
481
+ }
482
+ return block;
483
+ }
484
+
485
+ /**
486
+ Runs the required block after the specified number of milliseconds have passed.
487
+ Time is counted only once Iodine started running (using {Iodine.start}).
488
+
489
+ Always returns a copy of the block object.
490
+ */
491
+ static VALUE iodine_run_after(VALUE self, VALUE milliseconds) {
492
+ if (TYPE(milliseconds) != T_FIXNUM) {
493
+ rb_raise(rb_eTypeError, "milliseconds must be a number");
494
+ return Qnil;
495
+ }
496
+ size_t milli = FIX2UINT(milliseconds);
497
+ // requires a block to be passed
498
+ rb_need_block();
499
+ VALUE block = rb_block_proc();
500
+ if (block == Qnil)
501
+ return Qfalse;
502
+ Registry.add(block);
503
+ server_run_after(milli, iodine_run_once, (void *)block);
504
+ return block;
505
+ }
506
+ /**
507
+ Runs the required block after the specified number of milliseconds have passed.
508
+ Time is counted only once Iodine started running (using {Iodine.start}).
509
+
510
+ Accepts:
511
+
512
+ milliseconds:: the number of milliseconds between event repetitions.
513
+
514
+ repetitions:: the number of event repetitions. Defaults to 0 (never ending).
515
+
516
+ block:: (required) a block is required, as otherwise there is nothing to
517
+ perform.
518
+
519
+ The event will repeat itself until the number of repetitions had been delpeted.
520
+
521
+ Always returns a copy of the block object.
522
+ */
523
+ static VALUE iodine_run_every(int argc, VALUE *argv, VALUE self) {
524
+ VALUE milliseconds, repetitions, block;
525
+
526
+ rb_scan_args(argc, argv, "11&", &milliseconds, &repetitions, &block);
527
+
528
+ if (TYPE(milliseconds) != T_FIXNUM) {
529
+ rb_raise(rb_eTypeError, "milliseconds must be a number.");
530
+ return Qnil;
531
+ }
532
+ if (repetitions != Qnil && TYPE(repetitions) != T_FIXNUM) {
533
+ rb_raise(rb_eTypeError, "repetitions must be a number or `nil`.");
534
+ return Qnil;
535
+ }
536
+
537
+ size_t milli = FIX2UINT(milliseconds);
538
+ size_t repeat = (repetitions == Qnil) ? 0 : FIX2UINT(repetitions);
539
+ // requires a block to be passed
540
+ rb_need_block();
541
+ Registry.add(block);
542
+ server_run_every(milli, repeat, iodine_run_always, (void *)block,
543
+ (void (*)(void *))Registry.remove);
544
+ return block;
545
+ }
546
+
547
+ static VALUE iodine_count(VALUE self) { return ULONG2NUM(server_count(NULL)); }
548
+ /* *****************************************************************************
549
+ Running the server
550
+ */
551
+
552
+ static void *srv_start_no_gvl(void *_) {
553
+ VALUE rb_th_i = rb_iv_get(Iodine, "@threads");
554
+ VALUE rb_pr_i = rb_iv_get(Iodine, "@processes");
555
+ size_t threads = (TYPE(rb_th_i) == T_FIXNUM) ? FIX2ULONG(rb_th_i) : 0;
556
+ size_t processes = (TYPE(rb_pr_i) == T_FIXNUM) ? FIX2ULONG(rb_pr_i) : 0;
557
+ server_run(.threads = threads, .processes = processes);
558
+ return NULL;
559
+ }
560
+
561
+ static void unblck(void *_) { server_stop(); }
562
+ /**
563
+ Starts the Iodine event loop. This will hang the thread until an interrupt
564
+ (`^C`) signal is received.
565
+
566
+ Returns the Iodine module.
567
+ */
568
+ static VALUE iodine_start(VALUE self) {
569
+ if (iodine_http_review() == -1) {
570
+ perror("Iodine couldn't start HTTP service... port busy? ");
571
+ return Qnil;
572
+ }
573
+ rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self, unblck, NULL);
574
+
575
+ return self;
576
+ }
577
+
578
+ /* *****************************************************************************
579
+ Initializing the library
580
+ */
581
+
582
+ ////////////////////////////////////////////////////////////////////////
583
+ // Ruby loads the library and invokes the Init_<lib_name> function...
584
+ //
585
+ // Here we connect all the C code to the Ruby interface, completing the bridge
586
+ // between Lib-Server and Ruby.
587
+ void Init_iodine(void) {
588
+ // initialize globally used IDs, for faster access to the Ruby layer.
589
+ call_proc_id = rb_intern("call");
590
+ new_func_id = rb_intern("new");
591
+ on_start_func_id = rb_intern("on_start");
592
+ on_finish_func_id = rb_intern("on_finish");
593
+ on_open_func_id = rb_intern("on_open");
594
+ on_message_func_id = rb_intern("on_message");
595
+ on_data_func_id = rb_intern("on_data");
596
+ on_shutdown_func_id = rb_intern("on_shutdown");
597
+ on_close_func_id = rb_intern("on_close");
598
+ on_ready_func_id = rb_intern("on_ready");
599
+ ping_func_id = rb_intern("ping");
600
+ buff_var_id = rb_intern("scrtbuffer");
601
+ fd_var_id = rb_intern("scrtfd");
602
+ timeout_var_id = rb_intern("@timeout");
603
+ to_s_method_id = rb_intern("to_s");
604
+
605
+ BinaryEncodingIndex = rb_enc_find_index("binary"); // sets encoding for data
606
+ UTF8EncodingIndex = rb_enc_find_index("UTF-8"); // sets encoding for data
607
+ BinaryEncoding = rb_enc_find("binary"); // sets encoding for data
608
+ UTF8Encoding = rb_enc_find("UTF-8"); // sets encoding for data
609
+
610
+ // The core Iodine module wraps libserver functionality and little more.
611
+ Iodine = rb_define_module("Iodine");
612
+
613
+ // get-set version
614
+ {
615
+ Iodine_Version = rb_const_get(Iodine, rb_intern("VERSION"));
616
+ if (Iodine_Version == Qnil)
617
+ Iodine_Version_Str = "0.2.0";
618
+ else
619
+ Iodine_Version_Str = StringValueCStr(Iodine_Version);
620
+ }
621
+ // the Iodine singleton functions
622
+ rb_define_module_function(Iodine, "listen", iodine_listen_dyn_protocol, 2);
623
+ rb_define_module_function(Iodine, "start", iodine_start, 0);
624
+ rb_define_module_function(Iodine, "count", iodine_count, 0);
625
+ rb_define_module_function(Iodine, "run", iodine_run_async, 0);
626
+ rb_define_module_function(Iodine, "run_after", iodine_run_after, 1);
627
+ rb_define_module_function(Iodine, "run_every", iodine_run_every, -1);
628
+
629
+ // Every Protocol (and Server?) instance will hold a reference to the server
630
+ // define the Server Ruby class.
631
+ IodineBase = rb_define_module_under(Iodine, "Base");
632
+
633
+ // Initialize the registry under the Iodine core
634
+ Registry.init(Iodine);
635
+ // Initialize the Dynamic Protocol
636
+ Init_DynamicProtocol();
637
+
638
+ // initialize the Http server
639
+ // must be done only after all the globals, including BinaryEncoding, are set
640
+ Init_iodine_http();
641
+ }