iodine 0.3.6 → 0.4.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.

Potentially problematic release.


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

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/LIMITS.md +25 -0
  4. data/README.md +39 -80
  5. data/SPEC-Websocket-Draft.md +129 -4
  6. data/bin/echo +2 -2
  7. data/bin/http-hello +1 -0
  8. data/bin/updated api +113 -0
  9. data/bin/ws-echo +0 -1
  10. data/examples/broadcast.ru +56 -0
  11. data/examples/echo.ru +57 -0
  12. data/examples/hello.ru +30 -0
  13. data/examples/redis.ru +69 -0
  14. data/examples/shootout.ru +53 -0
  15. data/exe/iodine +2 -80
  16. data/ext/iodine/defer.c +11 -5
  17. data/ext/iodine/empty.h +26 -0
  18. data/ext/iodine/evio.h +1 -1
  19. data/ext/iodine/facil.c +103 -61
  20. data/ext/iodine/facil.h +20 -12
  21. data/ext/iodine/fio_dict.c +446 -0
  22. data/ext/iodine/fio_dict.h +90 -0
  23. data/ext/iodine/fio_hash_table.h +370 -0
  24. data/ext/iodine/fio_list.h +30 -3
  25. data/ext/iodine/http.c +169 -37
  26. data/ext/iodine/http.h +33 -10
  27. data/ext/iodine/http1.c +78 -42
  28. data/ext/iodine/http_request.c +6 -0
  29. data/ext/iodine/http_request.h +3 -0
  30. data/ext/iodine/http_response.c +43 -11
  31. data/ext/iodine/iodine.c +380 -0
  32. data/ext/iodine/iodine.h +62 -0
  33. data/ext/iodine/iodine_helpers.c +235 -0
  34. data/ext/iodine/iodine_helpers.h +13 -0
  35. data/ext/iodine/iodine_http.c +409 -241
  36. data/ext/iodine/iodine_http.h +7 -14
  37. data/ext/iodine/iodine_protocol.c +626 -0
  38. data/ext/iodine/iodine_protocol.h +13 -0
  39. data/ext/iodine/iodine_pubsub.c +646 -0
  40. data/ext/iodine/iodine_pubsub.h +27 -0
  41. data/ext/iodine/iodine_websockets.c +796 -0
  42. data/ext/iodine/iodine_websockets.h +19 -0
  43. data/ext/iodine/pubsub.c +544 -0
  44. data/ext/iodine/pubsub.h +215 -0
  45. data/ext/iodine/random.c +4 -4
  46. data/ext/iodine/rb-call.c +1 -5
  47. data/ext/iodine/rb-defer.c +3 -20
  48. data/ext/iodine/rb-rack-io.c +22 -22
  49. data/ext/iodine/rb-rack-io.h +3 -4
  50. data/ext/iodine/rb-registry.c +111 -118
  51. data/ext/iodine/redis_connection.c +277 -0
  52. data/ext/iodine/redis_connection.h +77 -0
  53. data/ext/iodine/redis_engine.c +398 -0
  54. data/ext/iodine/redis_engine.h +68 -0
  55. data/ext/iodine/resp.c +842 -0
  56. data/ext/iodine/resp.h +253 -0
  57. data/ext/iodine/sock.c +26 -12
  58. data/ext/iodine/sock.h +14 -3
  59. data/ext/iodine/spnlock.inc +19 -2
  60. data/ext/iodine/websockets.c +299 -11
  61. data/ext/iodine/websockets.h +159 -6
  62. data/lib/iodine.rb +104 -1
  63. data/lib/iodine/cli.rb +106 -0
  64. data/lib/iodine/monkeypatch.rb +40 -0
  65. data/lib/iodine/pubsub.rb +70 -0
  66. data/lib/iodine/version.rb +1 -1
  67. data/lib/iodine/websocket.rb +12 -0
  68. data/lib/rack/handler/iodine.rb +33 -7
  69. metadata +35 -7
  70. data/ext/iodine/iodine_core.c +0 -760
  71. data/ext/iodine/iodine_core.h +0 -79
  72. data/ext/iodine/iodine_websocket.c +0 -551
  73. data/ext/iodine/iodine_websocket.h +0 -22
  74. data/lib/iodine/http.rb +0 -4
