iodine 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

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