iodine 0.5.2 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +63 -100
- data/bin/raw-rbhttp +12 -7
- data/examples/config.ru +8 -7
- data/examples/echo.ru +8 -7
- data/examples/info.md +41 -35
- data/examples/pubsub_engine.ru +12 -12
- data/examples/redis.ru +10 -12
- data/examples/shootout.ru +19 -42
- data/exe/iodine +116 -1
- data/ext/iodine/defer.c +1 -1
- data/ext/iodine/facil.c +12 -8
- data/ext/iodine/facil.h +2 -2
- data/ext/iodine/iodine.c +177 -343
- data/ext/iodine/iodine.h +18 -72
- data/ext/iodine/iodine_caller.c +132 -0
- data/ext/iodine/iodine_caller.h +21 -0
- data/ext/iodine/iodine_connection.c +841 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +391 -0
- data/ext/iodine/iodine_defer.h +7 -0
- data/ext/iodine/{rb-fiobj2rb.h → iodine_fiobj2rb.h} +6 -6
- data/ext/iodine/iodine_helpers.c +51 -5
- data/ext/iodine/iodine_helpers.h +2 -3
- data/ext/iodine/iodine_http.c +284 -141
- data/ext/iodine/iodine_http.h +2 -2
- data/ext/iodine/iodine_json.c +13 -13
- data/ext/iodine/iodine_json.h +1 -1
- data/ext/iodine/iodine_pubsub.c +573 -823
- data/ext/iodine/iodine_pubsub.h +15 -27
- data/ext/iodine/{rb-rack-io.c → iodine_rack_io.c} +30 -8
- data/ext/iodine/{rb-rack-io.h → iodine_rack_io.h} +1 -0
- data/ext/iodine/iodine_store.c +136 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +385 -0
- data/ext/iodine/iodine_tcp.h +9 -0
- data/lib/iodine.rb +73 -171
- data/lib/iodine/connection.rb +34 -0
- data/lib/iodine/pubsub.rb +5 -18
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -182
- metadata +17 -18
- data/ext/iodine/iodine_protocol.c +0 -689
- data/ext/iodine/iodine_protocol.h +0 -13
- data/ext/iodine/iodine_websockets.c +0 -550
- data/ext/iodine/iodine_websockets.h +0 -17
- data/ext/iodine/rb-call.c +0 -156
- data/ext/iodine/rb-call.h +0 -70
- data/ext/iodine/rb-defer.c +0 -124
- data/ext/iodine/rb-registry.c +0 -150
- data/ext/iodine/rb-registry.h +0 -34
- data/lib/iodine/cli.rb +0 -89
- data/lib/iodine/monkeypatch.rb +0 -46
- data/lib/iodine/protocol.rb +0 -42
- data/lib/iodine/websocket.rb +0 -16
@@ -1,13 +0,0 @@
|
|
1
|
-
#ifndef H_IODINE_PROTOCOL_H
|
2
|
-
#define H_IODINE_PROTOCOL_H
|
3
|
-
/*
|
4
|
-
Copyright: Boaz segev, 2016-2017
|
5
|
-
License: MIT
|
6
|
-
|
7
|
-
Feel free to copy, use and enjoy according to the license provided.
|
8
|
-
*/
|
9
|
-
#include "iodine.h"
|
10
|
-
|
11
|
-
void Iodine_init_protocol(void);
|
12
|
-
|
13
|
-
#endif
|
@@ -1,550 +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_websockets.h"
|
8
|
-
#include "iodine_pubsub.h"
|
9
|
-
|
10
|
-
#include "pubsub.h"
|
11
|
-
#include "websockets.h"
|
12
|
-
|
13
|
-
#include <arpa/inet.h>
|
14
|
-
#include <ruby/io.h>
|
15
|
-
|
16
|
-
/* *****************************************************************************
|
17
|
-
Core helpers and data
|
18
|
-
***************************************************************************** */
|
19
|
-
|
20
|
-
static VALUE IodineWebsocket; // 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
|
-
static ID iodine_on_ready_id; // id for the on_ready method
|
24
|
-
|
25
|
-
#define set_uuid(object, uuid) \
|
26
|
-
rb_ivar_set((object), iodine_fd_var_id, ULONG2NUM((uuid)))
|
27
|
-
|
28
|
-
inline static intptr_t get_uuid(VALUE obj) {
|
29
|
-
VALUE i = rb_ivar_get(obj, iodine_fd_var_id);
|
30
|
-
return i != Qnil ? (intptr_t)FIX2ULONG(i) : 0;
|
31
|
-
}
|
32
|
-
|
33
|
-
#define set_ws(object, ws) \
|
34
|
-
rb_ivar_set((object), ws_var_id, ULONG2NUM(((VALUE)(ws))))
|
35
|
-
|
36
|
-
inline static ws_s *get_ws(VALUE obj) {
|
37
|
-
VALUE i = rb_ivar_get(obj, ws_var_id);
|
38
|
-
if (i == Qnil)
|
39
|
-
return NULL;
|
40
|
-
return (ws_s *)FIX2ULONG(i);
|
41
|
-
}
|
42
|
-
|
43
|
-
#define set_handler(ws, handler) websocket_udata_set((ws), (VALUE)handler)
|
44
|
-
#define get_handler(ws) ((VALUE)websocket_udata((ws_s *)(ws)))
|
45
|
-
|
46
|
-
/* *****************************************************************************
|
47
|
-
SSE / Websocket Ruby API
|
48
|
-
***************************************************************************** */
|
49
|
-
|
50
|
-
/** Closes the websocket connection. The connection is only closed after
|
51
|
-
* existing data in the outgoing buffer is sent. */
|
52
|
-
static VALUE iodine_ws_close(VALUE self) {
|
53
|
-
void *ws = get_ws(self);
|
54
|
-
if (!ws) {
|
55
|
-
return Qfalse;
|
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
|
-
}
|
76
|
-
return self;
|
77
|
-
}
|
78
|
-
|
79
|
-
/**
|
80
|
-
* Writes data to the websocket.
|
81
|
-
*
|
82
|
-
* Returns `true` on success or `false if the websocket was closed or an error
|
83
|
-
* occurred.
|
84
|
-
*
|
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
|
-
*/
|
89
|
-
static VALUE iodine_ws_write(VALUE self, VALUE data) {
|
90
|
-
Check_Type(data, T_STRING);
|
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");
|
95
|
-
return Qfalse;
|
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;
|
120
|
-
}
|
121
|
-
|
122
|
-
/**
|
123
|
-
Returns the number of pending writes or -1 if the connection is closed
|
124
|
-
*/
|
125
|
-
static VALUE iodine_ws_has_pending(VALUE self) {
|
126
|
-
intptr_t uuid = get_uuid(self);
|
127
|
-
if (!uuid || sock_isclosed(uuid))
|
128
|
-
return INT2NUM(-1);
|
129
|
-
return SIZET2NUM(sock_pending(uuid));
|
130
|
-
}
|
131
|
-
|
132
|
-
/**
|
133
|
-
Returns true is the connection is open, false if it isn't.
|
134
|
-
*/
|
135
|
-
static VALUE iodine_ws_is_open(VALUE self) {
|
136
|
-
intptr_t uuid = get_uuid(self);
|
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
|
-
// }
|
150
|
-
|
151
|
-
/* *****************************************************************************
|
152
|
-
Websocket defer
|
153
|
-
***************************************************************************** */
|
154
|
-
|
155
|
-
static void iodine_perform_defer(intptr_t uuid, protocol_s *protocol,
|
156
|
-
void *arg) {
|
157
|
-
(void)(uuid);
|
158
|
-
VALUE obj = protocol->service == WEBSOCKET_ID_STR ? get_handler(protocol)
|
159
|
-
: (VALUE)(protocol + 1);
|
160
|
-
RubyCaller.call2((VALUE)arg, iodine_call_proc_id, 1, &obj);
|
161
|
-
Registry.remove((VALUE)arg);
|
162
|
-
}
|
163
|
-
|
164
|
-
static void iodine_defer_fallback(intptr_t uuid, void *arg) {
|
165
|
-
(void)(uuid);
|
166
|
-
Registry.remove((VALUE)arg);
|
167
|
-
}
|
168
|
-
|
169
|
-
/**
|
170
|
-
Schedules a block of code to execute at a later time, **if** the connection is
|
171
|
-
still open and while preventing concurent code from running for the same
|
172
|
-
connection object.
|
173
|
-
|
174
|
-
An optional `conn_id` argument can be passed along, so that the block of code
|
175
|
-
will run for the requested connection rather then this connection.
|
176
|
-
|
177
|
-
**Careful**: as this might cause this connection's object to run code
|
178
|
-
concurrently when data owned by this connection is accessed from within the
|
179
|
-
block of code.
|
180
|
-
|
181
|
-
On success returns the block, otherwise (connection invalid) returns `false`. A
|
182
|
-
sucessful event registration doesn't guaranty that the block will be called (the
|
183
|
-
connection might close between the event registration and the execution).
|
184
|
-
*/
|
185
|
-
static VALUE iodine_defer(int argc, VALUE *argv, VALUE self) {
|
186
|
-
intptr_t fd;
|
187
|
-
// check arguments.
|
188
|
-
if (argc > 1)
|
189
|
-
rb_raise(rb_eArgError, "this function expects no more then 1 (optional) "
|
190
|
-
"argument.");
|
191
|
-
else if (argc == 1) {
|
192
|
-
Check_Type(*argv, T_FIXNUM);
|
193
|
-
fd = FIX2LONG(*argv);
|
194
|
-
if (!sock_isvalid(fd))
|
195
|
-
return Qfalse;
|
196
|
-
} else
|
197
|
-
fd = iodine_get_fd(self);
|
198
|
-
if (!fd)
|
199
|
-
rb_raise(rb_eArgError, "This method requires a target connection.");
|
200
|
-
// requires a block to be passed
|
201
|
-
rb_need_block();
|
202
|
-
VALUE block = rb_block_proc();
|
203
|
-
if (block == Qnil)
|
204
|
-
return Qfalse;
|
205
|
-
Registry.add(block);
|
206
|
-
|
207
|
-
facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
|
208
|
-
.fallback = iodine_defer_fallback);
|
209
|
-
return block;
|
210
|
-
}
|
211
|
-
|
212
|
-
#if 0 /* deprecated */
|
213
|
-
/**
|
214
|
-
Schedules a block of code to run for the specified websocket at a later time,
|
215
|
-
(**if** the connection is open). The block will run within the connection's
|
216
|
-
lock, offering a fast concurrency synchronizing tool.
|
217
|
-
|
218
|
-
The block of code will receive the websocket's callback object. i.e.
|
219
|
-
|
220
|
-
Iodine::Websocket.defer(uuid) {|ws| ws.write "I'm doing this" }
|
221
|
-
|
222
|
-
On success returns the block, otherwise (connection invalid) returns `false`.
|
223
|
-
|
224
|
-
A sucessful event registration doesn't guaranty that the block will be called
|
225
|
-
(the connection might close between the event registration and the execution).
|
226
|
-
*/
|
227
|
-
static VALUE iodine_class_defer(VALUE self, VALUE ws_uuid) {
|
228
|
-
(void)(self);
|
229
|
-
intptr_t fd = FIX2LONG(ws_uuid);
|
230
|
-
if (!sock_isvalid(fd))
|
231
|
-
return Qfalse;
|
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
|
-
#endif
|
245
|
-
|
246
|
-
/* *****************************************************************************
|
247
|
-
Websocket Pub/Sub API
|
248
|
-
***************************************************************************** */
|
249
|
-
|
250
|
-
// clang-format off
|
251
|
-
/**
|
252
|
-
Subscribes the connection to a Pub/Sub channel.
|
253
|
-
|
254
|
-
The method accepts 1-2 arguments and an optional block. These are all valid ways
|
255
|
-
to call the method:
|
256
|
-
|
257
|
-
subscribe("my_stream") {|from, msg| p msg }
|
258
|
-
subscribe("my_stream", match: :redis) {|from, msg| p msg }
|
259
|
-
subscribe(to: "my_stream") {|from, msg| p msg }
|
260
|
-
subscribe to: "my_stream", match: :redis, handler: MyProc
|
261
|
-
|
262
|
-
The first argument must be either a String or a Hash.
|
263
|
-
|
264
|
-
The second, optional, argument must be a Hash (if given).
|
265
|
-
|
266
|
-
The options Hash supports the following possible keys (other keys are ignored,
|
267
|
-
all keys are Symbols):
|
268
|
-
|
269
|
-
: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.
|
270
|
-
|
271
|
-
:to :: The channel / subject to subscribe to.
|
272
|
-
|
273
|
-
:as :: valid for WebSocket connections only. can be either `:text` or `:binary`. `:text` is the default transport for pub/sub events.
|
274
|
-
|
275
|
-
Returns an {Iodine::PubSub::Subscription} object that answers to:
|
276
|
-
|
277
|
-
#.close :: closes the connection.
|
278
|
-
|
279
|
-
#.to_s :: returns the subscription's target (stream / channel / subject).
|
280
|
-
|
281
|
-
#.==(str) :: returns true if the string is an exact match for the target (even if the target itself is a pattern).
|
282
|
-
|
283
|
-
*/
|
284
|
-
static VALUE iodine_ws_subscribe(int argc, VALUE *argv, VALUE self) {
|
285
|
-
// clang-format on
|
286
|
-
ws_s *owner = get_ws(self);
|
287
|
-
return iodine_subscribe(argc, argv, owner,
|
288
|
-
(iodine_pubsub_type_e)iodine_get_cdata(self));
|
289
|
-
}
|
290
|
-
|
291
|
-
/* *****************************************************************************
|
292
|
-
WebSocket Callbacks
|
293
|
-
***************************************************************************** */
|
294
|
-
|
295
|
-
static void ws_on_open(ws_s *ws) {
|
296
|
-
VALUE handler = get_handler(ws);
|
297
|
-
if (!handler)
|
298
|
-
return;
|
299
|
-
set_uuid(handler, websocket_uuid(ws));
|
300
|
-
set_ws(handler, ws);
|
301
|
-
iodine_set_cdata(handler, (void *)IODINE_PUBSUB_WEBSOCKET);
|
302
|
-
RubyCaller.call(handler, iodine_on_open_func_id);
|
303
|
-
}
|
304
|
-
static void ws_on_close(intptr_t uuid, void *handler_) {
|
305
|
-
VALUE handler = (VALUE)handler_;
|
306
|
-
if (!handler) {
|
307
|
-
fprintf(stderr,
|
308
|
-
"ERROR: (iodine websockets) Closing a handlerless websocket?!\n");
|
309
|
-
return;
|
310
|
-
}
|
311
|
-
set_ws(handler, NULL);
|
312
|
-
set_uuid(handler, 0);
|
313
|
-
iodine_set_cdata(handler, (void *)IODINE_PUBSUB_GLOBAL);
|
314
|
-
RubyCaller.call(handler, iodine_on_close_func_id);
|
315
|
-
Registry.remove(handler);
|
316
|
-
(void)uuid;
|
317
|
-
}
|
318
|
-
static void ws_on_shutdown(ws_s *ws) {
|
319
|
-
VALUE handler = get_handler(ws);
|
320
|
-
if (!handler)
|
321
|
-
return;
|
322
|
-
RubyCaller.call(handler, iodine_on_shutdown_func_id);
|
323
|
-
}
|
324
|
-
static void ws_on_ready(ws_s *ws) {
|
325
|
-
VALUE handler = get_handler(ws);
|
326
|
-
if (!handler)
|
327
|
-
return;
|
328
|
-
RubyCaller.call(handler, iodine_on_drained_func_id);
|
329
|
-
}
|
330
|
-
|
331
|
-
struct ws_on_data_args_s {
|
332
|
-
ws_s *ws;
|
333
|
-
void *data;
|
334
|
-
size_t length;
|
335
|
-
uint8_t is_text;
|
336
|
-
};
|
337
|
-
static void *ws_on_data_inGIL(void *args_) {
|
338
|
-
struct ws_on_data_args_s *a = args_;
|
339
|
-
VALUE handler = get_handler(a->ws);
|
340
|
-
if (!handler) {
|
341
|
-
fprintf(stderr, "ERROR: iodine can't find Websocket handler!\n");
|
342
|
-
return NULL;
|
343
|
-
}
|
344
|
-
VALUE buffer = rb_str_new(a->data, a->length);
|
345
|
-
if (a->is_text)
|
346
|
-
rb_enc_associate(buffer, IodineUTF8Encoding);
|
347
|
-
else
|
348
|
-
rb_enc_associate(buffer, IodineBinaryEncoding);
|
349
|
-
rb_funcallv(handler, iodine_on_message_func_id, 1, &buffer);
|
350
|
-
return NULL;
|
351
|
-
}
|
352
|
-
static void ws_on_data(ws_s *ws, char *data, size_t length, uint8_t is_text) {
|
353
|
-
struct ws_on_data_args_s a = {
|
354
|
-
.ws = ws, .data = data, .length = length, .is_text = is_text};
|
355
|
-
RubyCaller.call_c(ws_on_data_inGIL, &a);
|
356
|
-
(void)(data);
|
357
|
-
}
|
358
|
-
|
359
|
-
//////////////
|
360
|
-
// Empty callbacks for default implementations.
|
361
|
-
|
362
|
-
/** Please implement your own callback for this event. */
|
363
|
-
static VALUE empty_func(VALUE self) {
|
364
|
-
(void)(self);
|
365
|
-
return Qnil;
|
366
|
-
}
|
367
|
-
|
368
|
-
/** Please implement your own callback for this event. */
|
369
|
-
static VALUE empty_func_drained(VALUE self) {
|
370
|
-
RubyCaller.call(self, iodine_on_ready_id);
|
371
|
-
(void)(self);
|
372
|
-
return Qnil;
|
373
|
-
}
|
374
|
-
|
375
|
-
/** DEPRECATED! Please override {on_drained} instead. */
|
376
|
-
static VALUE empty_func_on_ready(VALUE self) {
|
377
|
-
(void)(self);
|
378
|
-
return Qnil;
|
379
|
-
}
|
380
|
-
|
381
|
-
/* *****************************************************************************
|
382
|
-
SSE Callbacks
|
383
|
-
***************************************************************************** */
|
384
|
-
|
385
|
-
/**
|
386
|
-
* The (optional) on_open callback will be called once the EventSource
|
387
|
-
* connection is established.
|
388
|
-
*/
|
389
|
-
static void iodine_sse_on_open(http_sse_s *sse) {
|
390
|
-
VALUE handler = (VALUE)sse->udata;
|
391
|
-
if (!handler)
|
392
|
-
return;
|
393
|
-
set_uuid(handler, http_sse2uuid(sse));
|
394
|
-
set_ws(handler, sse);
|
395
|
-
iodine_set_cdata(handler, (void *)IODINE_PUBSUB_SSE);
|
396
|
-
RubyCaller.call(handler, iodine_on_open_func_id);
|
397
|
-
}
|
398
|
-
|
399
|
-
/**
|
400
|
-
* The (optional) on_ready callback will be after a the underlying socket's
|
401
|
-
* buffer changes it's state to empty.
|
402
|
-
*
|
403
|
-
* If the socket's buffer is never used, the callback is never called.
|
404
|
-
*/
|
405
|
-
static void iodine_sse_on_ready(http_sse_s *sse) {
|
406
|
-
VALUE handler = (VALUE)sse->udata;
|
407
|
-
if (!handler)
|
408
|
-
return;
|
409
|
-
RubyCaller.call(handler, iodine_on_drained_func_id);
|
410
|
-
}
|
411
|
-
|
412
|
-
/**
|
413
|
-
* The (optional) on_shutdown callback will be called if a connection is still
|
414
|
-
* open while the server is shutting down (called before `on_close`).
|
415
|
-
*/
|
416
|
-
static void iodine_sse_on_shutdown(http_sse_s *sse) {
|
417
|
-
VALUE handler = (VALUE)sse->udata;
|
418
|
-
if (!handler)
|
419
|
-
return;
|
420
|
-
RubyCaller.call(handler, iodine_on_shutdown_func_id);
|
421
|
-
}
|
422
|
-
/**
|
423
|
-
* The (optional) on_close callback will be called once a connection is
|
424
|
-
* terminated or failed to be established.
|
425
|
-
*
|
426
|
-
* The `uuid` is the connection's unique ID that can identify the Websocket. A
|
427
|
-
* value of `uuid == 0` indicates the Websocket connection wasn't established
|
428
|
-
* (an error occured).
|
429
|
-
*
|
430
|
-
* The `udata` is the user data as set during the upgrade or using the
|
431
|
-
* `websocket_udata_set` function.
|
432
|
-
*/
|
433
|
-
static void iodine_sse_on_close(http_sse_s *sse) {
|
434
|
-
VALUE handler = (VALUE)sse->udata;
|
435
|
-
if (!handler) {
|
436
|
-
fprintf(stderr,
|
437
|
-
"ERROR: (iodine websockets) Closing a handlerless websocket?!\n");
|
438
|
-
return;
|
439
|
-
}
|
440
|
-
set_ws(handler, NULL);
|
441
|
-
set_uuid(handler, 0);
|
442
|
-
iodine_set_cdata(handler, (void *)IODINE_PUBSUB_GLOBAL);
|
443
|
-
RubyCaller.call(handler, iodine_on_close_func_id);
|
444
|
-
Registry.remove(handler);
|
445
|
-
}
|
446
|
-
|
447
|
-
/* *****************************************************************************
|
448
|
-
Upgrading
|
449
|
-
***************************************************************************** */
|
450
|
-
|
451
|
-
static VALUE iodine_prep_ws_handler(VALUE handler) {
|
452
|
-
// make sure we have a valid handler, with the Websocket Protocol mixin.
|
453
|
-
if (handler == Qnil || handler == Qfalse || TYPE(handler) == T_FIXNUM ||
|
454
|
-
TYPE(handler) == T_STRING || TYPE(handler) == T_SYMBOL)
|
455
|
-
return Qnil;
|
456
|
-
if (TYPE(handler) == T_CLASS || TYPE(handler) == T_MODULE) {
|
457
|
-
// include the Protocol module
|
458
|
-
rb_include_module(handler, IodineWebsocket);
|
459
|
-
rb_extend_object(handler, IodineWebsocket);
|
460
|
-
handler = RubyCaller.call(handler, iodine_new_func_id);
|
461
|
-
if (handler == Qnil || handler == Qfalse)
|
462
|
-
return Qnil;
|
463
|
-
Registry.add(handler);
|
464
|
-
// check that we created a handler
|
465
|
-
} else {
|
466
|
-
Registry.add(handler);
|
467
|
-
// include the Protocol module in the object's class
|
468
|
-
VALUE p_class = rb_obj_class(handler);
|
469
|
-
rb_include_module(p_class, IodineWebsocket);
|
470
|
-
rb_extend_object(p_class, IodineWebsocket);
|
471
|
-
}
|
472
|
-
return handler;
|
473
|
-
}
|
474
|
-
|
475
|
-
void iodine_upgrade_websocket(http_s *h, VALUE handler) {
|
476
|
-
// add the handler to the registry
|
477
|
-
handler = iodine_prep_ws_handler(handler);
|
478
|
-
if (handler == Qnil)
|
479
|
-
goto failed;
|
480
|
-
// send upgrade response and set new protocol
|
481
|
-
http_upgrade2ws(.http = h, .udata = (void *)handler, .on_close = ws_on_close,
|
482
|
-
.on_open = ws_on_open, .on_shutdown = ws_on_shutdown,
|
483
|
-
.on_ready = ws_on_ready, .on_message = ws_on_data);
|
484
|
-
return;
|
485
|
-
failed:
|
486
|
-
http_send_error(h, 400);
|
487
|
-
return;
|
488
|
-
}
|
489
|
-
|
490
|
-
void iodine_upgrade_sse(http_s *h, VALUE handler) {
|
491
|
-
// add the handler to the registry
|
492
|
-
handler = iodine_prep_ws_handler(handler);
|
493
|
-
if (handler == Qnil)
|
494
|
-
goto failed;
|
495
|
-
// send upgrade response and set new protocol
|
496
|
-
http_upgrade2sse(h, .udata = (void *)handler, .on_open = iodine_sse_on_open,
|
497
|
-
.on_ready = iodine_sse_on_ready,
|
498
|
-
.on_shutdown = iodine_sse_on_shutdown,
|
499
|
-
.on_close = iodine_sse_on_close);
|
500
|
-
return;
|
501
|
-
failed:
|
502
|
-
http_send_error(h, 400);
|
503
|
-
return;
|
504
|
-
}
|
505
|
-
|
506
|
-
/* *****************************************************************************
|
507
|
-
Initialization
|
508
|
-
***************************************************************************** */
|
509
|
-
|
510
|
-
void Iodine_init_websocket(void) {
|
511
|
-
// get IDs and data that's used often
|
512
|
-
ws_var_id = rb_intern("iodine_ws_ptr"); // when upgrading
|
513
|
-
dup_func_id = rb_intern("dup"); // when upgrading
|
514
|
-
iodine_on_ready_id = rb_intern2("on_ready", 8);
|
515
|
-
|
516
|
-
// the Ruby websockets protocol class.
|
517
|
-
IodineWebsocket = rb_define_module_under(Iodine, "Websocket");
|
518
|
-
if (IodineWebsocket == Qfalse)
|
519
|
-
fprintf(stderr, "WTF?!\n"), exit(-1);
|
520
|
-
// // callbacks and handlers
|
521
|
-
rb_define_method(IodineWebsocket, "on_open", empty_func, 0);
|
522
|
-
|
523
|
-
// rb_define_method(IodineWebsocket, "on_message", empty_func_message, 1);
|
524
|
-
|
525
|
-
rb_define_method(IodineWebsocket, "on_shutdown", empty_func, 0);
|
526
|
-
rb_define_method(IodineWebsocket, "on_close", empty_func, 0);
|
527
|
-
rb_define_method(IodineWebsocket, "on_drained", empty_func_drained, 0);
|
528
|
-
rb_define_method(IodineWebsocket, "write", iodine_ws_write, 1);
|
529
|
-
rb_define_method(IodineWebsocket, "close", iodine_ws_close, 0);
|
530
|
-
|
531
|
-
/// Deprectaed!
|
532
|
-
rb_define_method(IodineWebsocket, "on_ready", empty_func_on_ready, 0);
|
533
|
-
|
534
|
-
// rb_define_method(IodineWebsocket, "_cid", iodine_ws_uuid, 0);
|
535
|
-
// rb_define_method(IodineWebsocket, "_sock", iodine_ws_uuid, 0);
|
536
|
-
|
537
|
-
rb_define_method(IodineWebsocket, "pending", iodine_ws_has_pending, 0);
|
538
|
-
rb_define_method(IodineWebsocket, "open?", iodine_ws_is_open, 0);
|
539
|
-
rb_define_method(IodineWebsocket, "defer", iodine_defer, -1);
|
540
|
-
|
541
|
-
// rb_define_method(IodineWebsocket, "each", iodine_ws_each, 0);
|
542
|
-
|
543
|
-
rb_define_method(IodineWebsocket, "subscribe", iodine_ws_subscribe, -1);
|
544
|
-
rb_define_method(IodineWebsocket, "publish", iodine_publish, -1);
|
545
|
-
|
546
|
-
// rb_define_singleton_method(IodineWebsocket, "defer", iodine_class_defer,
|
547
|
-
// 1);
|
548
|
-
|
549
|
-
rb_define_singleton_method(IodineWebsocket, "publish", iodine_publish, -1);
|
550
|
-
}
|