iodine 0.4.19 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/rb-registry.h
CHANGED
data/ext/iodine/redis_engine.c
CHANGED
@@ -1,398 +1,836 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz segev, 2017
|
3
|
-
License: MIT
|
4
|
-
of), which might be subject to their own licenses.
|
2
|
+
Copyright: Boaz segev, 2016-2017
|
3
|
+
License: MIT
|
5
4
|
|
6
|
-
Feel free to copy, use and enjoy
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
7
6
|
*/
|
8
7
|
#include "spnlock.inc"
|
9
8
|
|
10
|
-
#include "
|
11
|
-
#include "
|
9
|
+
#include "fio_llist.h"
|
10
|
+
#include "fiobj4sock.h"
|
12
11
|
#include "redis_engine.h"
|
13
|
-
#include "
|
12
|
+
#include "resp_parser.h"
|
14
13
|
|
15
|
-
#
|
14
|
+
#define REDIS_READ_BUFFER 8192
|
16
15
|
/* *****************************************************************************
|
17
|
-
|
16
|
+
The Redis Engine and Callbacks Object
|
18
17
|
***************************************************************************** */
|
19
18
|
|
20
19
|
typedef struct {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
uintptr_t id_protection;
|
21
|
+
pubsub_engine_s en;
|
22
|
+
struct redis_engine_internal_s {
|
23
|
+
protocol_s protocol;
|
24
|
+
uintptr_t uuid;
|
25
|
+
resp_parser_s parser;
|
26
|
+
uintptr_t is_pub;
|
27
|
+
FIOBJ str;
|
28
|
+
FIOBJ ary;
|
29
|
+
uintptr_t ary_count;
|
30
|
+
uintptr_t buf_pos;
|
31
|
+
} pub_data, sub_data;
|
32
|
+
fio_ls_embd_s callbacks;
|
33
|
+
spn_lock_i lock;
|
28
34
|
char *address;
|
29
35
|
char *port;
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
char *auth;
|
37
|
+
FIOBJ last_ch;
|
38
|
+
size_t auth_len;
|
39
|
+
size_t ref;
|
40
|
+
uint8_t ping_int;
|
41
|
+
uint8_t sent;
|
42
|
+
uint8_t flag;
|
43
|
+
uint8_t buf[];
|
36
44
|
} redis_engine_s;
|
37
45
|
|
38
46
|
typedef struct {
|
39
|
-
|
40
|
-
void (*callback)(pubsub_engine_s *e,
|
47
|
+
fio_ls_embd_s node;
|
48
|
+
void (*callback)(pubsub_engine_s *e, FIOBJ reply, void *udata);
|
41
49
|
void *udata;
|
42
|
-
size_t
|
43
|
-
uint8_t
|
44
|
-
}
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
size_t cmd_len;
|
51
|
+
uint8_t cmd[];
|
52
|
+
} redis_commands_s;
|
53
|
+
|
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))
|
56
|
+
|
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. */
|
64
|
+
#define parser2data(prsr) \
|
65
|
+
FIO_LS_EMBD_OBJ(struct redis_engine_internal_s, parser, (prsr))
|
66
|
+
|
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
|
+
|
72
|
+
/** cleans up and frees the engine data. */
|
73
|
+
static inline void redis_free(redis_engine_s *r) {
|
74
|
+
if (spn_sub(&r->ref, 1))
|
75
|
+
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);
|
78
|
+
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)));
|
52
82
|
}
|
53
|
-
|
83
|
+
free(r);
|
54
84
|
}
|
55
85
|
|
86
|
+
/**
|
87
|
+
* Defined later, converts FIOBJ objects into a RESP string (client mode).
|
88
|
+
*
|
89
|
+
* Don't call `fiobj_free`, object will self-destruct.
|
90
|
+
*/
|
91
|
+
static FIOBJ fiobj2resp_tmp(FIOBJ obj1, FIOBJ obj2);
|
92
|
+
|
56
93
|
/* *****************************************************************************
|
57
|
-
|
94
|
+
Command / Callback handling
|
58
95
|
***************************************************************************** */
|
59
|
-
static void redis_pub_send(void *e, void *uuid) {
|
60
|
-
redis_engine_s *r = e;
|
61
|
-
callbacks_s *cb;
|
62
|
-
spn_lock(&r->lock);
|
63
96
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
dealloc_engine(r);
|
74
|
-
(void)uuid;
|
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);
|
75
106
|
}
|
76
107
|
|
77
|
-
|
78
|
-
|
79
|
-
|
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;
|
80
128
|
}
|
81
129
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
(char *)resp_obj2str(resp_obj2arr(msg)->array[1])->string,
|
93
|
-
.len = resp_obj2str(resp_obj2arr(msg)->array[1])->len},
|
94
|
-
.msg = {
|
95
|
-
.data =
|
96
|
-
(char *)resp_obj2str(resp_obj2arr(msg)->array[2])->string,
|
97
|
-
.len = resp_obj2str(resp_obj2arr(msg)->array[2])->len});
|
98
|
-
return;
|
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);
|
99
140
|
}
|
100
|
-
(void)uuid;
|
101
141
|
}
|
102
142
|
|
103
|
-
static void
|
104
|
-
|
105
|
-
callbacks_s *cb;
|
143
|
+
static void redis_cmd_reply(redis_engine_s *r, FIOBJ reply) {
|
144
|
+
uint8_t schedule = 0;
|
106
145
|
spn_lock(&r->lock);
|
107
|
-
|
146
|
+
r->sent = 0;
|
147
|
+
fio_ls_embd_s *node = fio_ls_embd_shift(&r->callbacks);
|
148
|
+
schedule = fio_ls_embd_any(&r->callbacks);
|
108
149
|
spn_unlock(&r->lock);
|
109
|
-
if (
|
110
|
-
|
111
|
-
if (cb->callback)
|
112
|
-
cb->callback(&r->engine, msg, cb->udata);
|
113
|
-
free(cb);
|
114
|
-
} else {
|
115
|
-
uint8_t buffer[64] = {0};
|
116
|
-
size_t len = 63;
|
117
|
-
resp_format(NULL, buffer, &len, msg);
|
150
|
+
if (!node) {
|
151
|
+
/* TODO: possible ping? from server?! not likely... */
|
118
152
|
fprintf(stderr,
|
119
|
-
"
|
120
|
-
|
121
|
-
len, (char *)buffer);
|
153
|
+
"WARNING: (redis) received a reply when no command was sent\n");
|
154
|
+
return;
|
122
155
|
}
|
123
|
-
(void)
|
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));
|
124
161
|
}
|
125
162
|
|
126
163
|
/* *****************************************************************************
|
127
|
-
|
164
|
+
Connection Establishment
|
128
165
|
***************************************************************************** */
|
129
166
|
|
130
|
-
static
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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;
|
136
177
|
}
|
137
178
|
|
138
|
-
static
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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);
|
145
186
|
|
146
|
-
|
147
|
-
|
148
|
-
if (!defer_fork_is_active()) {
|
149
|
-
dealloc_engine(r);
|
187
|
+
if (!facil_is_running() || !r->flag) {
|
188
|
+
sock_close(uuid);
|
150
189
|
return;
|
151
190
|
}
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
191
|
+
|
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);
|
203
|
+
} else {
|
204
|
+
r->sent = 0;
|
205
|
+
defer(redis_send_cmd_queue, r, NULL);
|
163
206
|
}
|
164
|
-
|
207
|
+
fprintf(stderr, "INFO: (redis %d) publishing connection established.\n",
|
208
|
+
(int)getpid());
|
165
209
|
}
|
166
|
-
|
167
|
-
static void
|
168
|
-
redis_engine_s *r =
|
169
|
-
|
170
|
-
|
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);
|
171
219
|
return;
|
172
220
|
}
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
}
|
221
|
+
|
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);
|
182
229
|
}
|
183
|
-
|
230
|
+
fprintf(stderr, "INFO: (redis %d) subscription connection established.\n",
|
231
|
+
(int)getpid());
|
184
232
|
}
|
185
233
|
|
186
|
-
static void
|
187
|
-
|
188
|
-
|
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);
|
189
241
|
return;
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
r->pub_state = 1;
|
196
|
-
spn_lock(&r->lock);
|
197
|
-
callbacks_s *cb;
|
198
|
-
fio_list_for_each(callbacks_s, node, cb, r->callbacks) { cb->sent = 0; }
|
199
|
-
spn_unlock(&r->lock);
|
200
|
-
schedule_pub_send(r, uuid);
|
242
|
+
}
|
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;
|
201
247
|
}
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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);
|
206
252
|
return;
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
253
|
+
}
|
254
|
+
if (facil_parent_pid() != getpid()) {
|
255
|
+
/* respawned as worker */
|
256
|
+
redis_on_pub_connect_fail(uuid, pr);
|
257
|
+
return;
|
258
|
+
}
|
259
|
+
r->sub_data.uuid = 0;
|
260
|
+
defer(redis_deferred_connect, r, (void *)0);
|
214
261
|
(void)uuid;
|
215
262
|
}
|
216
263
|
|
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);
|
271
|
+
|
272
|
+
} 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);
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
217
280
|
/* *****************************************************************************
|
218
|
-
|
281
|
+
The Protocol layer (connection handling)
|
219
282
|
***************************************************************************** */
|
220
283
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
284
|
+
static void redis_on_data(intptr_t uuid, protocol_s *pr) {
|
285
|
+
redis_engine_s *r = prot2redis(pr);
|
286
|
+
struct redis_engine_internal_s *internal =
|
287
|
+
FIO_LS_EMBD_OBJ(struct redis_engine_internal_s, protocol, pr);
|
288
|
+
uint8_t *buf;
|
289
|
+
if (internal->is_pub) {
|
290
|
+
buf = r->buf + REDIS_READ_BUFFER;
|
291
|
+
} else {
|
292
|
+
buf = r->buf;
|
227
293
|
}
|
228
|
-
|
229
|
-
|
230
|
-
|
294
|
+
ssize_t i = sock_read(uuid, buf + internal->buf_pos,
|
295
|
+
REDIS_READ_BUFFER - internal->buf_pos);
|
296
|
+
if (i <= 0)
|
297
|
+
return;
|
298
|
+
internal->buf_pos += i;
|
299
|
+
i = resp_parse(&internal->parser, buf, internal->buf_pos);
|
300
|
+
if (i) {
|
301
|
+
memmove(buf, buf + internal->buf_pos - i, i);
|
231
302
|
}
|
232
|
-
|
233
|
-
: resp_str2obj("SUBSCRIBE", 9);
|
234
|
-
resp_obj2arr(cmd)->array[1] =
|
235
|
-
(ch ? resp_str2obj(ch, ch_len) : resp_nil2obj());
|
236
|
-
void *buffer = malloc(32 + ch_len);
|
237
|
-
size_t size = 32 + ch_len;
|
238
|
-
if (resp_format(e->sub_parser, buffer, &size, cmd))
|
239
|
-
fprintf(stderr, "ERROR: RESP format? size = %lu ch = %lu\n", size, ch_len);
|
240
|
-
sock_write2(.uuid = e->sub, .buffer = buffer, .length = size, .move = 1);
|
241
|
-
resp_free_object(cmd);
|
242
|
-
return 0;
|
303
|
+
internal->buf_pos = i;
|
243
304
|
}
|
244
305
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
+
}
|
249
321
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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);
|
332
|
+
} else {
|
333
|
+
redis_free(r);
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
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);
|
340
|
+
(void)pr;
|
341
|
+
}
|
342
|
+
|
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);
|
346
|
+
(void)pr;
|
347
|
+
}
|
348
|
+
|
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);
|
254
355
|
return;
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
size_t size = 32 + ch_len;
|
261
|
-
if (!resp_format(e->sub_parser, buffer, &size, cmd) && size <= (32 + ch_len))
|
262
|
-
sock_write2(.uuid = e->sub, .buffer = buffer, .length = size, .move = 1);
|
263
|
-
resp_free_object(cmd);
|
264
|
-
}
|
265
|
-
|
266
|
-
/** Should return 0 on success and -1 on failure. */
|
267
|
-
static int publish(const pubsub_engine_s *eng, const char *ch, size_t ch_len,
|
268
|
-
const char *msg, size_t msg_len, uint8_t use_pattern) {
|
269
|
-
if (!msg || use_pattern || !ch)
|
270
|
-
return -1;
|
271
|
-
resp_object_s *cmd = resp_arr2obj(3, NULL);
|
272
|
-
if (!cmd)
|
273
|
-
return -1;
|
274
|
-
resp_obj2arr(cmd)->array[0] = resp_str2obj("PUBLISH", 7);
|
275
|
-
resp_obj2arr(cmd)->array[1] = resp_str2obj(ch, ch_len);
|
276
|
-
resp_obj2arr(cmd)->array[2] = resp_str2obj(msg, msg_len);
|
277
|
-
redis_engine_send((pubsub_engine_s *)eng, cmd, NULL, NULL);
|
278
|
-
resp_free_object(cmd);
|
279
|
-
return 0;
|
356
|
+
}
|
357
|
+
redis_commands_s *cmd = malloc(sizeof(*cmd) + 15);
|
358
|
+
*cmd = (redis_commands_s){.cmd_len = 14};
|
359
|
+
memcpy(cmd->cmd, "*1\r\n$4\r\nPING\r\n\0", 15);
|
360
|
+
redis_attach_cmd(r, cmd);
|
280
361
|
}
|
281
362
|
|
282
363
|
/* *****************************************************************************
|
283
|
-
|
364
|
+
Engine Callbacks
|
284
365
|
***************************************************************************** */
|
285
366
|
|
286
|
-
static void
|
287
|
-
|
288
|
-
(
|
289
|
-
|
290
|
-
|
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)
|
374
|
+
fiobj_str_write(cmd, "*2\r\n$10\r\nPSUBSCRIBE\r\n$", 22);
|
375
|
+
else
|
376
|
+
fiobj_str_write(cmd, "*2\r\n$9\r\nSUBSCRIBE\r\n$", 20);
|
377
|
+
fiobj_str_join(cmd, fiobj_num_tmp(ch_str.len));
|
378
|
+
fiobj_str_write(cmd, "\r\n", 2);
|
379
|
+
fiobj_str_write(cmd, ch_str.data, ch_str.len);
|
380
|
+
fiobj_str_write(cmd, "\r\n", 2);
|
381
|
+
// {
|
382
|
+
// fio_cstr_s s = fiobj_obj2cstr(cmd);
|
383
|
+
// fprintf(stderr, "%s\n", s.data);
|
384
|
+
// }
|
385
|
+
fiobj_send_free(r->sub_data.uuid, cmd);
|
386
|
+
}
|
387
|
+
}
|
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);
|
396
|
+
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);
|
402
|
+
// {
|
403
|
+
// fio_cstr_s s = fiobj_obj2cstr(cmd);
|
404
|
+
// fprintf(stderr, "%s\n", s.data);
|
405
|
+
// }
|
406
|
+
fiobj_send_free(r->sub_data.uuid, cmd);
|
407
|
+
}
|
291
408
|
}
|
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
|
+
|
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);
|
421
|
+
*cmd = (redis_commands_s){.cmd_len = 0};
|
422
|
+
memcpy(cmd->cmd, "*3\r\n$7\r\nPUBLISH\r\n$", 18);
|
423
|
+
char *buf = (char *)cmd->cmd + 18;
|
424
|
+
buf += fio_ltoa((void *)buf, ch_str.len, 10);
|
425
|
+
*buf++ = '\r';
|
426
|
+
*buf++ = '\n';
|
427
|
+
memcpy(buf, ch_str.data, ch_str.len);
|
428
|
+
buf += ch_str.len;
|
429
|
+
*buf++ = '\r';
|
430
|
+
*buf++ = '\n';
|
431
|
+
*buf++ = '$';
|
432
|
+
msg_str = fiobj_obj2cstr(msg);
|
433
|
+
buf += fio_ltoa(buf, msg_str.len, 10);
|
434
|
+
*buf++ = '\r';
|
435
|
+
*buf++ = '\n';
|
436
|
+
memcpy(buf, msg_str.data, msg_str.len);
|
437
|
+
buf += msg_str.len;
|
438
|
+
*buf++ = '\r';
|
439
|
+
*buf++ = '\n';
|
440
|
+
*buf = 0;
|
441
|
+
// fprintf(stderr, "%s\n", cmd->cmd);
|
442
|
+
cmd->cmd_len = (uintptr_t)buf - (uintptr_t)(cmd + 1);
|
443
|
+
redis_attach_cmd(r, cmd);
|
444
|
+
fiobj_free(msg);
|
445
|
+
return 0;
|
446
|
+
}
|
447
|
+
/* *****************************************************************************
|
448
|
+
Object Creation
|
449
|
+
***************************************************************************** */
|
292
450
|
|
293
|
-
|
294
|
-
|
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
|
+
}
|
463
|
+
}
|
295
464
|
|
296
|
-
function names speak for themselves ;-)
|
297
|
-
*/
|
298
465
|
#undef redis_engine_create
|
299
|
-
pubsub_engine_s *redis_engine_create(struct redis_engine_create_args
|
300
|
-
if (!
|
466
|
+
pubsub_engine_s *redis_engine_create(struct redis_engine_create_args args) {
|
467
|
+
if (!args.address)
|
301
468
|
return NULL;
|
302
|
-
|
303
|
-
|
304
|
-
size_t port_len =
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
.
|
317
|
-
.
|
318
|
-
.
|
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 */
|
319
524
|
};
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
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;
|
545
|
+
}
|
339
546
|
|
340
|
-
|
341
|
-
|
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");
|
551
|
+
return;
|
552
|
+
}
|
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);
|
559
|
+
}
|
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);
|
342
567
|
}
|
343
568
|
|
344
569
|
/**
|
345
|
-
|
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,
|
584
|
+
void *udata),
|
585
|
+
void *udata) {
|
586
|
+
if (engine == PUBSUB_CLUSTER_ENGINE || engine == PUBSUB_PROCESS_ENGINE) {
|
587
|
+
fprintf(stderr, "WARNING: (redis send) trying to use one of the "
|
588
|
+
"core engines\n");
|
589
|
+
return -1;
|
590
|
+
}
|
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);
|
604
|
+
return 0;
|
605
|
+
}
|
346
606
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
redis_engine_s *r = (redis_engine_s *)engine;
|
607
|
+
/* *****************************************************************************
|
608
|
+
Simple RESP formatting
|
609
|
+
***************************************************************************** */
|
351
610
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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
|
+
}
|
357
647
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
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);
|
675
|
+
}
|
676
|
+
return dest;
|
362
677
|
}
|
363
678
|
|
364
679
|
/* *****************************************************************************
|
365
|
-
|
680
|
+
RESP parser callbacks
|
366
681
|
***************************************************************************** */
|
367
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;
|
691
|
+
}
|
692
|
+
|
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
|
+
}
|
732
|
+
}
|
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;
|
748
|
+
}
|
749
|
+
dest->str = o;
|
750
|
+
}
|
751
|
+
|
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
|
+
}
|
773
|
+
|
368
774
|
/**
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
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;
|
801
|
+
}
|
378
802
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
+
}
|
384
810
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
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;
|
397
835
|
return 0;
|
398
836
|
}
|