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
@@ -4,23 +4,28 @@
4
4
  #define __AGOO_CON_H__
5
5
 
6
6
  #include <poll.h>
7
+ #include <pthread.h>
7
8
  #include <stdbool.h>
8
9
  #include <stdint.h>
9
10
 
10
11
  #include "err.h"
11
- #include "request.h"
12
+ #include "req.h"
12
13
  #include "response.h"
13
14
  #include "server.h"
14
- #include "types.h"
15
+ #include "kinds.h"
15
16
 
16
17
  #define MAX_HEADER_SIZE 8192
17
18
 
18
19
  struct _Upgraded;
20
+ struct _Req;
21
+ struct _Res;
22
+ struct _Bind;
23
+ struct _Queue;
19
24
 
20
25
  typedef struct _Con {
21
26
  struct _Con *next;
22
27
  int sock;
23
- ConKind kind;
28
+ struct _Bind *bind;
24
29
  struct pollfd *pp;
25
30
  uint64_t id;
26
31
  char buf[MAX_HEADER_SIZE];
@@ -33,17 +38,28 @@ typedef struct _Con {
33
38
  bool closing;
34
39
  bool dead;
35
40
  volatile bool hijacked;
36
- Req req;
37
- Res res_head;
38
- Res res_tail;
41
+ struct _Req *req;
42
+ struct _Res *res_head;
43
+ struct _Res *res_tail;
39
44
 
40
45
  struct _Upgraded *up; // only set for push connections
41
46
  } *Con;
42
47
 
43
- extern Con con_create(Err err, int sock, uint64_t id);
48
+ typedef struct _ConLoop {
49
+ struct _ConLoop *next;
50
+ struct _Queue pub_queue;
51
+ pthread_t thread;
52
+ int id;
53
+ } *ConLoop;
54
+
55
+ extern Con con_create(Err err, int sock, uint64_t id, struct _Bind *b);
44
56
  extern void con_destroy(Con c);
45
57
  extern const char* con_header_value(const char *header, int hlen, const char *key, int *vlen);
46
58
 
47
- extern void* con_loop(void *ctx);
59
+ extern struct _ConLoop* conloop_create(Err err, int id);
60
+
61
+ extern bool con_http_read(Con c);
62
+ extern bool con_http_write(Con c);
63
+ extern short con_http_events(Con c);
48
64
 
49
65
  #endif /* __AGOO_CON_H__ */
@@ -35,6 +35,7 @@ atomic_int mem_hook_pattern = 0;
35
35
  atomic_int mem_http_slot = 0;
36
36
  atomic_int mem_log_entry = 0;
37
37
  atomic_int mem_log_what = 0;
38
+ atomic_int mem_log_tid = 0;
38
39
  atomic_int mem_mime_slot = 0;
39
40
  atomic_int mem_page = 0;
40
41
  atomic_int mem_page_msg = 0;
@@ -102,6 +103,7 @@ print_stats() {
102
103
  printf(" mem_http_slot: %d\n", mem_http_slot);
103
104
  printf(" mem_log_entry: %d\n", mem_log_entry);
104
105
  printf(" mem_log_what: %d\n", mem_log_what);
106
+ printf(" mem_log_tid: %d\n", mem_log_tid);
105
107
  printf(" mem_mime_slot: %d\n", mem_mime_slot);
106
108
  printf(" mem_page: %d\n", mem_page);
107
109
  printf(" mem_page_msg: %d\n", mem_page_msg);
@@ -29,6 +29,7 @@ extern atomic_int mem_hook_pattern;
29
29
  extern atomic_int mem_http_slot;
30
30
  extern atomic_int mem_log_entry;
31
31
  extern atomic_int mem_log_what;
32
+ extern atomic_int mem_log_tid;
32
33
  extern atomic_int mem_mime_slot;
33
34
  extern atomic_int mem_page;
34
35
  extern atomic_int mem_page_msg;
@@ -54,7 +54,7 @@ const char*
54
54
  err_str(ErrCode code) {
55
55
  const char *str = NULL;
56
56
 
57
- if (code <= ELAST) {
57
+ if (code < ERR_START) {
58
58
  str = strerror(code);
59
59
  }
60
60
  if (NULL == str) {
@@ -5,10 +5,7 @@
5
5
 
6
6
  #include <errno.h>
7
7
 
8
- #ifndef ELAST
9
- #define ELAST 300
10
- #endif
11
-
8
+ #define ERR_START 300
12
9
  #define ERR_INIT { 0, { 0 } }
13
10
 
14
11
  typedef enum {
@@ -16,7 +13,7 @@ typedef enum {
16
13
  ERR_MEMORY = ENOMEM,
17
14
  ERR_DENIED = EACCES,
18
15
  ERR_IMPL = ENOSYS,
19
- ERR_PARSE = ELAST + 1,
16
+ ERR_PARSE = ERR_START,
20
17
  ERR_READ,
21
18
  ERR_WRITE,
22
19
  ERR_OVERFLOW,
@@ -0,0 +1,109 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <signal.h>
4
+ #include <stdio.h>
5
+
6
+ #include <ruby.h>
7
+
8
+ #include "debug.h"
9
+ #include "error_stream.h"
10
+ #include "pub.h"
11
+ #include "rack_logger.h"
12
+ #include "request.h"
13
+ #include "rresponse.h"
14
+ #include "rlog.h"
15
+ #include "rserver.h"
16
+ #include "rupgraded.h"
17
+ #include "server.h"
18
+ #include "upgraded.h"
19
+
20
+ void
21
+ agoo_shutdown() {
22
+ rserver_shutdown(Qnil);
23
+ log_close();
24
+ }
25
+
26
+ /* Document-method: shutdown
27
+ *
28
+ * call-seq: shutdown()
29
+ *
30
+ * Shutdown the server and logger.
31
+ */
32
+ static VALUE
33
+ ragoo_shutdown(VALUE self) {
34
+ agoo_shutdown();
35
+ debug_rreport();
36
+ return Qnil;
37
+ }
38
+
39
+ /* Document-method: publish
40
+ *
41
+ * call-seq: publish(subject, message)
42
+ *
43
+ * Publish a message on the given subject. A subject is normally a String but
44
+ * Symbols can also be used as can any other object that responds to #to_s.
45
+ */
46
+ VALUE
47
+ ragoo_publish(VALUE self, VALUE subject, VALUE message) {
48
+ int slen;
49
+ const char *subj = extract_subject(subject, &slen);
50
+
51
+ rb_check_type(message, T_STRING);
52
+ server_publish(pub_publish(subj, slen, StringValuePtr(message), (int)RSTRING_LEN(message)));
53
+
54
+ return Qnil;
55
+ }
56
+
57
+ /* Document-method: unsubscribe
58
+ *
59
+ * call-seq: unsubscribe(subject)
60
+ *
61
+ * Unsubscribes on client listeners on the specified subject. Subjects are
62
+ * normally Strings but Symbols can also be used as can any other object that
63
+ * responds to #to_s.
64
+ */
65
+ static VALUE
66
+ ragoo_unsubscribe(VALUE self, VALUE subject) {
67
+ rb_check_type(subject, T_STRING);
68
+
69
+ server_publish(pub_unsubscribe(NULL, StringValuePtr(subject), (int)RSTRING_LEN(subject)));
70
+
71
+ return Qnil;
72
+ }
73
+
74
+ static void
75
+ sig_handler(int sig) {
76
+ agoo_shutdown();
77
+
78
+ debug_report();
79
+ // Use exit instead of rb_exit as rb_exit segfaults most of the time.
80
+ //rb_exit(0);
81
+ exit(0);
82
+ }
83
+
84
+
85
+ /* Document-module: Agoo
86
+ *
87
+ * A High Performance HTTP Server that supports the Ruby rack API. The word
88
+ * agoo is a Japanese word for a type of flying fish.
89
+ */
90
+ void
91
+ Init_agoo() {
92
+ VALUE mod = rb_define_module("Agoo");
93
+
94
+ rlog_init(mod);
95
+ error_stream_init(mod);
96
+ rack_logger_init(mod);
97
+ request_init(mod);
98
+ response_init(mod);
99
+ server_init(mod);
100
+ upgraded_init(mod);
101
+
102
+ rb_define_module_function(mod, "shutdown", ragoo_shutdown, 0);
103
+ rb_define_module_function(mod, "publish", ragoo_publish, 2);
104
+ rb_define_module_function(mod, "unsubscribe", ragoo_unsubscribe, 1);
105
+
106
+ signal(SIGINT, sig_handler);
107
+ signal(SIGTERM, sig_handler);
108
+ signal(SIGPIPE, SIG_IGN);
109
+ }
@@ -0,0 +1,1220 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <ctype.h>
4
+ #include <netdb.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include <unistd.h>
8
+
9
+ #include "bind.h"
10
+ #include "con.h"
11
+ #include "debug.h"
12
+ #include "dtime.h"
13
+ #include "hook.h"
14
+ #include "http.h"
15
+ #include "log.h"
16
+ #include "page.h"
17
+ #include "pub.h"
18
+ #include "res.h"
19
+ #include "seg.h"
20
+ #include "server.h"
21
+ #include "sse.h"
22
+ #include "subject.h"
23
+ #include "upgraded.h"
24
+ #include "websocket.h"
25
+
26
+ #define CON_TIMEOUT 5.0
27
+ #define INITIAL_POLL_SIZE 1024
28
+
29
+ static bool con_ws_read(Con c);
30
+ static bool con_ws_write(Con c);
31
+ static short con_ws_events(Con c);
32
+ static bool con_sse_write(Con c);
33
+ static short con_sse_events(Con c);
34
+
35
+ static struct _Bind ws_bind = {
36
+ .kind = CON_WS,
37
+ .read = con_ws_read,
38
+ .write = con_ws_write,
39
+ .events = con_ws_events,
40
+ };
41
+
42
+ static struct _Bind sse_bind = {
43
+ .kind = CON_SSE,
44
+ .read = NULL,
45
+ .write = con_sse_write,
46
+ .events = con_sse_events,
47
+ };
48
+
49
+ Con
50
+ con_create(Err err, int sock, uint64_t id, Bind b) {
51
+ Con c;
52
+
53
+ if (NULL == (c = (Con)malloc(sizeof(struct _Con)))) {
54
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for a connection.");
55
+ } else {
56
+ DEBUG_ALLOC(mem_con, c)
57
+ memset(c, 0, sizeof(struct _Con));
58
+ c->sock = sock;
59
+ c->id = id;
60
+ c->timeout = dtime() + CON_TIMEOUT;
61
+ c->bind = b;
62
+ }
63
+ return c;
64
+ }
65
+
66
+ void
67
+ con_destroy(Con c) {
68
+ atomic_fetch_sub(&the_server.con_cnt, 1);
69
+
70
+ if (CON_WS == c->bind->kind || CON_SSE == c->bind->kind) {
71
+ ws_req_close(c);
72
+ }
73
+ if (0 < c->sock) {
74
+ close(c->sock);
75
+ c->sock = 0;
76
+ }
77
+ if (NULL != c->req) {
78
+ req_destroy(c->req);
79
+ }
80
+ if (NULL != c->up) {
81
+ upgraded_release_con(c->up);
82
+ c->up = NULL;
83
+ }
84
+ DEBUG_FREE(mem_con, c)
85
+ free(c);
86
+ }
87
+
88
+ const char*
89
+ con_header_value(const char *header, int hlen, const char *key, int *vlen) {
90
+ // Search for \r then check for \n and then the key followed by a :. Keep
91
+ // trying until the end of the header.
92
+ const char *h = header;
93
+ const char *hend = header + hlen;
94
+ const char *value;
95
+ int klen = (int)strlen(key);
96
+
97
+ while (h < hend) {
98
+ if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
99
+ h += klen + 1;
100
+ for (; ' ' == *h; h++) {
101
+ }
102
+ value = h;
103
+ for (; '\r' != *h && '\0' != *h; h++) {
104
+ }
105
+ *vlen = (int)(h - value);
106
+
107
+ return value;
108
+ }
109
+ for (; h < hend; h++) {
110
+ if ('\r' == *h && '\n' == *(h + 1)) {
111
+ h += 2;
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ return NULL;
117
+ }
118
+
119
+ static long
120
+ bad_request(Con c, int status, int line) {
121
+ Res res;
122
+ const char *msg = http_code_message(status);
123
+
124
+ if (NULL == (res = res_create(c))) {
125
+ log_cat(&error_cat, "memory allocation of response failed on connection %llu @ %d.", (unsigned long long)c->id, line);
126
+ } else {
127
+ char buf[256];
128
+ int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", status, msg);
129
+ Text message = text_create(buf, cnt);
130
+
131
+ if (NULL == c->res_tail) {
132
+ c->res_head = res;
133
+ } else {
134
+ c->res_tail->next = res;
135
+ }
136
+ c->res_tail = res;
137
+ res->close = true;
138
+ res_set_message(res, message);
139
+ }
140
+ return -1;
141
+ }
142
+
143
+ static bool
144
+ should_close(const char *header, int hlen) {
145
+ const char *v;
146
+ int vlen = 0;
147
+
148
+ if (NULL != (v = con_header_value(header, hlen, "Connection", &vlen))) {
149
+ return (5 == vlen && 0 == strncasecmp("Close", v, 5));
150
+ }
151
+ return false;
152
+ }
153
+
154
+ static bool
155
+ page_response(Con c, Page p, char *hend) {
156
+ Res res;
157
+ char *b;
158
+
159
+ if (NULL == (res = res_create(c))) {
160
+ return true;
161
+ }
162
+ if (NULL == c->res_tail) {
163
+ c->res_head = res;
164
+ } else {
165
+ c->res_tail->next = res;
166
+ }
167
+ c->res_tail = res;
168
+
169
+ b = strstr(c->buf, "\r\n");
170
+ res->close = should_close(b, (int)(hend - b));
171
+ if (res->close) {
172
+ c->closing = true;
173
+ }
174
+ res_set_message(res, p->resp);
175
+
176
+ return false;
177
+ }
178
+
179
+ // rserver
180
+ static void
181
+ push_error(Upgraded up, const char *msg, int mlen) {
182
+ if (NULL != up && the_server.ctx_nil_value != up->ctx && up->on_error) {
183
+ Req req = req_create(mlen);
184
+
185
+ if (NULL == req) {
186
+ return;
187
+ }
188
+ memcpy(req->msg, msg, mlen);
189
+ req->msg[mlen] = '\0';
190
+ req->up = up;
191
+ req->method = ON_ERROR;
192
+ req->hook = hook_create(NONE, NULL, up->ctx, PUSH_HOOK, &the_server.eval_queue);
193
+ upgraded_ref(up);
194
+ queue_push(&the_server.eval_queue, (void*)req);
195
+ }
196
+ }
197
+
198
+ // Returns:
199
+ // 0 - when header has not been read
200
+ // message length - when length can be determined
201
+ // -1 on a bad request
202
+ // negative of message length - when message is handled here.
203
+ static long
204
+ con_header_read(Con c) {
205
+ char *hend = strstr(c->buf, "\r\n\r\n");
206
+ Method method;
207
+ struct _Seg path;
208
+ char *query = NULL;
209
+ char *qend;
210
+ char *b;
211
+ size_t clen = 0;
212
+ long mlen;
213
+ Hook hook = NULL;
214
+ Page p;
215
+ struct _Err err = ERR_INIT;
216
+
217
+ if (NULL == hend) {
218
+ if (sizeof(c->buf) - 1 <= c->bcnt) {
219
+ return bad_request(c, 431, __LINE__);
220
+ }
221
+ return 0;
222
+ }
223
+ if (req_cat.on) {
224
+ *hend = '\0';
225
+ log_cat(&req_cat, "%llu: %s", (unsigned long long)c->id, c->buf);
226
+ *hend = '\r';
227
+ }
228
+ for (b = c->buf; ' ' != *b; b++) {
229
+ if ('\0' == *b) {
230
+ return bad_request(c, 400, __LINE__);
231
+ }
232
+ }
233
+ switch (toupper(*c->buf)) {
234
+ case 'G':
235
+ if (3 != b - c->buf || 0 != strncmp("GET", c->buf, 3)) {
236
+ return bad_request(c, 400, __LINE__);
237
+ }
238
+ method = GET;
239
+ break;
240
+ case 'P': {
241
+ const char *v;
242
+ int vlen = 0;
243
+ char *vend;
244
+
245
+ if (3 == b - c->buf && 0 == strncmp("PUT", c->buf, 3)) {
246
+ method = PUT;
247
+ } else if (4 == b - c->buf && 0 == strncmp("POST", c->buf, 4)) {
248
+ method = POST;
249
+ } else {
250
+ return bad_request(c, 400, __LINE__);
251
+ }
252
+ if (NULL == (v = con_header_value(c->buf, (int)(hend - c->buf), "Content-Length", &vlen))) {
253
+ return bad_request(c, 411, __LINE__);
254
+ }
255
+ clen = (size_t)strtoul(v, &vend, 10);
256
+ if (vend != v + vlen) {
257
+ return bad_request(c, 411, __LINE__);
258
+ }
259
+ break;
260
+ }
261
+ case 'D':
262
+ if (6 != b - c->buf || 0 != strncmp("DELETE", c->buf, 6)) {
263
+ return bad_request(c, 400, __LINE__);
264
+ }
265
+ method = DELETE;
266
+ break;
267
+ case 'H':
268
+ if (4 != b - c->buf || 0 != strncmp("HEAD", c->buf, 4)) {
269
+ return bad_request(c, 400, __LINE__);
270
+ }
271
+ method = HEAD;
272
+ break;
273
+ case 'O':
274
+ if (7 != b - c->buf || 0 != strncmp("OPTIONS", c->buf, 7)) {
275
+ return bad_request(c, 400, __LINE__);
276
+ }
277
+ method = OPTIONS;
278
+ break;
279
+ case 'C':
280
+ if (7 != b - c->buf || 0 != strncmp("CONNECT", c->buf, 7)) {
281
+ return bad_request(c, 400, __LINE__);
282
+ }
283
+ method = CONNECT;
284
+ break;
285
+ default:
286
+ return bad_request(c, 400, __LINE__);
287
+ }
288
+ for (; ' ' == *b; b++) {
289
+ if ('\0' == *b) {
290
+ return bad_request(c, 400, __LINE__);
291
+ }
292
+ }
293
+ path.start = b;
294
+ for (; ' ' != *b; b++) {
295
+ switch (*b) {
296
+ case '?':
297
+ path.end = b;
298
+ query = b + 1;
299
+ break;
300
+ case '\0':
301
+ return bad_request(c, 400, __LINE__);
302
+ default:
303
+ break;
304
+ }
305
+ }
306
+ if (NULL == query) {
307
+ path.end = b;
308
+ query = b;
309
+ qend = b;
310
+ } else {
311
+ qend = b;
312
+ }
313
+ mlen = hend - c->buf + 4 + clen;
314
+ if (GET == method &&
315
+ NULL != (p = group_get(&err, path.start, (int)(path.end - path.start)))) {
316
+ if (page_response(c, p, hend)) {
317
+ return bad_request(c, 500, __LINE__);
318
+ }
319
+ return -mlen;
320
+ }
321
+ if (GET == method && the_server.root_first &&
322
+ NULL != (p = page_get(&err, path.start, (int)(path.end - path.start)))) {
323
+ if (page_response(c, p, hend)) {
324
+ return bad_request(c, 500, __LINE__);
325
+ }
326
+ return -mlen;
327
+ }
328
+ if (NULL == (hook = hook_find(the_server.hooks, method, &path))) {
329
+ if (GET == method) {
330
+ if (the_server.root_first) { // already checked
331
+ if (NULL != the_server.hook404) {
332
+ // There would be too many parameters to pass to a
333
+ // separate function so just goto the hook processing.
334
+ hook = the_server.hook404;
335
+ goto HOOKED;
336
+ }
337
+ return bad_request(c, 404, __LINE__);
338
+ }
339
+ if (NULL == (p = page_get(&err, path.start, (int)(path.end - path.start)))) {
340
+ if (NULL != the_server.hook404) {
341
+ // There would be too many parameters to pass to a
342
+ // separate function so just goto the hook processing.
343
+ hook = the_server.hook404;
344
+ goto HOOKED;
345
+ }
346
+ return bad_request(c, 404, __LINE__);
347
+ }
348
+ if (page_response(c, p, hend)) {
349
+ return bad_request(c, 500, __LINE__);
350
+ }
351
+ return -mlen;
352
+ }
353
+ return bad_request(c, 404, __LINE__);
354
+ }
355
+ HOOKED:
356
+ // Create request and populate.
357
+ if (NULL == (c->req = req_create(mlen))) {
358
+ return bad_request(c, 413, __LINE__);
359
+ }
360
+ if ((long)c->bcnt <= mlen) {
361
+ memcpy(c->req->msg, c->buf, c->bcnt);
362
+ if ((long)c->bcnt < mlen) {
363
+ memset(c->req->msg + c->bcnt, 0, mlen - c->bcnt);
364
+ }
365
+ } else {
366
+ memcpy(c->req->msg, c->buf, mlen);
367
+ }
368
+ c->req->msg[mlen] = '\0';
369
+ c->req->method = method;
370
+ c->req->upgrade = UP_NONE;
371
+ c->req->up = NULL;
372
+ c->req->path.start = c->req->msg + (path.start - c->buf);
373
+ c->req->path.len = (int)(path.end - path.start);
374
+ c->req->query.start = c->req->msg + (query - c->buf);
375
+ c->req->query.len = (int)(qend - query);
376
+ c->req->query.start[c->req->query.len] = '\0';
377
+ c->req->body.start = c->req->msg + (hend - c->buf + 4);
378
+ c->req->body.len = (unsigned int)clen;
379
+ b = strstr(b, "\r\n");
380
+ c->req->header.start = c->req->msg + (b + 2 - c->buf);
381
+ c->req->header.len = (unsigned int)(hend - b - 2);
382
+ c->req->res = NULL;
383
+ c->req->hook = hook;
384
+
385
+ return mlen;
386
+ }
387
+
388
+ static void
389
+ check_upgrade(Con c) {
390
+ const char *v;
391
+ int vlen = 0;
392
+
393
+ if (NULL == c->req) {
394
+ return;
395
+ }
396
+ if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Connection", &vlen))) {
397
+ if (NULL != strstr(v, "Upgrade")) {
398
+ if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Upgrade", &vlen))) {
399
+ if (0 == strncasecmp("WebSocket", v, vlen)) {
400
+ c->res_tail->close = false;
401
+ c->res_tail->con_kind = CON_WS;
402
+ return;
403
+ }
404
+ }
405
+ }
406
+ }
407
+ if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Accept", &vlen))) {
408
+ if (0 == strncasecmp("text/event-stream", v, vlen)) {
409
+ c->res_tail->close = false;
410
+ c->res_tail->con_kind = CON_SSE;
411
+ return;
412
+ }
413
+ }
414
+ }
415
+
416
+ bool
417
+ con_http_read(Con c) {
418
+ ssize_t cnt;
419
+
420
+ if (c->dead || 0 == c->sock || c->closing) {
421
+ return true;
422
+ }
423
+ if (NULL != c->req) {
424
+ cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
425
+ } else {
426
+ cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
427
+ }
428
+ c->timeout = dtime() + CON_TIMEOUT;
429
+ if (0 >= cnt) {
430
+ // If nothing read then no need to complain. Just close.
431
+ if (0 < c->bcnt) {
432
+ if (0 == cnt) {
433
+ log_cat(&warn_cat, "Nothing to read. Client closed socket on connection %llu.", (unsigned long long)c->id);
434
+ } else {
435
+ log_cat(&warn_cat, "Failed to read request. %s.", strerror(errno));
436
+ }
437
+ }
438
+ return true;
439
+ }
440
+ c->bcnt += cnt;
441
+ while (true) {
442
+ if (NULL == c->req) {
443
+ long mlen;
444
+
445
+ // Terminate with \0 for debug and strstr() check
446
+ c->buf[c->bcnt] = '\0';
447
+ if (0 > (mlen = con_header_read(c))) {
448
+ if (-1 == mlen) {
449
+ c->bcnt = 0;
450
+ *c->buf = '\0';
451
+ return false;
452
+ } else if (-mlen < (long)c->bcnt) {
453
+ mlen = -mlen;
454
+ memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
455
+ c->bcnt -= mlen;
456
+ } else {
457
+ c->bcnt = 0;
458
+ *c->buf = '\0';
459
+ return false;
460
+ }
461
+ continue;
462
+ }
463
+ }
464
+ if (NULL != c->req) {
465
+ if (c->req->mlen <= c->bcnt) {
466
+ Req req;
467
+ Res res;
468
+ long mlen;
469
+
470
+ if (debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
471
+ log_cat(&debug_cat, "request on %llu: %s", (unsigned long long)c->id, c->req->body.start);
472
+ }
473
+ if (NULL == (res = res_create(c))) {
474
+ c->req = NULL;
475
+ log_cat(&error_cat, "memory allocation of response failed on connection %llu.", (unsigned long long)c->id);
476
+ return bad_request(c, 500, __LINE__);
477
+ } else {
478
+ if (NULL == c->res_tail) {
479
+ c->res_head = res;
480
+ } else {
481
+ c->res_tail->next = res;
482
+ }
483
+ c->res_tail = res;
484
+ res->close = should_close(c->req->header.start, c->req->header.len);
485
+ if (res->close) {
486
+ c->closing = true;
487
+ }
488
+ }
489
+ c->req->res = res;
490
+ mlen = c->req->mlen;
491
+ check_upgrade(c);
492
+ req = c->req;
493
+ c->req = NULL;
494
+ queue_push(req->hook->queue, (void*)req);
495
+ if (mlen < (long)c->bcnt) {
496
+ memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
497
+ c->bcnt -= mlen;
498
+ } else {
499
+ c->bcnt = 0;
500
+ *c->buf = '\0';
501
+ break;
502
+ }
503
+ continue;
504
+ }
505
+ }
506
+ break;
507
+ }
508
+ return false;
509
+ }
510
+
511
+ static bool
512
+ con_ws_read(Con c) {
513
+ ssize_t cnt;
514
+ uint8_t *b;
515
+ uint8_t op;
516
+ long mlen;
517
+
518
+ if (NULL != c->req) {
519
+ cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
520
+ } else {
521
+ cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
522
+ }
523
+ c->timeout = dtime() + CON_TIMEOUT;
524
+ if (0 >= cnt) {
525
+ // If nothing read then no need to complain. Just close.
526
+ if (0 < c->bcnt) {
527
+ if (0 == cnt) {
528
+ log_cat(&warn_cat, "Nothing to read. Client closed socket on connection %llu.", (unsigned long long)c->id);
529
+ } else {
530
+ char msg[1024];
531
+ int len = snprintf(msg, sizeof(msg) - 1, "Failed to read WebSocket message. %s.", strerror(errno));
532
+
533
+ push_error(c->up, msg, len);
534
+ log_cat(&warn_cat, "Failed to read WebSocket message. %s.", strerror(errno));
535
+ }
536
+ }
537
+ return true;
538
+ }
539
+ c->bcnt += cnt;
540
+ while (true) {
541
+ if (NULL == c->req) {
542
+ if (c->bcnt < 2) {
543
+ return false; // Try again.
544
+ }
545
+ b = (uint8_t*)c->buf;
546
+ if (0 >= (mlen = ws_calc_len(c, b, c->bcnt))) {
547
+ return (mlen < 0);
548
+ }
549
+ op = 0x0F & *b;
550
+ switch (op) {
551
+ case WS_OP_TEXT:
552
+ case WS_OP_BIN:
553
+ if (ws_create_req(c, mlen)) {
554
+ return true;
555
+ }
556
+ break;
557
+ case WS_OP_CLOSE:
558
+ return true;
559
+ case WS_OP_PING:
560
+ if (mlen == (long)c->bcnt) {
561
+ ws_pong(c);
562
+ c->bcnt = 0;
563
+ }
564
+ break;
565
+ case WS_OP_PONG:
566
+ // ignore
567
+ if (mlen == (long)c->bcnt) {
568
+ c->bcnt = 0;
569
+ }
570
+ break;
571
+ case WS_OP_CONT:
572
+ default: {
573
+ char msg[1024];
574
+ int len = snprintf(msg, sizeof(msg) - 1, "WebSocket op 0x%02x not supported on %llu.",
575
+ op, (unsigned long long)c->id);
576
+
577
+ push_error(c->up, msg, len);
578
+ log_cat(&error_cat, "WebSocket op 0x%02x not supported on %llu.", op, (unsigned long long)c->id);
579
+ return true;
580
+ }
581
+ }
582
+ }
583
+ if (NULL != c->req) {
584
+ mlen = c->req->mlen;
585
+ c->req->mlen = ws_decode(c->req->msg, c->req->mlen);
586
+ if (mlen <= (long)c->bcnt) {
587
+ if (debug_cat.on) {
588
+ if (ON_MSG == c->req->method) {
589
+ log_cat(&debug_cat, "WebSocket message on %llu: %s", (unsigned long long)c->id, c->req->msg);
590
+ } else {
591
+ log_cat(&debug_cat, "WebSocket binary message on %llu", (unsigned long long)c->id);
592
+ }
593
+ }
594
+ }
595
+ upgraded_ref(c->up);
596
+ queue_push(&the_server.eval_queue, (void*)c->req);
597
+ if (mlen < (long)c->bcnt) {
598
+ memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
599
+ c->bcnt -= mlen;
600
+ } else {
601
+ c->bcnt = 0;
602
+ *c->buf = '\0';
603
+ c->req = NULL;
604
+ break;
605
+ }
606
+ c->req = NULL;
607
+ continue;
608
+ }
609
+ break;
610
+ }
611
+ return false;
612
+ }
613
+
614
+ // return true to remove/close connection
615
+ static bool
616
+ con_read(Con c) {
617
+ if (NULL != c->bind->read) {
618
+ return c->bind->read(c);
619
+ }
620
+ return true;
621
+ }
622
+
623
+ // return true to remove/close connection
624
+ bool
625
+ con_http_write(Con c) {
626
+ Text message = res_message(c->res_head);
627
+ ssize_t cnt;
628
+
629
+ c->timeout = dtime() + CON_TIMEOUT;
630
+ if (0 == c->wcnt) {
631
+ if (resp_cat.on) {
632
+ char buf[4096];
633
+ char *hend = strstr(message->text, "\r\n\r\n");
634
+
635
+ if (NULL == hend) {
636
+ hend = message->text + message->len;
637
+ }
638
+ if ((long)sizeof(buf) <= hend - message->text) {
639
+ hend = message->text + sizeof(buf) - 1;
640
+ }
641
+ memcpy(buf, message->text, hend - message->text);
642
+ buf[hend - message->text] = '\0';
643
+ log_cat(&resp_cat, "%llu: %s", (unsigned long long)c->id, buf);
644
+ }
645
+ if (debug_cat.on) {
646
+ log_cat(&debug_cat, "response on %llu: %s", (unsigned long long)c->id, message->text);
647
+ }
648
+ }
649
+ if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, MSG_DONTWAIT))) {
650
+ if (EAGAIN == errno) {
651
+ return false;
652
+ }
653
+ log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
654
+
655
+ return true;
656
+ }
657
+ c->wcnt += cnt;
658
+ if (c->wcnt == message->len) { // finished
659
+ Res res = c->res_head;
660
+ bool done = res->close;
661
+
662
+ c->res_head = res->next;
663
+ if (res == c->res_tail) {
664
+ c->res_tail = NULL;
665
+ }
666
+ c->wcnt = 0;
667
+ res_destroy(res);
668
+
669
+ return done;
670
+ }
671
+
672
+ return false;
673
+ }
674
+
675
+ static const char ping_msg[] = "\x89\x00";
676
+ static const char pong_msg[] = "\x8a\x00";
677
+
678
+ static bool
679
+ con_ws_write(Con c) {
680
+ Res res = c->res_head;
681
+ Text message = res_message(res);
682
+ ssize_t cnt;
683
+
684
+ if (NULL == message) {
685
+ if (res->ping) {
686
+ if (0 > (cnt = send(c->sock, ping_msg, sizeof(ping_msg) - 1, 0))) {
687
+ char msg[1024];
688
+ int len;
689
+
690
+ if (EAGAIN == errno) {
691
+ return false;
692
+ }
693
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
694
+ push_error(c->up, msg, len);
695
+
696
+ log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
697
+ ws_req_close(c);
698
+ res_destroy(res);
699
+
700
+ return true;
701
+ }
702
+ } else if (res->pong) {
703
+ if (0 > (cnt = send(c->sock, pong_msg, sizeof(pong_msg) - 1, 0))) {
704
+ char msg[1024];
705
+ int len;
706
+
707
+ if (EAGAIN == errno) {
708
+ return false;
709
+ }
710
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
711
+ push_error(c->up, msg, len);
712
+ log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
713
+ ws_req_close(c);
714
+ res_destroy(res);
715
+
716
+ return true;
717
+ }
718
+ } else {
719
+ ws_req_close(c);
720
+ c->res_head = res->next;
721
+ if (res == c->res_tail) {
722
+ c->res_tail = NULL;
723
+ }
724
+ res_destroy(res);
725
+ return true;
726
+ }
727
+ c->res_head = res->next;
728
+ if (res == c->res_tail) {
729
+ c->res_tail = NULL;
730
+ }
731
+ return false;
732
+ }
733
+ c->timeout = dtime() + CON_TIMEOUT;
734
+ if (0 == c->wcnt) {
735
+ Text t;
736
+
737
+ if (push_cat.on) {
738
+ if (message->bin) {
739
+ log_cat(&push_cat, "%llu binary", (unsigned long long)c->id);
740
+ } else {
741
+ log_cat(&push_cat, "%llu: %s", (unsigned long long)c->id, message->text);
742
+ }
743
+ }
744
+ t = ws_expand(message);
745
+ if (t != message) {
746
+ res_set_message(res, t);
747
+ message = t;
748
+ }
749
+ }
750
+ if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
751
+ char msg[1024];
752
+ int len;
753
+
754
+ if (EAGAIN == errno) {
755
+ return false;
756
+ }
757
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
758
+ push_error(c->up, msg, len);
759
+ log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
760
+ ws_req_close(c);
761
+
762
+ return true;
763
+ }
764
+ c->wcnt += cnt;
765
+ if (c->wcnt == message->len) { // finished
766
+ Res res = c->res_head;
767
+ bool done = res->close;
768
+
769
+ c->res_head = res->next;
770
+ if (res == c->res_tail) {
771
+ c->res_tail = NULL;
772
+ }
773
+ c->wcnt = 0;
774
+ res_destroy(res);
775
+
776
+ return done;
777
+ }
778
+ return false;
779
+ }
780
+
781
+ static bool
782
+ con_sse_write(Con c) {
783
+ Res res = c->res_head;
784
+ Text message = res_message(res);
785
+ ssize_t cnt;
786
+
787
+ if (NULL == message) {
788
+ ws_req_close(c);
789
+ res_destroy(res);
790
+ return true;
791
+ }
792
+ c->timeout = dtime() + CON_TIMEOUT *2;
793
+ if (0 == c->wcnt) {
794
+ Text t;
795
+
796
+ if (push_cat.on) {
797
+ log_cat(&push_cat, "%llu: %s", (unsigned long long)c->id, message->text);
798
+ }
799
+ t = sse_expand(message);
800
+ if (t != message) {
801
+ res_set_message(res, t);
802
+ message = t;
803
+ }
804
+ }
805
+ if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
806
+ char msg[1024];
807
+ int len;
808
+
809
+ if (EAGAIN == errno) {
810
+ return false;
811
+ }
812
+ len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
813
+ push_error(c->up, msg, len);
814
+ log_cat(&error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
815
+ ws_req_close(c);
816
+
817
+ return true;
818
+ }
819
+ c->wcnt += cnt;
820
+ if (c->wcnt == message->len) { // finished
821
+ Res res = c->res_head;
822
+ bool done = res->close;
823
+
824
+ c->res_head = res->next;
825
+ if (res == c->res_tail) {
826
+ c->res_tail = NULL;
827
+ }
828
+ c->wcnt = 0;
829
+ res_destroy(res);
830
+
831
+ return done;
832
+ }
833
+ return false;
834
+ }
835
+
836
+ static bool
837
+ con_write(Con c) {
838
+ bool remove = true;
839
+ ConKind kind = c->res_head->con_kind;
840
+
841
+ if (NULL != c->bind->write) {
842
+ remove = c->bind->write(c);
843
+ }
844
+ //if (kind != c->kind && CON_ANY != kind) {
845
+ if (CON_ANY != kind) {
846
+ switch (kind) {
847
+ case CON_WS:
848
+ c->bind = &ws_bind;
849
+ break;
850
+ case CON_SSE:
851
+ c->bind = &sse_bind;
852
+ break;
853
+ default:
854
+ break;
855
+ }
856
+ }
857
+ return remove;
858
+ }
859
+
860
+ static void
861
+ publish_pub(Pub pub) {
862
+ Upgraded up;
863
+ const char *sub = pub->subject->pattern;
864
+ int cnt = 0;
865
+
866
+ for (up = the_server.up_list; NULL != up; up = up->next) {
867
+ if (NULL != up->con && upgraded_match(up, sub)) {
868
+ Res res = res_create(up->con);
869
+
870
+ if (NULL != res) {
871
+ if (NULL == up->con->res_tail) {
872
+ up->con->res_head = res;
873
+ } else {
874
+ up->con->res_tail->next = res;
875
+ }
876
+ up->con->res_tail = res;
877
+ res->con_kind = CON_ANY;
878
+ res_set_message(res, text_dup(pub->msg));
879
+ cnt++;
880
+ }
881
+ }
882
+ }
883
+ }
884
+
885
+ static void
886
+ unsubscribe_pub(Pub pub) {
887
+ if (NULL == pub->up) {
888
+ Upgraded up;
889
+
890
+ for (up = the_server.up_list; NULL != up; up = up->next) {
891
+ upgraded_del_subject(up, pub->subject);
892
+ }
893
+ } else {
894
+ upgraded_del_subject(pub->up, pub->subject);
895
+ }
896
+ }
897
+
898
+ static void
899
+ process_pub_con(Pub pub) {
900
+ Upgraded up = pub->up;
901
+
902
+ if (NULL != up) {
903
+ int pending;
904
+
905
+ // TBD Change pending to be based on length of con queue
906
+ if (1 == (pending = atomic_fetch_sub(&up->pending, 1))) {
907
+ if (NULL != up && the_server.ctx_nil_value != up->ctx && up->on_empty) {
908
+ Req req = req_create(0);
909
+
910
+ req->up = up;
911
+ req->method = ON_EMPTY;
912
+ req->hook = hook_create(NONE, NULL, up->ctx, PUSH_HOOK, &the_server.eval_queue);
913
+ upgraded_ref(up);
914
+ queue_push(&the_server.eval_queue, (void*)req);
915
+ }
916
+ }
917
+ }
918
+ switch (pub->kind) {
919
+ case PUB_CLOSE:
920
+ // A close after already closed is used to decrement the reference
921
+ // count on the upgraded so it can be destroyed in the con loop
922
+ // threads.
923
+ if (NULL != up->con) {
924
+ Res res = res_create(up->con);
925
+
926
+ if (NULL != res) {
927
+ if (NULL == up->con->res_tail) {
928
+ up->con->res_head = res;
929
+ } else {
930
+ up->con->res_tail->next = res;
931
+ }
932
+ up->con->res_tail = res;
933
+ res->con_kind = up->con->bind->kind;
934
+ res->close = true;
935
+ }
936
+ }
937
+ break;
938
+ case PUB_WRITE: {
939
+ if (NULL == up->con) {
940
+ log_cat(&warn_cat, "Connection already closed. WebSocket write failed.");
941
+ } else {
942
+ Res res = res_create(up->con);
943
+
944
+ if (NULL != res) {
945
+ if (NULL == up->con->res_tail) {
946
+ up->con->res_head = res;
947
+ } else {
948
+ up->con->res_tail->next = res;
949
+ }
950
+ up->con->res_tail = res;
951
+ res->con_kind = CON_ANY;
952
+ res_set_message(res, pub->msg);
953
+ }
954
+ }
955
+ break;
956
+ case PUB_SUB:
957
+ upgraded_add_subject(pub->up, pub->subject);
958
+ pub->subject = NULL;
959
+ break;
960
+ case PUB_UN:
961
+ unsubscribe_pub(pub);
962
+ break;
963
+ case PUB_MSG:
964
+ publish_pub(pub);
965
+ break;
966
+ }
967
+ default:
968
+ break;
969
+ }
970
+ pub_destroy(pub);
971
+ }
972
+
973
+ short
974
+ con_http_events(Con c) {
975
+ short events = 0;
976
+
977
+ if (!c->closing) {
978
+ events = POLLIN | POLLOUT;
979
+ }
980
+ return events;
981
+ }
982
+
983
+ static short
984
+ con_ws_events(Con c) {
985
+ short events = 0;
986
+
987
+ if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL != res_message(c->res_head))) {
988
+ events = POLLIN | POLLOUT;
989
+ } else if (!c->closing) {
990
+ events = POLLIN;
991
+ }
992
+ return events;
993
+ }
994
+
995
+ static short
996
+ con_sse_events(Con c) {
997
+ short events = 0;
998
+
999
+ if (NULL != c->res_head && NULL != res_message(c->res_head)) {
1000
+ events = POLLOUT;
1001
+ }
1002
+ return events;
1003
+ }
1004
+
1005
+ static struct pollfd*
1006
+ poll_setup(Con c, Queue q, struct pollfd *pp) {
1007
+ // The first two pollfd are for the con_queue and the pub_queue in that
1008
+ // order.
1009
+ pp->fd = queue_listen(&the_server.con_queue);
1010
+ pp->events = POLLIN;
1011
+ pp->revents = 0;
1012
+ pp++;
1013
+ pp->fd = queue_listen(q);
1014
+ pp->events = POLLIN;
1015
+ pp->revents = 0;
1016
+ pp++;
1017
+ for (; NULL != c; c = c->next) {
1018
+ if (c->dead || 0 == c->sock) {
1019
+ continue;
1020
+ }
1021
+ if (c->hijacked) {
1022
+ c->sock = 0;
1023
+ continue;
1024
+ }
1025
+ c->pp = pp;
1026
+ pp->fd = c->sock;
1027
+ pp->events = c->bind->events(c);
1028
+ pp->revents = 0;
1029
+ pp++;
1030
+ }
1031
+ return pp;
1032
+ }
1033
+
1034
+ static bool
1035
+ remove_dead_res(Con c) {
1036
+ Res res;
1037
+
1038
+ while (NULL != (res = c->res_head)) {
1039
+ if (NULL == res_message(c->res_head) && !c->res_head->close && !c->res_head->ping) {
1040
+ break;
1041
+ }
1042
+ c->res_head = res->next;
1043
+ if (res == c->res_tail) {
1044
+ c->res_tail = NULL;
1045
+ }
1046
+ res_destroy(res);
1047
+ }
1048
+ return NULL == c->res_head;
1049
+ }
1050
+
1051
+ void*
1052
+ con_loop(void *x) {
1053
+ ConLoop loop = (ConLoop)x;
1054
+ Con c;
1055
+ Con prev;
1056
+ Con next;
1057
+ Con cons = NULL;
1058
+ size_t size = sizeof(struct pollfd) * INITIAL_POLL_SIZE;
1059
+ struct pollfd *pa = (struct pollfd*)malloc(size);
1060
+ struct pollfd *pend = pa + INITIAL_POLL_SIZE;
1061
+ struct pollfd *pp;
1062
+ int ccnt = 0;
1063
+ int i;
1064
+ double now;
1065
+ Pub pub;
1066
+
1067
+ atomic_fetch_add(&the_server.running, 1);
1068
+ memset(pa, 0, size);
1069
+ while (the_server.active) {
1070
+ while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
1071
+ c->next = cons;
1072
+ cons = c;
1073
+ ccnt++;
1074
+ if (pend - pa < ccnt + 2) {
1075
+ size_t cnt = (pend - pa) * 2;
1076
+
1077
+ size = sizeof(struct pollfd) * cnt;
1078
+ if (NULL == (pa = (struct pollfd*)malloc(size))) {
1079
+ log_cat(&error_cat, "Out of memory.");
1080
+ log_close();
1081
+ exit(EXIT_FAILURE);
1082
+
1083
+ return NULL;
1084
+ }
1085
+ pend = pa + cnt;
1086
+ }
1087
+ }
1088
+ while (NULL != (pub = (Pub)queue_pop(&loop->pub_queue, 0.0))) {
1089
+ process_pub_con(pub);
1090
+ }
1091
+ pp = poll_setup(cons, &loop->pub_queue, pa);
1092
+ if (0 > (i = poll(pa, (nfds_t)(pp - pa), 10))) {
1093
+ if (EAGAIN == errno) {
1094
+ continue;
1095
+ }
1096
+ log_cat(&error_cat, "Polling error. %s.", strerror(errno));
1097
+ // Either a signal or something bad like out of memory. Might as well exit.
1098
+ break;
1099
+ }
1100
+ now = dtime();
1101
+ if (0 < i) {
1102
+ // Check con_queue if an event is waiting.
1103
+ if (0 != (pa->revents & POLLIN)) {
1104
+ queue_release(&the_server.con_queue);
1105
+ while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
1106
+ c->next = cons;
1107
+ cons = c;
1108
+ ccnt++;
1109
+ if (pend - pa < ccnt + 2) {
1110
+ size_t cnt = (pend - pa) * 2;
1111
+
1112
+ size = sizeof(struct pollfd) * cnt;
1113
+ if (NULL == (pa = (struct pollfd*)malloc(size))) {
1114
+ log_cat(&error_cat, "Out of memory.");
1115
+ log_close();
1116
+ exit(EXIT_FAILURE);
1117
+
1118
+ return NULL;
1119
+ }
1120
+ pend = pa + cnt;
1121
+ }
1122
+ }
1123
+ }
1124
+ // Check pub_queue if an event is waiting.
1125
+ if (0 != (pa[1].revents & POLLIN)) {
1126
+ queue_release(&loop->pub_queue);
1127
+ while (NULL != (pub = (Pub)queue_pop(&loop->pub_queue, 0.0))) {
1128
+ process_pub_con(pub);
1129
+ }
1130
+ }
1131
+ }
1132
+ prev = NULL;
1133
+ for (c = cons; NULL != c; c = next) {
1134
+ next = c->next;
1135
+ if (0 == c->sock || NULL == c->pp) {
1136
+ continue;
1137
+ }
1138
+ pp = c->pp;
1139
+ if (0 != (pp->revents & POLLIN)) {
1140
+ if (con_read(c)) {
1141
+ c->dead = true;
1142
+ goto CON_CHECK;
1143
+ }
1144
+ }
1145
+ if (0 != (pp->revents & POLLOUT) && NULL != c->res_head && NULL != res_message(c->res_head)) {
1146
+ if (con_write(c)) {
1147
+ c->dead = true;
1148
+ goto CON_CHECK;
1149
+ }
1150
+ }
1151
+ if (0 != (pp->revents & (POLLERR | POLLHUP | POLLNVAL))) {
1152
+ if (0 < c->bcnt) {
1153
+ if (0 != (pp->revents & (POLLHUP | POLLNVAL))) {
1154
+ log_cat(&error_cat, "Socket %llu closed.", (unsigned long long)c->id);
1155
+ } else if (!c->closing) {
1156
+ log_cat(&error_cat, "Socket %llu error. %s", (unsigned long long)c->id, strerror(errno));
1157
+ }
1158
+ }
1159
+ c->dead = true;
1160
+ goto CON_CHECK;
1161
+ }
1162
+ CON_CHECK:
1163
+ if (c->dead || 0 == c->sock) {
1164
+ if (remove_dead_res(c)) {
1165
+ goto CON_RM;
1166
+ }
1167
+ } else if (0.0 == c->timeout || now < c->timeout) {
1168
+ prev = c;
1169
+ continue;
1170
+ } else if (c->closing) {
1171
+ if (remove_dead_res(c)) {
1172
+ goto CON_RM;
1173
+ }
1174
+ } else if (CON_WS == c->bind->kind || CON_SSE == c->bind->kind) {
1175
+ c->timeout = dtime() + CON_TIMEOUT;
1176
+ ws_ping(c);
1177
+ continue;
1178
+ } else {
1179
+ c->closing = true;
1180
+ c->timeout = now + 0.5;
1181
+ prev = c;
1182
+ continue;
1183
+ }
1184
+ prev = c;
1185
+ continue;
1186
+ CON_RM:
1187
+ if (NULL == prev) {
1188
+ cons = next;
1189
+ } else {
1190
+ prev->next = next;
1191
+ }
1192
+ ccnt--;
1193
+ log_cat(&con_cat, "Connection %llu closed.", (unsigned long long)c->id);
1194
+ con_destroy(c);
1195
+ }
1196
+ }
1197
+ while (NULL != (c = cons)) {
1198
+ cons = c->next;
1199
+ con_destroy(c);
1200
+ }
1201
+ atomic_fetch_sub(&the_server.running, 1);
1202
+
1203
+ return NULL;
1204
+ }
1205
+
1206
+ ConLoop
1207
+ conloop_create(Err err, int id) {
1208
+ ConLoop loop;
1209
+
1210
+ if (NULL == (loop = (ConLoop)malloc(sizeof(struct _ConLoop)))) {
1211
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for a connection thread.");
1212
+ } else {
1213
+ //DEBUG_ALLOC(mem_con, c);
1214
+ loop->next = NULL;
1215
+ queue_multi_init(&loop->pub_queue, 256, true, false);
1216
+ loop->id = id;
1217
+ pthread_create(&loop->thread, NULL, con_loop, loop);
1218
+ }
1219
+ return loop;
1220
+ }