@@ -1,25 +1,18 @@
1
+ #ifndef H_IODINE_HTTP_H
2
+ #define H_IODINE_HTTP_H
1
3
  /*
2
4
  Copyright: Boaz segev, 2016-2017
3
5
  License: MIT
4
6
 
5
7
  Feel free to copy, use and enjoy according to the license provided.
6
8
  */
7
- #ifndef IODINE_HTTP_H
8
- #define IODINE_HTTP_H
9
- #include "iodine_core.h"
10
- #include "rb-rack-io.h"
9
+ #include "iodine.h"
11
10
 
12
- /* the Iodine::Rack HTTP server class*/
13
- extern VALUE IodineHttp;
14
11
  /* these three are used also by rb-rack-io.c */
15
- extern VALUE R_HIJACK;
16
- extern VALUE R_HIJACK_IO;
17
- extern VALUE R_HIJACK_CB;
12
+ extern VALUE IODINE_R_HIJACK;
13
+ extern VALUE IODINE_R_HIJACK_IO;
14
+ extern VALUE IODINE_R_HIJACK_CB;
18
15
 
19
- /* Initializes the HTTP library */
20
- void Init_iodine_http(void);
21
-
22
- /* Reviews the HTTP settngs and initiates an HTTP service if required */
23
- int iodine_http_review(void);
16
+ void Iodine_init_http(void);
24
17
 
25
18
  #endif
