iodine 0.6.5 → 0.7.0

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

Potentially problematic release.


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

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -1,169 +0,0 @@
1
- /*
2
- Copyright: Boaz segev, 2016-2017
3
- License: MIT except for any non-public-domain algorithms (none that I'm aware
4
- of), which might be subject to their own licenses.
5
-
6
- Feel free to copy, use and enjoy in accordance with to the license(s).
7
- */
8
- #ifndef H_FIO_SHA2_H
9
- #define H_FIO_SHA2_H
10
-
11
- // clang-format off
12
- #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
13
- # if defined(__has_include)
14
- # if __has_include(<endian.h>)
15
- # include <endian.h>
16
- # elif __has_include(<sys/endian.h>)
17
- # include <sys/endian.h>
18
- # endif
19
- # endif
20
- # if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
21
- __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
22
- # define __BIG_ENDIAN__
23
- # endif
24
- #endif
25
-
26
- #ifndef UNUSED_FUNC
27
- # define UNUSED_FUNC __attribute__((unused))
28
- #endif
29
- // clang-format on
30
-
31
- #include <stdlib.h>
32
- #include <stdint.h>
33
-
34
- /* *****************************************************************************
35
- C++ extern
36
- */
37
- #if defined(__cplusplus)
38
- extern "C" {
39
- #endif
40
-
41
- /* ***************************************************************************
42
- SHA-2 hashing
43
- */
44
-
45
- /**
46
- SHA-2 function variants.
47
-
48
- This enum states the different SHA-2 function variants. placing SHA_512 at the
49
- beginning is meant to set this variant as the default (in case a 0 is passed).
50
- */
51
- typedef enum {
52
- SHA_512 = 1,
53
- SHA_512_256 = 3,
54
- SHA_512_224 = 5,
55
- SHA_384 = 7,
56
- SHA_256 = 2,
57
- SHA_224 = 4,
58
- } sha2_variant;
59
-
60
- /**
61
- SHA-2 hashing container - you should ignore the contents of this struct.
62
-
63
- The `sha2_s` type will contain all the SHA-2 data required to perform the
64
- hashing, managing it's encoding. If it's stack allocated, no freeing will be
65
- required.
66
-
67
- Use, for example:
68
-
69
- #include "mini-crypt.h"
70
- sha2_s sha2;
71
- fio_sha2_init(&sha2, SHA_512);
72
- fio_sha2_write(&sha2,
73
- "The quick brown fox jumps over the lazy dog", 43);
74
- char *hashed_result = fio_sha2_result(&sha2);
75
-
76
- */
77
- typedef struct {
78
- /* notice: we're counting bits, not bytes. max length: 2^128 bits */
79
- union {
80
- uint8_t bytes[16];
81
- uint8_t matrix[4][4];
82
- uint32_t words_small[4];
83
- uint64_t words[2];
84
- #if defined(__SIZEOF_INT128__)
85
- __uint128_t i;
86
- #endif
87
- } length;
88
- uint8_t buffer[128];
89
- union {
90
- uint32_t i32[16];
91
- uint64_t i64[8];
92
- uint8_t str[65]; /* added 64+1 for the NULL byte.*/
93
- } digest;
94
- sha2_variant type;
95
- } sha2_s;
96
-
97
- /**
98
- Initialize/reset the SHA-2 object.
99
-
100
- SHA-2 is actually a family of functions with different variants. When
101
- initializing the SHA-2 container, you must select the variant you intend to
102
- apply. The following are valid options (see the sha2_variant enum):
103
-
104
- - SHA_512 (== 0)
105
- - SHA_384
106
- - SHA_512_224
107
- - SHA_512_256
108
- - SHA_256
109
- - SHA_224
110
-
111
- */
112
- sha2_s fio_sha2_init(sha2_variant variant);
113
- /**
114
- Writes data to the SHA-2 buffer.
115
- */
116
- void fio_sha2_write(sha2_s *s, const void *data, size_t len);
117
- /**
118
- Finalizes the SHA-2 hash, returning the Hashed data.
119
-
120
- `sha2_result` can be called for the same object multiple times, but the
121
- finalization will only be performed the first time this function is called.
122
- */
123
- char *fio_sha2_result(sha2_s *s);
124
-
125
- /**
126
- An SHA2 helper function that performs initialiation, writing and finalizing.
127
- Uses the SHA2 512 variant.
128
- */
129
- static inline UNUSED_FUNC char *fio_sha2_512(sha2_s *s, const void *data,
130
- size_t len) {
131
- *s = fio_sha2_init(SHA_512);
132
- fio_sha2_write(s, data, len);
133
- return fio_sha2_result(s);
134
- }
135
-
136
- /**
137
- An SHA2 helper function that performs initialiation, writing and finalizing.
138
- Uses the SHA2 256 variant.
139
- */
140
- static inline UNUSED_FUNC char *fio_sha2_256(sha2_s *s, const void *data,
141
- size_t len) {
142
- *s = fio_sha2_init(SHA_256);
143
- fio_sha2_write(s, data, len);
144
- return fio_sha2_result(s);
145
- }
146
-
147
- /**
148
- An SHA2 helper function that performs initialiation, writing and finalizing.
149
- Uses the SHA2 384 variant.
150
- */
151
- static inline UNUSED_FUNC char *fio_sha2_384(sha2_s *s, const void *data,
152
- size_t len) {
153
- *s = fio_sha2_init(SHA_384);
154
- fio_sha2_write(s, data, len);
155
- return fio_sha2_result(s);
156
- }
157
-
158
- #if defined(DEBUG) && DEBUG == 1
159
- void fio_sha2_test(void);
160
- #endif
161
-
162
- /* *****************************************************************************
163
- C++ extern finish
164
- */
165
- #if defined(__cplusplus)
166
- }
167
- #endif
168
-
169
- #endif
@@ -1,867 +0,0 @@
1
- /*
2
- Copyright: Boaz segev, 2017
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #include "spnlock.inc"
8
-
9
- #include "facil.h"
10
- #include "fio_llist.h"
11
- #include "fiobj.h"
12
- #include "pubsub.h"
13
-
14
- #include <errno.h>
15
- #include <signal.h>
16
- #include <stdint.h>
17
- #include <stdio.h>
18
- #include <stdlib.h>
19
- #include <string.h>
20
-
21
- #include "fio_mem.h"
22
-
23
- /* used later on */
24
- static int pubsub_glob_match(uint8_t *data, size_t data_len, uint8_t *pattern,
25
- size_t pat_len);
26
-
27
- #define PUBSUB_FACIL_CLUSTER_CHANNEL_FILTER ((int32_t)-1)
28
- #define PUBSUB_FACIL_CLUSTER_PATTERN_FILTER ((int32_t)-2)
29
- #define PUBSUB_FACIL_CLUSTER_CHANNEL_SUB_FILTER ((int32_t)-3)
30
- #define PUBSUB_FACIL_CLUSTER_PATTERN_SUB_FILTER ((int32_t)-4)
31
- #define PUBSUB_FACIL_CLUSTER_CHANNEL_UNSUB_FILTER ((int32_t)-5)
32
- #define PUBSUB_FACIL_CLUSTER_PATTERN_UNSUB_FILTER ((int32_t)-6)
33
-
34
- /* *****************************************************************************
35
- The Hash Map (macros and the include instruction for `fio_hashmap.h`)
36
- ***************************************************************************** */
37
-
38
- /* the hash key type for string keys */
39
- typedef struct {
40
- uintptr_t hash;
41
- FIOBJ obj;
42
- } fio_hash_key_s;
43
-
44
- static inline int fio_hash_fiobj_keys_eq(fio_hash_key_s a, fio_hash_key_s b) {
45
- if (a.obj == b.obj)
46
- return 1;
47
- fio_cstr_s sa = fiobj_obj2cstr(a.obj);
48
- fio_cstr_s sb = fiobj_obj2cstr(b.obj);
49
- return sa.len == sb.len && !memcmp(sa.data, sb.data, sa.len);
50
- }
51
- /* define the macro to set the key type */
52
- #define FIO_HASH_KEY_TYPE fio_hash_key_s
53
- /* the macro that returns the key's hash value */
54
- #define FIO_HASH_KEY2UINT(key) ((key).hash)
55
- /* Compare the keys using length testing and `memcmp` */
56
- #define FIO_HASH_COMPARE_KEYS(k1, k2) \
57
- ((k1).obj == (k2).obj || fio_hash_fiobj_keys_eq((k1), (k2)))
58
- /* an "all bytes are zero" invalid key */
59
- #define FIO_HASH_KEY_INVALID ((fio_hash_key_s){.obj = FIOBJ_INVALID})
60
- /* tests if a key is the invalid key */
61
- #define FIO_HASH_KEY_ISINVALID(key) ((key).obj == FIOBJ_INVALID && !key.hash)
62
- /* creates a persistent copy of the key's string */
63
- #define FIO_HASH_KEY_COPY(key) \
64
- ((fio_hash_key_s){.hash = (key).hash, .obj = fiobj_dup((key).obj)})
65
- /* frees the allocated string */
66
- #define FIO_HASH_KEY_DESTROY(key) (fiobj_free((key).obj))
67
-
68
- #define FIO_OBJ2KEY(fiobj) \
69
- ((fio_hash_key_s){.hash = fiobj_obj2hash((fiobj)), .obj = (fiobj)})
70
-
71
- #include "fio_hashmap.h"
72
-
73
- /* *****************************************************************************
74
- Channel and Client Data Structures
75
- ***************************************************************************** */
76
-
77
- typedef struct {
78
- /* clients are nodes in a list. */
79
- fio_ls_embd_s node;
80
- /* a reference counter (how many messages pending) */
81
- size_t ref;
82
- /* a subscription counter (protection against multiple unsubscribe calls) */
83
- size_t sub_count;
84
- /* a pointer to the channel data */
85
- void *parent;
86
- /** The on message callback. the `*msg` pointer is to a temporary object. */
87
- void (*on_message)(pubsub_message_s *msg);
88
- /** An optional callback for when a subscription is fully canceled. */
89
- void (*on_unsubscribe)(void *udata1, void *udata2);
90
- /** Opaque user data#1 */
91
- void *udata1;
92
- /** Opaque user data#2 .. using two allows some allocations to be avoided. */
93
- void *udata2;
94
- /** Task lock (per client-channel combination */
95
- spn_lock_i lock;
96
- } client_s;
97
-
98
- typedef struct {
99
- /* the root for the client's list */
100
- fio_ls_embd_s clients;
101
- /** The channel name. */
102
- FIOBJ name;
103
- /** Use pattern matching for channel subscription. */
104
- unsigned use_pattern : 1;
105
- /** Use pattern matching for channel subscription. */
106
- unsigned publish2cluster : 1;
107
- } channel_s;
108
-
109
- static fio_hash_s patterns;
110
- static fio_hash_s channels;
111
- static fio_hash_s clients;
112
- static fio_hash_s engines;
113
- static spn_lock_i lock = SPN_LOCK_INIT;
114
- static spn_lock_i engn_lock = SPN_LOCK_INIT;
115
-
116
- /* *****************************************************************************
117
- Channel and Client Management
118
- ***************************************************************************** */
119
-
120
- /* for engine thingy */
121
- static void pubsub_on_channel_create(channel_s *ch);
122
- /* for engine thingy */
123
- static void pubsub_on_channel_destroy(channel_s *ch);
124
-
125
- static void pubsub_deferred_unsub(void *cl_, void *ignr) {
126
- client_s *cl = cl_;
127
- cl->on_unsubscribe(cl->udata1, cl->udata2);
128
- free(cl);
129
- (void)ignr;
130
- }
131
-
132
- static inline void client_test4free(client_s *cl) {
133
- if (spn_sub(&cl->ref, 1)) {
134
- /* client is still being used. */
135
- return;
136
- }
137
- if (cl->on_unsubscribe) {
138
- /* we'll call the callback before freeing the object. */
139
- defer(pubsub_deferred_unsub, cl, NULL);
140
- return;
141
- }
142
- free(cl);
143
- }
144
-
145
- static inline uint64_t client_compute_hash(client_s client) {
146
- return (((((uint64_t)(client.on_message) *
147
- ((uint64_t)client.udata1 ^ 0x736f6d6570736575ULL)) >>
148
- 5) |
149
- (((uint64_t)(client.on_unsubscribe) *
150
- ((uint64_t)client.udata1 ^ 0x736f6d6570736575ULL))
151
- << 47)) ^
152
- ((uint64_t)client.udata2 ^ 0x646f72616e646f6dULL));
153
- }
154
-
155
- static client_s *pubsub_client_new(client_s client, channel_s channel) {
156
- if (!client.on_message || !channel.name) {
157
- fprintf(stderr,
158
- "ERROR: (pubsub) subscription request failed. missing on of:\n"
159
- " 1. channel name.\n"
160
- " 2. massage handler.\n");
161
- if (client.on_unsubscribe)
162
- client.on_unsubscribe(client.udata1, client.udata2);
163
- return NULL;
164
- }
165
- uint64_t channel_hash = fiobj_obj2hash(channel.name);
166
- uint64_t client_hash = client_compute_hash(client);
167
- spn_lock(&lock);
168
- /* ignore if client exists. */
169
- client_s *cl = fio_hash_find(
170
- &clients, (fio_hash_key_s){.hash = client_hash, .obj = channel.name});
171
- if (cl) {
172
- cl->sub_count++;
173
- spn_unlock(&lock);
174
- return cl;
175
- }
176
- /* no client, we need a new client */
177
- cl = malloc(sizeof(*cl));
178
- if (!cl) {
179
- perror("FATAL ERROR: (pubsub) client memory allocation error");
180
- exit(errno);
181
- }
182
- *cl = client;
183
- cl->ref = 1;
184
- cl->sub_count = 1;
185
-
186
- fio_hash_insert(
187
- &clients, (fio_hash_key_s){.hash = client_hash, .obj = channel.name}, cl);
188
-
189
- /* test for existing channel */
190
- fio_hash_s *ch_hashmap = (channel.use_pattern ? &patterns : &channels);
191
- channel_s *ch = fio_hash_find(
192
- ch_hashmap, (fio_hash_key_s){.hash = channel_hash, .obj = channel.name});
193
- if (!ch) {
194
- /* open new channel */
195
- ch = malloc(sizeof(*ch));
196
- if (!ch) {
197
- perror("FATAL ERROR: (pubsub) channel memory allocation error");
198
- exit(errno);
199
- }
200
- *ch = (channel_s){
201
- .name = fiobj_dup(channel.name),
202
- .clients = FIO_LS_INIT(ch->clients),
203
- .use_pattern = channel.use_pattern,
204
- .publish2cluster = channel.publish2cluster,
205
- };
206
- fio_hash_insert(ch_hashmap,
207
- (fio_hash_key_s){.hash = channel_hash, .obj = channel.name},
208
- ch);
209
- pubsub_on_channel_create(ch);
210
- } else {
211
- /* channel exists */
212
- }
213
- cl->parent = ch;
214
- fio_ls_embd_push(&ch->clients, &cl->node);
215
- spn_unlock(&lock);
216
- return cl;
217
- }
218
-
219
- /** Destroys a client (and empty channels as well) */
220
- static int pubsub_client_destroy(client_s *client) {
221
- if (!client || !client->parent)
222
- return -1;
223
- channel_s *ch = client->parent;
224
-
225
- fio_hash_s *ch_hashmap = (ch->use_pattern ? &patterns : &channels);
226
- uint64_t channel_hash = fiobj_obj2hash(ch->name);
227
- uint64_t client_hash = client_compute_hash(*client);
228
- uint8_t is_ch_any;
229
- spn_lock(&lock);
230
- if ((client->sub_count -= 1)) {
231
- spn_unlock(&lock);
232
- return 0;
233
- }
234
- fio_ls_embd_remove(&client->node);
235
- fio_hash_insert(&clients,
236
- (fio_hash_key_s){.hash = client_hash, .obj = ch->name}, NULL);
237
- is_ch_any = fio_ls_embd_any(&ch->clients);
238
- if (is_ch_any) {
239
- /* channel still has client - we should keep it */
240
- (void)0;
241
- } else {
242
- channel_s *test = fio_hash_insert(
243
- ch_hashmap, (fio_hash_key_s){.hash = channel_hash, .obj = ch->name},
244
- NULL);
245
- if (test != ch) {
246
- fprintf(stderr,
247
- "FATAL ERROR: (pubsub) channel database corruption detected.\n");
248
- exit(-1);
249
- }
250
- if (ch_hashmap->capa > 32 && (ch_hashmap->pos >> 1) > ch_hashmap->count) {
251
- fio_hash_compact(ch_hashmap);
252
- }
253
- }
254
- if ((clients.pos >> 1) > clients.count) {
255
- // fprintf(stderr, "INFO: (pubsub) reducing client hash map %zu",
256
- // (size_t)clients.capa);
257
- fio_hash_compact(&clients);
258
- // fprintf(stderr, " => %zu (%zu clients)\n", (size_t)clients.capa,
259
- // (size_t)clients.count);
260
- }
261
- spn_unlock(&lock);
262
- client_test4free(client);
263
- if (is_ch_any) {
264
- return 0;
265
- }
266
- pubsub_on_channel_destroy(ch);
267
- fiobj_free(ch->name);
268
- free(ch);
269
- return 0;
270
- }
271
-
272
- /** finds a pointer to an existing client (matching registration details) */
273
- static inline client_s *pubsub_client_find(client_s client, channel_s channel) {
274
- /* the logic is written twice due to locking logic (we don't want to release
275
- * the lock for `pubsub_client_new`)
276
- */
277
- if (!client.on_message || !channel.name) {
278
- return NULL;
279
- }
280
- uint64_t client_hash = client_compute_hash(client);
281
- spn_lock(&lock);
282
- client_s *cl = fio_hash_find(
283
- &clients, (fio_hash_key_s){.hash = client_hash, .obj = channel.name});
284
- spn_unlock(&lock);
285
- return cl;
286
- }
287
-
288
- /* *****************************************************************************
289
- Subscription API
290
- ***************************************************************************** */
291
-
292
- /**
293
- * Subscribes to a specific channel.
294
- *
295
- * Returns a subscription pointer or NULL (failure).
296
- */
297
- #undef pubsub_subscribe
298
- pubsub_sub_pt pubsub_subscribe(struct pubsub_subscribe_args args) {
299
- channel_s channel = {
300
- .name = args.channel,
301
- .clients = FIO_LS_INIT(channel.clients),
302
- .use_pattern = args.use_pattern,
303
- .publish2cluster = 1,
304
- };
305
- client_s client = {.on_message = args.on_message,
306
- .on_unsubscribe = args.on_unsubscribe,
307
- .udata1 = args.udata1,
308
- .udata2 = args.udata2};
309
- return (pubsub_sub_pt)pubsub_client_new(client, channel);
310
- }
311
- #define pubsub_subscribe(...) \
312
- pubsub_subscribe((struct pubsub_subscribe_args){__VA_ARGS__})
313
-
314
- /**
315
- * This helper searches for an existing subscription.
316
- *
317
- * Use with care, NEVER call `pubsub_unsubscribe` more times than you have
318
- * called `pubsub_subscribe`, since the subscription handle memory is realesed
319
- * onnce the reference count reaches 0.
320
- *
321
- * Returns a subscription pointer or NULL (none found).
322
- */
323
- #undef pubsub_find_sub
324
- pubsub_sub_pt pubsub_find_sub(struct pubsub_subscribe_args args) {
325
- channel_s channel = {.name = args.channel, .use_pattern = args.use_pattern};
326
- client_s client = {.on_message = args.on_message,
327
- .on_unsubscribe = args.on_unsubscribe,
328
- .udata1 = args.udata1,
329
- .udata2 = args.udata2};
330
- return (pubsub_sub_pt)pubsub_client_find(client, channel);
331
- }
332
- #define pubsub_find_sub(...) \
333
- pubsub_find_sub((struct pubsub_subscribe_args){__VA_ARGS__})
334
-
335
- /**
336
- * This helper returns a temporary handle to an existing subscription's channel.
337
- *
338
- * To keep the handle beyond the lifetime of the subscription, use `fiobj_dup`.
339
- */
340
- FIOBJ pubsub_sub_channel(pubsub_sub_pt sub) {
341
- return (((channel_s *)((client_s *)sub)->parent))->name;
342
- }
343
-
344
- /**
345
- * Unsubscribes from a specific subscription.
346
- *
347
- * Returns 0 on success and -1 on failure.
348
- */
349
- int pubsub_unsubscribe(pubsub_sub_pt subscription) {
350
- if (!subscription)
351
- return -1;
352
- return pubsub_client_destroy((client_s *)subscription);
353
- }
354
-
355
- /**
356
- * Publishes a message to a channel belonging to a pub/sub service (engine).
357
- *
358
- * Returns 0 on success and -1 on failure.
359
- */
360
- #undef pubsub_publish
361
- int pubsub_publish(struct pubsub_message_s m) {
362
- if (!m.channel || !m.message)
363
- return -1;
364
- if (!m.engine) {
365
- m.engine = PUBSUB_DEFAULT_ENGINE;
366
- if (!m.engine) {
367
- m.engine = PUBSUB_CLUSTER_ENGINE;
368
- if (!m.engine) {
369
- fprintf(stderr,
370
- "FATAL ERROR: (pubsub) engine pointer data corrupted! \n");
371
- exit(-1);
372
- }
373
- }
374
- }
375
- return m.engine->publish(m.engine, m.channel, m.message);
376
- // We don't call `fiobj_free` because the data isn't placed into an accessible
377
- // object.
378
- }
379
- #define pubsub_publish(...) \
380
- pubsub_publish((struct pubsub_message_s){__VA_ARGS__})
381
-
382
- /* *****************************************************************************
383
- Engine handling and Management
384
- ***************************************************************************** */
385
-
386
- /* runs in lock(!) let'm all know */
387
- static void pubsub_on_channel_create(channel_s *ch) {
388
- if (ch->publish2cluster)
389
- PUBSUB_CLUSTER_ENGINE->subscribe(PUBSUB_CLUSTER_ENGINE, ch->name,
390
- ch->use_pattern);
391
- spn_lock(&engn_lock);
392
- FIO_HASH_FOR_LOOP(&engines, e_) {
393
- if (!e_ || !e_->obj)
394
- continue;
395
- pubsub_engine_s *e = e_->obj;
396
- e->subscribe(e, ch->name, ch->use_pattern);
397
- }
398
- spn_unlock(&engn_lock);
399
- }
400
-
401
- /* runs in lock(!) let'm all know */
402
- static void pubsub_on_channel_destroy(channel_s *ch) {
403
- if (ch->publish2cluster)
404
- PUBSUB_CLUSTER_ENGINE->unsubscribe(PUBSUB_CLUSTER_ENGINE, ch->name,
405
- ch->use_pattern);
406
- spn_lock(&engn_lock);
407
- FIO_HASH_FOR_LOOP(&engines, e_) {
408
- if (!e_ || !e_->obj)
409
- continue;
410
- pubsub_engine_s *e = e_->obj;
411
- e->unsubscribe(e, ch->name, ch->use_pattern);
412
- }
413
- spn_unlock(&engn_lock);
414
- }
415
-
416
- /** Registers an engine, so it's callback can be called. */
417
- void pubsub_engine_register(pubsub_engine_s *engine) {
418
- if (!engine) {
419
- return;
420
- }
421
- spn_lock(&engn_lock);
422
- fio_hash_insert(
423
- &engines,
424
- (fio_hash_key_s){.hash = (uintptr_t)engine, .obj = FIOBJ_INVALID},
425
- engine);
426
- if (engine->subscribe) {
427
- FIO_HASH_FOR_LOOP(&channels, i) {
428
- channel_s *ch = i->obj;
429
- engine->subscribe(engine, ch->name, 0);
430
- }
431
- FIO_HASH_FOR_LOOP(&patterns, i) {
432
- channel_s *ch = i->obj;
433
- engine->subscribe(engine, ch->name, 1);
434
- }
435
- }
436
- spn_unlock(&engn_lock);
437
- }
438
-
439
- /** Unregisters an engine, so it could be safely destroyed. */
440
- void pubsub_engine_deregister(pubsub_engine_s *engine) {
441
- if (engines.map == NULL) {
442
- return;
443
- }
444
- spn_lock(&engn_lock);
445
- if (PUBSUB_DEFAULT_ENGINE == engine)
446
- PUBSUB_DEFAULT_ENGINE = (pubsub_engine_s *)PUBSUB_CLUSTER_ENGINE;
447
- void *old = fio_hash_insert(
448
- &engines,
449
- (fio_hash_key_s){.hash = (uintptr_t)engine, .obj = FIOBJ_INVALID}, NULL);
450
- fio_hash_compact(&engines);
451
- spn_unlock(&engn_lock);
452
- // if (!old) {
453
- // fprintf(stderr, "WARNING: (pub/sub) Deregister error,"
454
- // " not registered?\n");
455
- // }
456
- }
457
-
458
- /**
459
- * Engines can ask facil.io to resubscribe to all active channels.
460
- *
461
- * This allows engines that lost their connection to their Pub/Sub service to
462
- * resubscribe all the currently active channels with the new connection.
463
- *
464
- * CAUTION: This is an evented task... try not to free the engine's memory while
465
- * resubscriptions are under way...
466
- */
467
- void pubsub_engine_resubscribe(pubsub_engine_s *eng) {
468
- spn_lock(&lock);
469
- FIO_HASH_FOR_LOOP(&channels, i) {
470
- channel_s *ch = i->obj;
471
- eng->subscribe(eng, ch->name, 0);
472
- }
473
- FIO_HASH_FOR_LOOP(&patterns, i) {
474
- channel_s *ch = i->obj;
475
- eng->subscribe(eng, ch->name, 1);
476
- }
477
- spn_unlock(&lock);
478
- }
479
-
480
- /* *****************************************************************************
481
- PUBSUB_PROCESS_ENGINE: Single Process Engine and `pubsub_defer`
482
- ***************************************************************************** */
483
-
484
- typedef struct {
485
- size_t ref;
486
- FIOBJ channel;
487
- FIOBJ msg;
488
- } msg_wrapper_s;
489
-
490
- typedef struct {
491
- msg_wrapper_s *wrapper;
492
- pubsub_message_s msg;
493
- } msg_container_s;
494
-
495
- static void msg_wrapper_free(msg_wrapper_s *m) {
496
- if (spn_sub(&m->ref, 1))
497
- return;
498
- fiobj_free(m->channel);
499
- fiobj_free(m->msg);
500
- fio_free(m);
501
- }
502
-
503
- /* calls a client's `on_message` callback */
504
- void pubsub_en_process_deferred_on_message(void *cl_, void *m_) {
505
- msg_wrapper_s *m = m_;
506
- client_s *cl = cl_;
507
- if (spn_trylock(&cl->lock)) {
508
- defer(pubsub_en_process_deferred_on_message, cl, m);
509
- return;
510
- }
511
- msg_container_s arg = {.wrapper = m,
512
- .msg = {
513
- .channel = m->channel,
514
- .message = m->msg,
515
- .subscription = (pubsub_sub_pt)cl,
516
- .udata1 = cl->udata1,
517
- .udata2 = cl->udata2,
518
- }};
519
- cl->on_message(&arg.msg);
520
- spn_unlock(&cl->lock);
521
- msg_wrapper_free(m);
522
- client_test4free(cl_);
523
- }
524
-
525
- /* Must subscribe channel. Failures are ignored. */
526
- void pubsub_en_process_subscribe(const pubsub_engine_s *eng, FIOBJ channel,
527
- uint8_t use_pattern) {
528
- (void)eng;
529
- (void)channel;
530
- (void)use_pattern;
531
- }
532
-
533
- /* Must unsubscribe channel. Failures are ignored. */
534
- void pubsub_en_process_unsubscribe(const pubsub_engine_s *eng, FIOBJ channel,
535
- uint8_t use_pattern) {
536
- (void)eng;
537
- (void)channel;
538
- (void)use_pattern;
539
- }
540
- /** Should return 0 on success and -1 on failure. */
541
- int pubsub_en_process_publish(const pubsub_engine_s *eng, FIOBJ channel,
542
- FIOBJ msg) {
543
- uint64_t channel_hash = fiobj_obj2hash(channel);
544
- msg_wrapper_s *m = fio_malloc(sizeof(*m));
545
- int ret = -1;
546
- if (!m) {
547
- perror("FATAL ERROR: (pubsub) couldn't allocate message wrapper");
548
- exit(errno);
549
- }
550
- *m = (msg_wrapper_s){
551
- .ref = 1, .channel = fiobj_dup(channel), .msg = fiobj_dup(msg)};
552
- spn_lock(&lock);
553
- {
554
- /* test for direct match */
555
- channel_s *ch = fio_hash_find(
556
- &channels, (fio_hash_key_s){.hash = channel_hash, .obj = channel});
557
- if (ch) {
558
- ret = 0;
559
- FIO_LS_EMBD_FOR(&ch->clients, cl_) {
560
- client_s *cl = FIO_LS_EMBD_OBJ(client_s, node, cl_);
561
- spn_add(&m->ref, 1);
562
- spn_add(&cl->ref, 1);
563
- defer(pubsub_en_process_deferred_on_message, cl, m);
564
- }
565
- }
566
- }
567
- /* test for pattern match */
568
- fio_cstr_s ch_str = fiobj_obj2cstr(channel);
569
- FIO_HASH_FOR_LOOP(&patterns, ch_) {
570
- channel_s *ch = (channel_s *)ch_->obj;
571
- fio_cstr_s tmp = fiobj_obj2cstr(ch->name);
572
- if (pubsub_glob_match(ch_str.bytes, ch_str.len, tmp.bytes, tmp.len)) {
573
- ret = 0;
574
- FIO_LS_EMBD_FOR(&ch->clients, cl_) {
575
- client_s *cl = FIO_LS_EMBD_OBJ(client_s, node, cl_);
576
- spn_add(&m->ref, 1);
577
- spn_add(&cl->ref, 1);
578
- defer(pubsub_en_process_deferred_on_message, cl, m);
579
- }
580
- }
581
- }
582
- spn_unlock(&lock);
583
- msg_wrapper_free(m);
584
- return ret;
585
- (void)eng;
586
- }
587
-
588
- const pubsub_engine_s PUBSUB_PROCESS_ENGINE_S = {
589
- .subscribe = pubsub_en_process_subscribe,
590
- .unsubscribe = pubsub_en_process_unsubscribe,
591
- .publish = pubsub_en_process_publish,
592
- };
593
-
594
- const pubsub_engine_s *PUBSUB_PROCESS_ENGINE = &PUBSUB_PROCESS_ENGINE_S;
595
-
596
- /**
597
- * defers message hadling if it can't be performed (i.e., resource is busy) or
598
- * should be fragmented (allowing large tasks to be broken down).
599
- *
600
- * This should only be called from within the `on_message` callback.
601
- *
602
- * It's recommended that the `on_message` callback return immediately following
603
- * this function call, as code might run concurrently.
604
- *
605
- * Uses reference counting for zero copy.
606
- *
607
- * It's impossible to use a different `on_message` callbck without resorting to
608
- * memory allocations... so when in need, manage routing withing the
609
- * `on_message` callback.
610
- */
611
- void pubsub_defer(pubsub_message_s *msg) {
612
- msg_container_s *arg = FIO_LS_EMBD_OBJ(msg_container_s, msg, msg);
613
- spn_add(&arg->wrapper->ref, 1);
614
- spn_add(&((client_s *)arg->msg.subscription)->ref, 1);
615
- defer(pubsub_en_process_deferred_on_message, arg->msg.subscription,
616
- arg->wrapper);
617
- }
618
-
619
- /* *****************************************************************************
620
- Cluster Engine
621
- ***************************************************************************** */
622
-
623
- /* Must subscribe channel. Failures are ignored. */
624
- void pubsub_en_cluster_subscribe(const pubsub_engine_s *eng, FIOBJ channel,
625
- uint8_t use_pattern) {
626
- if (facil_is_running()) {
627
- facil_cluster_send((use_pattern ? PUBSUB_FACIL_CLUSTER_PATTERN_SUB_FILTER
628
- : PUBSUB_FACIL_CLUSTER_CHANNEL_SUB_FILTER),
629
- channel, FIOBJ_INVALID);
630
- }
631
- (void)eng;
632
- }
633
-
634
- /* Must unsubscribe channel. Failures are ignored. */
635
- void pubsub_en_cluster_unsubscribe(const pubsub_engine_s *eng, FIOBJ channel,
636
- uint8_t use_pattern) {
637
- if (facil_is_running()) {
638
- facil_cluster_send((use_pattern
639
- ? PUBSUB_FACIL_CLUSTER_PATTERN_UNSUB_FILTER
640
- : PUBSUB_FACIL_CLUSTER_CHANNEL_UNSUB_FILTER),
641
- channel, FIOBJ_INVALID);
642
- }
643
- (void)eng;
644
- }
645
- /** Should return 0 on success and -1 on failure. */
646
- int pubsub_en_cluster_publish(const pubsub_engine_s *eng, FIOBJ channel,
647
- FIOBJ msg) {
648
- if (facil_is_running()) {
649
- facil_cluster_send(PUBSUB_FACIL_CLUSTER_CHANNEL_FILTER, channel, msg);
650
- }
651
- return PUBSUB_PROCESS_ENGINE->publish(PUBSUB_PROCESS_ENGINE, channel, msg);
652
- (void)eng;
653
- }
654
-
655
- const pubsub_engine_s PUBSUB_CLUSTER_ENGINE_S = {
656
- .subscribe = pubsub_en_cluster_subscribe,
657
- .unsubscribe = pubsub_en_cluster_unsubscribe,
658
- .publish = pubsub_en_cluster_publish,
659
- };
660
-
661
- pubsub_engine_s const *PUBSUB_CLUSTER_ENGINE = &PUBSUB_CLUSTER_ENGINE_S;
662
- pubsub_engine_s *PUBSUB_DEFAULT_ENGINE =
663
- (pubsub_engine_s *)&PUBSUB_CLUSTER_ENGINE_S;
664
- /* *****************************************************************************
665
- Cluster Initialization and Messaging Protocol
666
- ***************************************************************************** */
667
-
668
- /* does nothing */
669
- static void pubsub_cluster_on_message_noop(pubsub_message_s *msg) { (void)msg; }
670
-
671
- /* registers to the channel */
672
- static void pubsub_cluster_subscribe2channel(void *ch, void *flag) {
673
- channel_s channel = {
674
- .name = (FIOBJ)ch,
675
- .clients = FIO_LS_INIT(channel.clients),
676
- .use_pattern = ((uintptr_t)flag & 1),
677
- .publish2cluster = 0,
678
- };
679
- client_s client = {.on_message = pubsub_cluster_on_message_noop};
680
- pubsub_client_new(client, channel);
681
- }
682
-
683
- /* deregisters from the channel if required */
684
- static void pubsub_cluster_unsubscribe2channel(void *ch, void *flag) {
685
- channel_s channel = {
686
- .name = (FIOBJ)ch,
687
- .clients = FIO_LS_INIT(channel.clients),
688
- .use_pattern = ((uintptr_t)flag & 1),
689
- .publish2cluster = 0,
690
- };
691
- client_s client = {.on_message = pubsub_cluster_on_message_noop};
692
- client_s *sub = pubsub_client_find(client, channel);
693
- pubsub_client_destroy(sub);
694
- }
695
-
696
- static void pubsub_cluster_facil_message(int32_t filter, FIOBJ channel,
697
- FIOBJ message) {
698
- // fprintf(stderr, "(%d) pubsub message filter %d (%s)\n", getpid(), filter,
699
- // fiobj_obj2cstr(channel).name);
700
- switch (filter) {
701
- case PUBSUB_FACIL_CLUSTER_CHANNEL_FILTER:
702
- PUBSUB_PROCESS_ENGINE->publish(PUBSUB_PROCESS_ENGINE, channel, message);
703
- break;
704
- case PUBSUB_FACIL_CLUSTER_CHANNEL_SUB_FILTER:
705
- pubsub_cluster_subscribe2channel((void *)channel, 0);
706
- break;
707
- case PUBSUB_FACIL_CLUSTER_PATTERN_SUB_FILTER:
708
- pubsub_cluster_subscribe2channel((void *)channel, (void *)1);
709
- break;
710
- case PUBSUB_FACIL_CLUSTER_CHANNEL_UNSUB_FILTER:
711
- pubsub_cluster_unsubscribe2channel((void *)channel, 0);
712
- break;
713
- case PUBSUB_FACIL_CLUSTER_PATTERN_UNSUB_FILTER:
714
- pubsub_cluster_unsubscribe2channel((void *)channel, (void *)1);
715
- break;
716
- }
717
- (void)filter;
718
- }
719
-
720
- void pubsub_cluster_init(void) {
721
- facil_cluster_set_handler(PUBSUB_FACIL_CLUSTER_CHANNEL_FILTER,
722
- pubsub_cluster_facil_message);
723
- facil_cluster_set_handler(PUBSUB_FACIL_CLUSTER_CHANNEL_SUB_FILTER,
724
- pubsub_cluster_facil_message);
725
- facil_cluster_set_handler(PUBSUB_FACIL_CLUSTER_PATTERN_SUB_FILTER,
726
- pubsub_cluster_facil_message);
727
- facil_cluster_set_handler(PUBSUB_FACIL_CLUSTER_CHANNEL_UNSUB_FILTER,
728
- pubsub_cluster_facil_message);
729
- facil_cluster_set_handler(PUBSUB_FACIL_CLUSTER_PATTERN_UNSUB_FILTER,
730
- pubsub_cluster_facil_message);
731
- }
732
-
733
- void pubsub_cluster_on_fork_start(void) {
734
- lock = SPN_LOCK_INIT;
735
- FIO_HASH_FOR_LOOP(&clients, pos) {
736
- if (pos->obj) {
737
- client_s *c = pos->obj;
738
- c->lock = SPN_LOCK_INIT;
739
- }
740
- }
741
- }
742
-
743
- void pubsub_cluster_on_fork_end(void) {
744
- lock = SPN_LOCK_INIT;
745
- FIO_HASH_FOR_LOOP(&engines, pos) {
746
- if (pos->obj) {
747
- pubsub_engine_s *e = pos->obj;
748
- if (e->on_startup)
749
- e->on_startup(e);
750
- }
751
- }
752
- }
753
-
754
- void pubsub_cluster_cleanup(void) {
755
- while (clients.count) {
756
- pubsub_client_destroy(fio_hash_last(&clients, NULL));
757
- }
758
- FIO_HASH_FOR_FREE(&clients, pos) {}
759
- fio_hash_free(&engines);
760
- fio_hash_free(&channels);
761
- fio_hash_free(&patterns);
762
- clients = (fio_hash_s)FIO_HASH_INIT;
763
- engines = (fio_hash_s)FIO_HASH_INIT;
764
- channels = (fio_hash_s)FIO_HASH_INIT;
765
- patterns = (fio_hash_s)FIO_HASH_INIT;
766
- lock = SPN_LOCK_INIT;
767
- }
768
-
769
- /* *****************************************************************************
770
- Glob Matching Helper
771
- ***************************************************************************** */
772
-
773
- /** A binary glob matching helper. Returns 1 on match, otherwise returns 0. */
774
- static int pubsub_glob_match(uint8_t *data, size_t data_len, uint8_t *pattern,
775
- size_t pat_len) {
776
- /* adapted and rewritten, with thankfulness, from the code at:
777
- * https://github.com/opnfv/kvmfornfv/blob/master/kernel/lib/glob.c
778
- *
779
- * Original version's copyright:
780
- * Copyright 2015 Open Platform for NFV Project, Inc. and its contributors
781
- * Under the MIT license.
782
- */
783
-
784
- /*
785
- * Backtrack to previous * on mismatch and retry starting one
786
- * character later in the string. Because * matches all characters
787
- * (no exception for /), it can be easily proved that there's
788
- * never a need to backtrack multiple levels.
789
- */
790
- uint8_t *back_pat = NULL, *back_str = data;
791
- size_t back_pat_len = 0, back_str_len = data_len;
792
-
793
- /*
794
- * Loop over each token (character or class) in pat, matching
795
- * it against the remaining unmatched tail of str. Return false
796
- * on mismatch, or true after matching the trailing nul bytes.
797
- */
798
- while (data_len) {
799
- uint8_t c = *data++;
800
- uint8_t d = *pattern++;
801
- data_len--;
802
- pat_len--;
803
-
804
- switch (d) {
805
- case '?': /* Wildcard: anything goes */
806
- break;
807
-
808
- case '*': /* Any-length wildcard */
809
- if (!pat_len) /* Optimize trailing * case */
810
- return 1;
811
- back_pat = pattern;
812
- back_pat_len = pat_len;
813
- back_str = --data; /* Allow zero-length match */
814
- back_str_len = ++data_len;
815
- break;
816
-
817
- case '[': { /* Character class */
818
- uint8_t match = 0, inverted = (*pattern == '^');
819
- uint8_t *cls = pattern + inverted;
820
- uint8_t a = *cls++;
821
-
822
- /*
823
- * Iterate over each span in the character class.
824
- * A span is either a single character a, or a
825
- * range a-b. The first span may begin with ']'.
826
- */
827
- do {
828
- uint8_t b = a;
829
-
830
- if (cls[0] == '-' && cls[1] != ']') {
831
- b = cls[1];
832
-
833
- cls += 2;
834
- if (a > b) {
835
- uint8_t tmp = a;
836
- a = b;
837
- b = tmp;
838
- }
839
- }
840
- match |= (a <= c && c <= b);
841
- } while ((a = *cls++) != ']');
842
-
843
- if (match == inverted)
844
- goto backtrack;
845
- pat_len -= cls - pattern;
846
- pattern = cls;
847
-
848
- } break;
849
- case '\\':
850
- d = *pattern++;
851
- pat_len--;
852
- /*FALLTHROUGH*/
853
- default: /* Literal character */
854
- if (c == d)
855
- break;
856
- backtrack:
857
- if (!back_pat)
858
- return 0; /* No point continuing */
859
- /* Try again from last *, one character later in str. */
860
- pattern = back_pat;
861
- data = ++back_str;
862
- data_len = --back_str_len;
863
- pat_len = back_pat_len;
864
- }
865
- }
866
- return !data_len && !pat_len;
867
- }