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
@@ -0,0 +1,304 @@
|
|
1
|
+
#include "iodine.h"
|
2
|
+
|
3
|
+
#include "fio_ary.h"
|
4
|
+
#include "fio_json_parser.h"
|
5
|
+
#include "fio_mem.h"
|
6
|
+
#include "fiobj.h"
|
7
|
+
#include "rb-fiobj2rb.h"
|
8
|
+
#include "rb-registry.h"
|
9
|
+
|
10
|
+
static VALUE max_nesting;
|
11
|
+
static VALUE allow_nan;
|
12
|
+
static VALUE symbolize_names;
|
13
|
+
static VALUE create_additions;
|
14
|
+
static VALUE object_class;
|
15
|
+
static VALUE array_class;
|
16
|
+
|
17
|
+
/* *****************************************************************************
|
18
|
+
JSON Callacks - these must be implemented in the C file that uses the parser
|
19
|
+
***************************************************************************** */
|
20
|
+
|
21
|
+
/* *****************************************************************************
|
22
|
+
JSON Parser handling (practically copied from the FIOBJ library)
|
23
|
+
***************************************************************************** */
|
24
|
+
|
25
|
+
typedef struct {
|
26
|
+
json_parser_s p;
|
27
|
+
VALUE key;
|
28
|
+
VALUE top;
|
29
|
+
VALUE target;
|
30
|
+
fio_ary_s stack;
|
31
|
+
uint8_t is_hash;
|
32
|
+
uint8_t symbolize;
|
33
|
+
} iodine_json_parser_s;
|
34
|
+
|
35
|
+
static inline void iodine_json_add2parser(iodine_json_parser_s *p, VALUE o) {
|
36
|
+
if (p->top) {
|
37
|
+
if (p->is_hash) {
|
38
|
+
if (p->key) {
|
39
|
+
rb_hash_aset(p->top, p->key, o);
|
40
|
+
Registry.remove(p->key);
|
41
|
+
p->key = (VALUE)0;
|
42
|
+
} else {
|
43
|
+
// if (p->symbolize) {
|
44
|
+
// o = rb_to_symbol(o);
|
45
|
+
// }
|
46
|
+
p->key = o;
|
47
|
+
Registry.add(o);
|
48
|
+
}
|
49
|
+
} else {
|
50
|
+
rb_ary_push(p->top, o);
|
51
|
+
}
|
52
|
+
} else {
|
53
|
+
Registry.add(o);
|
54
|
+
p->top = o;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
/** a NULL object was detected */
|
59
|
+
static void fio_json_on_null(json_parser_s *p) {
|
60
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, Qnil);
|
61
|
+
}
|
62
|
+
/** a TRUE object was detected */
|
63
|
+
static void fio_json_on_true(json_parser_s *p) {
|
64
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, Qtrue);
|
65
|
+
}
|
66
|
+
/** a FALSE object was detected */
|
67
|
+
static void fio_json_on_false(json_parser_s *p) {
|
68
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, Qfalse);
|
69
|
+
}
|
70
|
+
/** a Numberl was detected (long long). */
|
71
|
+
static void fio_json_on_number(json_parser_s *p, long long i) {
|
72
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, LONG2NUM(i));
|
73
|
+
}
|
74
|
+
/** a Float was detected (double). */
|
75
|
+
static void fio_json_on_float(json_parser_s *p, double f) {
|
76
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, DBL2NUM(f));
|
77
|
+
}
|
78
|
+
/** a String was detected (int / float). update `pos` to point at ending */
|
79
|
+
static void fio_json_on_string(json_parser_s *p, void *start, size_t length) {
|
80
|
+
/* Ruby overhead for a rb_str_buf_new is very high. Double copy is faster. */
|
81
|
+
char *tmp = fio_malloc(length);
|
82
|
+
size_t new_len = fio_json_unescape_str(tmp, start, length);
|
83
|
+
VALUE buf;
|
84
|
+
if (((iodine_json_parser_s *)p)->symbolize &&
|
85
|
+
((iodine_json_parser_s *)p)->is_hash &&
|
86
|
+
!((iodine_json_parser_s *)p)->key) {
|
87
|
+
ID id = rb_intern2(tmp, new_len);
|
88
|
+
buf = rb_id2sym(id);
|
89
|
+
} else {
|
90
|
+
buf = rb_str_new(tmp, new_len);
|
91
|
+
}
|
92
|
+
iodine_json_add2parser((iodine_json_parser_s *)p, buf);
|
93
|
+
fio_free(tmp);
|
94
|
+
}
|
95
|
+
/** a dictionary object was detected, should return 0 unless error occurred. */
|
96
|
+
static int fio_json_on_start_object(json_parser_s *p) {
|
97
|
+
iodine_json_parser_s *pr = (iodine_json_parser_s *)p;
|
98
|
+
if (pr->target) {
|
99
|
+
/* push NULL, don't free the objects */
|
100
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
101
|
+
pr->top = pr->target;
|
102
|
+
pr->target = 0;
|
103
|
+
} else {
|
104
|
+
VALUE h = rb_hash_new();
|
105
|
+
iodine_json_add2parser(pr, h);
|
106
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
107
|
+
pr->top = h;
|
108
|
+
}
|
109
|
+
pr->is_hash = 1;
|
110
|
+
return 0;
|
111
|
+
}
|
112
|
+
/** a dictionary object closure detected */
|
113
|
+
static void fio_json_on_end_object(json_parser_s *p) {
|
114
|
+
iodine_json_parser_s *pr = (iodine_json_parser_s *)p;
|
115
|
+
if (pr->key) {
|
116
|
+
fprintf(stderr, "WARNING: (JSON parsing) malformed JSON, "
|
117
|
+
"ignoring dangling Hash key.\n");
|
118
|
+
Registry.remove(pr->key);
|
119
|
+
pr->key = (VALUE)0;
|
120
|
+
}
|
121
|
+
pr->top = (VALUE)fio_ary_pop(&pr->stack);
|
122
|
+
pr->is_hash = (TYPE(pr->top) == T_HASH);
|
123
|
+
}
|
124
|
+
/** an array object was detected */
|
125
|
+
static int fio_json_on_start_array(json_parser_s *p) {
|
126
|
+
iodine_json_parser_s *pr = (iodine_json_parser_s *)p;
|
127
|
+
if (pr->target)
|
128
|
+
return -1;
|
129
|
+
VALUE ary = rb_ary_new();
|
130
|
+
iodine_json_add2parser(pr, ary);
|
131
|
+
fio_ary_push(&pr->stack, (void *)pr->top);
|
132
|
+
pr->top = ary;
|
133
|
+
pr->is_hash = 0;
|
134
|
+
return 0;
|
135
|
+
}
|
136
|
+
/** an array closure was detected */
|
137
|
+
static void fio_json_on_end_array(json_parser_s *p) {
|
138
|
+
iodine_json_parser_s *pr = (iodine_json_parser_s *)p;
|
139
|
+
pr->top = (VALUE)fio_ary_pop(&pr->stack);
|
140
|
+
pr->is_hash = (TYPE(pr->top) == T_HASH);
|
141
|
+
}
|
142
|
+
/** the JSON parsing is complete */
|
143
|
+
static void fio_json_on_json(json_parser_s *p) { (void)p; /* do nothing */ }
|
144
|
+
/** the JSON parsing is complete */
|
145
|
+
static void fio_json_on_error(json_parser_s *p) {
|
146
|
+
iodine_json_parser_s *pr = (iodine_json_parser_s *)p;
|
147
|
+
#if DEBUG
|
148
|
+
fprintf(stderr, "ERROR: JSON on error called.\n");
|
149
|
+
#endif
|
150
|
+
Registry.remove((VALUE)fio_ary_index(&pr->stack, 0));
|
151
|
+
Registry.remove(pr->key);
|
152
|
+
fio_ary_free(&pr->stack);
|
153
|
+
*pr = (iodine_json_parser_s){.top = 0};
|
154
|
+
}
|
155
|
+
|
156
|
+
/* *****************************************************************************
|
157
|
+
Iodine JSON Implementation
|
158
|
+
***************************************************************************** */
|
159
|
+
|
160
|
+
static inline VALUE iodine_json_convert(VALUE str, fiobj2rb_settings_s s) {
|
161
|
+
|
162
|
+
iodine_json_parser_s p = {.top = 0, .symbolize = s.str2sym};
|
163
|
+
size_t consumed = fio_json_parse(&p.p, RSTRING_PTR(str), RSTRING_LEN(str));
|
164
|
+
if (!consumed || p.p.depth) {
|
165
|
+
Registry.remove((VALUE)fio_ary_index(&p.stack, 0));
|
166
|
+
p.top = FIOBJ_INVALID;
|
167
|
+
}
|
168
|
+
fio_ary_free(&p.stack);
|
169
|
+
if (p.key) {
|
170
|
+
Registry.remove((VALUE)p.key);
|
171
|
+
}
|
172
|
+
if (!p.top) {
|
173
|
+
rb_raise(rb_eEncodingError, "Malformed JSON format.");
|
174
|
+
}
|
175
|
+
Registry.remove(p.top);
|
176
|
+
return p.top;
|
177
|
+
}
|
178
|
+
|
179
|
+
// static inline VALUE iodine_json_convert2(VALUE str, fiobj2rb_settings_s s) {
|
180
|
+
// FIOBJ json;
|
181
|
+
// if (!fiobj_json2obj(&json, RSTRING_PTR(str), RSTRING_LEN(str)) || !json) {
|
182
|
+
// rb_raise(rb_eRuntimeError, "JSON parsing failed. Not JSON?");
|
183
|
+
// return Qnil;
|
184
|
+
// }
|
185
|
+
// VALUE ret = fiobj2rb_deep(json, s.str2sym);
|
186
|
+
// fiobj_free(json);
|
187
|
+
// return ret;
|
188
|
+
// }
|
189
|
+
|
190
|
+
static inline void iodine_json_update_settings(VALUE h,
|
191
|
+
fiobj2rb_settings_s *s) {
|
192
|
+
VALUE tmp;
|
193
|
+
if (rb_hash_aref(h, max_nesting) != Qnil)
|
194
|
+
fprintf(stderr,
|
195
|
+
"WARNING: max_nesting ignored on this JSON implementation.\n");
|
196
|
+
if (rb_hash_aref(h, allow_nan) != Qnil)
|
197
|
+
fprintf(stderr, "WARNING: allow_nan ignored on this JSON implementation. "
|
198
|
+
"NaN always allowed.\n");
|
199
|
+
if (rb_hash_aref(h, create_additions) != Qnil)
|
200
|
+
fprintf(stderr,
|
201
|
+
"WARNING: create_additions ignored on this JSON implementation.\n");
|
202
|
+
if (rb_hash_aref(h, object_class) != Qnil)
|
203
|
+
fprintf(stderr,
|
204
|
+
"WARNING: object_class ignored on this JSON implementation.\n");
|
205
|
+
if (rb_hash_aref(h, array_class) != Qnil)
|
206
|
+
fprintf(stderr,
|
207
|
+
"WARNING: array_class ignored on this JSON implementation.\n");
|
208
|
+
if ((tmp = rb_hash_aref(h, symbolize_names)) != Qnil) {
|
209
|
+
if (tmp == Qtrue)
|
210
|
+
s->str2sym = 1;
|
211
|
+
else if (tmp == Qfalse)
|
212
|
+
s->str2sym = 0;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
Parse a JSON string using the iodine lenient parser (it's also faster).
|
218
|
+
*/
|
219
|
+
static VALUE iodine_json_parse(int argc, VALUE *argv, VALUE self) {
|
220
|
+
fiobj2rb_settings_s s = {.str2sym = 0};
|
221
|
+
if (argc > 2)
|
222
|
+
rb_raise(rb_eTypeError, "function requires supports up to two arguments.");
|
223
|
+
if (argc == 2) {
|
224
|
+
Check_Type(argv[1], T_HASH);
|
225
|
+
iodine_json_update_settings(argv[1], &s);
|
226
|
+
}
|
227
|
+
if (argc >= 1)
|
228
|
+
Check_Type(argv[0], T_STRING);
|
229
|
+
else
|
230
|
+
rb_raise(rb_eTypeError, "function requires at least one argument.");
|
231
|
+
return iodine_json_convert(argv[0], s);
|
232
|
+
(void)self;
|
233
|
+
}
|
234
|
+
|
235
|
+
/**
|
236
|
+
Parse a JSON string using the iodine lenient parser with a default Symbol
|
237
|
+
rather than String key (this is often faster than the regular
|
238
|
+
{Iodine::JSON.parse} function).
|
239
|
+
*/
|
240
|
+
static VALUE iodine_json_parse_bang(int argc, VALUE *argv, VALUE self) {
|
241
|
+
fiobj2rb_settings_s s = {.str2sym = 0};
|
242
|
+
if (argc > 2)
|
243
|
+
rb_raise(rb_eTypeError, "function requires supports up to two arguments.");
|
244
|
+
if (argc == 2) {
|
245
|
+
Check_Type(argv[1], T_HASH);
|
246
|
+
iodine_json_update_settings(argv[1], &s);
|
247
|
+
}
|
248
|
+
if (argc >= 1)
|
249
|
+
Check_Type(argv[0], T_STRING);
|
250
|
+
else
|
251
|
+
rb_raise(rb_eTypeError, "function requires at least one argument.");
|
252
|
+
return iodine_json_convert(argv[0], s);
|
253
|
+
(void)self;
|
254
|
+
}
|
255
|
+
|
256
|
+
void Iodine_init_json(void) {
|
257
|
+
/**
|
258
|
+
Iodine::JSON offers a fast(er) JSON parser that is also lenient and supports
|
259
|
+
some JSON extensions such as Hex number recognition and comments.
|
260
|
+
|
261
|
+
You can test the parser using:
|
262
|
+
|
263
|
+
JSON_FILENAME="foo.json"
|
264
|
+
|
265
|
+
require 'json'
|
266
|
+
require 'iodine'
|
267
|
+
TIMES = 100
|
268
|
+
STR = IO.binread(JSON_FILENAME); nil
|
269
|
+
|
270
|
+
JSON.parse(STR) == Iodine::JSON.parse(STR) # => true
|
271
|
+
JSON.parse(STR,
|
272
|
+
symbolize_names: true) == Iodine::JSON.parse(STR,
|
273
|
+
symbolize_names: true) # => true
|
274
|
+
JSON.parse!(STR) == Iodine::JSON.parse!(STR) # => true/false (unknown)
|
275
|
+
|
276
|
+
# warm-up
|
277
|
+
TIMES.times { JSON.parse STR }
|
278
|
+
TIMES.times { Iodine::JSON.parse STR }
|
279
|
+
|
280
|
+
Benchmark.bm do |b|
|
281
|
+
sys = b.report("system") { TIMES.times { JSON.parse STR } }
|
282
|
+
sys_sym = b.report("system sym") { TIMES.times { JSON.parse STR,
|
283
|
+
symbolize_names: true } }
|
284
|
+
iodine = b.report("iodine") { TIMES.times { Iodine::JSON.parse STR } }
|
285
|
+
iodine_sym = b.report("iodine sym") do
|
286
|
+
TIMES.times { Iodine::JSON.parse STR,
|
287
|
+
symbolize_names: true }
|
288
|
+
end
|
289
|
+
puts "System / Iodine: #{sys/iodine}"
|
290
|
+
puts "System-sym/Iodine-sym: #{sys_sym/iodine_sym}"
|
291
|
+
end; nil
|
292
|
+
|
293
|
+
|
294
|
+
*/
|
295
|
+
VALUE tmp = rb_define_module_under(Iodine, "JSON");
|
296
|
+
max_nesting = ID2SYM(rb_intern("max_nesting"));
|
297
|
+
allow_nan = ID2SYM(rb_intern("allow_nan"));
|
298
|
+
symbolize_names = ID2SYM(rb_intern("symbolize_names"));
|
299
|
+
create_additions = ID2SYM(rb_intern("create_additions"));
|
300
|
+
object_class = ID2SYM(rb_intern("object_class"));
|
301
|
+
array_class = ID2SYM(rb_intern("array_class"));
|
302
|
+
rb_define_module_function(tmp, "parse", iodine_json_parse, -1);
|
303
|
+
rb_define_module_function(tmp, "parse!", iodine_json_parse_bang, -1);
|
304
|
+
}
|
@@ -5,9 +5,16 @@ License: MIT
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
7
|
#include "iodine_protocol.h"
|
8
|
+
#include "iodine_pubsub.h"
|
9
|
+
|
10
|
+
#include "fio_llist.h"
|
11
|
+
#include "fiobj4sock.h"
|
12
|
+
#include "pubsub.h"
|
8
13
|
|
9
14
|
#include <ruby/io.h>
|
10
15
|
|
16
|
+
static ID iodine_close_func_id;
|
17
|
+
|
11
18
|
/* *****************************************************************************
|
12
19
|
The protocol object
|
13
20
|
***************************************************************************** */
|
@@ -15,6 +22,7 @@ The protocol object
|
|
15
22
|
typedef struct {
|
16
23
|
protocol_s protocol;
|
17
24
|
VALUE handler;
|
25
|
+
fio_ls_s subscriptions;
|
18
26
|
} iodine_protocol_s;
|
19
27
|
|
20
28
|
VALUE IodineProtocol;
|
@@ -30,12 +38,15 @@ static void iodine_clear_task(intptr_t origin, void *block_) {
|
|
30
38
|
(void)origin;
|
31
39
|
}
|
32
40
|
|
33
|
-
static void iodine_perform_task(intptr_t uuid, protocol_s *pr, void *block_)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
41
|
+
// static void iodine_perform_task(intptr_t uuid, protocol_s *pr, void *block_)
|
42
|
+
// {
|
43
|
+
// if (pr->service == iodine_protocol_service) {
|
44
|
+
// RubyCaller.call2((VALUE)block_, iodine_call_proc_id, 1, (VALUE *)(pr +
|
45
|
+
// 1));
|
46
|
+
// }
|
47
|
+
// (void)uuid;
|
48
|
+
// }
|
49
|
+
|
39
50
|
static void iodine_perform_task_and_free(intptr_t uuid, protocol_s *pr,
|
40
51
|
void *block_) {
|
41
52
|
if (pr->service == iodine_protocol_service) {
|
@@ -65,6 +76,20 @@ static VALUE not_implemented(VALUE self) {
|
|
65
76
|
return Qnil;
|
66
77
|
}
|
67
78
|
|
79
|
+
/** DEPRECATED! Please override {on_drained} instead. */
|
80
|
+
static VALUE not_implemented_on_ready(VALUE self, VALUE data) {
|
81
|
+
(void)(self);
|
82
|
+
(void)(data);
|
83
|
+
return Qnil;
|
84
|
+
}
|
85
|
+
|
86
|
+
/** Override this callback to handle the event. */
|
87
|
+
static VALUE not_implemented_drained(VALUE self) {
|
88
|
+
RubyCaller.call(self, rb_intern2("on_ready", 8));
|
89
|
+
(void)(self);
|
90
|
+
return Qnil;
|
91
|
+
}
|
92
|
+
|
68
93
|
/** Override this callback to handle the event. */
|
69
94
|
static VALUE not_implemented2(VALUE self, VALUE data) {
|
70
95
|
(void)(self);
|
@@ -95,42 +120,62 @@ static VALUE default_on_data(VALUE self) {
|
|
95
120
|
}
|
96
121
|
|
97
122
|
/* *****************************************************************************
|
98
|
-
|
123
|
+
Pub/Sub functions
|
99
124
|
***************************************************************************** */
|
100
125
|
|
101
|
-
|
102
|
-
static VALUE dyn_count(VALUE self) {
|
103
|
-
size_t count = facil_count(iodine_protocol_service);
|
104
|
-
return ULL2NUM(count);
|
105
|
-
(void)self;
|
106
|
-
}
|
107
|
-
|
126
|
+
// clang-format off
|
108
127
|
/**
|
109
|
-
|
128
|
+
Subscribes the connection to a Pub/Sub channel.
|
129
|
+
|
130
|
+
Since this connection's data packaging is unknown, a block (or handler) is required to handle pub/sub events.
|
131
|
+
|
132
|
+
The method accepts 1-2 arguments and an optional block. These are all valid ways
|
133
|
+
to call the method:
|
134
|
+
|
135
|
+
subscribe("my_stream") {|from, msg| p msg }
|
136
|
+
subscribe("my_stream", match: :redis) {|from, msg| p msg }
|
137
|
+
subscribe(to: "my_stream") {|from, msg| p msg }
|
138
|
+
subscribe to: "my_stream", match: :redis, handler: MyProc
|
110
139
|
|
111
|
-
|
140
|
+
The first argument must be either a String or a Hash.
|
112
141
|
|
113
|
-
|
142
|
+
The second, optional, argument must be a Hash (if given).
|
114
143
|
|
115
|
-
|
144
|
+
The options Hash supports the following possible keys (other keys are ignored, all keys are Symbols):
|
145
|
+
|
146
|
+
: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.
|
147
|
+
|
148
|
+
:to :: The channel / subject to subscribe to.
|
149
|
+
|
150
|
+
:handler :: and Proc like object, must answer to `call(from, msg)`.
|
151
|
+
|
152
|
+
Returns an {Iodine::PubSub::Subscription} object that answers to:
|
153
|
+
|
154
|
+
close :: closes the connection.
|
155
|
+
to_s :: returns the subscription's target (stream / channel / subject).
|
156
|
+
==(str) :: returns true if the string is an exact match for the target (even if the target itself is a pattern).
|
116
157
|
|
117
158
|
*/
|
118
|
-
static VALUE
|
119
|
-
|
120
|
-
|
121
|
-
if (
|
159
|
+
static VALUE iodine_proto_subscribe(int argc, VALUE *argv, VALUE self) {
|
160
|
+
// clang-format on
|
161
|
+
intptr_t uuid = iodine_get_fd(self);
|
162
|
+
if (!uuid || (VALUE)uuid == Qnil || uuid < 0)
|
163
|
+
return Qfalse;
|
164
|
+
VALUE sub = iodine_subscribe(argc, argv, NULL, IODINE_PUBSUB_GLOBAL);
|
165
|
+
if (sub == Qnil || sub == Qfalse)
|
122
166
|
return Qfalse;
|
123
|
-
|
124
|
-
if (!origin)
|
125
|
-
origin = -1;
|
167
|
+
Registry.add(sub);
|
126
168
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
return block;
|
169
|
+
iodine_protocol_s *pr = iodine_get_cdata(self);
|
170
|
+
|
171
|
+
fio_ls_push(&pr->subscriptions, (void *)sub);
|
172
|
+
return sub;
|
132
173
|
}
|
133
174
|
|
175
|
+
/* *****************************************************************************
|
176
|
+
Published functions
|
177
|
+
***************************************************************************** */
|
178
|
+
|
134
179
|
/**
|
135
180
|
Reads up to `n` bytes from the network connection.
|
136
181
|
The number of bytes to be read (n) is:
|
@@ -222,7 +267,7 @@ static VALUE dyn_write_move(VALUE self, VALUE data) {
|
|
222
267
|
Registry.add(data);
|
223
268
|
intptr_t fd = iodine_get_fd(self);
|
224
269
|
if (sock_write2(.uuid = fd, .buffer = RSTRING_PTR(data),
|
225
|
-
.length = RSTRING_LEN(data),
|
270
|
+
.length = RSTRING_LEN(data),
|
226
271
|
.dealloc = (void (*)(void *))Registry.remove))
|
227
272
|
return Qfalse;
|
228
273
|
return self;
|
@@ -352,7 +397,7 @@ static VALUE dyn_defer(int argc, VALUE *argv, VALUE self) {
|
|
352
397
|
return Qfalse;
|
353
398
|
Registry.add(block);
|
354
399
|
facil_defer(.uuid = fd, .task = iodine_perform_task_and_free,
|
355
|
-
.
|
400
|
+
.type = FIO_PR_LOCK_TASK, .arg = (void *)block,
|
356
401
|
.fallback = iodine_clear_task);
|
357
402
|
return block;
|
358
403
|
}
|
@@ -370,7 +415,7 @@ static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
|
|
370
415
|
/** called when the socket is ready to be written to. */
|
371
416
|
static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
|
372
417
|
(void)(fduuid);
|
373
|
-
RubyCaller.call(dyn_prot(protocol)->handler,
|
418
|
+
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_drained_func_id);
|
374
419
|
}
|
375
420
|
/** called when the server is shutting down,
|
376
421
|
* but before closing the connection. */
|
@@ -378,11 +423,23 @@ static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
|
|
378
423
|
(void)(fduuid);
|
379
424
|
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_shutdown_func_id);
|
380
425
|
}
|
426
|
+
|
427
|
+
static void *clear_subscriptions_inGVL(void *pr_) {
|
428
|
+
iodine_protocol_s *pr = pr_;
|
429
|
+
while (fio_ls_any(&pr->subscriptions)) {
|
430
|
+
RubyCaller.call((VALUE)fio_ls_pop(&pr->subscriptions),
|
431
|
+
iodine_close_func_id);
|
432
|
+
}
|
433
|
+
return NULL;
|
434
|
+
}
|
435
|
+
|
381
436
|
/** called when the connection was closed, but will not run concurrently */
|
382
437
|
static void dyn_protocol_on_close(intptr_t uuid, protocol_s *protocol) {
|
383
438
|
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_close_func_id);
|
384
439
|
iodine_set_fd(dyn_prot(protocol)->handler, 0);
|
440
|
+
iodine_set_cdata(dyn_prot(protocol)->handler, NULL);
|
385
441
|
Registry.remove(dyn_prot(protocol)->handler);
|
442
|
+
RubyCaller.call_c(clear_subscriptions_inGVL, protocol);
|
386
443
|
free(protocol);
|
387
444
|
(void)uuid;
|
388
445
|
}
|
@@ -401,7 +458,6 @@ Connection management API
|
|
401
458
|
static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
|
402
459
|
uint8_t timeout) {
|
403
460
|
Registry.add(handler);
|
404
|
-
iodine_set_fd(handler, fduuid);
|
405
461
|
iodine_protocol_s *protocol = malloc(sizeof(*protocol));
|
406
462
|
if (protocol == NULL) {
|
407
463
|
Registry.remove(handler);
|
@@ -416,16 +472,23 @@ static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
|
|
416
472
|
.protocol.ping = dyn_protocol_ping,
|
417
473
|
.protocol.service = iodine_protocol_service,
|
418
474
|
.handler = handler,
|
475
|
+
.subscriptions = FIO_LS_INIT(protocol->subscriptions),
|
419
476
|
};
|
477
|
+
iodine_set_fd(handler, fduuid);
|
478
|
+
iodine_set_cdata(handler, protocol);
|
420
479
|
RubyCaller.call(handler, iodine_on_open_func_id);
|
421
480
|
return &protocol->protocol;
|
422
481
|
}
|
423
482
|
|
424
|
-
static
|
483
|
+
static void on_open_dyn_protocol(intptr_t fduuid, void *udata) {
|
425
484
|
VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
|
426
485
|
uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
427
486
|
VALUE handler = RubyCaller.call((VALUE)udata, iodine_new_func_id);
|
428
|
-
|
487
|
+
if (handler == Qnil) {
|
488
|
+
sock_close(fduuid);
|
489
|
+
return;
|
490
|
+
}
|
491
|
+
facil_attach(fduuid, dyn_set_protocol(fduuid, handler, timeout));
|
429
492
|
}
|
430
493
|
|
431
494
|
/** Sets up a listening socket. Conncetions received at the assigned port will
|
@@ -483,12 +546,12 @@ VALUE dyn_switch_prot(VALUE self, VALUE handler) {
|
|
483
546
|
return handler;
|
484
547
|
}
|
485
548
|
|
486
|
-
static
|
549
|
+
static void on_open_dyn_protocol_instance(intptr_t fduuid, void *udata) {
|
487
550
|
VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
|
488
551
|
uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
489
552
|
protocol_s *pr = dyn_set_protocol(fduuid, (VALUE)udata, timeout);
|
490
553
|
Registry.remove((VALUE)udata);
|
491
|
-
|
554
|
+
facil_attach(fduuid, pr);
|
492
555
|
}
|
493
556
|
|
494
557
|
/**
|
@@ -587,9 +650,9 @@ Library Initialization
|
|
587
650
|
// between Lib-Server and Ruby.
|
588
651
|
void Iodine_init_protocol(void) {
|
589
652
|
|
653
|
+
iodine_close_func_id = rb_intern("close");
|
654
|
+
|
590
655
|
/* add Iodine module functions */
|
591
|
-
// rb_define_module_function(Iodine, "defer", dyn_defer, -1);
|
592
|
-
// rb_define_module_function(Iodine, "each", dyn_run_each, 0);
|
593
656
|
rb_define_module_function(Iodine, "listen", iodine_listen, 2);
|
594
657
|
rb_define_module_function(Iodine, "connect", iodine_connect, 3);
|
595
658
|
rb_define_module_function(Iodine, "attach_io", iodine_attach_io, 2);
|
@@ -601,20 +664,17 @@ void Iodine_init_protocol(void) {
|
|
601
664
|
rb_define_method(IodineProtocol, "on_close", not_implemented, 0);
|
602
665
|
rb_define_method(IodineProtocol, "on_message", not_implemented2, 1);
|
603
666
|
rb_define_method(IodineProtocol, "on_data", default_on_data, 0);
|
604
|
-
rb_define_method(IodineProtocol, "on_ready",
|
667
|
+
rb_define_method(IodineProtocol, "on_ready", not_implemented_on_ready, 0);
|
668
|
+
rb_define_method(IodineProtocol, "on_drained", not_implemented_drained, 0);
|
605
669
|
rb_define_method(IodineProtocol, "on_shutdown", not_implemented, 0);
|
606
670
|
rb_define_method(IodineProtocol, "ping", not_implemented_ping, 0);
|
607
671
|
|
608
672
|
/* Add module functions */
|
609
673
|
rb_define_singleton_method(IodineProtocol, "defer", dyn_defer, -1);
|
610
|
-
rb_define_singleton_method(IodineProtocol, "count", dyn_count, 0);
|
611
|
-
rb_define_singleton_method(IodineProtocol, "each", dyn_run_each, 0);
|
612
674
|
|
613
675
|
/* Add module instance methods */
|
614
|
-
// rb_define_method(IodineProtocol, "each", dyn_run_each, 0);
|
615
676
|
rb_define_method(IodineProtocol, "open?", dyn_is_open, 0);
|
616
677
|
rb_define_method(IodineProtocol, "conn_id", dyn_uuid, 0);
|
617
|
-
rb_define_method(IodineProtocol, "count", dyn_count, 0);
|
618
678
|
rb_define_method(IodineProtocol, "read", dyn_read, -1);
|
619
679
|
rb_define_method(IodineProtocol, "write", dyn_write, 1);
|
620
680
|
rb_define_method(IodineProtocol, "write!", dyn_write_move, 1);
|
@@ -624,4 +684,6 @@ void Iodine_init_protocol(void) {
|
|
624
684
|
rb_define_method(IodineProtocol, "switch_protocol", dyn_switch_prot, 1);
|
625
685
|
rb_define_method(IodineProtocol, "timeout=", dyn_set_timeout, 1);
|
626
686
|
rb_define_method(IodineProtocol, "timeout", dyn_get_timeout, 0);
|
687
|
+
rb_define_method(IodineProtocol, "subscribe", iodine_proto_subscribe, -1);
|
688
|
+
rb_define_method(IodineProtocol, "publish", iodine_publish, -1);
|
627
689
|
}
|