iodine 0.4.19 → 0.5.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -9,7 +9,17 @@ Feel free to copy, use and enjoy according to the license provided.
9
9
  #include "iodine.h"
10
10
  #include "pubsub.h"
11
11
 
12
+ typedef enum {
13
+ IODINE_PUBSUB_GLOBAL,
14
+ IODINE_PUBSUB_WEBSOCKET,
15
+ IODINE_PUBSUB_SSE
16
+ } iodine_pubsub_type_e;
17
+
12
18
  extern VALUE IodineEngine;
19
+ extern ID iodine_engine_pubid;
20
+ VALUE iodine_publish(int argc, VALUE *argv, VALUE self);
21
+ VALUE iodine_subscribe(int argc, VALUE *argv, void *owner,
22
+ iodine_pubsub_type_e type);
13
23
 
14
24
  typedef struct {
15
25
  pubsub_engine_s engine;
@@ -20,17 +20,10 @@ Core helpers and data
20
20
  static VALUE IodineWebsocket; // The Iodine::Http::Websocket class
21
21
  static ID ws_var_id; // id for websocket pointer
22
22
  static ID dup_func_id; // id for the buffer.dup method
23
+ static ID iodine_on_ready_id; // id for the on_ready method
23
24
 
24
- static VALUE force_var_id;
25
- static VALUE channel_var_id;
26
- static VALUE pattern_var_id;
27
- static VALUE text_var_id;
28
- static VALUE binary_var_id;
29
- static VALUE engine_var_id;
30
- static VALUE message_var_id;
31
-
32
- #define set_uuid(object, request) \
33
- rb_ivar_set((object), iodine_fd_var_id, ULONG2NUM((request)->fd))
25
+ #define set_uuid(object, uuid) \
26
+ rb_ivar_set((object), iodine_fd_var_id, ULONG2NUM((uuid)))
34
27
 
35
28
  inline static intptr_t get_uuid(VALUE obj) {
36
29
  VALUE i = rb_ivar_get(obj, iodine_fd_var_id);
@@ -51,85 +44,35 @@ inline static ws_s *get_ws(VALUE obj) {
51
44
  #define get_handler(ws) ((VALUE)websocket_udata((ws_s *)(ws)))
52
45
 
53
46
  /* *****************************************************************************
54
- Buffer management - Rubyfy the way the buffer is handled.
55
- ***************************************************************************** */
56
-
57
- // struct buffer_s {
58
- // void *data;
59
- // size_t size;
60
- // };
61
- //
62
- // /** returns a buffer_s struct, with a buffer (at least) `size` long. */
63
- // struct buffer_s create_ws_buffer(ws_s *owner);
64
- //
65
- // /** returns a buffer_s struct, with a buffer (at least) `size` long. */
66
- // struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
67
- //
68
- // /** releases an existing buffer. */
69
- // void free_ws_buffer(ws_s *owner, struct buffer_s);
70
- //
71
- // /** Sets the initial buffer size. (4Kb)*/
72
- // #define WS_INITIAL_BUFFER_SIZE 4096
73
- //
74
- // // buffer increments by 4,096 Bytes (4Kb)
75
- // #define round_up_buffer_size(size) ((((size) >> 12) + 1) << 12)
76
- //
77
- // struct buffer_args {
78
- // struct buffer_s buffer;
79
- // ws_s *ws;
80
- // };
81
- //
82
- // void *ruby_land_buffer(void *_buf) {
83
- // #define args ((struct buffer_args *)(_buf))
84
- // if (args->buffer.data == NULL) {
85
- // VALUE rbbuff = rb_str_buf_new(WS_INITIAL_BUFFER_SIZE);
86
- // rb_ivar_set(get_handler(args->ws), iodine_buff_var_id, rbbuff);
87
- // rb_str_set_len(rbbuff, 0);
88
- // rb_enc_associate(rbbuff, IodineBinaryEncoding);
89
- // args->buffer.data = RSTRING_PTR(rbbuff);
90
- // args->buffer.size = WS_INITIAL_BUFFER_SIZE;
91
- //
92
- // } else {
93
- // VALUE rbbuff = rb_ivar_get(get_handler(args->ws), iodine_buff_var_id);
94
- // rb_str_modify(rbbuff);
95
- // rb_str_resize(rbbuff, args->buffer.size);
96
- // args->buffer.data = RSTRING_PTR(rbbuff);
97
- // args->buffer.size = rb_str_capacity(rbbuff);
98
- // }
99
- // return NULL;
100
- // #undef args
101
- // }
102
- //
103
- // struct buffer_s create_ws_buffer(ws_s *owner) {
104
- // struct buffer_args args = {.ws = owner};
105
- // RubyCaller.call_c(ruby_land_buffer, &args);
106
- // return args.buffer;
107
- // }
108
- //
109
- // struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buffer) {
110
- // buffer.size = round_up_buffer_size(buffer.size);
111
- // struct buffer_args args = {.ws = owner, .buffer = buffer};
112
- // RubyCaller.call_c(ruby_land_buffer, &args);
113
- // return args.buffer;
114
- // }
115
- // void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
116
- // (void)(owner);
117
- // (void)(buff);
118
- // }
119
- //
120
- // #undef round_up_buffer_size
121
-
122
- /* *****************************************************************************
123
- Websocket Ruby API
47
+ SSE / Websocket Ruby API
124
48
  ***************************************************************************** */
125
49
 
126
50
  /** Closes the websocket connection. The connection is only closed after
127
51
  * existing data in the outgoing buffer is sent. */
128
52
  static VALUE iodine_ws_close(VALUE self) {
129
- ws_s *ws = get_ws(self);
130
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
53
+ void *ws = get_ws(self);
54
+ if (!ws) {
131
55
  return Qfalse;
132
- websocket_close(ws);
56
+ }
57
+ iodine_pubsub_type_e c_type = (iodine_pubsub_type_e)iodine_get_cdata(self);
58
+ switch (c_type) {
59
+ case IODINE_PUBSUB_WEBSOCKET:
60
+ /* WebSockets*/
61
+ if (((protocol_s *)ws)->service != WEBSOCKET_ID_STR) {
62
+ return Qfalse;
63
+ }
64
+ websocket_close(ws);
65
+ break;
66
+ case IODINE_PUBSUB_SSE:
67
+ /* SSE */
68
+ http_sse_close(ws);
69
+ break;
70
+ case IODINE_PUBSUB_GLOBAL:
71
+ /* fallthrough */
72
+ default:
73
+ return Qfalse;
74
+ break;
75
+ }
133
76
  return self;
134
77
  }
135
78
 
@@ -139,52 +82,71 @@ static VALUE iodine_ws_close(VALUE self) {
139
82
  * Returns `true` on success or `false if the websocket was closed or an error
140
83
  * occurred.
141
84
  *
142
- * `write` will return immediately UNLESS resources are insufficient. If the
143
- * global `write` buffer is full, `write` will block until a buffer "packet"
144
- * becomes available and can be assigned to the socket. */
85
+ * `write` will return immediately, adding the data to the outgoing queue.
86
+ *
87
+ * If the connection is closed, `write` will raise an exception.
88
+ */
145
89
  static VALUE iodine_ws_write(VALUE self, VALUE data) {
146
90
  Check_Type(data, T_STRING);
147
- ws_s *ws = get_ws(self);
148
- // if ((void *)ws == (void *)0x04 || (void *)data == (void *)0x04 ||
149
- // RSTRING_PTR(data) == (void *)0x04)
150
- // fprintf(stderr, "iodine_ws_write: self = %p ; data = %p\n"
151
- // "\t\tString ptr: %p, String length: %lu\n",
152
- // (void *)ws, (void *)data, RSTRING_PTR(data), RSTRING_LEN(data));
153
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
91
+ void *ws = get_ws(self);
92
+ iodine_pubsub_type_e c_type = (iodine_pubsub_type_e)iodine_get_cdata(self);
93
+ if (!ws || !c_type) {
94
+ rb_raise(rb_eIOError, "Connection is closed");
154
95
  return Qfalse;
155
- websocket_write(ws, RSTRING_PTR(data), RSTRING_LEN(data),
156
- rb_enc_get(data) == IodineUTF8Encoding);
157
- return Qtrue;
158
- }
159
-
160
- /** Returns the number of active websocket connections (including connections
161
- * that are in the process of closing down). */
162
- static VALUE iodine_ws_count(VALUE self) {
163
- return LONG2FIX(websocket_count());
164
- (void)self;
96
+ }
97
+ switch (c_type) {
98
+ case IODINE_PUBSUB_WEBSOCKET:
99
+ /* WebSockets*/
100
+ if (((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
101
+ goto error;
102
+ websocket_write(ws, RSTRING_PTR(data), RSTRING_LEN(data),
103
+ rb_enc_get(data) == IodineUTF8Encoding);
104
+ return Qtrue;
105
+ break;
106
+ case IODINE_PUBSUB_SSE:
107
+ /* SSE */
108
+ http_sse_write(
109
+ ws, .data = {.data = RSTRING_PTR(data), .len = RSTRING_LEN(data)});
110
+ return Qtrue;
111
+ break;
112
+ case IODINE_PUBSUB_GLOBAL:
113
+ /* fallthrough */
114
+ default:
115
+ error:
116
+ rb_raise(rb_eIOError, "Connection is closed");
117
+ return Qfalse;
118
+ }
119
+ return Qfalse;
165
120
  }
166
121
 
167
122
  /**
168
- Returns a weak indication as to the state of the socket's buffer. If the server
169
- has data in the buffer that wasn't written to the socket, `has_pending?` will
170
- return `true`, otherwise `false` will be returned.
123
+ Returns the number of pending writes or -1 if the connection is closed
171
124
  */
172
125
  static VALUE iodine_ws_has_pending(VALUE self) {
173
126
  intptr_t uuid = get_uuid(self);
174
- return sock_has_pending(uuid) ? Qtrue : Qfalse;
127
+ if (!uuid || sock_isclosed(uuid))
128
+ return INT2NUM(-1);
129
+ return SIZET2NUM(sock_pending(uuid));
175
130
  }
176
131
 
177
132
  /**
178
- Returns a connection's UUID which is valid for *this process* (not a machine
179
- or internet unique value).
180
-
181
- This can be used together with a true process wide UUID to uniquely identify a
182
- connection across the internet.
133
+ Returns true is the connection is open, false if it isn't.
183
134
  */
184
- static VALUE iodine_ws_uuid(VALUE self) {
135
+ static VALUE iodine_ws_is_open(VALUE self) {
185
136
  intptr_t uuid = get_uuid(self);
186
- return LONG2FIX(uuid);
187
- }
137
+ if (uuid && sock_isvalid(uuid))
138
+ return Qtrue;
139
+ return Qfalse;
140
+ }
141
+
142
+ /** Returns a local (per process) unique identifier for the conection. */
143
+ // static VALUE iodine_ws_uuid(VALUE self) {
144
+ // intptr_t uuid = get_uuid(self);
145
+ // return ULL2NUM(uuid);
146
+ // if (uuid && sock_isvalid(uuid))
147
+ // return Qtrue;
148
+ // return Qfalse;
149
+ // }
188
150
 
189
151
  /* *****************************************************************************
190
152
  Websocket defer
@@ -247,419 +209,120 @@ static VALUE iodine_defer(int argc, VALUE *argv, VALUE self) {
247
209
  return block;
248
210
  }
249
211
 
250
- /* *****************************************************************************
251
- Websocket Pub/Sub API
252
- ***************************************************************************** */
253
-
254
- static void iodine_on_unsubscribe(void *u) {
255
- if (u && (VALUE)u != Qnil && u != (VALUE)Qfalse)
256
- Registry.remove((VALUE)u);
257
- }
258
-
259
- static void *on_pubsub_notificationinGVL(websocket_pubsub_notification_s *n) {
260
- VALUE rbn[2];
261
- rbn[0] = rb_str_new(n->channel.name, n->channel.len);
262
- Registry.add(rbn[0]);
263
- rbn[1] = rb_str_new(n->msg.data, n->msg.len);
264
- Registry.add(rbn[1]);
265
- RubyCaller.call2((VALUE)n->udata, iodine_call_proc_id, 2, rbn);
266
- Registry.remove(rbn[0]);
267
- Registry.remove(rbn[1]);
268
- return NULL;
269
- }
270
-
271
- static void on_pubsub_notificationin(websocket_pubsub_notification_s n) {
272
- RubyCaller.call_c((void *(*)(void *))on_pubsub_notificationinGVL, &n);
273
- }
274
-
275
- /**
276
- Subscribes the websocket to a channel belonging to a specific pub/sub service
277
- (using an {Iodine::PubSub::Engine} to connect Iodine to the service).
278
-
279
- The function accepts a single argument (a Hash) and an optional block.
280
-
281
- If no block is provided, the message is sent directly to the websocket client.
282
-
283
- Accepts a single Hash argument with the following possible options:
284
-
285
- :engine :: If provided, the engine to use for pub/sub. Otherwise the default
286
- engine is used.
287
-
288
- :channel :: Required (unless :pattern). The channel to subscribe to.
289
-
290
- :pattern :: An alternative to the required :channel, subscribes to a pattern.
291
-
292
- :force :: This can be set to either nil, :text or :binary and controls the way
293
- the message will be forwarded to the websocket client. This is only valid if no
294
- block was provided. Defaults to smart encoding based testing.
295
-
296
-
297
- */
298
- static VALUE iodine_ws_subscribe(VALUE self, VALUE args) {
299
- Check_Type(args, T_HASH);
300
- ws_s *ws = get_ws(self);
301
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
302
- return Qfalse;
303
- uint8_t use_pattern = 0, force_text = 0, force_binary = 0;
304
-
305
- VALUE rb_ch = rb_hash_aref(args, channel_var_id);
306
- if (rb_ch == Qnil || rb_ch == Qfalse) {
307
- use_pattern = 1;
308
- rb_ch = rb_hash_aref(args, pattern_var_id);
309
- if (rb_ch == Qnil || rb_ch == Qfalse)
310
- rb_raise(rb_eArgError, "channel is required for pub/sub methods.");
311
- }
312
- if (TYPE(rb_ch) == T_SYMBOL)
313
- rb_ch = rb_sym2str(rb_ch);
314
- Check_Type(rb_ch, T_STRING);
315
-
316
- VALUE tmp = rb_hash_aref(args, force_var_id);
317
- if (tmp == text_var_id)
318
- force_text = 1;
319
- else if (tmp == binary_var_id)
320
- force_binary = 1;
321
-
322
- VALUE block = 0;
323
- if (rb_block_given_p()) {
324
- block = rb_block_proc();
325
- Registry.add(block);
326
- }
327
-
328
- pubsub_engine_s *engine =
329
- iodine_engine_ruby2facil(rb_hash_aref(args, engine_var_id));
330
-
331
- uintptr_t subid = websocket_subscribe(
332
- ws, .channel.name = RSTRING_PTR(rb_ch), .channel.len = RSTRING_LEN(rb_ch),
333
- .engine = engine, .use_pattern = use_pattern, .force_text = force_text,
334
- .force_binary = force_binary,
335
- .on_message = (block ? on_pubsub_notificationin : NULL),
336
- .on_unsubscribe = (block ? iodine_on_unsubscribe : NULL),
337
- .udata = (void *)block);
338
- if (!subid)
339
- return Qnil;
340
- return ULL2NUM(subid);
341
- }
342
212
  /**
343
- Searches for the subscription ID for the describes subscription.
344
-
345
- Takes the same arguments as {subscribe}, a single Hash argument with the
346
- following possible options:
347
-
348
- :engine :: If provided, the engine to use for pub/sub. Otherwise the default
349
- engine is used.
213
+ Schedules a block of code to run for the specified websocket at a later time,
214
+ (**if** the connection is open). The block will run within the connection's
215
+ lock, offering a fast concurrency synchronizing tool.
350
216
 
351
- :channel :: The subscription's channel.
217
+ The block of code will receive the websocket's callback object. i.e.
352
218
 
353
- :pattern :: An alternative to the required :channel, subscribes to a pattern.
219
+ Iodine::Websocket.defer(uuid) {|ws| ws.write "I'm doing this" }
354
220
 
355
- :force :: This can be set to either nil, :text or :binary and controls the way
356
- the message will be forwarded to the websocket client. This is only valid if no
357
- block was provided. Defaults to smart encoding based testing.
221
+ On success returns the block, otherwise (connection invalid) returns `false`.
358
222
 
223
+ A sucessful event registration doesn't guaranty that the block will be called
224
+ (the connection might close between the event registration and the execution).
359
225
  */
360
- static VALUE iodine_ws_is_subscribed(VALUE self, VALUE args) {
361
- Check_Type(args, T_HASH);
362
- ws_s *ws = get_ws(self);
363
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
226
+ static VALUE iodine_class_defer(VALUE self, VALUE ws_uuid) {
227
+ (void)(self);
228
+ intptr_t fd = FIX2LONG(ws_uuid);
229
+ if (!sock_isvalid(fd))
364
230
  return Qfalse;
365
- uint8_t use_pattern = 0, force_text = 0, force_binary = 0;
366
-
367
- VALUE rb_ch = rb_hash_aref(args, channel_var_id);
368
- if (rb_ch == Qnil || rb_ch == Qfalse) {
369
- use_pattern = 1;
370
- rb_ch = rb_hash_aref(args, pattern_var_id);
371
- if (rb_ch == Qnil || rb_ch == Qfalse)
372
- rb_raise(rb_eArgError, "channel is required for pub/sub methods.");
373
- }
374
- if (TYPE(rb_ch) == T_SYMBOL)
375
- rb_ch = rb_sym2str(rb_ch);
376
- Check_Type(rb_ch, T_STRING);
377
-
378
- VALUE tmp = rb_hash_aref(args, force_var_id);
379
- if (tmp == text_var_id)
380
- force_text = 1;
381
- else if (tmp == binary_var_id)
382
- force_binary = 1;
383
-
384
- VALUE block = 0;
385
- if (rb_block_given_p()) {
386
- block = rb_block_proc();
387
- }
388
-
389
- pubsub_engine_s *engine =
390
- iodine_engine_ruby2facil(rb_hash_aref(args, engine_var_id));
391
-
392
- uintptr_t subid = websocket_find_sub(
393
- ws, .channel.name = RSTRING_PTR(rb_ch), .channel.len = RSTRING_LEN(rb_ch),
394
- .engine = engine, .use_pattern = use_pattern, .force_text = force_text,
395
- .force_binary = force_binary,
396
- .on_message = (block ? on_pubsub_notificationin : NULL),
397
- .udata = (void *)block);
398
- if (!subid)
399
- return Qnil;
400
- return LONG2NUM(subid);
401
- }
402
-
403
- /**
404
- Cancels the subscription matching `sub_id`.
405
- */
406
- static VALUE iodine_ws_unsubscribe(VALUE self, VALUE sub_id) {
407
- if (sub_id == Qnil || sub_id == Qfalse)
408
- return Qnil;
409
- ws_s *ws = get_ws(self);
410
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
231
+ // requires a block to be passed
232
+ rb_need_block();
233
+ VALUE block = rb_block_proc();
234
+ if (block == Qnil)
411
235
  return Qfalse;
412
- Check_Type(sub_id, T_FIXNUM);
413
- websocket_unsubscribe(ws, NUM2LONG(sub_id));
414
- return Qnil;
415
- }
416
-
417
- /**
418
- Publishes a message to a channel.
419
-
420
- Accepts a single Hash argument with the following possible options:
421
-
422
- :engine :: If provided, the engine to use for pub/sub. Otherwise the default
423
- engine is used.
424
-
425
- :channel :: Required (unless :pattern). The channel to publish to.
426
-
427
- :pattern :: An alternative to the required :channel, publishes to a pattern.
428
- This is NOT supported by Redis and it's limited to the local process cluster.
429
-
430
- :message :: REQUIRED. The message to be published.
431
- :
432
- */
433
- static VALUE iodine_ws_publish(VALUE self, VALUE args) {
434
- Check_Type(args, T_HASH);
435
- uint8_t use_pattern = 0;
436
-
437
- VALUE rb_ch = rb_hash_aref(args, channel_var_id);
438
- if (rb_ch == Qnil || rb_ch == Qfalse) {
439
- use_pattern = 1;
440
- rb_ch = rb_hash_aref(args, pattern_var_id);
441
- if (rb_ch == Qnil || rb_ch == Qfalse)
442
- rb_raise(rb_eArgError, "channel is required for pub/sub methods.");
443
- }
444
- if (TYPE(rb_ch) == T_SYMBOL)
445
- rb_ch = rb_sym2str(rb_ch);
446
- Check_Type(rb_ch, T_STRING);
236
+ Registry.add(block);
447
237
 
448
- VALUE rb_msg = rb_hash_aref(args, message_var_id);
449
- if (rb_msg == Qnil || rb_msg == Qfalse) {
450
- rb_raise(rb_eArgError, "message is required for the :publish method.");
451
- }
452
- Check_Type(rb_msg, T_STRING);
453
-
454
- pubsub_engine_s *engine =
455
- iodine_engine_ruby2facil(rb_hash_aref(args, engine_var_id));
456
-
457
- intptr_t subid =
458
- pubsub_publish(.engine = engine, .channel.name = (RSTRING_PTR(rb_ch)),
459
- .channel.len = (RSTRING_LEN(rb_ch)),
460
- .msg.data = (RSTRING_PTR(rb_msg)),
461
- .msg.len = (RSTRING_LEN(rb_msg)),
462
- .use_pattern = use_pattern);
463
- if (!subid)
464
- return Qfalse;
465
- return Qtrue;
466
- (void)self;
238
+ facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
239
+ .fallback = iodine_defer_fallback);
240
+ return block;
467
241
  }
468
242
 
469
243
  /* *****************************************************************************
470
- Websocket Multi-Write - Deprecated
244
+ Websocket Pub/Sub API
471
245
  ***************************************************************************** */
472
246
 
473
- // static uint8_t iodine_ws_if_callback(ws_s *ws, void *block) {
474
- // if (!ws)
475
- // return 0;
476
- // VALUE handler = get_handler(ws);
477
- // uint8_t ret = 0;
478
- // if (handler)
479
- // ret = RubyCaller.call2((VALUE)block, iodine_call_proc_id, 1, &handler);
480
- // return ret && ret != Qnil && ret != Qfalse;
481
- // }
482
- //
483
- // static void iodine_ws_write_each_complete(ws_s *ws, void *block) {
484
- // (void)ws;
485
- // if ((VALUE)block != Qnil)
486
- // Registry.remove((VALUE)block);
487
- // }
488
-
247
+ // clang-format off
489
248
  /**
490
- * Writes data to all the Websocket connections sharing the same process
491
- * (worker) except `self`.
492
- *
493
- * If a block is given, it will be passed each Websocket connection in turn
494
- * (much like `each`) and send the data only if the block returns a "truthy"
495
- * value (i.e. NOT `false` or `nil`).
496
- *
497
- * See both {#write} and {#each} for more details.
498
- */
499
- // static VALUE iodine_ws_multiwrite(VALUE self, VALUE data) {
500
- // Check_Type(data, T_STRING);
501
- // ws_s *ws = get_ws(self);
502
- // // if ((void *)ws == (void *)0x04 || (void *)data == (void *)0x04 ||
503
- // // RSTRING_PTR(data) == (void *)0x04)
504
- // // fprintf(stderr, "iodine_ws_write: self = %p ; data = %p\n"
505
- // // "\t\tString ptr: %p, String length: %lu\n",
506
- // // (void *)ws, (void *)data, RSTRING_PTR(data),
507
- // RSTRING_LEN(data)); if (!ws || ((protocol_s *)ws)->service !=
508
- // WEBSOCKET_ID_STR)
509
- // ws = NULL;
510
- //
511
- // VALUE block = Qnil;
512
- // if (rb_block_given_p())
513
- // block = rb_block_proc();
514
- // if (block != Qnil)
515
- // Registry.add(block);
516
- // websocket_write_each(.origin = ws, .data = RSTRING_PTR(data),
517
- // .length = RSTRING_LEN(data),
518
- // .is_text = (rb_enc_get(data) == IodineUTF8Encoding),
519
- // .on_finished = iodine_ws_write_each_complete,
520
- // .filter =
521
- // ((block == Qnil) ? NULL : iodine_ws_if_callback),
522
- // .arg = (void *)block);
523
- // return Qtrue;
524
- // }
249
+ Subscribes the connection to a Pub/Sub channel.
525
250
 
526
- /* *****************************************************************************
527
- Websocket task performance
528
- */
251
+ The method accepts 1-2 arguments and an optional block. These are all valid ways
252
+ to call the method:
529
253
 
530
- static void iodine_ws_perform_each_task(intptr_t fd, protocol_s *protocol,
531
- void *data) {
532
- (void)(fd);
533
- VALUE handler = get_handler(protocol);
534
- if (handler)
535
- RubyCaller.call2((VALUE)data, iodine_call_proc_id, 1, &handler);
536
- }
537
- static void iodine_ws_finish_each_task(intptr_t fd, void *data) {
538
- (void)(fd);
539
- Registry.remove((VALUE)data);
540
- }
254
+ subscribe("my_stream") {|from, msg| p msg }
255
+ subscribe("my_stream", match: :redis) {|from, msg| p msg }
256
+ subscribe(to: "my_stream") {|from, msg| p msg }
257
+ subscribe to: "my_stream", match: :redis, handler: MyProc
541
258
 
542
- inline static void iodine_ws_run_each(intptr_t origin, VALUE block) {
543
- facil_each(.origin = origin, .service = WEBSOCKET_ID_STR,
544
- .task = iodine_ws_perform_each_task, .arg = (void *)block,
545
- .on_complete = iodine_ws_finish_each_task);
546
- }
259
+ The first argument must be either a String or a Hash.
547
260
 
548
- /** Performs a block of code for each websocket connection. The function returns
549
- the block of code.
261
+ The second, optional, argument must be a Hash (if given).
550
262
 
551
- The block of code should accept a single variable which is the websocket
552
- connection.
263
+ The options Hash supports the following possible keys (other keys are ignored,
264
+ all keys are Symbols):
553
265
 
554
- i.e.:
266
+ :match :: The channel / subject name matching type to be used. Valid value is: `:redis`. Future versions hope to support `:nats` and `:rabbit` patern matching as well.
555
267
 
556
- def on_message data
557
- msg = data.dup; # data will be overwritten once the function exists.
558
- each {|ws| ws.write msg}
559
- end
268
+ :to :: The channel / subject to subscribe to.
560
269
 
270
+ :as :: valid for WebSocket connections only. can be either `:text` or `:binary`. `:text` is the default transport for pub/sub events.
561
271
 
562
- The block of code will be executed asynchronously, to avoid having two blocks
563
- of code running at the same time and minimizing race conditions when using
564
- multilple threads.
565
- */
566
- static VALUE iodine_ws_each(VALUE self) {
567
- // requires a block to be passed
568
- rb_need_block();
569
- VALUE block = rb_block_proc();
570
- if (block == Qnil)
571
- return Qnil;
572
- Registry.add(block);
573
- intptr_t fd = get_uuid(self);
574
- iodine_ws_run_each(fd, block);
575
- return block;
576
- }
272
+ Returns an {Iodine::PubSub::Subscription} object that answers to:
577
273
 
578
- /**
579
- Runs the required block for each websocket.
274
+ #.close :: closes the connection.
580
275
 
581
- Tasks will be performed asynchronously, within each connection's lock, so no
582
- connection will have more then one task being performed at the same time
583
- (similar to {#defer}).
276
+ #.to_s :: returns the subscription's target (stream / channel / subject).
584
277
 
585
- Also, unlike {Iodine.run}, the block will **not** be called unless the
586
- websocket is still open at the time it's execution begins.
278
+ #.==(str) :: returns true if the string is an exact match for the target (even if the target itself is a pattern).
587
279
 
588
- Always returns `self`.
589
280
  */
590
- static VALUE iodine_ws_class_each(VALUE self) {
591
- // requires a block to be passed
592
- rb_need_block();
593
- VALUE block = rb_block_proc();
594
- if (block == Qnil)
595
- return Qfalse;
596
- Registry.add(block);
597
- iodine_ws_run_each(-1, block);
598
- return self;
281
+ static VALUE iodine_ws_subscribe(int argc, VALUE *argv, VALUE self) {
282
+ // clang-format on
283
+ ws_s *owner = get_ws(self);
284
+ return iodine_subscribe(argc, argv, owner,
285
+ (iodine_pubsub_type_e)iodine_get_cdata(self));
599
286
  }
600
287
 
601
- /**
602
- Schedules a block of code to run for the specified websocket at a later time,
603
- (**if** the connection is open). The block will run within the connection's
604
- lock, offering a fast concurrency synchronizing tool.
605
-
606
- The block of code will receive the websocket's callback object. i.e.
607
-
608
- Iodine::Websocket.defer(uuid) {|ws| ws.write "I'm doing this" }
609
-
610
- On success returns the block, otherwise (connection invalid) returns `false`.
611
-
612
- A sucessful event registration doesn't guaranty that the block will be called
613
- (the connection might close between the event registration and the execution).
614
- */
615
- static VALUE iodine_class_defer(VALUE self, VALUE ws_uuid) {
616
- (void)(self);
617
- intptr_t fd = FIX2LONG(ws_uuid);
618
- if (!sock_isvalid(fd))
619
- return Qfalse;
620
- // requires a block to be passed
621
- rb_need_block();
622
- VALUE block = rb_block_proc();
623
- if (block == Qnil)
624
- return Qfalse;
625
- Registry.add(block);
626
-
627
- facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
628
- .fallback = iodine_defer_fallback);
629
- return block;
630
- }
288
+ /* *****************************************************************************
289
+ WebSocket Callbacks
290
+ ***************************************************************************** */
631
291
 
632
- //////////////////////////////////////
633
- // Protocol functions
634
- void ws_on_open(ws_s *ws) {
292
+ static void ws_on_open(ws_s *ws) {
635
293
  VALUE handler = get_handler(ws);
636
294
  if (!handler)
637
295
  return;
296
+ set_uuid(handler, websocket_uuid(ws));
638
297
  set_ws(handler, ws);
298
+ iodine_set_cdata(handler, (void *)IODINE_PUBSUB_WEBSOCKET);
639
299
  RubyCaller.call(handler, iodine_on_open_func_id);
640
300
  }
641
- void ws_on_close(ws_s *ws) {
642
- VALUE handler = get_handler(ws);
301
+ static void ws_on_close(intptr_t uuid, void *handler_) {
302
+ VALUE handler = (VALUE)handler_;
643
303
  if (!handler) {
644
304
  fprintf(stderr,
645
305
  "ERROR: (iodine websockets) Closing a handlerless websocket?!\n");
646
306
  return;
647
307
  }
308
+ set_ws(handler, NULL);
309
+ set_uuid(handler, 0);
310
+ iodine_set_cdata(handler, (void *)IODINE_PUBSUB_GLOBAL);
648
311
  RubyCaller.call(handler, iodine_on_close_func_id);
649
- set_ws(handler, Qnil);
650
312
  Registry.remove(handler);
313
+ (void)uuid;
651
314
  }
652
- void ws_on_shutdown(ws_s *ws) {
315
+ static void ws_on_shutdown(ws_s *ws) {
653
316
  VALUE handler = get_handler(ws);
654
317
  if (!handler)
655
318
  return;
656
319
  RubyCaller.call(handler, iodine_on_shutdown_func_id);
657
320
  }
658
- void ws_on_ready(ws_s *ws) {
321
+ static void ws_on_ready(ws_s *ws) {
659
322
  VALUE handler = get_handler(ws);
660
323
  if (!handler)
661
324
  return;
662
- RubyCaller.call(handler, iodine_on_ready_func_id);
325
+ RubyCaller.call(handler, iodine_on_drained_func_id);
663
326
  }
664
327
 
665
328
  struct ws_on_data_args_s {
@@ -668,7 +331,7 @@ struct ws_on_data_args_s {
668
331
  size_t length;
669
332
  uint8_t is_text;
670
333
  };
671
- void *ws_on_data_inGIL(void *args_) {
334
+ static void *ws_on_data_inGIL(void *args_) {
672
335
  struct ws_on_data_args_s *a = args_;
673
336
  VALUE handler = get_handler(a->ws);
674
337
  if (!handler) {
@@ -683,7 +346,7 @@ void *ws_on_data_inGIL(void *args_) {
683
346
  rb_funcallv(handler, iodine_on_message_func_id, 1, &buffer);
684
347
  return NULL;
685
348
  }
686
- void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
349
+ static void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
687
350
  struct ws_on_data_args_s a = {
688
351
  .ws = ws, .data = data, .length = length, .is_text = is_text};
689
352
  RubyCaller.call_c(ws_on_data_inGIL, &a);
@@ -699,45 +362,141 @@ static VALUE empty_func(VALUE self) {
699
362
  return Qnil;
700
363
  }
701
364
 
365
+ /** Please implement your own callback for this event. */
366
+ static VALUE empty_func_drained(VALUE self) {
367
+ RubyCaller.call(self, iodine_on_ready_id);
368
+ (void)(self);
369
+ return Qnil;
370
+ }
371
+
372
+ /** DEPRECATED! Please override {on_drained} instead. */
373
+ static VALUE empty_func_on_ready(VALUE self) {
374
+ (void)(self);
375
+ return Qnil;
376
+ }
377
+
378
+ /* *****************************************************************************
379
+ SSE Callbacks
380
+ ***************************************************************************** */
381
+
382
+ /**
383
+ * The (optional) on_open callback will be called once the EventSource
384
+ * connection is established.
385
+ */
386
+ static void iodine_sse_on_open(http_sse_s *sse) {
387
+ VALUE handler = (VALUE)sse->udata;
388
+ if (!handler)
389
+ return;
390
+ set_uuid(handler, http_sse2uuid(sse));
391
+ set_ws(handler, sse);
392
+ iodine_set_cdata(handler, (void *)IODINE_PUBSUB_SSE);
393
+ RubyCaller.call(handler, iodine_on_open_func_id);
394
+ }
395
+
396
+ /**
397
+ * The (optional) on_ready callback will be after a the underlying socket's
398
+ * buffer changes it's state to empty.
399
+ *
400
+ * If the socket's buffer is never used, the callback is never called.
401
+ */
402
+ static void iodine_sse_on_ready(http_sse_s *sse) {
403
+ VALUE handler = (VALUE)sse->udata;
404
+ if (!handler)
405
+ return;
406
+ RubyCaller.call(handler, iodine_on_drained_func_id);
407
+ }
408
+
409
+ /**
410
+ * The (optional) on_shutdown callback will be called if a connection is still
411
+ * open while the server is shutting down (called before `on_close`).
412
+ */
413
+ static void iodine_sse_on_shutdown(http_sse_s *sse) {
414
+ VALUE handler = (VALUE)sse->udata;
415
+ if (!handler)
416
+ return;
417
+ RubyCaller.call(handler, iodine_on_shutdown_func_id);
418
+ }
419
+ /**
420
+ * The (optional) on_close callback will be called once a connection is
421
+ * terminated or failed to be established.
422
+ *
423
+ * The `uuid` is the connection's unique ID that can identify the Websocket. A
424
+ * value of `uuid == 0` indicates the Websocket connection wasn't established
425
+ * (an error occured).
426
+ *
427
+ * The `udata` is the user data as set during the upgrade or using the
428
+ * `websocket_udata_set` function.
429
+ */
430
+ static void iodine_sse_on_close(http_sse_s *sse) {
431
+ VALUE handler = (VALUE)sse->udata;
432
+ if (!handler) {
433
+ fprintf(stderr,
434
+ "ERROR: (iodine websockets) Closing a handlerless websocket?!\n");
435
+ return;
436
+ }
437
+ set_ws(handler, NULL);
438
+ set_uuid(handler, 0);
439
+ iodine_set_cdata(handler, (void *)IODINE_PUBSUB_GLOBAL);
440
+ RubyCaller.call(handler, iodine_on_close_func_id);
441
+ Registry.remove(handler);
442
+ }
443
+
702
444
  /* *****************************************************************************
703
445
  Upgrading
704
446
  ***************************************************************************** */
705
447
 
706
- void iodine_websocket_upgrade(http_request_s *request,
707
- http_response_s *response, VALUE handler,
708
- size_t max_msg, uint8_t ping) {
448
+ static VALUE iodine_prep_ws_handler(VALUE handler) {
709
449
  // make sure we have a valid handler, with the Websocket Protocol mixin.
710
450
  if (handler == Qnil || handler == Qfalse || TYPE(handler) == T_FIXNUM ||
711
451
  TYPE(handler) == T_STRING || TYPE(handler) == T_SYMBOL)
712
- goto failed;
452
+ return Qnil;
713
453
  if (TYPE(handler) == T_CLASS || TYPE(handler) == T_MODULE) {
714
454
  // include the Protocol module
715
455
  rb_include_module(handler, IodineWebsocket);
716
456
  rb_extend_object(handler, IodineWebsocket);
717
457
  handler = RubyCaller.call(handler, iodine_new_func_id);
718
458
  if (handler == Qnil || handler == Qfalse)
719
- goto failed;
459
+ return Qnil;
460
+ Registry.add(handler);
720
461
  // check that we created a handler
721
462
  } else {
463
+ Registry.add(handler);
722
464
  // include the Protocol module in the object's class
723
465
  VALUE p_class = rb_obj_class(handler);
724
466
  rb_include_module(p_class, IodineWebsocket);
725
467
  rb_extend_object(p_class, IodineWebsocket);
726
468
  }
469
+ return handler;
470
+ }
471
+
472
+ void iodine_upgrade_websocket(http_s *h, VALUE handler) {
473
+ // add the handler to the registry
474
+ handler = iodine_prep_ws_handler(handler);
475
+ if (handler == Qnil)
476
+ goto failed;
477
+ // send upgrade response and set new protocol
478
+ http_upgrade2ws(.http = h, .udata = (void *)handler, .on_close = ws_on_close,
479
+ .on_open = ws_on_open, .on_shutdown = ws_on_shutdown,
480
+ .on_ready = ws_on_ready, .on_message = ws_on_data);
481
+ return;
482
+ failed:
483
+ http_send_error(h, 400);
484
+ return;
485
+ }
486
+
487
+ void iodine_upgrade_sse(http_s *h, VALUE handler) {
727
488
  // add the handler to the registry
728
- Registry.add(handler);
729
- // set the UUID for the connection
730
- set_uuid(handler, request);
489
+ handler = iodine_prep_ws_handler(handler);
490
+ if (handler == Qnil)
491
+ goto failed;
731
492
  // send upgrade response and set new protocol
732
- websocket_upgrade(.request = request, .response = response,
733
- .udata = (void *)handler, .on_close = ws_on_close,
734
- .on_open = ws_on_open, .on_shutdown = ws_on_shutdown,
735
- .on_ready = ws_on_ready, .on_message = ws_on_data,
736
- .max_msg_size = max_msg, .timeout = ping);
493
+ http_upgrade2sse(h, .udata = (void *)handler, .on_open = iodine_sse_on_open,
494
+ .on_ready = iodine_sse_on_ready,
495
+ .on_shutdown = iodine_sse_on_shutdown,
496
+ .on_close = iodine_sse_on_close);
737
497
  return;
738
498
  failed:
739
- response->status = 400;
740
- http_response_finish(response);
499
+ http_send_error(h, 400);
741
500
  return;
742
501
  }
743
502
 
@@ -749,14 +508,7 @@ void Iodine_init_websocket(void) {
749
508
  // get IDs and data that's used often
750
509
  ws_var_id = rb_intern("iodine_ws_ptr"); // when upgrading
751
510
  dup_func_id = rb_intern("dup"); // when upgrading
752
-
753
- force_var_id = ID2SYM(rb_intern("fource"));
754
- channel_var_id = ID2SYM(rb_intern("channel"));
755
- pattern_var_id = ID2SYM(rb_intern("pattern"));
756
- message_var_id = ID2SYM(rb_intern("message"));
757
- engine_var_id = ID2SYM(rb_intern("engine"));
758
- text_var_id = ID2SYM(rb_intern("text"));
759
- binary_var_id = ID2SYM(rb_intern("binary"));
511
+ iodine_on_ready_id = rb_intern2("on_ready", 8);
760
512
 
761
513
  // the Ruby websockets protocol class.
762
514
  IodineWebsocket = rb_define_module_under(Iodine, "Websocket");
@@ -764,26 +516,32 @@ void Iodine_init_websocket(void) {
764
516
  fprintf(stderr, "WTF?!\n"), exit(-1);
765
517
  // // callbacks and handlers
766
518
  rb_define_method(IodineWebsocket, "on_open", empty_func, 0);
519
+
767
520
  // rb_define_method(IodineWebsocket, "on_message", empty_func_message, 1);
521
+
768
522
  rb_define_method(IodineWebsocket, "on_shutdown", empty_func, 0);
769
523
  rb_define_method(IodineWebsocket, "on_close", empty_func, 0);
770
- rb_define_method(IodineWebsocket, "on_ready", empty_func, 0);
524
+ rb_define_method(IodineWebsocket, "on_drained", empty_func_drained, 0);
771
525
  rb_define_method(IodineWebsocket, "write", iodine_ws_write, 1);
772
526
  rb_define_method(IodineWebsocket, "close", iodine_ws_close, 0);
773
527
 
774
- rb_define_method(IodineWebsocket, "conn_id", iodine_ws_uuid, 0);
775
- rb_define_method(IodineWebsocket, "has_pending?", iodine_ws_has_pending, 0);
528
+ /// Deprectaed!
529
+ rb_define_method(IodineWebsocket, "on_ready", empty_func_on_ready, 0);
530
+
531
+ // rb_define_method(IodineWebsocket, "_cid", iodine_ws_uuid, 0);
532
+ // rb_define_method(IodineWebsocket, "_sock", iodine_ws_uuid, 0);
533
+
534
+ rb_define_method(IodineWebsocket, "pending", iodine_ws_has_pending, 0);
535
+ rb_define_method(IodineWebsocket, "open?", iodine_ws_is_open, 0);
776
536
  rb_define_method(IodineWebsocket, "defer", iodine_defer, -1);
537
+
777
538
  // rb_define_method(IodineWebsocket, "each", iodine_ws_each, 0);
778
539
 
779
- rb_define_method(IodineWebsocket, "subscribe", iodine_ws_subscribe, 1);
780
- rb_define_method(IodineWebsocket, "unsubscribe", iodine_ws_unsubscribe, 1);
781
- rb_define_method(IodineWebsocket, "subscribed?", iodine_ws_is_subscribed, 1);
782
- rb_define_method(IodineWebsocket, "publish", iodine_ws_publish, 1);
540
+ rb_define_method(IodineWebsocket, "subscribe", iodine_ws_subscribe, -1);
541
+ rb_define_method(IodineWebsocket, "publish", iodine_publish, -1);
783
542
 
784
- rb_define_singleton_method(IodineWebsocket, "each", iodine_ws_class_each, 0);
785
- rb_define_singleton_method(IodineWebsocket, "defer", iodine_class_defer, 1);
786
- rb_define_singleton_method(IodineWebsocket, "count", iodine_ws_count, 0);
787
- // rb_define_singleton_method(IodineWebsocket, "publish", iodine_ws_publish,
543
+ // rb_define_singleton_method(IodineWebsocket, "defer", iodine_class_defer,
788
544
  // 1);
545
+
546
+ rb_define_singleton_method(IodineWebsocket, "publish", iodine_publish, -1);
789
547
  }