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,14 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_RRESPONSE_H__
|
4
|
+
#define __AGOO_RRESPONSE_H__
|
5
|
+
|
6
|
+
#include <ruby.h>
|
7
|
+
|
8
|
+
#include "text.h"
|
9
|
+
|
10
|
+
extern void response_init(VALUE mod);
|
11
|
+
extern VALUE response_new();
|
12
|
+
extern Text response_text(VALUE self);
|
13
|
+
|
14
|
+
#endif // __AGOO_RRESPONSE_H__
|
data/ext/agoo/rserver.c
CHANGED
@@ -16,12 +16,16 @@
|
|
16
16
|
#include "dtime.h"
|
17
17
|
#include "err.h"
|
18
18
|
#include "http.h"
|
19
|
+
#include "log.h"
|
19
20
|
#include "page.h"
|
20
21
|
#include "pub.h"
|
21
|
-
#include "response.h"
|
22
22
|
#include "request.h"
|
23
|
+
#include "res.h"
|
24
|
+
#include "response.h"
|
23
25
|
#include "rhook.h"
|
26
|
+
#include "rresponse.h"
|
24
27
|
#include "rserver.h"
|
28
|
+
#include "rupgraded.h"
|
25
29
|
#include "server.h"
|
26
30
|
#include "sse.h"
|
27
31
|
#include "sub.h"
|
@@ -61,19 +65,19 @@ server_mark(void *ptr) {
|
|
61
65
|
Upgraded up;
|
62
66
|
|
63
67
|
rb_gc_mark(rserver);
|
64
|
-
pthread_mutex_lock(&
|
65
|
-
for (up =
|
66
|
-
if (Qnil != up->
|
67
|
-
rb_gc_mark(up->
|
68
|
+
pthread_mutex_lock(&the_server.up_lock);
|
69
|
+
for (up = the_server.up_list; NULL != up; up = up->next) {
|
70
|
+
if (Qnil != (VALUE)up->ctx) {
|
71
|
+
rb_gc_mark((VALUE)up->ctx);
|
68
72
|
}
|
69
|
-
if (Qnil != up->env) {
|
70
|
-
rb_gc_mark(up->env);
|
73
|
+
if (Qnil != (VALUE)up->env) {
|
74
|
+
rb_gc_mark((VALUE)up->env);
|
71
75
|
}
|
72
|
-
if (Qnil != up->wrap) {
|
73
|
-
rb_gc_mark(up->wrap);
|
76
|
+
if (Qnil != (VALUE)up->wrap) {
|
77
|
+
rb_gc_mark((VALUE)up->wrap);
|
74
78
|
}
|
75
79
|
}
|
76
|
-
pthread_mutex_unlock(&
|
80
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
77
81
|
}
|
78
82
|
|
79
83
|
static void
|
@@ -94,8 +98,7 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
94
98
|
the_rserver.worker_cnt = 1;
|
95
99
|
the_server.running = 0;
|
96
100
|
the_server.listen_thread = 0;
|
97
|
-
the_server.
|
98
|
-
the_rserver.max_push_pending = 32;
|
101
|
+
the_server.con_loops = NULL;
|
99
102
|
the_server.root_first = false;
|
100
103
|
the_server.binds = NULL;
|
101
104
|
|
@@ -121,12 +124,12 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
121
124
|
}
|
122
125
|
}
|
123
126
|
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("max_push_pending"))))) {
|
124
|
-
int
|
127
|
+
int mpp = FIX2INT(v);
|
125
128
|
|
126
|
-
if (0 <=
|
127
|
-
the_server.
|
129
|
+
if (0 <= mpp || mpp < 1000) {
|
130
|
+
the_server.max_push_pending = mpp;
|
128
131
|
} else {
|
129
|
-
rb_raise(rb_eArgError, "
|
132
|
+
rb_raise(rb_eArgError, "max_push_pending must be between 0 and 1000.");
|
130
133
|
}
|
131
134
|
}
|
132
135
|
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("pedantic"))))) {
|
@@ -160,7 +163,7 @@ configure(Err err, int port, const char *root, VALUE options) {
|
|
160
163
|
url_bind(v);
|
161
164
|
break;
|
162
165
|
case T_ARRAY:
|
163
|
-
len = RARRAY_LEN(v);
|
166
|
+
len = (int)RARRAY_LEN(v);
|
164
167
|
for (i = 0; i < len; i++) {
|
165
168
|
url_bind(rb_ary_entry(v, i));
|
166
169
|
}
|
@@ -236,17 +239,12 @@ rserver_init(int argc, VALUE *argv, VALUE self) {
|
|
236
239
|
options = argv[2];
|
237
240
|
}
|
238
241
|
server_setup();
|
239
|
-
|
242
|
+
the_server.ctx_nil_value = (void*)Qnil;
|
243
|
+
the_server.env_nil_value = (void*)Qnil;
|
240
244
|
|
241
245
|
if (ERR_OK != configure(&err, port, root, options)) {
|
242
246
|
rb_raise(rb_eArgError, "%s", err.msg);
|
243
247
|
}
|
244
|
-
queue_multi_init(&the_rserver.pub_queue, 256, true, false);
|
245
|
-
queue_multi_init(&the_rserver.eval_queue, 1024, false, true);
|
246
|
-
|
247
|
-
pthread_mutex_init(&the_rserver.up_lock, 0);
|
248
|
-
the_rserver.up_list = NULL;
|
249
|
-
|
250
248
|
the_server.inited = true;
|
251
249
|
|
252
250
|
return Qnil;
|
@@ -463,8 +461,8 @@ handle_rack_inner(void *x) {
|
|
463
461
|
t->len = sizeof(err500) - 1;
|
464
462
|
break;
|
465
463
|
}
|
466
|
-
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &
|
467
|
-
|
464
|
+
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &the_server.eval_queue);
|
465
|
+
rupgraded_create(req->res->con, handler, request_env(req, Qnil));
|
468
466
|
|
469
467
|
t->len = snprintf(t->text, 1024, "HTTP/1.1 101 %s\r\n", status_msg);
|
470
468
|
t = ws_add_headers(req, t);
|
@@ -476,8 +474,8 @@ handle_rack_inner(void *x) {
|
|
476
474
|
t->len = sizeof(err500) - 1;
|
477
475
|
break;
|
478
476
|
}
|
479
|
-
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &
|
480
|
-
|
477
|
+
req->hook = hook_create(NONE, NULL, (void*)handler, PUSH_HOOK, &the_server.eval_queue);
|
478
|
+
rupgraded_create(req->res->con, handler, request_env(req, Qnil));
|
481
479
|
t = sse_upgrade(req, t);
|
482
480
|
res_set_message(req->res, t);
|
483
481
|
queue_wakeup(&the_server.con_queue);
|
@@ -556,7 +554,7 @@ handle_push_inner(void *x) {
|
|
556
554
|
switch (req->method) {
|
557
555
|
case ON_MSG:
|
558
556
|
if (req->up->on_msg && NULL != req->hook) {
|
559
|
-
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, req->up->wrap, rb_str_new(req->msg, req->mlen));
|
557
|
+
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, (VALUE)req->up->wrap, rb_str_new(req->msg, req->mlen));
|
560
558
|
}
|
561
559
|
break;
|
562
560
|
case ON_BIN:
|
@@ -564,19 +562,19 @@ handle_push_inner(void *x) {
|
|
564
562
|
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
565
563
|
|
566
564
|
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
567
|
-
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, req->up->wrap, rstr);
|
565
|
+
rb_funcall((VALUE)req->hook->handler, on_message_id, 2, (VALUE)req->up->wrap, rstr);
|
568
566
|
}
|
569
567
|
break;
|
570
568
|
case ON_CLOSE:
|
571
569
|
upgraded_ref(req->up);
|
572
|
-
|
570
|
+
server_publish(pub_close(req->up));
|
573
571
|
if (req->up->on_close && NULL != req->hook) {
|
574
|
-
rb_funcall((VALUE)req->hook->handler, on_close_id, 1, req->up->wrap);
|
572
|
+
rb_funcall((VALUE)req->hook->handler, on_close_id, 1, (VALUE)req->up->wrap);
|
575
573
|
}
|
576
574
|
break;
|
577
575
|
case ON_SHUTDOWN:
|
578
576
|
if (NULL != req->hook) {
|
579
|
-
rb_funcall((VALUE)req->hook->handler, rb_intern("on_shutdown"), 1, req->up->wrap);
|
577
|
+
rb_funcall((VALUE)req->hook->handler, rb_intern("on_shutdown"), 1, (VALUE)req->up->wrap);
|
580
578
|
}
|
581
579
|
break;
|
582
580
|
case ON_ERROR:
|
@@ -584,12 +582,12 @@ handle_push_inner(void *x) {
|
|
584
582
|
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
585
583
|
|
586
584
|
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
587
|
-
rb_funcall((VALUE)req->hook->handler, on_error_id, 2, req->up->wrap, rstr);
|
585
|
+
rb_funcall((VALUE)req->hook->handler, on_error_id, 2, (VALUE)req->up->wrap, rstr);
|
588
586
|
}
|
589
587
|
break;
|
590
588
|
case ON_EMPTY:
|
591
589
|
if (req->up->on_empty && NULL != req->hook) {
|
592
|
-
rb_funcall((VALUE)req->hook->handler, on_drained_id, 1, req->up->wrap);
|
590
|
+
rb_funcall((VALUE)req->hook->handler, on_drained_id, 1, (VALUE)req->up->wrap);
|
593
591
|
}
|
594
592
|
break;
|
595
593
|
default:
|
@@ -640,6 +638,10 @@ handle_protected(Req req, bool gvi) {
|
|
640
638
|
handle_push(req);
|
641
639
|
}
|
642
640
|
break;
|
641
|
+
case FUNC_HOOK:
|
642
|
+
req->hook->func(req);
|
643
|
+
queue_wakeup(&the_server.con_queue);
|
644
|
+
break;
|
643
645
|
default: {
|
644
646
|
char buf[256];
|
645
647
|
int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n");
|
@@ -659,9 +661,9 @@ process_loop(void *ptr) {
|
|
659
661
|
|
660
662
|
atomic_fetch_add(&the_server.running, 1);
|
661
663
|
while (the_server.active) {
|
662
|
-
if (NULL != (req = (Req)queue_pop(&
|
664
|
+
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
663
665
|
handle_protected(req, true);
|
664
|
-
|
666
|
+
req_destroy(req);
|
665
667
|
}
|
666
668
|
}
|
667
669
|
atomic_fetch_sub(&the_server.running, 1);
|
@@ -721,9 +723,9 @@ rserver_start(VALUE self) {
|
|
721
723
|
Req req;
|
722
724
|
|
723
725
|
while (the_server.active) {
|
724
|
-
if (NULL != (req = (Req)queue_pop(&
|
726
|
+
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
725
727
|
handle_protected(req, false);
|
726
|
-
|
728
|
+
req_destroy(req);
|
727
729
|
} else {
|
728
730
|
rb_thread_schedule();
|
729
731
|
}
|
@@ -754,7 +756,6 @@ rserver_start(VALUE self) {
|
|
754
756
|
|
755
757
|
static void
|
756
758
|
stop_runners() {
|
757
|
-
sub_cleanup(&the_rserver.sub_cache);
|
758
759
|
// The preferred method of waiting for the ruby threads would be either a
|
759
760
|
// join or even a kill but since we may not have the gvl here that would
|
760
761
|
// cause a segfault. Instead we set a timeout and wait for the running
|
@@ -785,9 +786,6 @@ rserver_shutdown(VALUE self) {
|
|
785
786
|
if (the_server.inited) {
|
786
787
|
server_shutdown("Agoo", stop_runners);
|
787
788
|
|
788
|
-
queue_cleanup(&the_rserver.pub_queue);
|
789
|
-
queue_cleanup(&the_rserver.eval_queue);
|
790
|
-
|
791
789
|
if (1 < the_rserver.worker_cnt && getpid() == *the_rserver.worker_pids) {
|
792
790
|
int i;
|
793
791
|
int status;
|
@@ -863,7 +861,7 @@ handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
|
|
863
861
|
} else {
|
864
862
|
rb_raise(rb_eArgError, "invalid method");
|
865
863
|
}
|
866
|
-
if (NULL == (hook = rhook_create(meth, pat, handler, &
|
864
|
+
if (NULL == (hook = rhook_create(meth, pat, handler, &the_server.eval_queue))) {
|
867
865
|
rb_raise(rb_eStandardError, "out of memory.");
|
868
866
|
} else {
|
869
867
|
Hook h;
|
@@ -891,7 +889,7 @@ handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
|
|
891
889
|
*/
|
892
890
|
static VALUE
|
893
891
|
handle_not_found(VALUE self, VALUE handler) {
|
894
|
-
if (NULL == (the_server.hook404 = rhook_create(GET, "/", handler, &
|
892
|
+
if (NULL == (the_server.hook404 = rhook_create(GET, "/", handler, &the_server.eval_queue))) {
|
895
893
|
rb_raise(rb_eStandardError, "out of memory.");
|
896
894
|
}
|
897
895
|
rb_gc_register_address((VALUE*)&the_server.hook404->handler);
|
data/ext/agoo/rserver.h
CHANGED
@@ -3,35 +3,14 @@
|
|
3
3
|
#ifndef __AGOO_RSERVER_H__
|
4
4
|
#define __AGOO_RSERVER_H__
|
5
5
|
|
6
|
-
#include <pthread.h>
|
7
|
-
#include <stdbool.h>
|
8
|
-
#include <stdatomic.h>
|
9
|
-
|
10
6
|
#include <ruby.h>
|
11
7
|
|
12
|
-
#include "log.h"
|
13
|
-
#include "queue.h"
|
14
|
-
#include "sub.h"
|
15
|
-
#include "upgraded.h"
|
16
|
-
|
17
8
|
#define MAX_WORKERS 32
|
18
9
|
|
19
10
|
typedef struct _RServer {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
struct _Queue eval_queue;
|
25
|
-
|
26
|
-
// upgrade
|
27
|
-
pthread_mutex_t up_lock;
|
28
|
-
int max_push_pending;
|
29
|
-
Upgraded up_list;
|
30
|
-
|
31
|
-
// threads and workers
|
32
|
-
int worker_cnt;
|
33
|
-
int worker_pids[MAX_WORKERS];
|
34
|
-
VALUE *eval_threads; // Qnil terminated
|
11
|
+
int worker_cnt;
|
12
|
+
int worker_pids[MAX_WORKERS];
|
13
|
+
VALUE *eval_threads; // Qnil terminated
|
35
14
|
} *RServer;
|
36
15
|
|
37
16
|
extern struct _RServer the_rserver;
|
@@ -0,0 +1,303 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
|
5
|
+
#include <ruby.h>
|
6
|
+
#include <ruby/encoding.h>
|
7
|
+
|
8
|
+
#include "con.h"
|
9
|
+
#include "debug.h"
|
10
|
+
#include "pub.h"
|
11
|
+
#include "rserver.h"
|
12
|
+
#include "rupgraded.h"
|
13
|
+
#include "server.h"
|
14
|
+
#include "subject.h"
|
15
|
+
#include "upgraded.h"
|
16
|
+
|
17
|
+
static VALUE upgraded_class = Qundef;
|
18
|
+
|
19
|
+
static VALUE sse_sym;
|
20
|
+
static VALUE websocket_sym;
|
21
|
+
|
22
|
+
static ID on_open_id = 0;
|
23
|
+
static ID to_s_id = 0;
|
24
|
+
|
25
|
+
static Upgraded
|
26
|
+
get_upgraded(VALUE self) {
|
27
|
+
Upgraded up = NULL;
|
28
|
+
|
29
|
+
if (the_server.active) {
|
30
|
+
pthread_mutex_lock(&the_server.up_lock);
|
31
|
+
if (NULL != (up = DATA_PTR(self))) {
|
32
|
+
atomic_fetch_add(&up->ref_cnt, 1);
|
33
|
+
}
|
34
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
35
|
+
}
|
36
|
+
return up;
|
37
|
+
}
|
38
|
+
|
39
|
+
const char*
|
40
|
+
extract_subject(VALUE subject, int *slen) {
|
41
|
+
const char *subj;
|
42
|
+
|
43
|
+
switch (rb_type(subject)) {
|
44
|
+
case T_STRING:
|
45
|
+
subj = StringValuePtr(subject);
|
46
|
+
*slen = (int)RSTRING_LEN(subject);
|
47
|
+
break;
|
48
|
+
case T_SYMBOL:
|
49
|
+
subj = rb_id2name(rb_sym2id(subject));
|
50
|
+
*slen = strlen(subj);
|
51
|
+
break;
|
52
|
+
default:
|
53
|
+
subject = rb_funcall(subject, to_s_id, 0);
|
54
|
+
subj = StringValuePtr(subject);
|
55
|
+
*slen = (int)RSTRING_LEN(subject);
|
56
|
+
break;
|
57
|
+
}
|
58
|
+
return subj;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* Document-method: write
|
62
|
+
*
|
63
|
+
* call-seq: write(msg)
|
64
|
+
*
|
65
|
+
* Writes a message to the WebSocket or SSE connection. Returns true if the
|
66
|
+
* message has been queued and false otherwise. A closed connection or too
|
67
|
+
* many pending messages could cause a value of false to be returned.
|
68
|
+
*/
|
69
|
+
static VALUE
|
70
|
+
rup_write(VALUE self, VALUE msg) {
|
71
|
+
Upgraded up = get_upgraded(self);
|
72
|
+
const char *message;
|
73
|
+
size_t mlen;
|
74
|
+
bool bin = false;
|
75
|
+
|
76
|
+
if (NULL == up) {
|
77
|
+
return Qfalse;
|
78
|
+
}
|
79
|
+
if (T_STRING == rb_type(msg)) {
|
80
|
+
message = StringValuePtr(msg);
|
81
|
+
mlen = RSTRING_LEN(msg);
|
82
|
+
if (RB_ENCODING_IS_ASCII8BIT(msg)) {
|
83
|
+
bin = true;
|
84
|
+
}
|
85
|
+
} else {
|
86
|
+
volatile VALUE rs = rb_funcall(msg, to_s_id, 0);
|
87
|
+
|
88
|
+
message = StringValuePtr(rs);
|
89
|
+
mlen = RSTRING_LEN(rs);
|
90
|
+
}
|
91
|
+
return upgraded_write(up, message, mlen, bin, false) ? Qtrue : Qfalse;
|
92
|
+
}
|
93
|
+
|
94
|
+
/* Document-method: subscribe
|
95
|
+
*
|
96
|
+
* call-seq: subscribe(subject)
|
97
|
+
*
|
98
|
+
* Subscribes to messages published on the specified subject. The subject is a
|
99
|
+
* dot delimited string that can include a '*' character as a wild card that
|
100
|
+
* matches any set of characters. The '>' character matches all remaining
|
101
|
+
* characters. Examples: people.fred.log, people.*.log, people.fred.>
|
102
|
+
*
|
103
|
+
* Symbols can also be used as can any other object that responds to #to_s.
|
104
|
+
*/
|
105
|
+
static VALUE
|
106
|
+
rup_subscribe(VALUE self, VALUE subject) {
|
107
|
+
Upgraded up;
|
108
|
+
int slen;
|
109
|
+
const char *subj = extract_subject(subject, &slen);
|
110
|
+
|
111
|
+
if (NULL != (up = get_upgraded(self))) {
|
112
|
+
upgraded_subscribe(up, subj, slen, false);
|
113
|
+
}
|
114
|
+
return Qnil;
|
115
|
+
}
|
116
|
+
|
117
|
+
/* Document-method: unsubscribe
|
118
|
+
*
|
119
|
+
* call-seq: unsubscribe(subject=nil)
|
120
|
+
*
|
121
|
+
* Unsubscribes to messages on the provided subject. If the subject is nil
|
122
|
+
* then all subscriptions for the object are removed.
|
123
|
+
*
|
124
|
+
* Symbols can also be used as can any other object that responds to #to_s.
|
125
|
+
*/
|
126
|
+
static VALUE
|
127
|
+
rup_unsubscribe(int argc, VALUE *argv, VALUE self) {
|
128
|
+
Upgraded up;
|
129
|
+
const char *subject = NULL;
|
130
|
+
int slen = 0;
|
131
|
+
|
132
|
+
if (0 < argc) {
|
133
|
+
subject = extract_subject(argv[0], &slen);
|
134
|
+
}
|
135
|
+
if (NULL != (up = get_upgraded(self))) {
|
136
|
+
upgraded_unsubscribe(up, subject, slen, false);
|
137
|
+
}
|
138
|
+
return Qnil;
|
139
|
+
}
|
140
|
+
|
141
|
+
/* Document-method: close
|
142
|
+
*
|
143
|
+
* call-seq: close()
|
144
|
+
*
|
145
|
+
* Closes the connections associated with the handler.
|
146
|
+
*/
|
147
|
+
static VALUE
|
148
|
+
rup_close(VALUE self) {
|
149
|
+
Upgraded up = get_upgraded(self);
|
150
|
+
|
151
|
+
if (NULL != up) {
|
152
|
+
upgraded_close(up, false);
|
153
|
+
}
|
154
|
+
return Qnil;
|
155
|
+
}
|
156
|
+
|
157
|
+
/* Document-method: pending
|
158
|
+
*
|
159
|
+
* call-seq: pending()
|
160
|
+
*
|
161
|
+
* Returns the number of pending WebSocket or SSE writes. If the connection is
|
162
|
+
* closed then -1 is returned.
|
163
|
+
*/
|
164
|
+
static VALUE
|
165
|
+
rup_pending(VALUE self) {
|
166
|
+
Upgraded up = get_upgraded(self);
|
167
|
+
int pending = -1;
|
168
|
+
|
169
|
+
if (NULL != up) {
|
170
|
+
pending = upgraded_pending(up);
|
171
|
+
atomic_fetch_sub(&up->ref_cnt, 1);
|
172
|
+
}
|
173
|
+
return INT2NUM(pending);
|
174
|
+
}
|
175
|
+
|
176
|
+
/* Document-method: open?
|
177
|
+
*
|
178
|
+
* call-seq: open?()
|
179
|
+
*
|
180
|
+
* Returns true if the connection is open and false otherwise.
|
181
|
+
*/
|
182
|
+
static VALUE
|
183
|
+
rup_open(VALUE self) {
|
184
|
+
Upgraded up = get_upgraded(self);
|
185
|
+
int pending = -1;
|
186
|
+
|
187
|
+
if (NULL != up) {
|
188
|
+
pending = atomic_load(&up->pending);
|
189
|
+
atomic_fetch_sub(&up->ref_cnt, 1);
|
190
|
+
}
|
191
|
+
return 0 <= pending ? Qtrue : Qfalse;
|
192
|
+
}
|
193
|
+
|
194
|
+
/* Document-method: protocol
|
195
|
+
*
|
196
|
+
* call-seq: protocol()
|
197
|
+
*
|
198
|
+
* Returns the protocol of the upgraded connection as either :websocket or
|
199
|
+
* :sse. If no longer connected nil is returned.
|
200
|
+
*/
|
201
|
+
static VALUE
|
202
|
+
rup_protocol(VALUE self) {
|
203
|
+
VALUE pro = Qnil;
|
204
|
+
|
205
|
+
if (the_server.active) {
|
206
|
+
Upgraded up;
|
207
|
+
|
208
|
+
pthread_mutex_lock(&the_server.up_lock);
|
209
|
+
if (NULL != (up = DATA_PTR(self)) && NULL != up->con) {
|
210
|
+
switch (up->con->bind->kind) {
|
211
|
+
case CON_WS:
|
212
|
+
pro = websocket_sym;
|
213
|
+
break;
|
214
|
+
case CON_SSE:
|
215
|
+
pro = sse_sym;
|
216
|
+
break;
|
217
|
+
default:
|
218
|
+
break;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
pthread_mutex_unlock(&the_server.up_lock);
|
222
|
+
}
|
223
|
+
return pro;
|
224
|
+
}
|
225
|
+
|
226
|
+
static void
|
227
|
+
on_destroy(Upgraded up) {
|
228
|
+
if (Qnil != (VALUE)up->wrap) {
|
229
|
+
DATA_PTR((VALUE)up->wrap) = NULL;
|
230
|
+
up->wrap = (void*)Qnil;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
Upgraded
|
235
|
+
rupgraded_create(Con c, VALUE obj, VALUE env) {
|
236
|
+
Upgraded up;
|
237
|
+
|
238
|
+
if (!the_server.active) {
|
239
|
+
rb_raise(rb_eIOError, "Server shutdown.");
|
240
|
+
}
|
241
|
+
if (NULL != (up = upgraded_create(c, (void*)obj, (void*)env))) {
|
242
|
+
up->on_empty = rb_respond_to(obj, rb_intern("on_drained"));
|
243
|
+
up->on_close = rb_respond_to(obj, rb_intern("on_close"));
|
244
|
+
up->on_shut = rb_respond_to(obj, rb_intern("on_shutdown"));
|
245
|
+
up->on_msg = rb_respond_to(obj, rb_intern("on_message"));
|
246
|
+
up->on_error = rb_respond_to(obj, rb_intern("on_error"));
|
247
|
+
up->on_destroy = on_destroy;
|
248
|
+
|
249
|
+
up->wrap = (void*)Data_Wrap_Struct(upgraded_class, NULL, NULL, up);
|
250
|
+
|
251
|
+
server_add_upgraded(up);
|
252
|
+
|
253
|
+
if (rb_respond_to(obj, on_open_id)) {
|
254
|
+
rb_funcall(obj, on_open_id, 1, (VALUE)up->wrap);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
return up;
|
258
|
+
}
|
259
|
+
|
260
|
+
// Use the publish from the Agoo module.
|
261
|
+
extern VALUE ragoo_publish(VALUE self, VALUE subject, VALUE message);
|
262
|
+
|
263
|
+
/* Document-method: env
|
264
|
+
*
|
265
|
+
* call-seq: env()
|
266
|
+
*
|
267
|
+
* Returns the environment passed to the call method that initiated the
|
268
|
+
* Upgraded Object creation.
|
269
|
+
*/
|
270
|
+
static VALUE
|
271
|
+
env(VALUE self) {
|
272
|
+
Upgraded up = get_upgraded(self);
|
273
|
+
|
274
|
+
if (NULL != up) {
|
275
|
+
return (VALUE)up->env;
|
276
|
+
}
|
277
|
+
return Qnil;
|
278
|
+
}
|
279
|
+
|
280
|
+
/* Document-module: Agoo::Upgraded
|
281
|
+
*
|
282
|
+
* Adds methods to a handler of WebSocket and SSE connections.
|
283
|
+
*/
|
284
|
+
void
|
285
|
+
upgraded_init(VALUE mod) {
|
286
|
+
upgraded_class = rb_define_class_under(mod, "Upgraded", rb_cObject);
|
287
|
+
|
288
|
+
rb_define_method(upgraded_class, "write", rup_write, 1);
|
289
|
+
rb_define_method(upgraded_class, "subscribe", rup_subscribe, 1);
|
290
|
+
rb_define_method(upgraded_class, "unsubscribe", rup_unsubscribe, -1);
|
291
|
+
rb_define_method(upgraded_class, "close", rup_close, 0);
|
292
|
+
rb_define_method(upgraded_class, "pending", rup_pending, 0);
|
293
|
+
rb_define_method(upgraded_class, "protocol", rup_protocol, 0);
|
294
|
+
rb_define_method(upgraded_class, "publish", ragoo_publish, 2);
|
295
|
+
rb_define_method(upgraded_class, "open?", rup_open, 0);
|
296
|
+
rb_define_method(upgraded_class, "env", env, 0);
|
297
|
+
|
298
|
+
on_open_id = rb_intern("on_open");
|
299
|
+
to_s_id = rb_intern("to_s");
|
300
|
+
|
301
|
+
sse_sym = ID2SYM(rb_intern("sse")); rb_gc_register_address(&sse_sym);
|
302
|
+
websocket_sym = ID2SYM(rb_intern("websocket")); rb_gc_register_address(&websocket_sym);
|
303
|
+
}
|