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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -4
- data/SPEC-Websocket-Draft.md +3 -6
- data/bin/mustache.rb +128 -0
- data/examples/test_template.mustache +16 -0
- data/ext/iodine/fio.c +9397 -0
- data/ext/iodine/fio.h +4723 -0
- data/ext/iodine/fio_ary.h +353 -54
- data/ext/iodine/fio_cli.c +351 -361
- data/ext/iodine/fio_cli.h +84 -105
- data/ext/iodine/fio_hashmap.h +70 -16
- data/ext/iodine/fio_json_parser.h +35 -24
- data/ext/iodine/fio_siphash.c +104 -4
- data/ext/iodine/fio_siphash.h +18 -2
- data/ext/iodine/fio_str.h +1218 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +13 -8
- data/ext/iodine/fiobj4sock.h +6 -8
- data/ext/iodine/fiobj_ary.c +107 -17
- data/ext/iodine/fiobj_ary.h +36 -4
- data/ext/iodine/fiobj_data.c +146 -127
- data/ext/iodine/fiobj_data.h +25 -23
- data/ext/iodine/fiobj_hash.c +7 -7
- data/ext/iodine/fiobj_hash.h +6 -5
- data/ext/iodine/fiobj_json.c +20 -17
- data/ext/iodine/fiobj_json.h +5 -5
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +310 -0
- data/ext/iodine/fiobj_mustache.h +40 -0
- data/ext/iodine/fiobj_numbers.c +199 -94
- data/ext/iodine/fiobj_numbers.h +7 -7
- data/ext/iodine/fiobj_str.c +142 -333
- data/ext/iodine/fiobj_str.h +65 -55
- data/ext/iodine/fiobject.c +49 -11
- data/ext/iodine/fiobject.h +40 -39
- data/ext/iodine/http.c +382 -190
- data/ext/iodine/http.h +124 -80
- data/ext/iodine/http1.c +99 -127
- data/ext/iodine/http1.h +5 -5
- data/ext/iodine/http1_parser.c +3 -2
- data/ext/iodine/http1_parser.h +2 -2
- data/ext/iodine/http_internal.c +14 -12
- data/ext/iodine/http_internal.h +25 -19
- data/ext/iodine/iodine.c +37 -18
- data/ext/iodine/iodine.h +4 -0
- data/ext/iodine/iodine_caller.c +9 -2
- data/ext/iodine/iodine_caller.h +2 -0
- data/ext/iodine/iodine_connection.c +82 -117
- data/ext/iodine/iodine_defer.c +57 -50
- data/ext/iodine/iodine_defer.h +0 -1
- data/ext/iodine/iodine_fiobj2rb.h +4 -2
- data/ext/iodine/iodine_helpers.c +4 -4
- data/ext/iodine/iodine_http.c +25 -32
- data/ext/iodine/iodine_json.c +2 -1
- data/ext/iodine/iodine_mustache.c +423 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +48 -153
- data/ext/iodine/iodine_pubsub.h +5 -4
- data/ext/iodine/iodine_rack_io.c +7 -5
- data/ext/iodine/iodine_store.c +16 -13
- data/ext/iodine/iodine_tcp.c +26 -34
- data/ext/iodine/mustache_parser.h +1085 -0
- data/ext/iodine/redis_engine.c +740 -646
- data/ext/iodine/redis_engine.h +13 -15
- data/ext/iodine/resp_parser.h +11 -5
- data/ext/iodine/websocket_parser.h +13 -13
- data/ext/iodine/websockets.c +240 -393
- data/ext/iodine/websockets.h +52 -113
- data/lib/iodine.rb +1 -1
- data/lib/iodine/mustache.rb +140 -0
- data/lib/iodine/version.rb +1 -1
- metadata +15 -28
- data/ext/iodine/defer.c +0 -566
- data/ext/iodine/defer.h +0 -148
- data/ext/iodine/evio.c +0 -26
- data/ext/iodine/evio.h +0 -161
- data/ext/iodine/evio_callbacks.c +0 -26
- data/ext/iodine/evio_epoll.c +0 -251
- data/ext/iodine/evio_kqueue.c +0 -194
- data/ext/iodine/facil.c +0 -2325
- data/ext/iodine/facil.h +0 -616
- data/ext/iodine/fio_base64.c +0 -277
- data/ext/iodine/fio_base64.h +0 -71
- data/ext/iodine/fio_llist.h +0 -257
- data/ext/iodine/fio_mem.c +0 -675
- data/ext/iodine/fio_mem.h +0 -143
- data/ext/iodine/fio_random.c +0 -248
- data/ext/iodine/fio_random.h +0 -45
- data/ext/iodine/fio_sha1.c +0 -362
- data/ext/iodine/fio_sha1.h +0 -107
- data/ext/iodine/fio_sha2.c +0 -842
- data/ext/iodine/fio_sha2.h +0 -169
- data/ext/iodine/pubsub.c +0 -867
- data/ext/iodine/pubsub.h +0 -221
- data/ext/iodine/sock.c +0 -1366
- data/ext/iodine/sock.h +0 -566
- data/ext/iodine/spnlock.inc +0 -111
data/ext/iodine/redis_engine.c
CHANGED
@@ -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
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#include
|
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
|
-
|
21
|
-
pubsub_engine_s en;
|
25
|
+
fio_pubsub_engine_s en;
|
22
26
|
struct redis_engine_internal_s {
|
23
|
-
|
24
|
-
|
27
|
+
fio_protocol_s protocol;
|
28
|
+
intptr_t uuid;
|
25
29
|
resp_parser_s parser;
|
26
|
-
|
30
|
+
void (*on_message)(struct redis_engine_internal_s *parser, FIOBJ msg);
|
27
31
|
FIOBJ str;
|
28
32
|
FIOBJ ary;
|
29
|
-
|
30
|
-
|
33
|
+
uint32_t ary_count;
|
34
|
+
uint16_t buf_pos;
|
35
|
+
uint16_t nesting;
|
31
36
|
} pub_data, sub_data;
|
32
|
-
|
33
|
-
|
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
|
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)(
|
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
|
-
/**
|
55
|
-
#define
|
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
|
-
/**
|
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
|
-
|
68
|
-
|
69
|
-
|
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(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 (
|
86
|
+
if (fio_atomic_sub(&r->ref, 1))
|
75
87
|
return;
|
76
|
-
|
77
|
-
|
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->
|
80
|
-
|
81
|
-
|
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
|
-
|
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
|
-
*
|
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
|
174
|
+
static inline FIOBJ fiobj2resp_tmp(FIOBJ obj) {
|
175
|
+
return fiobj2resp(fiobj_str_tmp(), obj);
|
176
|
+
}
|
92
177
|
|
93
178
|
/* *****************************************************************************
|
94
|
-
|
179
|
+
RESP parser callbacks
|
95
180
|
***************************************************************************** */
|
96
181
|
|
97
|
-
|
98
|
-
static
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
109
|
-
static
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
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
|
-
|
235
|
-
static void
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
r->pub_data.uuid =
|
240
|
-
|
241
|
-
|
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->
|
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
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
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
|
-
|
391
|
+
Connection Callbacks (fio_protocol_s) and Engine
|
282
392
|
***************************************************************************** */
|
283
393
|
|
284
|
-
|
285
|
-
|
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
|
-
|
400
|
+
(struct redis_engine_internal_s *)pr;
|
288
401
|
uint8_t *buf;
|
289
|
-
if (internal->
|
290
|
-
buf =
|
402
|
+
if (internal->on_message == resp_on_sub_message) {
|
403
|
+
buf = sub2redis(pr)->buf + REDIS_READ_BUFFER;
|
291
404
|
} else {
|
292
|
-
buf =
|
405
|
+
buf = pub2redis(pr)->buf;
|
293
406
|
}
|
294
|
-
ssize_t i =
|
295
|
-
|
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
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
r
|
312
|
-
if (
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
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
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
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 =
|
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
|
-
|
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
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
FIOBJ cmd = fiobj_str_buf(96 +
|
373
|
-
if (
|
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
|
-
|
581
|
+
fiobj_str_write_i(cmd, channel.len);
|
378
582
|
fiobj_str_write(cmd, "\r\n", 2);
|
379
|
-
fiobj_str_write(cmd,
|
583
|
+
fiobj_str_write(cmd, channel.data, channel.len);
|
380
584
|
fiobj_str_write(cmd, "\r\n", 2);
|
381
585
|
// {
|
382
|
-
//
|
383
|
-
// fprintf(stderr, "%s\n",
|
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
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
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
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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
|
-
//
|
404
|
-
// fprintf(stderr, "%s\n",
|
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
|
-
|
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
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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,
|
626
|
+
buf += fio_ltoa((void *)buf, channel.len, 10);
|
425
627
|
*buf++ = '\r';
|
426
628
|
*buf++ = '\n';
|
427
|
-
memcpy(buf,
|
428
|
-
buf +=
|
629
|
+
memcpy(buf, channel.data, channel.len);
|
630
|
+
buf += channel.len;
|
429
631
|
*buf++ = '\r';
|
430
632
|
*buf++ = '\n';
|
431
633
|
*buf++ = '$';
|
432
|
-
|
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,
|
437
|
-
buf +=
|
637
|
+
memcpy(buf, msg.data, msg.len);
|
638
|
+
buf += msg.len;
|
438
639
|
*buf++ = '\r';
|
439
640
|
*buf++ = '\n';
|
440
641
|
*buf = 0;
|
441
|
-
|
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
|
-
|
445
|
-
|
645
|
+
return;
|
646
|
+
(void)is_json;
|
446
647
|
}
|
648
|
+
|
447
649
|
/* *****************************************************************************
|
448
|
-
|
650
|
+
Engine / Bridge Stub Callbacks (Child Process)
|
449
651
|
***************************************************************************** */
|
450
652
|
|
451
|
-
static void
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
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
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
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
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
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
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
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
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
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
|
-
*
|
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
|
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
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
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
|
-
|
787
|
+
Redis Engine Creation
|
609
788
|
***************************************************************************** */
|
610
789
|
|
611
|
-
|
612
|
-
*
|
613
|
-
|
614
|
-
|
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
|
-
|
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
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
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
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
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
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
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
|
-
|
804
|
-
|
805
|
-
|
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
|
-
*
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
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
|
}
|