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.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/README.md +12 -1
  4. data/ext/agoo/agoo.c +4 -3
  5. data/ext/agoo/bind.c +51 -11
  6. data/ext/agoo/bind.h +9 -0
  7. data/ext/agoo/con.c +127 -89
  8. data/ext/agoo/con.h +24 -8
  9. data/ext/agoo/debug.c +2 -0
  10. data/ext/agoo/debug.h +1 -0
  11. data/ext/agoo/err.c +1 -1
  12. data/ext/agoo/err.h +2 -5
  13. data/ext/agoo/foo/agoo.c +109 -0
  14. data/ext/agoo/foo/con.c +1220 -0
  15. data/ext/agoo/foo/con.h +65 -0
  16. data/ext/agoo/foo/page.c +699 -0
  17. data/ext/agoo/foo/pub.c +131 -0
  18. data/ext/agoo/foo/pub.h +40 -0
  19. data/ext/agoo/foo/rserver.c +1016 -0
  20. data/ext/agoo/foo/server.c +303 -0
  21. data/ext/agoo/foo/server.h +67 -0
  22. data/ext/agoo/foo/upgraded.c +182 -0
  23. data/ext/agoo/hook.c +31 -0
  24. data/ext/agoo/hook.h +9 -10
  25. data/ext/agoo/{types.h → kinds.h} +3 -3
  26. data/ext/agoo/log.c +168 -83
  27. data/ext/agoo/log.h +6 -2
  28. data/ext/agoo/page.c +38 -32
  29. data/ext/agoo/pub.c +16 -0
  30. data/ext/agoo/pub.h +1 -0
  31. data/ext/agoo/queue.h +1 -0
  32. data/ext/agoo/req.c +95 -0
  33. data/ext/agoo/req.h +48 -0
  34. data/ext/agoo/request.c +10 -38
  35. data/ext/agoo/request.h +1 -34
  36. data/ext/agoo/res.h +1 -3
  37. data/ext/agoo/response.c +6 -248
  38. data/ext/agoo/response.h +2 -6
  39. data/ext/agoo/rlog.c +2 -1
  40. data/ext/agoo/rresponse.c +252 -0
  41. data/ext/agoo/rresponse.h +14 -0
  42. data/ext/agoo/rserver.c +43 -45
  43. data/ext/agoo/rserver.h +3 -24
  44. data/ext/agoo/rupgraded.c +303 -0
  45. data/ext/agoo/rupgraded.h +17 -0
  46. data/ext/agoo/server.c +125 -8
  47. data/ext/agoo/server.h +26 -4
  48. data/ext/agoo/sse.c +3 -1
  49. data/ext/agoo/upgraded.c +42 -280
  50. data/ext/agoo/upgraded.h +16 -8
  51. data/ext/agoo/websocket.c +13 -9
  52. data/lib/agoo/base.rb +84 -0
  53. data/lib/agoo/client.rb +25 -0
  54. data/lib/agoo/version.rb +1 -1
  55. data/test/bind_test.rb +2 -2
  56. 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__
@@ -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(&the_rserver.up_lock);
65
- for (up = the_rserver.up_list; NULL != up; up = up->next) {
66
- if (Qnil != up->handler) {
67
- rb_gc_mark(up->handler);
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(&the_rserver.up_lock);
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.con_thread = 0;
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 tc = FIX2INT(v);
127
+ int mpp = FIX2INT(v);
125
128
 
126
- if (0 <= tc || tc < 1000) {
127
- the_server.thread_cnt = tc;
129
+ if (0 <= mpp || mpp < 1000) {
130
+ the_server.max_push_pending = mpp;
128
131
  } else {
129
- rb_raise(rb_eArgError, "thread_count must be between 0 and 1000.");
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
- sub_init(&the_rserver.sub_cache);
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, &the_rserver.eval_queue);
467
- upgraded_create(req->res->con, handler, request_env(req, Qnil));
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, &the_rserver.eval_queue);
480
- upgraded_create(req->res->con, handler, request_env(req, Qnil));
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
- queue_push(&the_rserver.pub_queue, pub_close(req->up));
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(&the_rserver.eval_queue, 0.1))) {
664
+ if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
663
665
  handle_protected(req, true);
664
- request_destroy(req);
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(&the_rserver.eval_queue, 0.1))) {
726
+ if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
725
727
  handle_protected(req, false);
726
- request_destroy(req);
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, &the_rserver.eval_queue))) {
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, &the_rserver.eval_queue))) {
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);
@@ -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
- // pub/sub (maybe common across servers,,,
21
- struct _Queue pub_queue;
22
- struct _SubCache sub_cache; // subscription cache
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
+ }