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,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__
@@ -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, 256, false, false);
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, 100))) {
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
- pthread_detach(the_server.con_thread);
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
+ }
@@ -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__
@@ -1,6 +1,8 @@
1
1
  // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
2
 
3
- #include "request.h"
3
+ #include <stdlib.h>
4
+
5
+ #include "req.h"
4
6
  #include "sse.h"
5
7
  #include "text.h"
6
8
 
@@ -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 (Qnil != up->wrap) {
28
- DATA_PTR(up->wrap) = NULL;
29
- up->wrap = Qnil;
17
+ if (NULL != up->on_destroy) {
18
+ up->on_destroy(up);
30
19
  }
31
20
  if (NULL == up->prev) {
32
- the_rserver.up_list = up->next;
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(&the_rserver.up_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(&the_rserver.up_lock);
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(&the_rserver.up_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(&the_rserver.up_lock);
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
- static Upgraded
151
- get_upgraded(VALUE self) {
152
- Upgraded up = NULL;
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 (NULL == up) {
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 Qfalse;
124
+ return false;
184
125
  }
185
- if (T_STRING == rb_type(msg)) {
186
- if (RB_ENCODING_IS_ASCII8BIT(msg)) {
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
- queue_push(&the_rserver.pub_queue, p);
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
- if (NULL != (up = get_upgraded(self))) {
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
- /* Document-method: unsubscribe
227
- *
228
- * call-seq: unsubscribe(subject=nil)
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
- return Qnil;
141
+ atomic_fetch_add(&up->pending, 1);
142
+ server_publish(pub_subscribe(up, subject, slen));
266
143
  }
267
144
 
268
- /* Document-method: pending
269
- *
270
- * call-seq: pending()
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
- return INT2NUM(pending);
150
+ atomic_fetch_add(&up->pending, 1);
151
+ server_publish(pub_unsubscribe(up, subject, slen));
285
152
  }
286
153
 
287
- /* Document-method: open?
288
- *
289
- * call-seq: open?()
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
- return 0 <= pending ? Qtrue : Qfalse;
159
+ atomic_fetch_add(&up->pending, 1);
160
+ server_publish(pub_close(up));
303
161
  }
304
162
 
305
- /* Document-method: protocol
306
- *
307
- * call-seq: protocol()
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, VALUE obj, VALUE env) {
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->handler = obj;
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
- }