iodine 0.3.6 → 0.4.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 (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,79 +0,0 @@
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
- #ifndef IODINE_CORE_H
8
- #define IODINE_CORE_H
9
- // clang-format off
10
- #include <ruby.h>
11
- #ifndef _GNU_SOURCE
12
- #define _GNU_SOURCE
13
- #endif
14
- #include <ruby/encoding.h>
15
- #include <ruby/thread.h>
16
- #include <ruby/version.h>
17
- #include <ruby/io.h>
18
- // clang-format on
19
-
20
- #include "rb-call.h"
21
- #include "rb-registry.h"
22
-
23
- #include "facil.h"
24
-
25
- #include <stdio.h>
26
- #include <stdlib.h>
27
- #include <string.h>
28
-
29
- #ifndef UNUSED_FUNC
30
- #define UNUSED_FUNC __attribute__((unused))
31
- #endif
32
-
33
- extern rb_encoding *BinaryEncoding;
34
- extern rb_encoding *UTF8Encoding;
35
- extern int BinaryEncodingIndex;
36
- extern int UTF8EncodingIndex;
37
-
38
- extern VALUE Iodine;
39
- extern VALUE IodineBase;
40
- extern VALUE Iodine_Version;
41
- extern ID call_proc_id;
42
- extern ID on_start_func_id;
43
- extern ID on_finish_func_id;
44
- extern ID new_func_id;
45
- extern ID on_open_func_id;
46
- extern ID on_message_func_id;
47
- extern ID on_data_func_id;
48
- extern ID on_ready_func_id;
49
- extern ID on_shutdown_func_id;
50
- extern ID on_close_func_id;
51
- extern ID ping_func_id;
52
- extern ID buff_var_id;
53
- extern ID fd_var_id;
54
- extern ID timeout_var_id;
55
- extern ID to_s_method_id;
56
-
57
- UNUSED_FUNC static inline void iodine_set_fd(VALUE handler, intptr_t fd) {
58
- rb_ivar_set(handler, fd_var_id, LONG2NUM((long)fd));
59
- }
60
- UNUSED_FUNC static inline intptr_t iodine_get_fd(VALUE handler) {
61
- return ((intptr_t)NUM2LONG(rb_ivar_get(handler, fd_var_id)));
62
- }
63
- /* *****************************************************************************
64
- The Core dynamic Iodine protocol - all protocols using `each` should follow
65
- this design.
66
- */
67
- typedef struct {
68
- protocol_s protocol;
69
- VALUE handler;
70
- } dyn_protocol_s;
71
-
72
- #define dyn_prot(protocol) ((dyn_protocol_s *)(protocol))
73
-
74
- /* "upgrades" a connection to a dynamic generic protocol */
75
- VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler);
76
- /* runs a task for each connection in the service. */
77
- void iodine_run_each(intptr_t origin, const char *service, VALUE block);
78
-
79
- #endif
@@ -1,551 +0,0 @@
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_websocket.h"
8
- #include "iodine_core.h"
9
- #include "iodine_http.h"
10
- #include "rb-call.h"
11
- #include "rb-registry.h"
12
- #include <arpa/inet.h>
13
- #include <ruby/io.h>
14
-
15
- /* *****************************************************************************
16
- Core helpers and data
17
- */
18
-
19
- static VALUE rWebsocket; // The Iodine::Http::Websocket class
20
- static VALUE rWebsocketClass; // The Iodine::Http::Websocket class
21
- static ID ws_var_id; // id for websocket pointer
22
- static ID dup_func_id; // id for the buffer.dup method
23
-
24
- size_t iodine_websocket_max_msg_size = 0;
25
- uint8_t iodine_websocket_timeout = 0;
26
-
27
- #define set_uuid(object, request) \
28
- rb_ivar_set((object), fd_var_id, ULONG2NUM((request)->fd))
29
-
30
- inline static intptr_t get_uuid(VALUE obj) {
31
- VALUE i = rb_ivar_get(obj, fd_var_id);
32
- return i != Qnil ? (intptr_t)FIX2ULONG(i) : 0;
33
- }
34
-
35
- #define set_ws(object, ws) \
36
- rb_ivar_set((object), ws_var_id, ULONG2NUM(((VALUE)(ws))))
37
-
38
- inline static ws_s *get_ws(VALUE obj) {
39
- VALUE i = rb_ivar_get(obj, ws_var_id);
40
- if (i == Qnil)
41
- return NULL;
42
- return (ws_s *)FIX2ULONG(i);
43
- }
44
-
45
- #define set_handler(ws, handler) websocket_udata_set((ws), (VALUE)handler)
46
- #define get_handler(ws) ((VALUE)websocket_udata((ws_s *)(ws)))
47
-
48
- /*******************************************************************************
49
- Buffer management - update to change the way the buffer is handled.
50
- */
51
- struct buffer_s {
52
- void *data;
53
- size_t size;
54
- };
55
-
56
- /** returns a buffer_s struct, with a buffer (at least) `size` long. */
57
- struct buffer_s create_ws_buffer(ws_s *owner);
58
-
59
- /** returns a buffer_s struct, with a buffer (at least) `size` long. */
60
- struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
61
-
62
- /** releases an existing buffer. */
63
- void free_ws_buffer(ws_s *owner, struct buffer_s);
64
-
65
- /** Sets the initial buffer size. (4Kb)*/
66
- #define WS_INITIAL_BUFFER_SIZE 4096
67
-
68
- // buffer increments by 4,096 Bytes (4Kb)
69
- #define round_up_buffer_size(size) ((((size) >> 12) + 1) << 12)
70
-
71
- struct buffer_args {
72
- struct buffer_s buffer;
73
- ws_s *ws;
74
- };
75
-
76
- void *ruby_land_buffer(void *_buf) {
77
- #define args ((struct buffer_args *)(_buf))
78
- if (args->buffer.data == NULL) {
79
- VALUE rbbuff = rb_str_buf_new(WS_INITIAL_BUFFER_SIZE);
80
- rb_ivar_set(get_handler(args->ws), buff_var_id, rbbuff);
81
- rb_str_set_len(rbbuff, 0);
82
- rb_enc_associate(rbbuff, BinaryEncoding);
83
- args->buffer.data = RSTRING_PTR(rbbuff);
84
- args->buffer.size = WS_INITIAL_BUFFER_SIZE;
85
-
86
- } else {
87
- VALUE rbbuff = rb_ivar_get(get_handler(args->ws), buff_var_id);
88
- rb_str_modify(rbbuff);
89
- rb_str_resize(rbbuff, args->buffer.size);
90
- args->buffer.data = RSTRING_PTR(rbbuff);
91
- args->buffer.size = rb_str_capacity(rbbuff);
92
- }
93
- return NULL;
94
- #undef args
95
- }
96
-
97
- struct buffer_s create_ws_buffer(ws_s *owner) {
98
- struct buffer_args args = {.ws = owner};
99
- RubyCaller.call_c(ruby_land_buffer, &args);
100
- return args.buffer;
101
- }
102
-
103
- struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buffer) {
104
- buffer.size = round_up_buffer_size(buffer.size);
105
- struct buffer_args args = {.ws = owner, .buffer = buffer};
106
- RubyCaller.call_c(ruby_land_buffer, &args);
107
- return args.buffer;
108
- }
109
- void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
110
- (void)(owner);
111
- (void)(buff);
112
- }
113
-
114
- #undef round_up_buffer_size
115
-
116
- /* *****************************************************************************
117
- Websocket Ruby API
118
- */
119
-
120
- /** Closes the websocket connection. The connection is only closed after
121
- * existing data in the outgoing buffer is sent. */
122
- static VALUE iodine_ws_close(VALUE self) {
123
- ws_s *ws = get_ws(self);
124
- if (((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
125
- return Qfalse;
126
- websocket_close(ws);
127
- return self;
128
- }
129
-
130
- /**
131
- * Writes data to the websocket.
132
- *
133
- * Returns `true` on success or `false if the websocket was closed or an error
134
- * occurred.
135
- *
136
- * `write` will return immediately UNLESS resources are insufficient. If the
137
- * global `write` buffer is full, `write` will block until a buffer "packet"
138
- * becomes available and can be assigned to the socket. */
139
- static VALUE iodine_ws_write(VALUE self, VALUE data) {
140
- Check_Type(data, T_STRING);
141
- ws_s *ws = get_ws(self);
142
- // if ((void *)ws == (void *)0x04 || (void *)data == (void *)0x04 ||
143
- // RSTRING_PTR(data) == (void *)0x04)
144
- // fprintf(stderr, "iodine_ws_write: self = %p ; data = %p\n"
145
- // "\t\tString ptr: %p, String length: %lu\n",
146
- // (void *)ws, (void *)data, RSTRING_PTR(data), RSTRING_LEN(data));
147
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
148
- return Qfalse;
149
- websocket_write(ws, RSTRING_PTR(data), RSTRING_LEN(data),
150
- rb_enc_get(data) == UTF8Encoding);
151
- return Qtrue;
152
- }
153
-
154
- /** Returns the number of active websocket connections (including connections
155
- * that are in the process of closing down). */
156
- static VALUE iodine_ws_count(VALUE self) {
157
- return LONG2FIX(websocket_count());
158
- (void)self;
159
- }
160
-
161
- /**
162
- Returns a weak indication as to the state of the socket's buffer. If the server
163
- has data in the buffer that wasn't written to the socket, `has_pending?` will
164
- return `true`, otherwise `false` will be returned.
165
- */
166
- static VALUE iodine_ws_has_pending(VALUE self) {
167
- intptr_t uuid = get_uuid(self);
168
- return sock_has_pending(uuid) ? Qtrue : Qfalse;
169
- }
170
-
171
- /**
172
- Returns a connection's UUID which is valid for **this process** (not a machine
173
- or internet unique value).
174
-
175
- This can be used together with a true process wide UUID to uniquely identify a
176
- connection across the internet.
177
- */
178
- static VALUE iodine_ws_uuid(VALUE self) {
179
- intptr_t uuid = get_uuid(self);
180
- return LONG2FIX(uuid);
181
- }
182
-
183
- /* *****************************************************************************
184
- Websocket defer
185
- */
186
-
187
- static void iodine_perform_defer(intptr_t uuid, protocol_s *protocol,
188
- void *arg) {
189
- (void)(uuid);
190
- VALUE obj = protocol->service == WEBSOCKET_ID_STR
191
- ? get_handler(protocol)
192
- : dyn_prot(protocol)->handler;
193
- RubyCaller.call2((VALUE)arg, call_proc_id, 1, &obj);
194
- Registry.remove((VALUE)arg);
195
- }
196
-
197
- static void iodine_defer_fallback(intptr_t uuid, void *arg) {
198
- (void)(uuid);
199
- Registry.remove((VALUE)arg);
200
- }
201
-
202
- /**
203
- Schedules a block of code to execute at a later time, **if** the connection is
204
- still
205
- open and while preventing concurent code from running for the same connection
206
- object.
207
-
208
- An optional `uuid` argument can be passed along, so that the block of code will
209
- run for the requested connection rather then this connection.
210
-
211
- **Careful**: as this might cause this connection's object to run code
212
- concurrently when data owned by this connection is accessed from within the
213
- block of code.
214
-
215
- On success returns the block, otherwise (connection invalid) returns `false`. A
216
- sucessful event registration doesn't guaranty that the block will be called (the
217
- connection might close between the event registration and the execution).
218
- */
219
- static VALUE iodine_defer(int argc, VALUE *argv, VALUE self) {
220
- intptr_t fd;
221
- // check arguments.
222
- if (argc > 1)
223
- rb_raise(rb_eArgError, "this function expects no more then 1 (optional) "
224
- "argument.");
225
- else if (argc == 1) {
226
- Check_Type(*argv, T_FIXNUM);
227
- fd = FIX2LONG(*argv);
228
- if (!sock_isvalid(fd))
229
- return Qfalse;
230
- } else
231
- fd = iodine_get_fd(self);
232
- // requires a block to be passed
233
- rb_need_block();
234
- VALUE block = rb_block_proc();
235
- if (block == Qnil)
236
- return Qfalse;
237
- Registry.add(block);
238
-
239
- facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
240
- .fallback = iodine_defer_fallback);
241
- return block;
242
- }
243
-
244
- /* *****************************************************************************
245
- Websocket Multi-Write
246
- */
247
-
248
- static uint8_t iodine_ws_if_callback(ws_s *ws, void *block) {
249
- if (!ws)
250
- return 0;
251
- VALUE handler = get_handler(ws);
252
- uint8_t ret = 0;
253
- if (handler)
254
- ret = RubyCaller.call2((VALUE)block, call_proc_id, 1, &handler);
255
- return ret && ret != Qnil && ret != Qfalse;
256
- }
257
-
258
- static void iodine_ws_write_each_complete(ws_s *ws, void *block) {
259
- (void)ws;
260
- if ((VALUE)block != Qnil)
261
- Registry.remove((VALUE)block);
262
- }
263
-
264
- /**
265
- * Writes data to all the Websocket connections sharing the same process
266
- * (worker) except `self`.
267
- *
268
- * If a block is given, it will be passed each Websocket connection in turn
269
- * (much like `each`) and send the data only if the block returns a "truthy"
270
- * value (i.e. NOT `false` or `nil`).
271
- *
272
- * See both {#write} and {#each} for more details.
273
- */
274
- static VALUE iodine_ws_multiwrite(VALUE self, VALUE data) {
275
- Check_Type(data, T_STRING);
276
- ws_s *ws = get_ws(self);
277
- // if ((void *)ws == (void *)0x04 || (void *)data == (void *)0x04 ||
278
- // RSTRING_PTR(data) == (void *)0x04)
279
- // fprintf(stderr, "iodine_ws_write: self = %p ; data = %p\n"
280
- // "\t\tString ptr: %p, String length: %lu\n",
281
- // (void *)ws, (void *)data, RSTRING_PTR(data), RSTRING_LEN(data));
282
- if (!ws || ((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
283
- ws = NULL;
284
-
285
- VALUE block = Qnil;
286
- if (rb_block_given_p())
287
- block = rb_block_proc();
288
- if (block != Qnil)
289
- Registry.add(block);
290
- websocket_write_each(.origin = ws, .data = RSTRING_PTR(data),
291
- .length = RSTRING_LEN(data),
292
- .is_text = (rb_enc_get(data) == UTF8Encoding),
293
- .on_finished = iodine_ws_write_each_complete,
294
- .filter =
295
- ((block == Qnil) ? NULL : iodine_ws_if_callback),
296
- .arg = (void *)block);
297
- return Qtrue;
298
- }
299
-
300
- /* *****************************************************************************
301
- Websocket task performance
302
- */
303
-
304
- static void iodine_ws_perform_each_task(intptr_t fd, protocol_s *protocol,
305
- void *data) {
306
- (void)(fd);
307
- VALUE handler = get_handler(protocol);
308
- if (handler)
309
- RubyCaller.call2((VALUE)data, call_proc_id, 1, &handler);
310
- }
311
- static void iodine_ws_finish_each_task(intptr_t fd, void *data) {
312
- (void)(fd);
313
- Registry.remove((VALUE)data);
314
- }
315
-
316
- inline static void iodine_ws_run_each(intptr_t origin, VALUE block) {
317
- facil_each(.origin = origin, .service = WEBSOCKET_ID_STR,
318
- .task = iodine_ws_perform_each_task, .arg = (void *)block,
319
- .on_complete = iodine_ws_finish_each_task);
320
- }
321
-
322
- /** Performs a block of code for each websocket connection. The function returns
323
- the block of code.
324
-
325
- The block of code should accept a single variable which is the websocket
326
- connection.
327
-
328
- i.e.:
329
-
330
- def on_message data
331
- msg = data.dup; # data will be overwritten once the function exists.
332
- each {|ws| ws.write msg}
333
- end
334
-
335
-
336
- The block of code will be executed asynchronously, to avoid having two blocks
337
- of code running at the same time and minimizing race conditions when using
338
- multilple threads.
339
- */
340
- static VALUE iodine_ws_each(VALUE self) {
341
- // requires a block to be passed
342
- rb_need_block();
343
- VALUE block = rb_block_proc();
344
- if (block == Qnil)
345
- return Qnil;
346
- Registry.add(block);
347
- intptr_t fd = get_uuid(self);
348
- iodine_ws_run_each(fd, block);
349
- return block;
350
- }
351
-
352
- /**
353
- Runs the required block for each dynamic protocol connection.
354
-
355
- Tasks will be performed asynchronously, within each connections lock, so no
356
- connection will have
357
- more then one task being performed at the same time (similar to {#defer}).
358
-
359
- Also, unlike {Iodine.run}, the block will **not** be called unless the
360
- connection remains open at the time it's execution is scheduled.
361
-
362
- Always returns `self`.
363
- */
364
- static VALUE iodine_ws_class_each(VALUE self) {
365
- // requires a block to be passed
366
- rb_need_block();
367
- VALUE block = rb_block_proc();
368
- if (block == Qnil)
369
- return Qfalse;
370
- Registry.add(block);
371
- iodine_ws_run_each(-1, block);
372
- return self;
373
- }
374
-
375
- /**
376
- Schedules a block of code to run for the specified connection at a later time,
377
- (**if** the connection is open) and while preventing concurent code from running
378
- for the same connection object.
379
-
380
- The block of code will receive the connection's object. i.e.
381
-
382
- Iodine::Websocket.defer(uuid) {|ws| ws.write "I'm doing this" }
383
-
384
- On success returns the block, otherwise (connection invalid) returns `false`. A
385
- sucessful event registration doesn't guaranty that the block will be called (the
386
- connection might close between the event registration and the execution).
387
- */
388
- static VALUE iodine_class_defer(VALUE self, VALUE ws_uuid) {
389
- (void)(self);
390
- intptr_t fd = FIX2LONG(ws_uuid);
391
- if (!sock_isvalid(fd))
392
- return Qfalse;
393
- // requires a block to be passed
394
- rb_need_block();
395
- VALUE block = rb_block_proc();
396
- if (block == Qnil)
397
- return Qfalse;
398
- Registry.add(block);
399
-
400
- facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
401
- .fallback = iodine_defer_fallback);
402
- return block;
403
- }
404
-
405
- //////////////////////////////////////
406
- // Protocol functions
407
- void ws_on_open(ws_s *ws) {
408
- VALUE handler = get_handler(ws);
409
- if (!handler)
410
- return;
411
- set_ws(handler, ws);
412
- RubyCaller.call(handler, on_open_func_id);
413
- }
414
- void ws_on_close(ws_s *ws) {
415
- VALUE handler = get_handler(ws);
416
- if (!handler)
417
- return;
418
- RubyCaller.call(handler, on_close_func_id);
419
- set_ws(handler, Qnil);
420
- Registry.remove(handler);
421
- }
422
- void ws_on_shutdown(ws_s *ws) {
423
- VALUE handler = get_handler(ws);
424
- if (!handler)
425
- return;
426
- RubyCaller.call(handler, on_shutdown_func_id);
427
- }
428
- void ws_on_ready(ws_s *ws) {
429
- VALUE handler = get_handler(ws);
430
- if (!handler)
431
- return;
432
- RubyCaller.call(handler, on_ready_func_id);
433
- }
434
- void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
435
- (void)(data);
436
- VALUE handler = get_handler(ws);
437
- if (!handler)
438
- return;
439
- VALUE buffer = rb_ivar_get(handler, buff_var_id);
440
- if (is_text)
441
- rb_enc_associate(buffer, UTF8Encoding);
442
- else
443
- rb_enc_associate(buffer, BinaryEncoding);
444
- rb_str_set_len(buffer, length);
445
- RubyCaller.call2(handler, on_message_func_id, 1, &buffer);
446
- }
447
-
448
- //////////////////////////////////////
449
- // Protocol constructor
450
-
451
- void iodine_websocket_upgrade(http_request_s *request,
452
- http_response_s *response, VALUE handler) {
453
- // make sure we have a valid handler, with the Websocket Protocol mixin.
454
- if (handler == Qnil || handler == Qfalse) {
455
- response->status = 400;
456
- http_response_finish(response);
457
- return;
458
- }
459
- if (TYPE(handler) == T_CLASS) {
460
- // include the Protocol module
461
- rb_include_module(handler, rWebsocket);
462
- rb_extend_object(handler, rWebsocketClass);
463
- handler = RubyCaller.call(handler, new_func_id);
464
- // check that we created a handler
465
- } else {
466
- // include the Protocol module in the object's class
467
- VALUE p_class = rb_obj_class(handler);
468
- rb_include_module(p_class, rWebsocket);
469
- rb_extend_object(p_class, rWebsocketClass);
470
- }
471
- // add the handler to the registry
472
- Registry.add(handler);
473
- // set the UUID for the connection
474
- set_uuid(handler, request);
475
- // send upgrade response and set new protocol
476
- websocket_upgrade(.request = request, .response = response,
477
- .udata = (void *)handler, .on_close = ws_on_close,
478
- .on_open = ws_on_open, .on_shutdown = ws_on_shutdown,
479
- .on_ready = ws_on_ready, .on_message = ws_on_data,
480
- .max_msg_size = iodine_websocket_max_msg_size,
481
- .timeout = iodine_websocket_timeout);
482
- }
483
-
484
- //////////////
485
- // Empty callbacks for default implementations.
486
-
487
- /** Please implement your own callback for this event.
488
- */
489
- static VALUE empty_func(VALUE self) {
490
- (void)(self);
491
- return Qnil;
492
- }
493
- // /* The `on_message(data)` callback is the main method for any websocket
494
- // implementation. It is the only required callback for a websocket handler
495
- // (without this handler, errors will occur).
496
- //
497
- // <b>NOTICE</b>: the data passed to the `on_message` callback is the actual
498
- // recycble network buffer, not a copy! <b>Use `data.dup` before moving the data
499
- // out of the function's scope</b> to prevent data corruption (i.e. when
500
- // using the data within an `each` block). For example (broadcasting):
501
- //
502
- // def on_message data
503
- // msg = data.dup; # data will be overwritten once the function exists.
504
- // each {|ws| ws.write msg}
505
- // end
506
- //
507
- // Please override this method and implement your own callback.
508
- // */
509
- // static VALUE def_dyn_message(VALUE self, VALUE data) {
510
- // fprintf(stderr,
511
- // "WARNING: websocket handler on_message override missing or "
512
- // "bypassed.\n");
513
- // return Qnil;
514
- // }
515
-
516
- /////////////////////////////
517
- // initialize the class and the whole of the Iodine/http library
518
- void Init_iodine_websocket(void) {
519
- // get IDs and data that's used often
520
- ws_var_id = rb_intern("iodine_ws_ptr"); // when upgrading
521
- dup_func_id = rb_intern("dup"); // when upgrading
522
-
523
- // the Ruby websockets protocol class.
524
- rWebsocket = rb_define_module_under(Iodine, "Websocket");
525
- if (rWebsocket == Qfalse)
526
- fprintf(stderr, "WTF?!\n"), exit(-1);
527
- // // callbacks and handlers
528
- rb_define_method(rWebsocket, "on_open", empty_func, 0);
529
- // rb_define_method(rWebsocket, "on_message", def_dyn_message, 1);
530
- rb_define_method(rWebsocket, "on_shutdown", empty_func, 0);
531
- rb_define_method(rWebsocket, "on_close", empty_func, 0);
532
- rb_define_method(rWebsocket, "on_ready", empty_func, 0);
533
- rb_define_method(rWebsocket, "write", iodine_ws_write, 1);
534
- rb_define_method(rWebsocket, "each_write", iodine_ws_multiwrite, 1);
535
- rb_define_method(rWebsocket, "close", iodine_ws_close, 0);
536
-
537
- // rb_define_method(rWebsocket, "uuid", iodine_ws_uuid, 0);
538
- rb_define_method(rWebsocket, "conn_id", iodine_ws_uuid, 0);
539
- rb_define_method(rWebsocket, "has_pending?", iodine_ws_has_pending, 0);
540
- rb_define_method(rWebsocket, "defer", iodine_defer, -1);
541
- rb_define_method(rWebsocket, "each", iodine_ws_each, 0);
542
- rb_define_method(rWebsocket, "count", iodine_ws_count, 0);
543
-
544
- rb_define_singleton_method(rWebsocket, "each", iodine_ws_class_each, 0);
545
- rb_define_singleton_method(rWebsocket, "defer", iodine_class_defer, 1);
546
- rb_define_singleton_method(rWebsocket, "each_write", iodine_ws_multiwrite, 1);
547
-
548
- rWebsocketClass = rb_define_module_under(IodineBase, "WebsocketClass");
549
- rb_define_method(rWebsocketClass, "each", iodine_ws_class_each, 0);
550
- rb_define_method(rWebsocketClass, "defer", iodine_class_defer, 1);
551
- }