isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,957 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #define FIO_INCLUDE_LINKED_LIST
9
+ #define FIO_INCLUDE_STR
10
+ // #define DEBUG 1
11
+ #include <fio.h>
12
+
13
+ #include <fiobj.h>
14
+
15
+ #include <redis_engine.h>
16
+ #include <resp_parser.h>
17
+
18
+ #define REDIS_READ_BUFFER 8192
19
+ /* *****************************************************************************
20
+ The Redis Engine and Callbacks Object
21
+ ***************************************************************************** */
22
+
23
+ typedef struct {
24
+ fio_pubsub_engine_s en;
25
+ struct redis_engine_internal_s {
26
+ fio_protocol_s protocol;
27
+ intptr_t uuid;
28
+ resp_parser_s parser;
29
+ void (*on_message)(struct redis_engine_internal_s *parser, FIOBJ msg);
30
+ FIOBJ str;
31
+ FIOBJ ary;
32
+ uint32_t ary_count;
33
+ uint16_t buf_pos;
34
+ uint16_t nesting;
35
+ } pub_data, sub_data;
36
+ subscription_s *publication_forwarder;
37
+ subscription_s *cmd_forwarder;
38
+ subscription_s *cmd_reply;
39
+ char *address;
40
+ char *port;
41
+ char *auth;
42
+ FIOBJ last_ch;
43
+ size_t auth_len;
44
+ size_t ref;
45
+ fio_ls_embd_s queue;
46
+ fio_lock_i lock;
47
+ fio_lock_i lock_connection;
48
+ uint8_t ping_int;
49
+ volatile uint8_t pub_sent;
50
+ volatile uint8_t flag;
51
+ uint8_t buf[];
52
+ } redis_engine_s;
53
+
54
+ typedef struct {
55
+ fio_ls_embd_s node;
56
+ void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply, void *udata);
57
+ void *udata;
58
+ size_t cmd_len;
59
+ uint8_t cmd[];
60
+ } redis_commands_s;
61
+
62
+ /** converts from a publishing protocol to an `redis_engine_s`. */
63
+ #define pub2redis(pr) FIO_LS_EMBD_OBJ(redis_engine_s, pub_data, (pr))
64
+ /** converts from a subscribing protocol to an `redis_engine_s`. */
65
+ #define sub2redis(pr) FIO_LS_EMBD_OBJ(redis_engine_s, sub_data, (pr))
66
+
67
+ /** converts from a `resp_parser_s` to the internal data structure. */
68
+ #define parser2data(prsr) \
69
+ FIO_LS_EMBD_OBJ(struct redis_engine_internal_s, parser, (prsr))
70
+
71
+ /* releases any resources used by an internal engine*/
72
+ static inline void redis_internal_reset(struct redis_engine_internal_s *i) {
73
+ i->buf_pos = 0;
74
+ i->parser = (resp_parser_s){.obj_countdown = 0, .expecting = 0};
75
+ fiobj_free((FIOBJ)fio_ct_if(i->ary == FIOBJ_INVALID, (uintptr_t)i->str,
76
+ (uintptr_t)i->ary));
77
+ i->str = FIOBJ_INVALID;
78
+ i->ary = FIOBJ_INVALID;
79
+ i->ary_count = 0;
80
+ i->nesting = 0;
81
+ i->uuid = -1;
82
+ }
83
+
84
+ /** cleans up and frees the engine data. */
85
+ static inline void redis_free(redis_engine_s *r) {
86
+ if (fio_atomic_sub(&r->ref, 1))
87
+ return;
88
+ FIO_LOG_DEBUG("freeing redis engine for %s:%s", r->address, r->port);
89
+ redis_internal_reset(&r->pub_data);
90
+ redis_internal_reset(&r->sub_data);
91
+ fiobj_free(r->last_ch);
92
+ while (fio_ls_embd_any(&r->queue)) {
93
+ fio_free(
94
+ FIO_LS_EMBD_OBJ(redis_commands_s, node, fio_ls_embd_pop(&r->queue)));
95
+ }
96
+ fio_unsubscribe(r->publication_forwarder);
97
+ r->publication_forwarder = NULL;
98
+ fio_unsubscribe(r->cmd_forwarder);
99
+ r->cmd_forwarder = NULL;
100
+ fio_unsubscribe(r->cmd_reply);
101
+ r->cmd_reply = NULL;
102
+ fio_free(r);
103
+ }
104
+
105
+ /* *****************************************************************************
106
+ Simple RESP formatting
107
+ ***************************************************************************** */
108
+
109
+ inline static void fiobj2resp___internal(FIOBJ dest, FIOBJ obj) {
110
+ fio_str_info_s s;
111
+ switch (FIOBJ_TYPE(obj)) {
112
+ case FIOBJ_T_NULL:
113
+ fiobj_str_write(dest, "$-1\r\n", 5);
114
+ break;
115
+ case FIOBJ_T_ARRAY:
116
+ fiobj_str_write(dest, "*", 1);
117
+ fiobj_str_write_i(dest, fiobj_ary_count(obj));
118
+ fiobj_str_write(dest, "\r\n", 2);
119
+ break;
120
+ case FIOBJ_T_HASH:
121
+ fiobj_str_write(dest, "*", 1);
122
+ fiobj_str_write_i(dest, fiobj_hash_count(obj) * 2);
123
+ fiobj_str_write(dest, "\r\n", 2);
124
+ break;
125
+ case FIOBJ_T_TRUE:
126
+ fiobj_str_write(dest, "$4\r\ntrue\r\n", 10);
127
+ break;
128
+ case FIOBJ_T_FALSE:
129
+ fiobj_str_write(dest, "$4\r\nfalse\r\n", 11);
130
+ break;
131
+ #if 0
132
+ /* Numbers aren't as good for commands as one might think... */
133
+ case FIOBJ_T_NUMBER:
134
+ fiobj_str_write(dest, ":", 1);
135
+ fiobj_str_write_i(dest, fiobj_obj2num(obj));
136
+ fiobj_str_write(dest, "\r\n", 2);
137
+ break;
138
+ #else
139
+ case FIOBJ_T_NUMBER: /* overflow */
140
+ #endif
141
+ case FIOBJ_T_FLOAT: /* overflow */
142
+ case FIOBJ_T_UNKNOWN: /* overflow */
143
+ case FIOBJ_T_STRING: /* overflow */
144
+ case FIOBJ_T_DATA:
145
+ s = fiobj_obj2cstr(obj);
146
+ fiobj_str_write(dest, "$", 1);
147
+ fiobj_str_write_i(dest, s.len);
148
+ fiobj_str_write(dest, "\r\n", 2);
149
+ fiobj_str_write(dest, s.data, s.len);
150
+ fiobj_str_write(dest, "\r\n", 2);
151
+ break;
152
+ }
153
+ }
154
+
155
+ static int fiobj2resp_task(FIOBJ o, void *dest_) {
156
+ if (fiobj_hash_key_in_loop())
157
+ fiobj2resp___internal((FIOBJ)dest_, fiobj_hash_key_in_loop());
158
+ fiobj2resp___internal((FIOBJ)dest_, o);
159
+ return 0;
160
+ }
161
+
162
+ /**
163
+ * Converts FIOBJ objects into a RESP string (client mode).
164
+ */
165
+ static FIOBJ fiobj2resp(FIOBJ dest, FIOBJ obj) {
166
+ fiobj_each2(obj, fiobj2resp_task, (void *)dest);
167
+ return dest;
168
+ }
169
+
170
+ /**
171
+ * Converts FIOBJ objects into a RESP string (client mode).
172
+ *
173
+ * Don't call `fiobj_free`, object will self-destruct.
174
+ */
175
+ static inline FIOBJ fiobj2resp_tmp(FIOBJ obj) {
176
+ return fiobj2resp(fiobj_str_tmp(), obj);
177
+ }
178
+
179
+ /* *****************************************************************************
180
+ RESP parser callbacks
181
+ ***************************************************************************** */
182
+
183
+ /** a local static callback, called when a parser / protocol error occurs. */
184
+ static int resp_on_parser_error(resp_parser_s *parser) {
185
+ struct redis_engine_internal_s *i = parser2data(parser);
186
+ FIO_LOG_ERROR("(redis) parser error - attempting to restart connection.\n");
187
+ fio_close(i->uuid);
188
+ return -1;
189
+ }
190
+
191
+ /** a local static callback, called when the RESP message is complete. */
192
+ static int resp_on_message(resp_parser_s *parser) {
193
+ struct redis_engine_internal_s *i = parser2data(parser);
194
+ FIOBJ msg = i->ary ? i->ary : i->str;
195
+ i->on_message(i, msg);
196
+ /* cleanup */
197
+ fiobj_free(msg);
198
+ i->ary = FIOBJ_INVALID;
199
+ i->str = FIOBJ_INVALID;
200
+ return 0;
201
+ }
202
+
203
+ /** a local helper to add parsed objects to the data store. */
204
+ static inline void resp_add_obj(struct redis_engine_internal_s *dest, FIOBJ o) {
205
+ if (dest->ary) {
206
+ fiobj_ary_push(dest->ary, o);
207
+ --dest->ary_count;
208
+ if (!dest->ary_count && dest->nesting) {
209
+ FIOBJ tmp = fiobj_ary_shift(dest->ary);
210
+ dest->ary_count = fiobj_obj2num(tmp);
211
+ fiobj_free(tmp);
212
+ dest->ary = fiobj_ary_shift(dest->ary);
213
+ --dest->nesting;
214
+ }
215
+ }
216
+ dest->str = o;
217
+ }
218
+
219
+ /** a local static callback, called when a Number object is parsed. */
220
+ static int resp_on_number(resp_parser_s *parser, int64_t num) {
221
+ struct redis_engine_internal_s *data = parser2data(parser);
222
+ resp_add_obj(data, fiobj_num_new(num));
223
+ return 0;
224
+ }
225
+ /** a local static callback, called when a OK message is received. */
226
+ static int resp_on_okay(resp_parser_s *parser) {
227
+ struct redis_engine_internal_s *data = parser2data(parser);
228
+ resp_add_obj(data, fiobj_true());
229
+ return 0;
230
+ }
231
+ /** a local static callback, called when NULL is received. */
232
+ static int resp_on_null(resp_parser_s *parser) {
233
+ struct redis_engine_internal_s *data = parser2data(parser);
234
+ resp_add_obj(data, fiobj_null());
235
+ return 0;
236
+ }
237
+
238
+ /**
239
+ * a local static callback, called when a String should be allocated.
240
+ *
241
+ * `str_len` is the expected number of bytes that will fill the final string
242
+ * object, without any NUL byte marker (the string might be binary).
243
+ *
244
+ * If this function returns any value besides 0, parsing is stopped.
245
+ */
246
+ static int resp_on_start_string(resp_parser_s *parser, size_t str_len) {
247
+ struct redis_engine_internal_s *data = parser2data(parser);
248
+ resp_add_obj(data, fiobj_str_buf(str_len));
249
+ return 0;
250
+ }
251
+ /** a local static callback, called as String objects are streamed. */
252
+ static int resp_on_string_chunk(resp_parser_s *parser, void *data, size_t len) {
253
+ struct redis_engine_internal_s *i = parser2data(parser);
254
+ fiobj_str_write(i->str, data, len);
255
+ return 0;
256
+ }
257
+ /** a local static callback, called when a String object had finished
258
+ * streaming.
259
+ */
260
+ static int resp_on_end_string(resp_parser_s *parser) {
261
+ return 0;
262
+ (void)parser;
263
+ }
264
+
265
+ /** a local static callback, called an error message is received. */
266
+ static int resp_on_err_msg(resp_parser_s *parser, void *data, size_t len) {
267
+ struct redis_engine_internal_s *i = parser2data(parser);
268
+ resp_add_obj(i, fiobj_str_new(data, len));
269
+ return 0;
270
+ }
271
+
272
+ /**
273
+ * a local static callback, called when an Array should be allocated.
274
+ *
275
+ * `array_len` is the expected number of objects that will fill the Array
276
+ * object.
277
+ *
278
+ * There's no `resp_on_end_array` callback since the RESP protocol assumes the
279
+ * message is finished along with the Array (`resp_on_message` is called).
280
+ * However, just in case a non-conforming client/server sends nested Arrays,
281
+ * the callback should test against possible overflow or nested Array endings.
282
+ *
283
+ * If this function returns any value besides 0, parsing is stopped.
284
+ */
285
+ static int resp_on_start_array(resp_parser_s *parser, size_t array_len) {
286
+ struct redis_engine_internal_s *i = parser2data(parser);
287
+ if (i->ary) {
288
+ ++i->nesting;
289
+ FIOBJ tmp = fiobj_ary_new2(array_len + 2);
290
+ fiobj_ary_push(tmp, fiobj_num_new(i->ary_count));
291
+ fiobj_ary_push(tmp, fiobj_num_new(i->ary));
292
+ i->ary = tmp;
293
+ } else {
294
+ i->ary = fiobj_ary_new2(array_len + 2);
295
+ }
296
+ i->ary_count = array_len;
297
+ return 0;
298
+ }
299
+
300
+ /* *****************************************************************************
301
+ Publication and Command Handling
302
+ ***************************************************************************** */
303
+
304
+ /* the deferred callback handler */
305
+ static void redis_perform_callback(void *e, void *cmd_) {
306
+ redis_commands_s *cmd = cmd_;
307
+ FIOBJ reply = (FIOBJ)cmd->node.next;
308
+ if (cmd->callback)
309
+ cmd->callback(e, reply, cmd->udata);
310
+ fiobj_free(reply);
311
+ FIO_LOG_DEBUG("Handled: %s\n", cmd->cmd);
312
+ fio_free(cmd);
313
+ }
314
+
315
+ /* send command within lock, to ensure flag integrity */
316
+ static void redis_send_next_command_unsafe(redis_engine_s *r) {
317
+ if (!r->pub_sent && fio_ls_embd_any(&r->queue)) {
318
+ r->pub_sent = 1;
319
+ redis_commands_s *cmd =
320
+ FIO_LS_EMBD_OBJ(redis_commands_s, node, r->queue.next);
321
+ fio_write2(r->pub_data.uuid, .data.buffer = cmd->cmd,
322
+ .length = cmd->cmd_len, .after.dealloc = FIO_DEALLOC_NOOP);
323
+ FIO_LOG_DEBUG("(redis %d) Sending (%zu bytes):\n%s\n", (int)getpid(),
324
+ cmd->cmd_len, cmd->cmd);
325
+ }
326
+ }
327
+
328
+ /* attach a command to the queue */
329
+ static void redis_attach_cmd(redis_engine_s *r, redis_commands_s *cmd) {
330
+ fio_lock(&r->lock);
331
+ fio_ls_embd_push(&r->queue, &cmd->node);
332
+ redis_send_next_command_unsafe(r);
333
+ fio_unlock(&r->lock);
334
+ }
335
+
336
+ /** a local static callback, called when the RESP message is complete. */
337
+ static void resp_on_pub_message(struct redis_engine_internal_s *i, FIOBJ msg) {
338
+ redis_engine_s *r = pub2redis(i);
339
+ // #if DEBUG
340
+ if (FIO_LOG_LEVEL >= FIO_LOG_LEVEL_DEBUG) {
341
+ FIOBJ json = fiobj_obj2json(msg, 1);
342
+ FIO_LOG_DEBUG("Redis reply:\n%s\n", fiobj_obj2cstr(json).data);
343
+ fiobj_free(json);
344
+ }
345
+ // #endif
346
+ /* publishing / command parser */
347
+ fio_lock(&r->lock);
348
+ fio_ls_embd_s *node = fio_ls_embd_shift(&r->queue);
349
+ r->pub_sent = 0;
350
+ redis_send_next_command_unsafe(r);
351
+ fio_unlock(&r->lock);
352
+ if (!node) {
353
+ /* TODO: possible ping? from server?! not likely... */
354
+ FIO_LOG_WARNING("(redis %d) received a reply when no command was sent.",
355
+ (int)getpid());
356
+ return;
357
+ }
358
+ node->next = (void *)fiobj_dup(msg);
359
+ fio_defer(redis_perform_callback, &r->en,
360
+ FIO_LS_EMBD_OBJ(redis_commands_s, node, node));
361
+ }
362
+
363
+ /* *****************************************************************************
364
+ Subscription Message Handling
365
+ ***************************************************************************** */
366
+
367
+ /** a local static callback, called when the RESP message is complete. */
368
+ static void resp_on_sub_message(struct redis_engine_internal_s *i, FIOBJ msg) {
369
+ redis_engine_s *r = sub2redis(i);
370
+ /* subscriotion parser */
371
+ if (FIOBJ_TYPE(msg) != FIOBJ_T_ARRAY) {
372
+ if (FIOBJ_TYPE(msg) != FIOBJ_T_STRING || fiobj_obj2cstr(msg).len != 4 ||
373
+ fiobj_obj2cstr(msg).data[0] != 'P') {
374
+ FIO_LOG_WARNING("(redis) unexpected data format in "
375
+ "subscription stream (%zu bytes):\n %s\n",
376
+ fiobj_obj2cstr(msg).len, fiobj_obj2cstr(msg).data);
377
+ }
378
+ } else {
379
+ // FIOBJ *ary = fiobj_ary2ptr(msg);
380
+ // for (size_t i = 0; i < fiobj_ary_count(msg); ++i) {
381
+ // fio_str_info_s tmp = fiobj_obj2cstr(ary[i]);
382
+ // fprintf(stderr, "(%lu) %s\n", (unsigned long)i, tmp.data);
383
+ // }
384
+ fio_str_info_s tmp = fiobj_obj2cstr(fiobj_ary_index(msg, 0));
385
+ if (tmp.len == 7) { /* "message" */
386
+ fiobj_free(r->last_ch);
387
+ r->last_ch = fiobj_dup(fiobj_ary_index(msg, 1));
388
+ fio_publish(.channel = fiobj_obj2cstr(r->last_ch),
389
+ .message = fiobj_obj2cstr(fiobj_ary_index(msg, 2)),
390
+ .engine = FIO_PUBSUB_CLUSTER);
391
+ } else if (tmp.len == 8) { /* "pmessage" */
392
+ if (!fiobj_iseq(r->last_ch, fiobj_ary_index(msg, 2)))
393
+ fio_publish(.channel = fiobj_obj2cstr(fiobj_ary_index(msg, 2)),
394
+ .message = fiobj_obj2cstr(fiobj_ary_index(msg, 3)),
395
+ .engine = FIO_PUBSUB_CLUSTER);
396
+ }
397
+ }
398
+ }
399
+
400
+ /* *****************************************************************************
401
+ Connection Callbacks (fio_protocol_s) and Engine
402
+ ***************************************************************************** */
403
+
404
+ /** defined later - connects to Redis */
405
+ static void redis_connect(void *r, void *i);
406
+
407
+ #define defer_redis_connect(r, i) \
408
+ do { \
409
+ fio_atomic_add(&(r)->ref, 1); \
410
+ fio_defer(redis_connect, (r), (i)); \
411
+ } while (0);
412
+
413
+ /** Called when a data is available, but will not run concurrently */
414
+ static void redis_on_data(intptr_t uuid, fio_protocol_s *pr) {
415
+ struct redis_engine_internal_s *internal =
416
+ (struct redis_engine_internal_s *)pr;
417
+ uint8_t *buf;
418
+ if (internal->on_message == resp_on_sub_message) {
419
+ buf = sub2redis(pr)->buf + REDIS_READ_BUFFER;
420
+ } else {
421
+ buf = pub2redis(pr)->buf;
422
+ }
423
+ ssize_t i = fio_read(uuid, buf + internal->buf_pos,
424
+ REDIS_READ_BUFFER - internal->buf_pos);
425
+ if (i <= 0)
426
+ return;
427
+
428
+ internal->buf_pos += i;
429
+ i = resp_parse(&internal->parser, buf, internal->buf_pos);
430
+ if (i) {
431
+ memmove(buf, buf + internal->buf_pos - i, i);
432
+ }
433
+ internal->buf_pos = i;
434
+ }
435
+
436
+ /** Called when the connection was closed, but will not run concurrently */
437
+ static void redis_on_close(intptr_t uuid, fio_protocol_s *pr) {
438
+ struct redis_engine_internal_s *internal =
439
+ (struct redis_engine_internal_s *)pr;
440
+ redis_internal_reset(internal);
441
+ redis_engine_s *r;
442
+ if (internal->on_message == resp_on_sub_message) {
443
+ r = sub2redis(pr);
444
+ fiobj_free(r->last_ch);
445
+ r->last_ch = FIOBJ_INVALID;
446
+ if (r->flag) {
447
+ /* reconnection for subscription connection. */
448
+ if (uuid != -1) {
449
+ FIO_LOG_WARNING("(redis %d) subscription connection lost. "
450
+ "Reconnecting...",
451
+ (int)getpid());
452
+ }
453
+ fio_atomic_sub(&r->ref, 1);
454
+ defer_redis_connect(r, internal);
455
+ } else {
456
+ redis_free(r);
457
+ }
458
+ } else {
459
+ r = pub2redis(pr);
460
+ if (r->flag && uuid != -1) {
461
+ FIO_LOG_WARNING("(redis %d) publication connection lost. "
462
+ "Reconnecting...",
463
+ (int)getpid());
464
+ }
465
+ r->pub_sent = 0;
466
+ fio_close(r->sub_data.uuid);
467
+ redis_free(r);
468
+ }
469
+ (void)uuid;
470
+ }
471
+
472
+ /** Called before the facil.io reactor is shut down. */
473
+ static uint8_t redis_on_shutdown(intptr_t uuid, fio_protocol_s *pr) {
474
+ fio_write2(uuid, .data.buffer = "*1\r\n$4\r\nQUIT\r\n", .length = 14,
475
+ .after.dealloc = FIO_DEALLOC_NOOP);
476
+ return 0;
477
+ (void)pr;
478
+ }
479
+
480
+ /** Called on connection timeout. */
481
+ static void redis_sub_ping(intptr_t uuid, fio_protocol_s *pr) {
482
+ fio_write2(uuid, .data.buffer = "*1\r\n$4\r\nPING\r\n", .length = 14,
483
+ .after.dealloc = FIO_DEALLOC_NOOP);
484
+ (void)pr;
485
+ }
486
+
487
+ /** Called on connection timeout. */
488
+ static void redis_pub_ping(intptr_t uuid, fio_protocol_s *pr) {
489
+ redis_engine_s *r = pub2redis(pr);
490
+ if (fio_ls_embd_any(&r->queue)) {
491
+ FIO_LOG_WARNING("(redis) Redis server unresponsive, disconnecting.");
492
+ fio_close(uuid);
493
+ return;
494
+ }
495
+ redis_commands_s *cmd = fio_malloc(sizeof(*cmd) + 15);
496
+ FIO_ASSERT_ALLOC(cmd);
497
+ *cmd = (redis_commands_s){.cmd_len = 14};
498
+ memcpy(cmd->cmd, "*1\r\n$4\r\nPING\r\n\0", 15);
499
+ redis_attach_cmd(r, cmd);
500
+ }
501
+
502
+ /* *****************************************************************************
503
+ Connecting to Redis
504
+ ***************************************************************************** */
505
+
506
+ static void redis_on_auth(fio_pubsub_engine_s *e, FIOBJ reply, void *udata) {
507
+ if (FIOBJ_TYPE_IS(reply, FIOBJ_T_TRUE)) {
508
+ fio_str_info_s s = fiobj_obj2cstr(reply);
509
+ FIO_LOG_WARNING("(redis) Authentication FAILED."
510
+ " %.*s",
511
+ (int)s.len, s.data);
512
+ }
513
+ (void)e;
514
+ (void)udata;
515
+ }
516
+
517
+ static void redis_on_connect(intptr_t uuid, void *i_) {
518
+ struct redis_engine_internal_s *i = i_;
519
+ redis_engine_s *r;
520
+ i->uuid = uuid;
521
+
522
+ if (i->on_message == resp_on_sub_message) {
523
+ r = sub2redis(i);
524
+ if (r->auth_len) {
525
+ fio_write2(uuid, .data.buffer = r->auth, .length = r->auth_len,
526
+ .after.dealloc = FIO_DEALLOC_NOOP);
527
+ }
528
+ fio_pubsub_reattach(&r->en);
529
+ if (r->pub_data.uuid == -1) {
530
+ defer_redis_connect(r, &r->pub_data);
531
+ }
532
+ FIO_LOG_INFO("(redis %d) subscription connection established.",
533
+ (int)getpid());
534
+ } else {
535
+ r = pub2redis(i);
536
+ if (r->auth_len) {
537
+ redis_commands_s *cmd = fio_malloc(sizeof(*cmd) + r->auth_len);
538
+ FIO_ASSERT_ALLOC(cmd);
539
+ *cmd =
540
+ (redis_commands_s){.cmd_len = r->auth_len, .callback = redis_on_auth};
541
+ memcpy(cmd->cmd, r->auth, r->auth_len);
542
+ fio_lock(&r->lock);
543
+ r->pub_sent = 0;
544
+ fio_ls_embd_unshift(&r->queue, &cmd->node);
545
+ redis_send_next_command_unsafe(r);
546
+ fio_unlock(&r->lock);
547
+ } else {
548
+ fio_lock(&r->lock);
549
+ r->pub_sent = 0;
550
+ redis_send_next_command_unsafe(r);
551
+ fio_unlock(&r->lock);
552
+ }
553
+ FIO_LOG_INFO("(redis %d) publication connection established.",
554
+ (int)getpid());
555
+ }
556
+
557
+ i->protocol.rsv = 0;
558
+ fio_attach(uuid, &i->protocol);
559
+ fio_timeout_set(uuid, r->ping_int);
560
+
561
+ return;
562
+ }
563
+
564
+ static void redis_on_connect_failed(intptr_t uuid, void *i_) {
565
+ struct redis_engine_internal_s *i = i_;
566
+ i->uuid = -1;
567
+ i->protocol.on_close(-1, &i->protocol);
568
+ (void)uuid;
569
+ }
570
+
571
+ static void redis_connect(void *r_, void *i_) {
572
+ redis_engine_s *r = r_;
573
+ struct redis_engine_internal_s *i = i_;
574
+ fio_lock(&r->lock_connection);
575
+ if (r->flag == 0 || i->uuid != -1 || !fio_is_running()) {
576
+ fio_unlock(&r->lock_connection);
577
+ redis_free(r);
578
+ return;
579
+ }
580
+ // fio_atomic_add(&r->ref, 1);
581
+ i->uuid = fio_connect(.address = r->address, .port = r->port,
582
+ .on_connect = redis_on_connect, .udata = i,
583
+ .on_fail = redis_on_connect_failed);
584
+ fio_unlock(&r->lock_connection);
585
+ }
586
+
587
+ /* *****************************************************************************
588
+ Engine / Bridge Callbacks (Root Process)
589
+ ***************************************************************************** */
590
+
591
+ static void redis_on_subscribe_root(const fio_pubsub_engine_s *eng,
592
+ fio_str_info_s channel,
593
+ fio_match_fn match) {
594
+ redis_engine_s *r = (redis_engine_s *)eng;
595
+ if (r->sub_data.uuid != -1) {
596
+ FIOBJ cmd = fiobj_str_buf(96 + channel.len);
597
+ if (match == FIO_MATCH_GLOB)
598
+ fiobj_str_write(cmd, "*2\r\n$10\r\nPSUBSCRIBE\r\n$", 22);
599
+ else
600
+ fiobj_str_write(cmd, "*2\r\n$9\r\nSUBSCRIBE\r\n$", 20);
601
+ fiobj_str_write_i(cmd, channel.len);
602
+ fiobj_str_write(cmd, "\r\n", 2);
603
+ fiobj_str_write(cmd, channel.data, channel.len);
604
+ fiobj_str_write(cmd, "\r\n", 2);
605
+ // {
606
+ // fio_str_info_s s = fiobj_obj2cstr(cmd);
607
+ // fprintf(stderr, "(%d) Sending Subscription (%p):\n%s\n", getpid(),
608
+ // (void *)r->sub_data.uuid, s.data);
609
+ // }
610
+ fiobj_send_free(r->sub_data.uuid, cmd);
611
+ }
612
+ }
613
+
614
+ static void redis_on_unsubscribe_root(const fio_pubsub_engine_s *eng,
615
+ fio_str_info_s channel,
616
+ fio_match_fn match) {
617
+ redis_engine_s *r = (redis_engine_s *)eng;
618
+ if (r->sub_data.uuid != -1) {
619
+ fio_str_s *cmd = fio_str_new2();
620
+ fio_str_capa_assert(cmd, 96 + channel.len);
621
+ if (match == FIO_MATCH_GLOB)
622
+ fio_str_write(cmd, "*2\r\n$12\r\nPUNSUBSCRIBE\r\n$", 24);
623
+ else
624
+ fio_str_write(cmd, "*2\r\n$11\r\nUNSUBSCRIBE\r\n$", 23);
625
+ fio_str_write_i(cmd, channel.len);
626
+ fio_str_write(cmd, "\r\n", 2);
627
+ fio_str_write(cmd, channel.data, channel.len);
628
+ fio_str_write(cmd, "\r\n", 2);
629
+ // {
630
+ // fio_str_info_s s = fio_str_info(cmd);
631
+ // fprintf(stderr, "(%d) Cancel Subscription (%p):\n%s\n", getpid(),
632
+ // (void *)r->sub_data.uuid, s.data);
633
+ // }
634
+ fio_str_send_free2(r->sub_data.uuid, cmd);
635
+ }
636
+ }
637
+
638
+ static void redis_on_publish_root(const fio_pubsub_engine_s *eng,
639
+ fio_str_info_s channel, fio_str_info_s msg,
640
+ uint8_t is_json) {
641
+ redis_engine_s *r = (redis_engine_s *)eng;
642
+ redis_commands_s *cmd = fio_malloc(sizeof(*cmd) + channel.len + msg.len + 96);
643
+ FIO_ASSERT_ALLOC(cmd);
644
+ *cmd = (redis_commands_s){.cmd_len = 0};
645
+ memcpy(cmd->cmd, "*3\r\n$7\r\nPUBLISH\r\n$", 18);
646
+ char *buf = (char *)cmd->cmd + 18;
647
+ buf += fio_ltoa((void *)buf, channel.len, 10);
648
+ *buf++ = '\r';
649
+ *buf++ = '\n';
650
+ memcpy(buf, channel.data, channel.len);
651
+ buf += channel.len;
652
+ *buf++ = '\r';
653
+ *buf++ = '\n';
654
+ *buf++ = '$';
655
+ buf += fio_ltoa(buf, msg.len, 10);
656
+ *buf++ = '\r';
657
+ *buf++ = '\n';
658
+ memcpy(buf, msg.data, msg.len);
659
+ buf += msg.len;
660
+ *buf++ = '\r';
661
+ *buf++ = '\n';
662
+ *buf = 0;
663
+ FIO_LOG_DEBUG("(%d) Publishing:\n%s", (int)getpid(), cmd->cmd);
664
+ cmd->cmd_len = (uintptr_t)buf - (uintptr_t)(cmd + 1);
665
+ redis_attach_cmd(r, cmd);
666
+ return;
667
+ (void)is_json;
668
+ }
669
+
670
+ /* *****************************************************************************
671
+ Engine / Bridge Stub Callbacks (Child Process)
672
+ ***************************************************************************** */
673
+
674
+ static void redis_on_mock_subscribe_child(const fio_pubsub_engine_s *eng,
675
+ fio_str_info_s channel,
676
+ fio_match_fn match) {
677
+ /* do nothing, root process is notified about (un)subscriptions by facil.io */
678
+ (void)eng;
679
+ (void)channel;
680
+ (void)match;
681
+ }
682
+
683
+ static void redis_on_publish_child(const fio_pubsub_engine_s *eng,
684
+ fio_str_info_s channel, fio_str_info_s msg,
685
+ uint8_t is_json) {
686
+ /* attach engine data to channel (prepend) */
687
+ fio_str_s tmp = FIO_STR_INIT;
688
+ /* by using fio_str_s, short names are allocated on the stack */
689
+ fio_str_info_s tmp_info = fio_str_resize(&tmp, channel.len + 8);
690
+ fio_u2str64(tmp_info.data, (uintptr_t)eng);
691
+ memcpy(tmp_info.data + 8, channel.data, channel.len);
692
+ /* forward publication request to Root */
693
+ fio_publish(.filter = -1, .channel = tmp_info, .message = msg,
694
+ .engine = FIO_PUBSUB_ROOT, .is_json = is_json);
695
+ fio_str_free(&tmp);
696
+ (void)eng;
697
+ }
698
+
699
+ /* *****************************************************************************
700
+ Root Publication Handler
701
+ ***************************************************************************** */
702
+
703
+ /* listens to filter -1 and publishes and messages */
704
+ static void redis_on_internal_publish(fio_msg_s *msg) {
705
+ if (msg->channel.len < 8)
706
+ return; /* internal error, unexpected data */
707
+ void *en = (void *)(uintptr_t)fio_str2u64(msg->channel.data);
708
+ if (en != msg->udata1)
709
+ return; /* should be delivered by a different engine */
710
+ /* step after the engine data */
711
+ msg->channel.len -= 8;
712
+ msg->channel.data += 8;
713
+ /* forward to publishing */
714
+ FIO_LOG_DEBUG("Forwarding to engine %p, on channel %s", msg->udata1,
715
+ msg->channel.data);
716
+ redis_on_publish_root(msg->udata1, msg->channel, msg->msg, msg->is_json);
717
+ }
718
+
719
+ /* *****************************************************************************
720
+ Sending commands using the Root connection
721
+ ***************************************************************************** */
722
+
723
+ /* callback from the Redis reply */
724
+ static void redis_forward_reply(fio_pubsub_engine_s *e, FIOBJ reply,
725
+ void *udata) {
726
+ uint8_t *data = udata;
727
+ fio_pubsub_engine_s *engine = (fio_pubsub_engine_s *)(uintptr_t)fio_str2u64(data + 0);
728
+ void *callback = (void *)(uintptr_t)fio_str2u64(data + 8);
729
+ if (engine != e || !callback) {
730
+ FIO_LOG_DEBUG("Redis reply not forwarded (callback: %p)", callback);
731
+ return;
732
+ }
733
+ int32_t pid = (int32_t)fio_str2u32(data + 24);
734
+ FIOBJ rp = fiobj_obj2json(reply, 0);
735
+ fio_publish(.filter = (-10 - (int32_t)pid), .channel.data = (char *)data,
736
+ .channel.len = 28, .message = fiobj_obj2cstr(rp), .is_json = 1);
737
+ fiobj_free(rp);
738
+ }
739
+
740
+ /* listens to channel -2 for commands that need to be sent (only ROOT) */
741
+ static void redis_on_internal_cmd(fio_msg_s *msg) {
742
+ // void*(void *)fio_str2u64(msg->msg.data);
743
+ fio_pubsub_engine_s *engine =
744
+ (fio_pubsub_engine_s *)(uintptr_t)fio_str2u64(msg->channel.data + 0);
745
+ if (engine != msg->udata1) {
746
+ return;
747
+ }
748
+ redis_commands_s *cmd = fio_malloc(sizeof(*cmd) + msg->msg.len + 1 + 28);
749
+ FIO_ASSERT_ALLOC(cmd);
750
+ *cmd = (redis_commands_s){.callback = redis_forward_reply,
751
+ .udata = (cmd->cmd + msg->msg.len + 1),
752
+ .cmd_len = msg->msg.len};
753
+ memcpy(cmd->cmd, msg->msg.data, msg->msg.len);
754
+ memcpy(cmd->cmd + msg->msg.len + 1, msg->channel.data, 28);
755
+ redis_attach_cmd((redis_engine_s *)engine, cmd);
756
+ // fprintf(stderr, " *** Attached CMD (%d) ***\n%s\n", getpid(), cmd->cmd);
757
+ }
758
+
759
+ /* Listens on filter `-10 -getpid()` for incoming reply data */
760
+ static void redis_on_internal_reply(fio_msg_s *msg) {
761
+ fio_pubsub_engine_s *engine =
762
+ (fio_pubsub_engine_s *)(uintptr_t)fio_str2u64(msg->channel.data + 0);
763
+ if (engine != msg->udata1) {
764
+ FIO_LOG_DEBUG("Redis reply not forwarded (engine mismatch: %p != %p)",
765
+ (void *)engine, msg->udata1);
766
+ return;
767
+ }
768
+ FIOBJ reply;
769
+ fiobj_json2obj(&reply, msg->msg.data, msg->msg.len);
770
+ void (*callback)(fio_pubsub_engine_s *, FIOBJ, void *) = (void (*)(
771
+ fio_pubsub_engine_s *, FIOBJ, void *))(uintptr_t)fio_str2u64(msg->channel.data + 8);
772
+ void *udata = (void *)(uintptr_t)fio_str2u64(msg->channel.data + 16);
773
+ callback(engine, reply, udata);
774
+ fiobj_free(reply);
775
+ }
776
+
777
+ /* publishes a Redis command to Root's filter -2 */
778
+ intptr_t redis_engine_send(fio_pubsub_engine_s *engine, FIOBJ command,
779
+ void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply,
780
+ void *udata),
781
+ void *udata) {
782
+ if ((uintptr_t)engine < 4) {
783
+ FIO_LOG_WARNING("(redis send) trying to use one of the core engines");
784
+ return -1;
785
+ }
786
+ // if(fio_is_master()) {
787
+ // FIOBJ resp = fiobj2resp_tmp(fio_str_info_s obj1, FIOBJ obj2);
788
+ // TODO...
789
+ // } else {
790
+ /* forward publication request to Root */
791
+ fio_str_s tmp = FIO_STR_INIT;
792
+ fio_str_info_s ti = fio_str_resize(&tmp, 28);
793
+ /* combine metadata */
794
+ fio_u2str64(ti.data + 0, (uintptr_t)engine);
795
+ fio_u2str64(ti.data + 8, (uintptr_t)callback);
796
+ fio_u2str64(ti.data + 16, (uintptr_t)udata);
797
+ fio_u2str32(ti.data + 24, (uint32_t)getpid());
798
+ FIOBJ cmd = fiobj2resp_tmp(command);
799
+ fio_publish(.filter = -2, .channel = ti, .message = fiobj_obj2cstr(cmd),
800
+ .engine = FIO_PUBSUB_ROOT, .is_json = 0);
801
+ fio_str_free(&tmp);
802
+ // }
803
+ return 0;
804
+ }
805
+
806
+ /* *****************************************************************************
807
+ Redis Engine Creation
808
+ ***************************************************************************** */
809
+
810
+ static void redis_on_facil_start(void *r_) {
811
+ redis_engine_s *r = r_;
812
+ r->flag = 1;
813
+ if (!fio_is_valid(r->sub_data.uuid)) {
814
+ defer_redis_connect(r, &r->sub_data);
815
+ }
816
+ }
817
+ static void redis_on_facil_shutdown(void *r_) {
818
+ redis_engine_s *r = r_;
819
+ r->flag = 0;
820
+ }
821
+
822
+ static void redis_on_engine_fork(void *r_) {
823
+ redis_engine_s *r = r_;
824
+ r->flag = 0;
825
+ r->lock = FIO_LOCK_INIT;
826
+ fio_force_close(r->sub_data.uuid);
827
+ r->sub_data.uuid = -1;
828
+ fio_force_close(r->pub_data.uuid);
829
+ r->pub_data.uuid = -1;
830
+ while (fio_ls_embd_any(&r->queue)) {
831
+ redis_commands_s *cmd =
832
+ FIO_LS_EMBD_OBJ(redis_commands_s, node, fio_ls_embd_pop(&r->queue));
833
+ fio_free(cmd);
834
+ }
835
+ r->en = (fio_pubsub_engine_s){
836
+ .subscribe = redis_on_mock_subscribe_child,
837
+ .unsubscribe = redis_on_mock_subscribe_child,
838
+ .publish = redis_on_publish_child,
839
+ };
840
+ fio_unsubscribe(r->publication_forwarder);
841
+ r->publication_forwarder = NULL;
842
+ fio_unsubscribe(r->cmd_forwarder);
843
+ r->cmd_forwarder = NULL;
844
+ fio_unsubscribe(r->cmd_reply);
845
+ r->cmd_reply =
846
+ fio_subscribe(.filter = -10 - (int32_t)getpid(),
847
+ .on_message = redis_on_internal_reply, .udata1 = r);
848
+ }
849
+
850
+ fio_pubsub_engine_s *redis_engine_create
851
+ FIO_IGNORE_MACRO(struct redis_engine_create_args args) {
852
+ if (getpid() != fio_parent_pid()) {
853
+ FIO_LOG_FATAL("(redis) Redis engine initialization can only "
854
+ "be performed in the Root process.");
855
+ kill(0, SIGINT);
856
+ fio_stop();
857
+ return NULL;
858
+ }
859
+ if (!args.address.len && args.address.data)
860
+ args.address.len = strlen(args.address.data);
861
+ if (!args.port.len && args.port.data)
862
+ args.port.len = strlen(args.port.data);
863
+ if (!args.auth.len && args.auth.data) {
864
+ args.auth.len = strlen(args.auth.data);
865
+ }
866
+
867
+ if (!args.address.data || !args.address.len) {
868
+ args.address = (fio_str_info_s){.len = 9, .data = (char *)"localhost"};
869
+ }
870
+ if (!args.port.data || !args.port.len) {
871
+ args.port = (fio_str_info_s){.len = 4, .data = (char *)"6379"};
872
+ }
873
+ redis_engine_s *r =
874
+ fio_malloc(sizeof(*r) + args.port.len + 1 + args.address.len + 1 +
875
+ args.auth.len + 1 + (REDIS_READ_BUFFER * 2));
876
+ FIO_ASSERT_ALLOC(r);
877
+ *r = (redis_engine_s){
878
+ .en =
879
+ {
880
+ .subscribe = redis_on_subscribe_root,
881
+ .unsubscribe = redis_on_unsubscribe_root,
882
+ .publish = redis_on_publish_root,
883
+ },
884
+ .pub_data =
885
+ {
886
+ .protocol =
887
+ {
888
+ .on_data = redis_on_data,
889
+ .on_close = redis_on_close,
890
+ .on_shutdown = redis_on_shutdown,
891
+ .ping = redis_pub_ping,
892
+ },
893
+ .uuid = -1,
894
+ .on_message = resp_on_pub_message,
895
+ },
896
+ .sub_data =
897
+ {
898
+ .protocol =
899
+ {
900
+ .on_data = redis_on_data,
901
+ .on_close = redis_on_close,
902
+ .on_shutdown = redis_on_shutdown,
903
+ .ping = redis_sub_ping,
904
+ },
905
+ .on_message = resp_on_sub_message,
906
+ .uuid = -1,
907
+ },
908
+ .publication_forwarder =
909
+ fio_subscribe(.filter = -1, .udata1 = r,
910
+ .on_message = redis_on_internal_publish),
911
+ .cmd_forwarder = fio_subscribe(.filter = -2, .udata1 = r,
912
+ .on_message = redis_on_internal_cmd),
913
+ .cmd_reply =
914
+ fio_subscribe(.filter = -10 - (uint32_t)getpid(), .udata1 = r,
915
+ .on_message = redis_on_internal_reply),
916
+ .address = ((char *)(r + 1) + (REDIS_READ_BUFFER * 2)),
917
+ .port =
918
+ ((char *)(r + 1) + (REDIS_READ_BUFFER * 2) + args.address.len + 1),
919
+ .auth = ((char *)(r + 1) + (REDIS_READ_BUFFER * 2) + args.address.len +
920
+ args.port.len + 2),
921
+ .auth_len = args.auth.len,
922
+ .ref = 1,
923
+ .queue = FIO_LS_INIT(r->queue),
924
+ .lock = FIO_LOCK_INIT,
925
+ .lock_connection = FIO_LOCK_INIT,
926
+ .ping_int = args.ping_interval,
927
+ .flag = 1,
928
+ };
929
+ memcpy(r->address, args.address.data, args.address.len);
930
+ memcpy(r->port, args.port.data, args.port.len);
931
+ if (args.auth.len)
932
+ memcpy(r->auth, args.auth.data, args.auth.len);
933
+ fio_pubsub_attach(&r->en);
934
+ redis_on_facil_start(r);
935
+ fio_state_callback_add(FIO_CALL_IN_CHILD, redis_on_engine_fork, r);
936
+ fio_state_callback_add(FIO_CALL_ON_SHUTDOWN, redis_on_facil_shutdown, r);
937
+ /* if restarting */
938
+ fio_state_callback_add(FIO_CALL_PRE_START, redis_on_facil_start, r);
939
+
940
+ FIO_LOG_DEBUG("Redis engine initialized %p", (void *)r);
941
+ return &r->en;
942
+ }
943
+
944
+ /* *****************************************************************************
945
+ Redis Engine Destruction
946
+ ***************************************************************************** */
947
+
948
+ void redis_engine_destroy(fio_pubsub_engine_s *engine) {
949
+ redis_engine_s *r = (redis_engine_s *)engine;
950
+ r->flag = 0;
951
+ fio_pubsub_detach(&r->en);
952
+ fio_state_callback_remove(FIO_CALL_IN_CHILD, redis_on_engine_fork, r);
953
+ fio_state_callback_remove(FIO_CALL_ON_SHUTDOWN, redis_on_facil_shutdown, r);
954
+ fio_state_callback_remove(FIO_CALL_PRE_START, redis_on_facil_start, r);
955
+ FIO_LOG_DEBUG("Redis engine destroyed %p", (void *)r);
956
+ redis_free(r);
957
+ }