agoo 2.5.1 → 2.5.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +12 -1
- data/ext/agoo/agoo.c +4 -3
- data/ext/agoo/bind.c +51 -11
- data/ext/agoo/bind.h +9 -0
- data/ext/agoo/con.c +127 -89
- data/ext/agoo/con.h +24 -8
- data/ext/agoo/debug.c +2 -0
- data/ext/agoo/debug.h +1 -0
- data/ext/agoo/err.c +1 -1
- data/ext/agoo/err.h +2 -5
- data/ext/agoo/foo/agoo.c +109 -0
- data/ext/agoo/foo/con.c +1220 -0
- data/ext/agoo/foo/con.h +65 -0
- data/ext/agoo/foo/page.c +699 -0
- data/ext/agoo/foo/pub.c +131 -0
- data/ext/agoo/foo/pub.h +40 -0
- data/ext/agoo/foo/rserver.c +1016 -0
- data/ext/agoo/foo/server.c +303 -0
- data/ext/agoo/foo/server.h +67 -0
- data/ext/agoo/foo/upgraded.c +182 -0
- data/ext/agoo/hook.c +31 -0
- data/ext/agoo/hook.h +9 -10
- data/ext/agoo/{types.h → kinds.h} +3 -3
- data/ext/agoo/log.c +168 -83
- data/ext/agoo/log.h +6 -2
- data/ext/agoo/page.c +38 -32
- data/ext/agoo/pub.c +16 -0
- data/ext/agoo/pub.h +1 -0
- data/ext/agoo/queue.h +1 -0
- data/ext/agoo/req.c +95 -0
- data/ext/agoo/req.h +48 -0
- data/ext/agoo/request.c +10 -38
- data/ext/agoo/request.h +1 -34
- data/ext/agoo/res.h +1 -3
- data/ext/agoo/response.c +6 -248
- data/ext/agoo/response.h +2 -6
- data/ext/agoo/rlog.c +2 -1
- data/ext/agoo/rresponse.c +252 -0
- data/ext/agoo/rresponse.h +14 -0
- data/ext/agoo/rserver.c +43 -45
- data/ext/agoo/rserver.h +3 -24
- data/ext/agoo/rupgraded.c +303 -0
- data/ext/agoo/rupgraded.h +17 -0
- data/ext/agoo/server.c +125 -8
- data/ext/agoo/server.h +26 -4
- data/ext/agoo/sse.c +3 -1
- data/ext/agoo/upgraded.c +42 -280
- data/ext/agoo/upgraded.h +16 -8
- data/ext/agoo/websocket.c +13 -9
- data/lib/agoo/base.rb +84 -0
- data/lib/agoo/client.rb +25 -0
- data/lib/agoo/version.rb +1 -1
- data/test/bind_test.rb +2 -2
- metadata +22 -4
@@ -0,0 +1,17 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_RUPGRADED_H__
|
4
|
+
#define __AGOO_RUPGRADED_H__
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
|
8
|
+
#include "upgraded.h"
|
9
|
+
|
10
|
+
struct _Con;
|
11
|
+
|
12
|
+
extern void upgraded_init(VALUE mod);
|
13
|
+
extern Upgraded rupgraded_create(struct _Con *c, VALUE obj, VALUE env);
|
14
|
+
|
15
|
+
extern const char* extract_subject(VALUE subject, int *slen);
|
16
|
+
|
17
|
+
#endif // __AGOO_RUPGRADED_H__
|
data/ext/agoo/server.c
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
#include <netdb.h>
|
5
5
|
#include <netinet/tcp.h>
|
6
6
|
#include <poll.h>
|
7
|
+
#include <string.h>
|
7
8
|
#include <sys/socket.h>
|
8
9
|
#include <sys/types.h>
|
9
10
|
#include <unistd.h>
|
@@ -11,18 +12,40 @@
|
|
11
12
|
#include "con.h"
|
12
13
|
#include "dtime.h"
|
13
14
|
#include "http.h"
|
15
|
+
#include "hook.h"
|
14
16
|
#include "log.h"
|
15
17
|
#include "page.h"
|
18
|
+
#include "pub.h"
|
19
|
+
#include "upgraded.h"
|
16
20
|
|
17
21
|
#include "server.h"
|
18
22
|
|
23
|
+
#define LOOP_UP 512
|
24
|
+
#define MAX_LOOP 4
|
25
|
+
|
19
26
|
struct _Server the_server = {false};
|
20
27
|
|
21
28
|
void
|
22
29
|
server_setup() {
|
23
30
|
memset(&the_server, 0, sizeof(struct _Server));
|
31
|
+
pthread_mutex_init(&the_server.up_lock, 0);
|
32
|
+
the_server.up_list = NULL;
|
33
|
+
the_server.max_push_pending = 32;
|
24
34
|
pages_init();
|
25
|
-
queue_multi_init(&the_server.con_queue,
|
35
|
+
queue_multi_init(&the_server.con_queue, 1024, false, true);
|
36
|
+
queue_multi_init(&the_server.eval_queue, 1024, false, true);
|
37
|
+
}
|
38
|
+
|
39
|
+
static void
|
40
|
+
add_con_loop() {
|
41
|
+
struct _Err err = ERR_INIT;
|
42
|
+
ConLoop loop = conloop_create(&err, 0);
|
43
|
+
|
44
|
+
if (NULL != loop) {
|
45
|
+
loop->next = the_server.con_loops;
|
46
|
+
the_server.con_loops = loop;
|
47
|
+
the_server.loop_cnt++;
|
48
|
+
}
|
26
49
|
}
|
27
50
|
|
28
51
|
static void*
|
@@ -50,7 +73,7 @@ listen_loop(void *x) {
|
|
50
73
|
memset(&client_addr, 0, sizeof(client_addr));
|
51
74
|
atomic_fetch_add(&the_server.running, 1);
|
52
75
|
while (the_server.active) {
|
53
|
-
if (0 > (i = poll(pa, pcnt,
|
76
|
+
if (0 > (i = poll(pa, pcnt, 200))) {
|
54
77
|
if (EAGAIN == errno) {
|
55
78
|
continue;
|
56
79
|
}
|
@@ -62,24 +85,33 @@ listen_loop(void *x) {
|
|
62
85
|
continue;
|
63
86
|
}
|
64
87
|
for (b = the_server.binds, p = pa; NULL != b; b = b->next, p++) {
|
65
|
-
// TBD instead of b->port use b->id
|
66
88
|
if (0 != (p->revents & POLLIN)) {
|
67
89
|
if (0 > (client_sock = accept(p->fd, (struct sockaddr*)&client_addr, &alen))) {
|
68
90
|
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), strerror(errno));
|
69
|
-
} else if (NULL == (con = con_create(&err, client_sock, ++cnt))) {
|
91
|
+
} else if (NULL == (con = con_create(&err, client_sock, ++cnt, b))) {
|
70
92
|
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), err.msg);
|
71
93
|
close(client_sock);
|
72
94
|
cnt--;
|
73
95
|
err_clear(&err);
|
74
96
|
} else {
|
97
|
+
int con_cnt;
|
75
98
|
#ifdef OSX_OS
|
76
99
|
setsockopt(client_sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
|
100
|
+
#endif
|
101
|
+
#ifdef PLATFORM_LINUX
|
102
|
+
setsockopt(client_sock, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval));
|
77
103
|
#endif
|
78
104
|
fcntl(client_sock, F_SETFL, O_NONBLOCK);
|
105
|
+
//fcntl(client_sock, F_SETFL, FNDELAY);
|
79
106
|
setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
80
107
|
setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
81
108
|
log_cat(&con_cat, "Server with pid %d accepted connection %llu on %s [%d]",
|
82
109
|
getpid(), (unsigned long long)cnt, b->id, con->sock);
|
110
|
+
|
111
|
+
con_cnt = atomic_fetch_add(&the_server.con_cnt, 1);
|
112
|
+
if (MAX_LOOP > the_server.loop_cnt && the_server.loop_cnt * LOOP_UP < con_cnt) {
|
113
|
+
add_con_loop();
|
114
|
+
}
|
83
115
|
queue_push(&the_server.con_queue, (void*)con);
|
84
116
|
}
|
85
117
|
}
|
@@ -105,10 +137,11 @@ listen_loop(void *x) {
|
|
105
137
|
int
|
106
138
|
server_start(Err err, const char *app_name, const char *version) {
|
107
139
|
double giveup;
|
108
|
-
|
109
|
-
pthread_create(&the_server.listen_thread, NULL, listen_loop, NULL);
|
110
|
-
pthread_create(&the_server.con_thread, NULL, con_loop, NULL);
|
111
140
|
|
141
|
+
pthread_create(&the_server.listen_thread, NULL, listen_loop, NULL);
|
142
|
+
the_server.con_loops = conloop_create(err, 0);
|
143
|
+
the_server.loop_cnt = 1;
|
144
|
+
|
112
145
|
giveup = dtime() + 1.0;
|
113
146
|
while (dtime() < giveup) {
|
114
147
|
if (2 <= atomic_load(&the_server.running)) {
|
@@ -143,6 +176,8 @@ setup_listen(Err err) {
|
|
143
176
|
void
|
144
177
|
server_shutdown(const char *app_name, void (*stop)()) {
|
145
178
|
if (the_server.inited) {
|
179
|
+
ConLoop loop;
|
180
|
+
|
146
181
|
log_cat(&info_cat, "%s with pid %d shutting down.", app_name, getpid());
|
147
182
|
the_server.inited = false;
|
148
183
|
if (the_server.active) {
|
@@ -150,7 +185,9 @@ server_shutdown(const char *app_name, void (*stop)()) {
|
|
150
185
|
|
151
186
|
the_server.active = false;
|
152
187
|
pthread_detach(the_server.listen_thread);
|
153
|
-
|
188
|
+
for (loop = the_server.con_loops; NULL != loop; loop = loop->next) {
|
189
|
+
pthread_detach(loop->thread);
|
190
|
+
}
|
154
191
|
while (0 < atomic_load(&the_server.running)) {
|
155
192
|
dsleep(0.1);
|
156
193
|
if (giveup < dtime()) {
|
@@ -173,6 +210,10 @@ server_shutdown(const char *app_name, void (*stop)()) {
|
|
173
210
|
bind_destroy(b);
|
174
211
|
}
|
175
212
|
queue_cleanup(&the_server.con_queue);
|
213
|
+
for (loop = the_server.con_loops; NULL != loop; loop = loop->next) {
|
214
|
+
queue_cleanup(&loop->pub_queue);
|
215
|
+
}
|
216
|
+
queue_cleanup(&the_server.eval_queue);
|
176
217
|
|
177
218
|
pages_cleanup();
|
178
219
|
http_cleanup();
|
@@ -181,6 +222,82 @@ server_shutdown(const char *app_name, void (*stop)()) {
|
|
181
222
|
|
182
223
|
void
|
183
224
|
server_bind(Bind b) {
|
225
|
+
// If a bind with the same port already exists, replace it.
|
226
|
+
Bind prev = NULL;
|
227
|
+
|
228
|
+
if (NULL == b->read) {
|
229
|
+
b->read = con_http_read;
|
230
|
+
}
|
231
|
+
if (NULL == b->write) {
|
232
|
+
b->write = con_http_write;
|
233
|
+
}
|
234
|
+
if (NULL == b->events) {
|
235
|
+
b->events = con_http_events;
|
236
|
+
}
|
237
|
+
for (Bind bx = the_server.binds; NULL != bx; bx = bx->next) {
|
238
|
+
if (bx->port == b->port) {
|
239
|
+
b->next = bx->next;
|
240
|
+
if (NULL == prev) {
|
241
|
+
the_server.binds = b;
|
242
|
+
} else {
|
243
|
+
prev->next = b;
|
244
|
+
}
|
245
|
+
bind_destroy(bx);
|
246
|
+
return;
|
247
|
+
}
|
248
|
+
prev = bx;
|
249
|
+
}
|
184
250
|
b->next = the_server.binds;
|
185
251
|
the_server.binds = b;
|
186
252
|
}
|
253
|
+
|
254
|
+
void
|
255
|
+
server_add_upgraded(Upgraded up) {
|
256
|
+
pthread_mutex_lock(&the_server.up_lock);
|
257
|
+
if (NULL == the_server.up_list) {
|
258
|
+
up->next = NULL;
|
259
|
+
} else {
|
260
|
+
the_server.up_list->prev = up;
|
261
|
+
}
|
262
|
+
up->next = the_server.up_list;
|
263
|
+
the_server.up_list = up;
|
264
|
+
up->con->up = up;
|
265
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
266
|
+
}
|
267
|
+
|
268
|
+
int
|
269
|
+
server_add_func_hook(Err err,
|
270
|
+
Method method,
|
271
|
+
const char *pattern,
|
272
|
+
void (*func)(Req req),
|
273
|
+
Queue queue) {
|
274
|
+
Hook h;
|
275
|
+
Hook prev = NULL;
|
276
|
+
Hook hook = hook_func_create(method, pattern, func, queue);
|
277
|
+
|
278
|
+
if (NULL == hook) {
|
279
|
+
return err_set(err, ERR_MEMORY, "failed to allocate memory for HTTP server Hook.");
|
280
|
+
}
|
281
|
+
for (h = the_server.hooks; NULL != h; h = h->next) {
|
282
|
+
prev = h;
|
283
|
+
}
|
284
|
+
if (NULL != prev) {
|
285
|
+
prev->next = hook;
|
286
|
+
} else {
|
287
|
+
the_server.hooks = hook;
|
288
|
+
}
|
289
|
+
return ERR_OK;
|
290
|
+
}
|
291
|
+
|
292
|
+
void
|
293
|
+
server_publish(struct _Pub *pub) {
|
294
|
+
ConLoop loop;
|
295
|
+
|
296
|
+
for (loop = the_server.con_loops; NULL != loop; loop = loop->next) {
|
297
|
+
if (NULL == loop->next) {
|
298
|
+
queue_push(&loop->pub_queue, pub);
|
299
|
+
} else {
|
300
|
+
queue_push(&loop->pub_queue, pub_dup(pub));
|
301
|
+
}
|
302
|
+
}
|
303
|
+
}
|
data/ext/agoo/server.h
CHANGED
@@ -12,6 +12,11 @@
|
|
12
12
|
#include "hook.h"
|
13
13
|
#include "queue.h"
|
14
14
|
|
15
|
+
struct _ConLoop;
|
16
|
+
struct _Pub;
|
17
|
+
struct _Req;
|
18
|
+
struct _Upgraded;
|
19
|
+
|
15
20
|
typedef struct _Server {
|
16
21
|
volatile bool inited;
|
17
22
|
volatile bool active;
|
@@ -19,15 +24,23 @@ typedef struct _Server {
|
|
19
24
|
bool pedantic;
|
20
25
|
bool root_first;
|
21
26
|
pthread_t listen_thread;
|
22
|
-
pthread_t con_thread;
|
23
27
|
struct _Queue con_queue;
|
24
28
|
Hook hooks;
|
25
29
|
Hook hook404;
|
26
|
-
|
27
|
-
//int port; // TBD remove
|
28
|
-
//int fd; // TBD remove
|
29
30
|
Bind binds;
|
30
31
|
|
32
|
+
struct _Queue eval_queue;
|
33
|
+
|
34
|
+
struct _ConLoop *con_loops;
|
35
|
+
int loop_cnt;
|
36
|
+
atomic_int con_cnt;
|
37
|
+
|
38
|
+
struct _Upgraded *up_list;
|
39
|
+
pthread_mutex_t up_lock;
|
40
|
+
int max_push_pending;
|
41
|
+
void *env_nil_value;
|
42
|
+
void *ctx_nil_value;
|
43
|
+
|
31
44
|
// A count of the running threads from the wrapper or the server managed
|
32
45
|
// threads.
|
33
46
|
atomic_int running;
|
@@ -42,4 +55,13 @@ extern void server_bind(Bind b);
|
|
42
55
|
extern int setup_listen(Err err);
|
43
56
|
extern int server_start(Err err, const char *app_name, const char *version);
|
44
57
|
|
58
|
+
extern void server_add_upgraded(struct _Upgraded *up);
|
59
|
+
extern int server_add_func_hook(Err err,
|
60
|
+
Method method,
|
61
|
+
const char *pattern,
|
62
|
+
void (*func)(struct _Req *req),
|
63
|
+
Queue queue);
|
64
|
+
|
65
|
+
extern void server_publish(struct _Pub *pub);
|
66
|
+
|
45
67
|
#endif // __AGOO_SERVER_H__
|
data/ext/agoo/sse.c
CHANGED
data/ext/agoo/upgraded.c
CHANGED
@@ -1,35 +1,24 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
3
|
#include <stdio.h>
|
4
|
-
|
5
|
-
#include <ruby/encoding.h>
|
4
|
+
#include <string.h>
|
6
5
|
|
7
6
|
#include "con.h"
|
8
7
|
#include "debug.h"
|
9
8
|
#include "pub.h"
|
10
|
-
#include "rserver.h"
|
11
9
|
#include "server.h"
|
12
10
|
#include "subject.h"
|
13
11
|
#include "upgraded.h"
|
14
12
|
|
15
|
-
static VALUE upgraded_class = Qundef;
|
16
|
-
|
17
|
-
static VALUE sse_sym;
|
18
|
-
static VALUE websocket_sym;
|
19
|
-
|
20
|
-
static ID on_open_id = 0;
|
21
|
-
static ID to_s_id = 0;
|
22
|
-
|
23
13
|
static void
|
24
14
|
destroy(Upgraded up) {
|
25
15
|
Subject subject;
|
26
16
|
|
27
|
-
if (
|
28
|
-
|
29
|
-
up->wrap = Qnil;
|
17
|
+
if (NULL != up->on_destroy) {
|
18
|
+
up->on_destroy(up);
|
30
19
|
}
|
31
20
|
if (NULL == up->prev) {
|
32
|
-
|
21
|
+
the_server.up_list = up->next;
|
33
22
|
if (NULL != up->next) {
|
34
23
|
up->next->prev = NULL;
|
35
24
|
}
|
@@ -47,45 +36,23 @@ destroy(Upgraded up) {
|
|
47
36
|
free(up);
|
48
37
|
}
|
49
38
|
|
50
|
-
const char*
|
51
|
-
extract_subject(VALUE subject, int *slen) {
|
52
|
-
const char *subj;
|
53
|
-
|
54
|
-
switch (rb_type(subject)) {
|
55
|
-
case T_STRING:
|
56
|
-
subj = StringValuePtr(subject);
|
57
|
-
*slen = (int)RSTRING_LEN(subject);
|
58
|
-
break;
|
59
|
-
case T_SYMBOL:
|
60
|
-
subj = rb_id2name(rb_sym2id(subject));
|
61
|
-
*slen = strlen(subj);
|
62
|
-
break;
|
63
|
-
default:
|
64
|
-
subject = rb_funcall(subject, to_s_id, 0);
|
65
|
-
subj = StringValuePtr(subject);
|
66
|
-
*slen = (int)RSTRING_LEN(subject);
|
67
|
-
break;
|
68
|
-
}
|
69
|
-
return subj;
|
70
|
-
}
|
71
|
-
|
72
39
|
void
|
73
40
|
upgraded_release(Upgraded up) {
|
74
|
-
pthread_mutex_lock(&
|
41
|
+
pthread_mutex_lock(&the_server.up_lock);
|
75
42
|
if (atomic_fetch_sub(&up->ref_cnt, 1) <= 1) {
|
76
43
|
destroy(up);
|
77
44
|
}
|
78
|
-
pthread_mutex_unlock(&
|
45
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
79
46
|
}
|
80
47
|
|
81
48
|
void
|
82
49
|
upgraded_release_con(Upgraded up) {
|
83
|
-
pthread_mutex_lock(&
|
50
|
+
pthread_mutex_lock(&the_server.up_lock);
|
84
51
|
up->con = NULL;
|
85
52
|
if (atomic_fetch_sub(&up->ref_cnt, 1) <= 1) {
|
86
53
|
destroy(up);
|
87
54
|
}
|
88
|
-
pthread_mutex_unlock(&
|
55
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
89
56
|
}
|
90
57
|
|
91
58
|
// Called from the con_loop thread, no need to lock, this steals the subject
|
@@ -147,274 +114,69 @@ upgraded_ref(Upgraded up) {
|
|
147
114
|
atomic_fetch_add(&up->ref_cnt, 1);
|
148
115
|
}
|
149
116
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
if (the_server.active) {
|
155
|
-
pthread_mutex_lock(&the_rserver.up_lock);
|
156
|
-
if (NULL != (up = DATA_PTR(self))) {
|
157
|
-
atomic_fetch_add(&up->ref_cnt, 1);
|
158
|
-
}
|
159
|
-
pthread_mutex_unlock(&the_rserver.up_lock);
|
160
|
-
}
|
161
|
-
return up;
|
162
|
-
}
|
163
|
-
|
164
|
-
/* Document-method: write
|
165
|
-
*
|
166
|
-
* call-seq: write(msg)
|
167
|
-
*
|
168
|
-
* Writes a message to the WebSocket or SSE connection. Returns true if the
|
169
|
-
* message has been queued and false otherwise. A closed connection or too
|
170
|
-
* many pending messages could cause a value of false to be returned.
|
171
|
-
*/
|
172
|
-
static VALUE
|
173
|
-
up_write(VALUE self, VALUE msg) {
|
174
|
-
Upgraded up = get_upgraded(self);
|
175
|
-
Pub p;
|
117
|
+
bool
|
118
|
+
upgraded_write(Upgraded up, const char *message, size_t mlen, bool bin, bool inc_ref) {
|
119
|
+
Pub p;
|
176
120
|
|
177
|
-
if (
|
178
|
-
return Qfalse;
|
179
|
-
}
|
180
|
-
if (0 < the_rserver.max_push_pending && the_rserver.max_push_pending <= atomic_load(&up->pending)) {
|
121
|
+
if (0 < the_server.max_push_pending && the_server.max_push_pending <= atomic_load(&up->pending)) {
|
181
122
|
atomic_fetch_sub(&up->ref_cnt, 1);
|
182
123
|
// Too many pending messages.
|
183
|
-
return
|
124
|
+
return false;
|
184
125
|
}
|
185
|
-
if (
|
186
|
-
|
187
|
-
p = pub_write(up, StringValuePtr(msg), RSTRING_LEN(msg), true);
|
188
|
-
} else {
|
189
|
-
p = pub_write(up, StringValuePtr(msg), RSTRING_LEN(msg), false);
|
190
|
-
}
|
191
|
-
} else {
|
192
|
-
volatile VALUE rs = rb_funcall(msg, to_s_id, 0);
|
193
|
-
|
194
|
-
p = pub_write(up, StringValuePtr(rs), RSTRING_LEN(rs), false);
|
126
|
+
if (inc_ref) {
|
127
|
+
atomic_fetch_add(&up->ref_cnt, 1);
|
195
128
|
}
|
129
|
+
p = pub_write(up, message, mlen, bin);
|
196
130
|
atomic_fetch_add(&up->pending, 1);
|
197
|
-
|
198
|
-
|
199
|
-
return Qtrue;
|
200
|
-
}
|
201
|
-
|
202
|
-
/* Document-method: subscribe
|
203
|
-
*
|
204
|
-
* call-seq: subscribe(subject)
|
205
|
-
*
|
206
|
-
* Subscribes to messages published on the specified subject. The subject is a
|
207
|
-
* dot delimited string that can include a '*' character as a wild card that
|
208
|
-
* matches any set of characters. The '>' character matches all remaining
|
209
|
-
* characters. Examples: people.fred.log, people.*.log, people.fred.>
|
210
|
-
*
|
211
|
-
* Symbols can also be used as can any other object that responds to #to_s.
|
212
|
-
*/
|
213
|
-
static VALUE
|
214
|
-
up_subscribe(VALUE self, VALUE subject) {
|
215
|
-
Upgraded up;
|
216
|
-
int slen;
|
217
|
-
const char *subj = extract_subject(subject, &slen);
|
131
|
+
server_publish(p);
|
218
132
|
|
219
|
-
|
220
|
-
atomic_fetch_add(&up->pending, 1);
|
221
|
-
queue_push(&the_rserver.pub_queue, pub_subscribe(up, subj, slen));
|
222
|
-
}
|
223
|
-
return Qnil;
|
133
|
+
return true;
|
224
134
|
}
|
225
135
|
|
226
|
-
|
227
|
-
*
|
228
|
-
|
229
|
-
|
230
|
-
* Unsubscribes to messages on the provided subject. If the subject is nil
|
231
|
-
* then all subscriptions for the object are removed.
|
232
|
-
*
|
233
|
-
* Symbols can also be used as can any other object that responds to #to_s.
|
234
|
-
*/
|
235
|
-
static VALUE
|
236
|
-
up_unsubscribe(int argc, VALUE *argv, VALUE self) {
|
237
|
-
Upgraded up;
|
238
|
-
const char *subject = NULL;
|
239
|
-
int slen = 0;
|
240
|
-
|
241
|
-
if (0 < argc) {
|
242
|
-
subject = extract_subject(argv[0], &slen);
|
243
|
-
}
|
244
|
-
if (NULL != (up = get_upgraded(self))) {
|
245
|
-
atomic_fetch_add(&up->pending, 1);
|
246
|
-
queue_push(&the_rserver.pub_queue, pub_unsubscribe(up, subject, slen));
|
247
|
-
}
|
248
|
-
return Qnil;
|
249
|
-
}
|
250
|
-
|
251
|
-
/* Document-method: close
|
252
|
-
*
|
253
|
-
* call-seq: close()
|
254
|
-
*
|
255
|
-
* Closes the connections associated with the handler.
|
256
|
-
*/
|
257
|
-
static VALUE
|
258
|
-
up_close(VALUE self) {
|
259
|
-
Upgraded up = get_upgraded(self);
|
260
|
-
|
261
|
-
if (NULL != up) {
|
262
|
-
atomic_fetch_add(&up->pending, 1);
|
263
|
-
queue_push(&the_rserver.pub_queue, pub_close(up));
|
136
|
+
void
|
137
|
+
upgraded_subscribe(Upgraded up, const char *subject, int slen, bool inc_ref) {
|
138
|
+
if (inc_ref) {
|
139
|
+
atomic_fetch_add(&up->ref_cnt, 1);
|
264
140
|
}
|
265
|
-
|
141
|
+
atomic_fetch_add(&up->pending, 1);
|
142
|
+
server_publish(pub_subscribe(up, subject, slen));
|
266
143
|
}
|
267
144
|
|
268
|
-
|
269
|
-
*
|
270
|
-
|
271
|
-
|
272
|
-
* Returns the number of pending WebSocket or SSE writes. If the connection is
|
273
|
-
* closed then -1 is returned.
|
274
|
-
*/
|
275
|
-
static VALUE
|
276
|
-
pending(VALUE self) {
|
277
|
-
Upgraded up = get_upgraded(self);
|
278
|
-
int pending = -1;
|
279
|
-
|
280
|
-
if (NULL != up) {
|
281
|
-
pending = atomic_load(&up->pending);
|
282
|
-
atomic_fetch_sub(&up->ref_cnt, 1);
|
145
|
+
void
|
146
|
+
upgraded_unsubscribe(Upgraded up, const char *subject, int slen, bool inc_ref) {
|
147
|
+
if (inc_ref) {
|
148
|
+
atomic_fetch_add(&up->ref_cnt, 1);
|
283
149
|
}
|
284
|
-
|
150
|
+
atomic_fetch_add(&up->pending, 1);
|
151
|
+
server_publish(pub_unsubscribe(up, subject, slen));
|
285
152
|
}
|
286
153
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
* Returns true if the connection is open and false otherwise.
|
292
|
-
*/
|
293
|
-
static VALUE
|
294
|
-
up_open(VALUE self) {
|
295
|
-
Upgraded up = get_upgraded(self);
|
296
|
-
int pending = -1;
|
297
|
-
|
298
|
-
if (NULL != up) {
|
299
|
-
pending = atomic_load(&up->pending);
|
300
|
-
atomic_fetch_sub(&up->ref_cnt, 1);
|
154
|
+
void
|
155
|
+
upgraded_close(Upgraded up, bool inc_ref) {
|
156
|
+
if (inc_ref) {
|
157
|
+
atomic_fetch_add(&up->ref_cnt, 1);
|
301
158
|
}
|
302
|
-
|
159
|
+
atomic_fetch_add(&up->pending, 1);
|
160
|
+
server_publish(pub_close(up));
|
303
161
|
}
|
304
162
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
*
|
309
|
-
* Returns the protocol of the upgraded connection as either :websocket or
|
310
|
-
* :sse. If not longer connected nil is returned.
|
311
|
-
*/
|
312
|
-
static VALUE
|
313
|
-
protocol(VALUE self) {
|
314
|
-
VALUE pro = Qnil;
|
315
|
-
|
316
|
-
if (the_server.active) {
|
317
|
-
Upgraded up;
|
318
|
-
|
319
|
-
pthread_mutex_lock(&the_rserver.up_lock);
|
320
|
-
if (NULL != (up = DATA_PTR(self)) && NULL != up->con) {
|
321
|
-
switch (up->con->kind) {
|
322
|
-
case CON_WS:
|
323
|
-
pro = websocket_sym;
|
324
|
-
break;
|
325
|
-
case CON_SSE:
|
326
|
-
pro = sse_sym;
|
327
|
-
break;
|
328
|
-
default:
|
329
|
-
break;
|
330
|
-
}
|
331
|
-
}
|
332
|
-
pthread_mutex_unlock(&the_rserver.up_lock);
|
333
|
-
}
|
334
|
-
return pro;
|
163
|
+
int
|
164
|
+
upgraded_pending(Upgraded up) {
|
165
|
+
return atomic_load(&up->pending);
|
335
166
|
}
|
336
167
|
|
337
168
|
Upgraded
|
338
|
-
upgraded_create(Con c,
|
169
|
+
upgraded_create(Con c, void * ctx, void *env) {
|
339
170
|
Upgraded up = (Upgraded)malloc(sizeof(struct _Upgraded));
|
340
171
|
|
341
|
-
if (!the_server.active) {
|
342
|
-
rb_raise(rb_eIOError, "Server shutdown.");
|
343
|
-
}
|
344
172
|
if (NULL != up) {
|
345
173
|
DEBUG_ALLOC(mem_upgraded, up);
|
174
|
+
memset(up, 0, sizeof(struct _Upgraded));
|
346
175
|
up->con = c;
|
347
|
-
up->
|
176
|
+
up->ctx = ctx;
|
348
177
|
up->env = env;
|
349
178
|
atomic_init(&up->pending, 0);
|
350
179
|
atomic_init(&up->ref_cnt, 1); // start with 1 for the Con reference
|
351
|
-
up->on_empty = rb_respond_to(obj, rb_intern("on_drained"));
|
352
|
-
up->on_close = rb_respond_to(obj, rb_intern("on_close"));
|
353
|
-
up->on_shut = rb_respond_to(obj, rb_intern("on_shutdown"));
|
354
|
-
up->on_msg = rb_respond_to(obj, rb_intern("on_message"));
|
355
|
-
up->on_error = rb_respond_to(obj, rb_intern("on_error"));
|
356
|
-
up->wrap = Data_Wrap_Struct(upgraded_class, NULL, NULL, up);
|
357
|
-
up->subjects = NULL;
|
358
|
-
up->prev = NULL;
|
359
|
-
pthread_mutex_lock(&the_rserver.up_lock);
|
360
|
-
if (NULL == the_rserver.up_list) {
|
361
|
-
up->next = NULL;
|
362
|
-
} else {
|
363
|
-
the_rserver.up_list->prev = up;
|
364
|
-
}
|
365
|
-
up->next = the_rserver.up_list;
|
366
|
-
the_rserver.up_list = up;
|
367
|
-
c->up = up;
|
368
|
-
pthread_mutex_unlock(&the_rserver.up_lock);
|
369
|
-
|
370
|
-
if (rb_respond_to(obj, on_open_id)) {
|
371
|
-
rb_funcall(obj, on_open_id, 1, up->wrap);
|
372
|
-
}
|
373
180
|
}
|
374
181
|
return up;
|
375
182
|
}
|
376
|
-
|
377
|
-
// Use the publish from the Agoo module.
|
378
|
-
extern VALUE ragoo_publish(VALUE self, VALUE subject, VALUE message);
|
379
|
-
|
380
|
-
/* Document-method: env
|
381
|
-
*
|
382
|
-
* call-seq: env()
|
383
|
-
*
|
384
|
-
* Returns the environment passed to the call method that initiated the
|
385
|
-
* Upgraded Object creation.
|
386
|
-
*/
|
387
|
-
static VALUE
|
388
|
-
env(VALUE self) {
|
389
|
-
Upgraded up = get_upgraded(self);
|
390
|
-
|
391
|
-
if (NULL != up) {
|
392
|
-
return up->env;
|
393
|
-
}
|
394
|
-
return Qnil;
|
395
|
-
}
|
396
|
-
|
397
|
-
/* Document-module: Agoo::Upgraded
|
398
|
-
*
|
399
|
-
* Adds methods to a handler of WebSocket and SSE connections.
|
400
|
-
*/
|
401
|
-
void
|
402
|
-
upgraded_init(VALUE mod) {
|
403
|
-
upgraded_class = rb_define_class_under(mod, "Upgraded", rb_cObject);
|
404
|
-
|
405
|
-
rb_define_method(upgraded_class, "write", up_write, 1);
|
406
|
-
rb_define_method(upgraded_class, "subscribe", up_subscribe, 1);
|
407
|
-
rb_define_method(upgraded_class, "unsubscribe", up_unsubscribe, -1);
|
408
|
-
rb_define_method(upgraded_class, "close", up_close, 0);
|
409
|
-
rb_define_method(upgraded_class, "pending", pending, 0);
|
410
|
-
rb_define_method(upgraded_class, "protocol", protocol, 0);
|
411
|
-
rb_define_method(upgraded_class, "publish", ragoo_publish, 2);
|
412
|
-
rb_define_method(upgraded_class, "open?", up_open, 0);
|
413
|
-
rb_define_method(upgraded_class, "env", env, 0);
|
414
|
-
|
415
|
-
on_open_id = rb_intern("on_open");
|
416
|
-
to_s_id = rb_intern("to_s");
|
417
|
-
|
418
|
-
sse_sym = ID2SYM(rb_intern("sse")); rb_gc_register_address(&sse_sym);
|
419
|
-
websocket_sym = ID2SYM(rb_intern("websocket")); rb_gc_register_address(&websocket_sym);
|
420
|
-
}
|