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/fio_sha2.h
DELETED
@@ -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
|
data/ext/iodine/pubsub.c
DELETED
@@ -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
|
-
}
|