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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/iodine_pubsub.h
CHANGED
@@ -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
|
-
|
25
|
-
|
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
|
-
|
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
|
-
|
130
|
-
if (!ws
|
53
|
+
void *ws = get_ws(self);
|
54
|
+
if (!ws) {
|
131
55
|
return Qfalse;
|
132
|
-
|
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
|
143
|
-
*
|
144
|
-
*
|
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
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
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
|
-
|
127
|
+
if (!uuid || sock_isclosed(uuid))
|
128
|
+
return INT2NUM(-1);
|
129
|
+
return SIZET2NUM(sock_pending(uuid));
|
175
130
|
}
|
176
131
|
|
177
132
|
/**
|
178
|
-
Returns
|
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
|
135
|
+
static VALUE iodine_ws_is_open(VALUE self) {
|
185
136
|
intptr_t uuid = get_uuid(self);
|
186
|
-
|
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
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
217
|
+
The block of code will receive the websocket's callback object. i.e.
|
352
218
|
|
353
|
-
|
219
|
+
Iodine::Websocket.defer(uuid) {|ws| ws.write "I'm doing this" }
|
354
220
|
|
355
|
-
|
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
|
361
|
-
|
362
|
-
|
363
|
-
if (!
|
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
|
-
|
366
|
-
|
367
|
-
VALUE
|
368
|
-
if (
|
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
|
-
|
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
|
-
|
449
|
-
|
450
|
-
|
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
|
244
|
+
Websocket Pub/Sub API
|
471
245
|
***************************************************************************** */
|
472
246
|
|
473
|
-
//
|
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
|
-
|
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
|
-
|
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
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
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
|
-
|
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
|
-
|
549
|
-
the block of code.
|
261
|
+
The second, optional, argument must be a Hash (if given).
|
550
262
|
|
551
|
-
The
|
552
|
-
|
263
|
+
The options Hash supports the following possible keys (other keys are ignored,
|
264
|
+
all keys are Symbols):
|
553
265
|
|
554
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
591
|
-
//
|
592
|
-
|
593
|
-
|
594
|
-
|
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
|
-
|
603
|
-
|
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(
|
642
|
-
VALUE handler =
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
729
|
-
|
730
|
-
|
489
|
+
handler = iodine_prep_ws_handler(handler);
|
490
|
+
if (handler == Qnil)
|
491
|
+
goto failed;
|
731
492
|
// send upgrade response and set new protocol
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
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
|
-
|
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, "
|
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
|
-
|
775
|
-
rb_define_method(IodineWebsocket, "
|
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, "
|
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, "
|
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
|
}
|