@@ -0,0 +1,626 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #include "iodine_protocol.h"
8
+
9
+ #include <ruby/io.h>
10
+
11
+ /* *****************************************************************************
12
+ The protocol object
13
+ ***************************************************************************** */
14
+
15
+ typedef struct {
16
+ protocol_s protocol;
17
+ VALUE handler;
18
+ } iodine_protocol_s;
19
+
20
+ VALUE IodineProtocol;
21
+
22
+ static char *iodine_protocol_service = "Iodine Custom Protocol";
23
+
24
+ /* *****************************************************************************
25
+ Internal helpers
26
+ ***************************************************************************** */
27
+
28
+ static void iodine_clear_task(intptr_t origin, void *block_) {
29
+ Registry.remove((VALUE)block_);
30
+ (void)origin;
31
+ }
32
+
33
+ static void iodine_perform_task(intptr_t uuid, protocol_s *pr, void *block_) {
34
+ if (pr->service == iodine_protocol_service) {
35
+ RubyCaller.call2((VALUE)block_, iodine_call_proc_id, 1, (VALUE *)(pr + 1));
36
+ }
37
+ (void)uuid;
38
+ }
39
+ static void iodine_perform_task_and_free(intptr_t uuid, protocol_s *pr,
40
+ void *block_) {
41
+ if (pr->service == iodine_protocol_service) {
42
+ RubyCaller.call2((VALUE)block_, iodine_call_proc_id, 1, (VALUE *)(pr + 1));
43
+ }
44
+ Registry.remove((VALUE)block_);
45
+ (void)uuid;
46
+ }
47
+
48
+ static void remove_from_registry(intptr_t uuid, void *val) {
49
+ Registry.remove((VALUE)val);
50
+ (void)uuid;
51
+ }
52
+ /* *****************************************************************************
53
+ Function placeholders
54
+ ***************************************************************************** */
55
+
56
+ /** Override this callback to handle the event. The default implementation will
57
+ * close the connection. */
58
+ static VALUE not_implemented_ping(VALUE self) {
59
+ sock_close(iodine_get_fd(self));
60
+ return Qnil;
61
+ }
62
+ /** Override this callback to handle the event. */
63
+ static VALUE not_implemented(VALUE self) {
64
+ (void)(self);
65
+ return Qnil;
66
+ }
67
+
68
+ /** Override this callback to handle the event. */
69
+ static VALUE not_implemented2(VALUE self, VALUE data) {
70
+ (void)(self);
71
+ (void)(data);
72
+ return Qnil;
73
+ }
74
+
75
+ /**
76
+ A default on_data implementation will read up to 1Kb into a reusable buffer from
77
+ the socket and call the `on_message` callback.
78
+
79
+ It is recommended that you implement this callback if messages might require
80
+ more then 1Kb of space.
81
+ */
82
+ static VALUE dyn_read(int argc, VALUE *argv, VALUE self);
83
+ static VALUE default_on_data(VALUE self) {
84
+ VALUE buff = rb_ivar_get(self, iodine_buff_var_id);
85
+ if (buff == Qnil) {
86
+ rb_ivar_set(self, iodine_buff_var_id, (buff = rb_str_buf_new(1024)));
87
+ }
88
+ do {
89
+ dyn_read(1, &buff, self);
90
+ if (!RSTRING_LEN(buff))
91
+ return Qnil;
92
+ rb_funcall(self, iodine_on_message_func_id, 1, buff);
93
+ } while (RSTRING_LEN(buff) == (ssize_t)rb_str_capacity(buff));
94
+ return Qnil;
95
+ }
96
+
97
+ /* *****************************************************************************
98
+ Published functions
99
+ ***************************************************************************** */
100
+
101
+ /** Returns the number of total connections managed by Iodine. */
102
+ static VALUE dyn_count(VALUE self) {
103
+ size_t count = facil_count(iodine_protocol_service);
104
+ return ULL2NUM(count);
105
+ (void)self;
106
+ }
107
+
108
+ /**
109
+ Runs a task for each dynamic connection (not websockets or HTTP).
110
+
111
+ Requires a block and returns it as an object.
112
+
113
+ i.e., will write to every open dynamic connection:
114
+
115
+ Iodine.each {|obj| obj.write "hello!" }
116
+
117
+ */
118
+ static VALUE dyn_run_each(VALUE self) {
119
+ rb_need_block();
120
+ VALUE block = rb_block_proc();
121
+ if (block == Qnil)
122
+ return Qfalse;
123
+ intptr_t origin = iodine_get_fd(self);
124
+ if (!origin)
125
+ origin = -1;
126
+
127
+ Registry.add(block);
128
+ facil_each(.arg = (void *)block, .service = "IodineDynamic", .origin = origin,
129
+ .task_type = FIO_PR_LOCK_TASK, .task = iodine_perform_task,
130
+ .on_complete = iodine_clear_task);
131
+ return block;
132
+ }
133
+
134
+ /**
135
+ Reads `n` bytes from the network connection.
136
+ The number of bytes to be read (n) is:
137
+ - the number of bytes set in the optional `buffer_or_length` argument.
138
+ - the String capacity (not length) of the String passed as the optional
139
+ `buffer_or_length` argument.
140
+ - 1024 Bytes (1Kb) if the optional `buffer_or_length` is either missing or
141
+ contains a String who's capacity is less then 1Kb.
142
+ Returns a String (either the same one used as the buffer or a new one) on a
143
+ successful read. Returns `nil` if no data was available.
144
+ */
145
+ static VALUE dyn_read(int argc, VALUE *argv, VALUE self) {
146
+ if (argc > 1) {
147
+ rb_raise(
148
+ rb_eArgError,
149
+ "read accepts only one argument - a Fixnum (buffer length) or a String "
150
+ "(it's capacity - or 1Kb, whichever's the higher - will be used as "
151
+ "buffer's length).");
152
+ return Qnil;
153
+ }
154
+ VALUE buffer = (argc == 1 ? argv[0] : Qnil);
155
+ if (buffer != Qnil && TYPE(buffer) != T_FIXNUM && TYPE(buffer) != T_STRING) {
156
+ rb_raise(rb_eTypeError,
157
+ "buffer should either be a length (a new string will be created) "
158
+ "or a string (reading will be limited to the original string's "
159
+ "capacity or 1Kb - whichever the larger).");
160
+ return Qnil;
161
+ }
162
+ VALUE str;
163
+ long len;
164
+ intptr_t fd = iodine_get_fd(self);
165
+ if (buffer == Qnil) {
166
+ buffer = LONG2FIX(1024);
167
+ }
168
+ if (TYPE(buffer) == T_FIXNUM) {
169
+ len = FIX2LONG(buffer);
170
+ if (len <= 0)
171
+ len = 1024;
172
+ str = rb_str_buf_new(len);
173
+ // create a rb_String with X length and take it's pointer
174
+ // rb_str_resize(VALUE str, long len)
175
+ // RSTRING_PTR(str)
176
+ } else {
177
+ // take the string's pointer and length
178
+ len = rb_str_capacity(buffer);
179
+ // make sure the string is modifiable
180
+ rb_str_modify(buffer);
181
+ // resize the string if needed.
182
+ if (len < 1024)
183
+ rb_str_resize(buffer, (len = 1024));
184
+ str = buffer;
185
+ }
186
+ ssize_t in = sock_read(fd, RSTRING_PTR(str), len);
187
+ // make sure it's binary encoded
188
+ rb_enc_associate_index(str, IodineBinaryEncodingIndex);
189
+ // set actual size....
190
+ if (in > 0)
191
+ rb_str_set_len(str, (long)in);
192
+ else {
193
+ rb_str_set_len(str, 0);
194
+ str = Qnil;
195
+ }
196
+ // return empty string? or fix above if to return Qnil?
197
+ return str;
198
+ }
199
+
200
+ /**
201
+ Writes data to the connection. Returns `false` on error and `self` on success.
202
+ */
203
+ static VALUE dyn_write(VALUE self, VALUE data) {
204
+ Check_Type(data, T_STRING);
205
+ intptr_t fd = iodine_get_fd(self);
206
+ if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data))) {
207
+ return Qfalse;
208
+ }
209
+ return self;
210
+ }
211
+
212
+ /**
213
+ Moves a String to iodine's socket's buffer. This is a zero-copy write and
214
+ requires that the string remain unchanged during the process.
215
+
216
+ For example, Strings received by `on_message` can't be used, because they use a
217
+ recyclable buffer and they will be destroyed once `on_message` returns.
218
+ */
219
+ static VALUE dyn_write_move(VALUE self, VALUE data) {
220
+ Check_Type(data, T_STRING);
221
+ Registry.add(data);
222
+ intptr_t fd = iodine_get_fd(self);
223
+ if (sock_write2(.uuid = fd, .buffer = RSTRING_PTR(data),
224
+ .length = RSTRING_LEN(data), .move = 1,
225
+ .dealloc = (void (*)(void *))Registry.remove))
226
+ return Qfalse;
227
+ return self;
228
+ }
229
+
230
+ /**
231
+ Writes data to the connection. The data will be sent as soon as possible without
232
+ fragmantation of previously scheduled data.
233
+
234
+ Returns `false` on error and `self` on success.
235
+ */
236
+ static VALUE dyn_write_urgent(VALUE self, VALUE data) {
237
+ Check_Type(data, T_STRING);
238
+ intptr_t fd = iodine_get_fd(self);
239
+ Registry.add(data);
240
+ if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data))) {
241
+ return Qfalse;
242
+ }
243
+ return self;
244
+ }
245
+
246
+ /**
247
+ Update's a connection's timeout.
248
+
249
+ Returns self.
250
+ */
251
+ static VALUE dyn_set_timeout(VALUE self, VALUE timeout) {
252
+ intptr_t fd = iodine_get_fd(self);
253
+ unsigned int tout = FIX2UINT(timeout);
254
+ if (tout > 255)
255
+ tout = 255;
256
+ facil_set_timeout(fd, tout);
257
+ return self;
258
+ }
259
+
260
+ /**
261
+ Returns the connection's timeout.
262
+ */
263
+ static VALUE dyn_get_timeout(VALUE self) {
264
+ intptr_t fd = iodine_get_fd(self);
265
+ uint8_t tout = facil_get_timeout(fd);
266
+ unsigned int tout_int = tout;
267
+ return UINT2NUM(tout_int);
268
+ }
269
+
270
+ /**
271
+ Closes a connection.
272
+
273
+ The connection will be closed only once all the data was sent.
274
+
275
+ Returns self.
276
+ */
277
+ static VALUE dyn_close(VALUE self) {
278
+ intptr_t fd = iodine_get_fd(self);
279
+ sock_close(fd);
280
+ return self;
281
+ }
282
+
283
+ /**
284
+ Returns a connection's localized ID which is valid for *this process* (not a
285
+ machine or internet unique value).
286
+
287
+ Once the connection is closed and the `on_close` callback was called, this
288
+ method returns `nil`.
289
+
290
+ This can be used together with a true process wide UUID to uniquely identify a
291
+ connection across the internet.
292
+ */
293
+ static VALUE dyn_uuid(VALUE self) {
294
+ intptr_t uuid = iodine_get_fd(self);
295
+ if (!uuid || uuid == -1)
296
+ return Qnil;
297
+ return LONG2FIX(uuid);
298
+ }
299
+
300
+ /**
301
+ Returns true if the connection is open and false if closed.
302
+ */
303
+ static VALUE dyn_is_open(VALUE self) {
304
+ intptr_t uuid = iodine_get_fd(self);
305
+ if (uuid && sock_isvalid(uuid))
306
+ return Qtrue;
307
+ return Qfalse;
308
+ }
309
+
310
+ /**
311
+ Schedules a block to execute (defers the blocks execution).
312
+
313
+ When this function is called by a Protocol instance, a lock on the connection
314
+ will be used to prevent multiple tasks / callbacks from running concurrently.
315
+ i.e.
316
+
317
+ defer { write "this will run in a lock" }
318
+
319
+ Otherwise, the deferred task will run acconrding to the requested concurrency
320
+ model.
321
+
322
+ Iodine.defer { puts "this will run concurrently" }
323
+ Iodine.run { puts "this will run concurrently" }
324
+
325
+ Tasks scheduled before calling {Iodine.start} will run once for every process.
326
+
327
+ Returns the block given (or `false`).
328
+
329
+ **Notice**: There's a possibility that the rask will never be called if it was
330
+ associated with a specific connection (the method was called as an instance
331
+ method) and the connection was closed before the deferred task was performed.
332
+ */
333
+ static VALUE dyn_defer(int argc, VALUE *argv, VALUE self) {
334
+ rb_need_block();
335
+ intptr_t fd;
336
+ // check arguments.
337
+ if (argc > 1)
338
+ rb_raise(rb_eArgError, "this function expects no more then 1 (optional) "
339
+ "argument.");
340
+ else if (argc == 1) {
341
+ Check_Type(*argv, T_FIXNUM);
342
+ fd = FIX2LONG(*argv);
343
+ } else
344
+ fd = iodine_get_fd(self);
345
+
346
+ if (!sock_isvalid(fd))
347
+ return Qfalse;
348
+
349
+ VALUE block = rb_block_proc();
350
+ if (block == Qnil)
351
+ return Qfalse;
352
+ Registry.add(block);
353
+ facil_defer(.uuid = fd, .task = iodine_perform_task_and_free,
354
+ .task_type = FIO_PR_LOCK_TASK, .arg = (void *)block,
355
+ .fallback = iodine_clear_task);
356
+ return block;
357
+ }
358
+
359
+ /* *****************************************************************************
360
+ Connection management
361
+ ***************************************************************************** */
362
+ #define dyn_prot(protocol) ((iodine_protocol_s *)(protocol))
363
+
364
+ /** called when a data is available, but will not run concurrently */
365
+ static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
366
+ (void)(fduuid);
367
+ RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_data_func_id);
368
+ }
369
+ /** called when the socket is ready to be written to. */
370
+ static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
371
+ (void)(fduuid);
372
+ RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_ready_func_id);
373
+ }
374
+ /** called when the server is shutting down,
375
+ * but before closing the connection. */
376
+ static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
377
+ (void)(fduuid);
378
+ RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_shutdown_func_id);
379
+ }
380
+ /** called when the connection was closed, but will not run concurrently */
381
+ static void dyn_protocol_on_close(intptr_t uuid, protocol_s *protocol) {
382
+ RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_close_func_id);
383
+ iodine_set_fd(dyn_prot(protocol)->handler, 0);
384
+ Registry.remove(dyn_prot(protocol)->handler);
385
+ free(protocol);
386
+ (void)uuid;
387
+ }
388
+ /** called when a connection's timeout was reached */
389
+ static void dyn_protocol_ping(intptr_t fduuid, protocol_s *protocol) {
390
+ (void)(fduuid);
391
+ RubyCaller.call(dyn_prot(protocol)->handler, iodine_ping_func_id);
392
+ }
393
+
394
+ /* *****************************************************************************
395
+ Connection management API
396
+ *****************************************************************************
397
+ */
398
+
399
+ /** Update's a connection's handler and timeout. */
400
+ static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
401
+ uint8_t timeout) {
402
+ Registry.add(handler);
403
+ iodine_set_fd(handler, fduuid);
404
+ iodine_protocol_s *protocol = malloc(sizeof(*protocol));
405
+ if (protocol == NULL) {
406
+ Registry.remove(handler);
407
+ return NULL;
408
+ }
409
+ facil_set_timeout(fduuid, timeout);
410
+ *protocol = (iodine_protocol_s){
411
+ .protocol.on_data = dyn_protocol_on_data,
412
+ .protocol.on_close = dyn_protocol_on_close,
413
+ .protocol.on_shutdown = dyn_protocol_on_shutdown,
414
+ .protocol.on_ready = dyn_protocol_on_ready,
415
+ .protocol.ping = dyn_protocol_ping,
416
+ .protocol.service = iodine_protocol_service,
417
+ .handler = handler,
418
+ };
419
+ RubyCaller.call(handler, iodine_on_open_func_id);
420
+ return &protocol->protocol;
421
+ }
422
+
423
+ static protocol_s *on_open_dyn_protocol(intptr_t fduuid, void *udata) {
424
+ VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
425
+ uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
426
+ VALUE handler = RubyCaller.call((VALUE)udata, iodine_new_func_id);
427
+ return dyn_set_protocol(fduuid, handler, timeout);
428
+ }
429
+
430
+ /** Sets up a listening socket. Conncetions received at the assigned port will
431
+ be handled by the assigned handler.
432
+
433
+ Multiple services (listening sockets) can be registered before starting the
434
+ Iodine event loop. */
435
+ static VALUE iodine_listen(VALUE self, VALUE port, VALUE handler) {
436
+ // validate that the handler is a class and include the Iodine::Protocol
437
+ if (TYPE(handler) == T_CLASS) {
438
+ // include the Protocol module
439
+ // // do we neet to check?
440
+ // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
441
+ rb_include_module(handler, IodineProtocol);
442
+ rb_extend_object(handler, IodineProtocol);
443
+ } else {
444
+ rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
445
+ return Qnil;
446
+ }
447
+ if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
448
+ rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
449
+ if (TYPE(port) == T_FIXNUM)
450
+ port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
451
+ rb_ivar_set(self, rb_intern("_port"), port);
452
+ // listen
453
+ if (facil_listen(.port = StringValueCStr(port), .udata = (void *)handler,
454
+ .on_open = on_open_dyn_protocol) == -1)
455
+ return Qnil;
456
+ return self;
457
+ }
458
+
459
+ VALUE dyn_switch_prot(VALUE self, VALUE handler) {
460
+ uint8_t timeout;
461
+ intptr_t fd = iodine_get_fd(self);
462
+ if (TYPE(handler) == T_CLASS) {
463
+ // get the timeout
464
+ VALUE rb_tout = rb_ivar_get(handler, iodine_timeout_var_id);
465
+ timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
466
+ // include the Protocol module, preventing coder errors
467
+ rb_include_module(handler, IodineProtocol);
468
+ handler = RubyCaller.call(handler, iodine_new_func_id);
469
+ } else {
470
+ // include the Protocol module in the object's class
471
+ VALUE p_class = rb_obj_class(handler);
472
+ // include the Protocol module, preventing coder errors
473
+ rb_include_module(p_class, IodineProtocol);
474
+ // get the timeout
475
+ VALUE rb_tout = rb_ivar_get(p_class, iodine_timeout_var_id);
476
+ if (rb_tout == Qnil)
477
+ rb_tout = rb_ivar_get(handler, iodine_timeout_var_id);
478
+ timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
479
+ }
480
+ if (facil_attach(fd, dyn_set_protocol(fd, handler, timeout)))
481
+ return Qnil;
482
+ return handler;
483
+ }
484
+
485
+ static protocol_s *on_open_dyn_protocol_instance(intptr_t fduuid, void *udata) {
486
+ VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
487
+ uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
488
+ protocol_s *pr = dyn_set_protocol(fduuid, (VALUE)udata, timeout);
489
+ Registry.remove((VALUE)udata);
490
+ return pr;
491
+ }
492
+
493
+ /**
494
+ Connects (as a TCP/IP client) to a remote TCP/IP server.
495
+
496
+ i.e.
497
+
498
+ Iodine.connect "example.com", 5000, MyProtocolClass.new
499
+
500
+ */
501
+ static VALUE iodine_connect(VALUE self, VALUE address, VALUE port,
502
+ VALUE handler) {
503
+ if (TYPE(handler) == T_CLASS || TYPE(handler) == T_MODULE) {
504
+ // include the Protocol module, preventing coder errors
505
+ rb_include_module(handler, IodineProtocol);
506
+ handler = RubyCaller.call(handler, iodine_new_func_id);
507
+ } else {
508
+ // include the Protocol module in the object's class
509
+ VALUE p_class = rb_obj_class(handler);
510
+ // include the Protocol module, preventing coder errors
511
+ rb_include_module(p_class, IodineProtocol);
512
+ }
513
+ if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
514
+ rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
515
+ Registry.add(handler);
516
+ if (TYPE(port) == T_FIXNUM)
517
+ port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
518
+ // connect
519
+ intptr_t uuid = facil_connect(.port = StringValueCStr(port),
520
+ .address = StringValueCStr(address),
521
+ .udata = (void *)handler,
522
+ .on_connect = on_open_dyn_protocol_instance,
523
+ .on_fail = remove_from_registry);
524
+ if (uuid == -1)
525
+ return Qnil;
526
+ iodine_set_fd(handler, uuid);
527
+ return handler;
528
+ (void)self;
529
+ }
530
+
531
+ /**
532
+ Attaches an existing file descriptor (`fd`) (i.e., a pipe, a unix socket,
533
+ etc') as if it were a regular connection.
534
+
535
+ i.e.
536
+
537
+ Iodine.attach my_io_obj.to_i, MyProtocolClass.new
538
+
539
+ */
540
+ static VALUE iodine_attach_fd(VALUE self, VALUE rbfd, VALUE handler) {
541
+ Check_Type(rbfd, T_FIXNUM);
542
+ if (handler == Qnil || handler == Qfalse)
543
+ return Qfalse;
544
+ intptr_t uuid = FIX2INT(rbfd);
545
+ if (!uuid || uuid == -1)
546
+ return Qfalse;
547
+ /* make sure the uuid is connected to the sock library */
548
+ if (sock_fd2uuid(uuid) == -1)
549
+ sock_open(uuid);
550
+ if (TYPE(handler) == T_CLASS) {
551
+ // include the Protocol module, preventing coder errors
552
+ rb_include_module(handler, IodineProtocol);
553
+ handler = RubyCaller.call(handler, iodine_new_func_id);
554
+ } else {
555
+ // include the Protocol module in the object's class
556
+ VALUE p_class = rb_obj_class(handler);
557
+ // include the Protocol module, preventing coder errors
558
+ rb_include_module(p_class, IodineProtocol);
559
+ }
560
+ Registry.add(handler);
561
+ on_open_dyn_protocol_instance(uuid, (void *)handler);
562
+ return self;
563
+ }
564
+ /**
565
+ Attaches an existing IO object (i.e., a pipe, a unix socket, etc') as if it
566
+ were a regular connection.
567
+
568
+ i.e.
569
+
570
+ Iodine.attach my_io_obj, MyProtocolClass.new
571
+
572
+ */
573
+ static VALUE iodine_attach_io(VALUE self, VALUE io, VALUE handler) {
574
+ return iodine_attach_fd(self, RubyCaller.call(io, iodine_to_i_func_id),
575
+ handler);
576
+ }
577
+ /* *****************************************************************************
578
+ Library Initialization
579
+ *****************************************************************************
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 Iodine_init_protocol(void) {
588
+
589
+ /* add Iodine module functions */
590
+ // rb_define_module_function(Iodine, "defer", dyn_defer, -1);
591
+ // rb_define_module_function(Iodine, "each", dyn_run_each, 0);
592
+ rb_define_module_function(Iodine, "listen", iodine_listen, 2);
593
+ rb_define_module_function(Iodine, "connect", iodine_connect, 3);
594
+ rb_define_module_function(Iodine, "attach_io", iodine_attach_io, 2);
595
+ rb_define_module_function(Iodine, "attach_fd", iodine_attach_fd, 2);
596
+
597
+ /* Create the `Protocol` module and set stub functions */
598
+ IodineProtocol = rb_define_module_under(Iodine, "Protocol");
599
+ rb_define_method(IodineProtocol, "on_open", not_implemented, 0);
600
+ rb_define_method(IodineProtocol, "on_close", not_implemented, 0);
601
+ rb_define_method(IodineProtocol, "on_message", not_implemented2, 1);
602
+ rb_define_method(IodineProtocol, "on_data", default_on_data, 0);
603
+ rb_define_method(IodineProtocol, "on_ready", not_implemented, 0);
604
+ rb_define_method(IodineProtocol, "on_shutdown", not_implemented, 0);
605
+ rb_define_method(IodineProtocol, "ping", not_implemented_ping, 0);
606
+
607
+ /* Add module functions */
608
+ rb_define_singleton_method(IodineProtocol, "defer", dyn_defer, -1);
609
+ rb_define_singleton_method(IodineProtocol, "count", dyn_count, 0);
610
+ rb_define_singleton_method(IodineProtocol, "each", dyn_run_each, 0);
611
+
612
+ /* Add module instance methods */
613
+ // rb_define_method(IodineProtocol, "each", dyn_run_each, 0);
614
+ rb_define_method(IodineProtocol, "open?", dyn_is_open, 0);
615
+ rb_define_method(IodineProtocol, "conn_id", dyn_uuid, 0);
616
+ rb_define_method(IodineProtocol, "count", dyn_count, 0);
617
+ rb_define_method(IodineProtocol, "read", dyn_read, -1);
618
+ rb_define_method(IodineProtocol, "write", dyn_write, 1);
619
+ rb_define_method(IodineProtocol, "write!", dyn_write_move, 1);
620
+ rb_define_method(IodineProtocol, "write_urgent", dyn_write_urgent, 1);
621
+ rb_define_method(IodineProtocol, "close", dyn_close, 0);
622
+ rb_define_method(IodineProtocol, "defer", dyn_defer, -1);
623
+ rb_define_method(IodineProtocol, "switch_protocol", dyn_switch_prot, 1);
624
+ rb_define_method(IodineProtocol, "timeout=", dyn_set_timeout, 1);
625
+ rb_define_method(IodineProtocol, "timeout", dyn_get_timeout, 0);
626
+ }