agoo 1.2.2 → 2.0.0
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/README.md +11 -9
- data/ext/agoo/agoo.c +45 -0
- data/ext/agoo/base64.c +107 -0
- data/ext/agoo/base64.h +15 -0
- data/ext/agoo/ccache.c +301 -0
- data/ext/agoo/ccache.h +53 -0
- data/ext/agoo/con.c +522 -82
- data/ext/agoo/con.h +7 -5
- data/ext/agoo/debug.c +121 -7
- data/ext/agoo/debug.h +11 -6
- data/ext/agoo/error_stream.c +5 -6
- data/ext/agoo/error_stream.h +1 -1
- data/ext/agoo/extconf.rb +2 -1
- data/ext/agoo/hook.c +4 -4
- data/ext/agoo/hook.h +1 -0
- data/ext/agoo/http.c +2 -2
- data/ext/agoo/http.h +2 -0
- data/ext/agoo/log.c +604 -219
- data/ext/agoo/log.h +20 -7
- data/ext/agoo/page.c +20 -23
- data/ext/agoo/page.h +2 -0
- data/ext/agoo/pub.c +111 -0
- data/ext/agoo/pub.h +40 -0
- data/ext/agoo/queue.c +2 -2
- data/ext/agoo/rack_logger.c +15 -71
- data/ext/agoo/rack_logger.h +1 -1
- data/ext/agoo/request.c +96 -21
- data/ext/agoo/request.h +23 -12
- data/ext/agoo/res.c +5 -2
- data/ext/agoo/res.h +4 -0
- data/ext/agoo/response.c +13 -12
- data/ext/agoo/response.h +1 -2
- data/ext/agoo/server.c +290 -428
- data/ext/agoo/server.h +10 -10
- data/ext/agoo/sha1.c +148 -0
- data/ext/agoo/sha1.h +10 -0
- data/ext/agoo/sse.c +26 -0
- data/ext/agoo/sse.h +12 -0
- data/ext/agoo/sub.c +111 -0
- data/ext/agoo/sub.h +36 -0
- data/ext/agoo/subscription.c +54 -0
- data/ext/agoo/subscription.h +18 -0
- data/ext/agoo/text.c +26 -4
- data/ext/agoo/text.h +2 -0
- data/ext/agoo/types.h +13 -0
- data/ext/agoo/upgraded.c +148 -0
- data/ext/agoo/upgraded.h +13 -0
- data/ext/agoo/websocket.c +248 -0
- data/ext/agoo/websocket.h +27 -0
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +13 -6
- data/test/base_handler_test.rb +24 -22
- data/test/log_test.rb +146 -199
- data/test/rack_handler_test.rb +19 -20
- data/test/static_test.rb +30 -28
- metadata +23 -7
- data/test/rrr/test.rb +0 -26
- data/test/tests.rb +0 -8
data/ext/agoo/ccache.h
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_CCACHE_H__
|
4
|
+
#define __AGOO_CCACHE_H__
|
5
|
+
|
6
|
+
#include <pthread.h>
|
7
|
+
#include <stdatomic.h>
|
8
|
+
#include <stdbool.h>
|
9
|
+
#include <stdint.h>
|
10
|
+
|
11
|
+
#include <ruby.h>
|
12
|
+
|
13
|
+
#define CC_BUCKET_SIZE 1024
|
14
|
+
#define CC_BUCKET_MASK 1023
|
15
|
+
|
16
|
+
struct _Con;
|
17
|
+
|
18
|
+
typedef struct _CSlot {
|
19
|
+
struct _CSlot *next;
|
20
|
+
uint64_t cid;
|
21
|
+
struct _Con *con;
|
22
|
+
VALUE handler;
|
23
|
+
atomic_int pending;
|
24
|
+
atomic_int ref_cnt;
|
25
|
+
bool on_empty;
|
26
|
+
bool on_close;
|
27
|
+
bool on_shut;
|
28
|
+
bool on_msg;
|
29
|
+
} *CSlot;
|
30
|
+
|
31
|
+
typedef struct _CCache {
|
32
|
+
CSlot buckets[CC_BUCKET_SIZE];
|
33
|
+
pthread_mutex_t lock;
|
34
|
+
VALUE wrap;
|
35
|
+
} *CCache;
|
36
|
+
|
37
|
+
extern void cc_init(CCache cc);
|
38
|
+
extern void cc_cleanup(CCache cc);
|
39
|
+
extern CSlot cc_set_con(CCache cc, struct _Con *con);
|
40
|
+
extern void cc_set_handler(CCache cc, uint64_t cid, VALUE handler, bool on_empty, bool on_close, bool on_shut, bool on_msg);
|
41
|
+
extern void cc_remove(CCache cc, uint64_t cid);
|
42
|
+
|
43
|
+
extern void cc_remove_con(CCache cc, uint64_t cid);
|
44
|
+
extern VALUE cc_ref_dec(CCache cc, uint64_t cid);
|
45
|
+
|
46
|
+
extern struct _Con* cc_get_con(CCache cc, uint64_t cid);
|
47
|
+
extern VALUE cc_get_handler(CCache cc, uint64_t cid);
|
48
|
+
extern int cc_get_pending(CCache cc, uint64_t cid);
|
49
|
+
extern CSlot cc_get_slot(CCache cc, uint64_t cid);
|
50
|
+
|
51
|
+
extern void cc_pending_inc(CCache cc, uint64_t cid);
|
52
|
+
|
53
|
+
#endif /* __AGOO_CCACHE_H__ */
|
data/ext/agoo/con.c
CHANGED
@@ -8,14 +8,17 @@
|
|
8
8
|
#include "dtime.h"
|
9
9
|
#include "hook.h"
|
10
10
|
#include "http.h"
|
11
|
+
#include "pub.h"
|
11
12
|
#include "res.h"
|
12
13
|
#include "server.h"
|
14
|
+
#include "sse.h"
|
15
|
+
#include "websocket.h"
|
13
16
|
|
14
17
|
#define MAX_SOCK 4096
|
15
18
|
#define CON_TIMEOUT 5.0
|
16
19
|
|
17
20
|
Con
|
18
|
-
con_create(Err err,
|
21
|
+
con_create(Err err, int sock, uint64_t id) {
|
19
22
|
Con c;
|
20
23
|
|
21
24
|
if (MAX_SOCK <= sock) {
|
@@ -25,26 +28,33 @@ con_create(Err err, Server server, int sock, uint64_t id) {
|
|
25
28
|
if (NULL == (c = (Con)malloc(sizeof(struct _Con)))) {
|
26
29
|
err_set(err, ERR_MEMORY, "Failed to allocate memory for a connection.");
|
27
30
|
} else {
|
28
|
-
DEBUG_ALLOC(mem_con)
|
31
|
+
DEBUG_ALLOC(mem_con, c)
|
29
32
|
memset(c, 0, sizeof(struct _Con));
|
30
33
|
c->sock = sock;
|
31
|
-
c->
|
32
|
-
c->
|
34
|
+
c->id = id;
|
35
|
+
c->kind = CON_HTTP;
|
36
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
33
37
|
}
|
34
38
|
return c;
|
35
39
|
}
|
36
40
|
|
37
41
|
void
|
38
42
|
con_destroy(Con c) {
|
43
|
+
if (CON_WS == c->kind || CON_SSE == c->kind) {
|
44
|
+
ws_req_close(c);
|
45
|
+
}
|
39
46
|
if (0 < c->sock) {
|
40
47
|
close(c->sock);
|
41
48
|
c->sock = 0;
|
42
49
|
}
|
43
50
|
if (NULL != c->req) {
|
44
|
-
|
45
|
-
|
51
|
+
request_destroy(c->req);
|
52
|
+
}
|
53
|
+
if (NULL != c->slot) {
|
54
|
+
cc_remove_con(&the_server.con_cache, c->id);
|
55
|
+
c->slot = NULL;
|
46
56
|
}
|
47
|
-
DEBUG_FREE(mem_con)
|
57
|
+
DEBUG_FREE(mem_con, c)
|
48
58
|
free(c);
|
49
59
|
}
|
50
60
|
|
@@ -85,13 +95,12 @@ bad_request(Con c, int status, int line) {
|
|
85
95
|
const char *msg = http_code_message(status);
|
86
96
|
|
87
97
|
if (NULL == (res = res_create())) {
|
88
|
-
log_cat(&
|
98
|
+
log_cat(&error_cat, "memory allocation of response failed on connection %llu @ %d.", c->id, line);
|
89
99
|
} else {
|
90
100
|
char buf[256];
|
91
101
|
int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", status, msg);
|
92
102
|
Text message = text_create(buf, cnt);
|
93
103
|
|
94
|
-
DEBUG_ALLOC(mem_res)
|
95
104
|
if (NULL == c->res_tail) {
|
96
105
|
c->res_head = res;
|
97
106
|
} else {
|
@@ -122,7 +131,6 @@ should_close(const char *header, int hlen) {
|
|
122
131
|
// negative of message length - when message is handled here.
|
123
132
|
static long
|
124
133
|
con_header_read(Con c) {
|
125
|
-
Server server = c->server;
|
126
134
|
char *hend = strstr(c->buf, "\r\n\r\n");
|
127
135
|
Method method;
|
128
136
|
char *path;
|
@@ -140,9 +148,9 @@ con_header_read(Con c) {
|
|
140
148
|
}
|
141
149
|
return 0;
|
142
150
|
}
|
143
|
-
if (
|
151
|
+
if (req_cat.on) {
|
144
152
|
*hend = '\0';
|
145
|
-
log_cat(&
|
153
|
+
log_cat(&req_cat, "%llu: %s", c->id, c->buf);
|
146
154
|
*hend = '\r';
|
147
155
|
}
|
148
156
|
for (b = c->buf; ' ' != *b; b++) {
|
@@ -231,17 +239,17 @@ con_header_read(Con c) {
|
|
231
239
|
qend = b;
|
232
240
|
}
|
233
241
|
mlen = hend - c->buf + 4 + clen;
|
234
|
-
if (NULL == (hook = hook_find(
|
242
|
+
if (NULL == (hook = hook_find(the_server.hooks, method, path, pend))) {
|
235
243
|
if (GET == method) {
|
236
244
|
struct _Err err = ERR_INIT;
|
237
|
-
Page p = page_get(&err, &
|
245
|
+
Page p = page_get(&err, &the_server.pages, the_server.root, path, (int)(pend - path));
|
238
246
|
Res res;
|
239
247
|
|
240
248
|
if (NULL == p) {
|
241
|
-
if (NULL !=
|
249
|
+
if (NULL != the_server.hook404) {
|
242
250
|
// There would be too many parameters to pass to a
|
243
251
|
// separate function so just goto the hook processing.
|
244
|
-
hook =
|
252
|
+
hook = the_server.hook404;
|
245
253
|
goto HOOKED;
|
246
254
|
}
|
247
255
|
return bad_request(c, 404, __LINE__);
|
@@ -258,8 +266,6 @@ con_header_read(Con c) {
|
|
258
266
|
|
259
267
|
b = strstr(c->buf, "\r\n");
|
260
268
|
res->close = should_close(b, (int)(hend - b));
|
261
|
-
|
262
|
-
text_ref(p->resp);
|
263
269
|
res_set_message(res, p->resp);
|
264
270
|
|
265
271
|
return -mlen;
|
@@ -267,10 +273,9 @@ con_header_read(Con c) {
|
|
267
273
|
}
|
268
274
|
HOOKED:
|
269
275
|
// Create request and populate.
|
270
|
-
if (NULL == (c->req = (
|
276
|
+
if (NULL == (c->req = request_create(mlen))) {
|
271
277
|
return bad_request(c, 413, __LINE__);
|
272
278
|
}
|
273
|
-
DEBUG_ALLOC(mem_req)
|
274
279
|
if ((long)c->bcnt <= mlen) {
|
275
280
|
memcpy(c->req->msg, c->buf, c->bcnt);
|
276
281
|
if ((long)c->bcnt < mlen) {
|
@@ -280,13 +285,13 @@ HOOKED:
|
|
280
285
|
memcpy(c->req->msg, c->buf, mlen);
|
281
286
|
}
|
282
287
|
c->req->msg[mlen] = '\0';
|
283
|
-
c->req->server = server;
|
284
288
|
c->req->method = method;
|
289
|
+
c->req->upgrade = UP_NONE;
|
290
|
+
c->req->cid = c->id;
|
285
291
|
c->req->path.start = c->req->msg + (path - c->buf);
|
286
292
|
c->req->path.len = (int)(pend - path);
|
287
293
|
c->req->query.start = c->req->msg + (query - c->buf);
|
288
294
|
c->req->query.len = (int)(qend - query);
|
289
|
-
c->req->mlen = mlen;
|
290
295
|
c->req->body.start = c->req->msg + (hend - c->buf + 4);
|
291
296
|
c->req->body.len = (unsigned int)clen;
|
292
297
|
b = strstr(b, "\r\n");
|
@@ -303,9 +308,36 @@ HOOKED:
|
|
303
308
|
return mlen;
|
304
309
|
}
|
305
310
|
|
306
|
-
|
311
|
+
static void
|
312
|
+
check_upgrade(Con c) {
|
313
|
+
const char *v;
|
314
|
+
int vlen = 0;
|
315
|
+
|
316
|
+
if (NULL == c->req) {
|
317
|
+
return;
|
318
|
+
}
|
319
|
+
if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Connection", &vlen))) {
|
320
|
+
if (NULL != strstr(v, "Upgrade")) {
|
321
|
+
if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Upgrade", &vlen))) {
|
322
|
+
if (0 == strncasecmp("WebSocket", v, vlen)) {
|
323
|
+
c->res_tail->close = false;
|
324
|
+
c->res_tail->con_kind = CON_WS;
|
325
|
+
return;
|
326
|
+
}
|
327
|
+
}
|
328
|
+
}
|
329
|
+
}
|
330
|
+
if (NULL != (v = con_header_value(c->req->header.start, c->req->header.len, "Accept", &vlen))) {
|
331
|
+
if (0 == strncasecmp("text/event-stream", v, vlen)) {
|
332
|
+
c->res_tail->close = false;
|
333
|
+
c->res_tail->con_kind = CON_SSE;
|
334
|
+
return;
|
335
|
+
}
|
336
|
+
}
|
337
|
+
}
|
338
|
+
|
307
339
|
static bool
|
308
|
-
|
340
|
+
con_http_read(Con c) {
|
309
341
|
ssize_t cnt;
|
310
342
|
|
311
343
|
if (NULL != c->req) {
|
@@ -318,9 +350,9 @@ con_read(Con c) {
|
|
318
350
|
// If nothing read then no need to complain. Just close.
|
319
351
|
if (0 < c->bcnt) {
|
320
352
|
if (0 == cnt) {
|
321
|
-
log_cat(&
|
353
|
+
log_cat(&warn_cat, "Nothing to read. Client closed socket on connection %llu.", c->id);
|
322
354
|
} else {
|
323
|
-
log_cat(&
|
355
|
+
log_cat(&warn_cat, "Failed to read request. %s.", strerror(errno));
|
324
356
|
}
|
325
357
|
}
|
326
358
|
return true;
|
@@ -351,14 +383,16 @@ con_read(Con c) {
|
|
351
383
|
}
|
352
384
|
if (NULL != c->req) {
|
353
385
|
if (c->req->mlen <= c->bcnt) {
|
386
|
+
Req req;
|
354
387
|
Res res;
|
388
|
+
long mlen;
|
355
389
|
|
356
|
-
if (
|
357
|
-
log_cat(&
|
390
|
+
if (debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
|
391
|
+
log_cat(&debug_cat, "request on %llu: %s", c->id, c->req->body.start);
|
358
392
|
}
|
359
393
|
if (NULL == (res = res_create())) {
|
360
394
|
c->req = NULL;
|
361
|
-
log_cat(&
|
395
|
+
log_cat(&error_cat, "memory allocation of response failed on connection %llu.", c->id);
|
362
396
|
return bad_request(c, 500, __LINE__);
|
363
397
|
} else {
|
364
398
|
if (NULL == c->res_tail) {
|
@@ -370,17 +404,19 @@ con_read(Con c) {
|
|
370
404
|
res->close = should_close(c->req->header.start, c->req->header.len);
|
371
405
|
}
|
372
406
|
c->req->res = res;
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
407
|
+
mlen = c->req->mlen;
|
408
|
+
check_upgrade(c);
|
409
|
+
req = c->req;
|
410
|
+
c->req = NULL;
|
411
|
+
queue_push(&the_server.eval_queue, (void*)req);
|
412
|
+
if (mlen < (long)c->bcnt) {
|
413
|
+
memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
|
414
|
+
c->bcnt -= mlen;
|
377
415
|
} else {
|
378
416
|
c->bcnt = 0;
|
379
417
|
*c->buf = '\0';
|
380
|
-
c->req = NULL;
|
381
418
|
break;
|
382
419
|
}
|
383
|
-
c->req = NULL;
|
384
420
|
continue;
|
385
421
|
}
|
386
422
|
}
|
@@ -389,33 +425,137 @@ con_read(Con c) {
|
|
389
425
|
return false;
|
390
426
|
}
|
391
427
|
|
428
|
+
static bool
|
429
|
+
con_ws_read(Con c) {
|
430
|
+
ssize_t cnt;
|
431
|
+
uint8_t *b;
|
432
|
+
uint8_t op;
|
433
|
+
long mlen;
|
434
|
+
|
435
|
+
if (NULL != c->req) {
|
436
|
+
cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
|
437
|
+
} else {
|
438
|
+
cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
|
439
|
+
}
|
440
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
441
|
+
if (0 >= cnt) {
|
442
|
+
// If nothing read then no need to complain. Just close.
|
443
|
+
if (0 < c->bcnt) {
|
444
|
+
if (0 == cnt) {
|
445
|
+
log_cat(&warn_cat, "Nothing to read. Client closed socket on connection %llu.", c->id);
|
446
|
+
} else {
|
447
|
+
log_cat(&warn_cat, "Failed to read WebSocket message. %s.", strerror(errno));
|
448
|
+
}
|
449
|
+
}
|
450
|
+
return true;
|
451
|
+
}
|
452
|
+
c->bcnt += cnt;
|
453
|
+
while (true) {
|
454
|
+
if (NULL == c->req) {
|
455
|
+
if (c->bcnt < 2) {
|
456
|
+
return false; // Try again.
|
457
|
+
}
|
458
|
+
b = (uint8_t*)c->buf;
|
459
|
+
if (0 >= (mlen = ws_calc_len(c, b, c->bcnt))) {
|
460
|
+
return (mlen < 0);
|
461
|
+
}
|
462
|
+
op = 0x0F & *b;
|
463
|
+
switch (op) {
|
464
|
+
case WS_OP_TEXT:
|
465
|
+
case WS_OP_BIN:
|
466
|
+
if (ws_create_req(c, mlen)) {
|
467
|
+
return true;
|
468
|
+
}
|
469
|
+
break;
|
470
|
+
case WS_OP_CLOSE:
|
471
|
+
return true;
|
472
|
+
case WS_OP_PING:
|
473
|
+
ws_pong(c);
|
474
|
+
break;
|
475
|
+
case WS_OP_PONG:
|
476
|
+
// ignore
|
477
|
+
break;
|
478
|
+
case WS_OP_CONT:
|
479
|
+
default:
|
480
|
+
log_cat(&error_cat, "WebSocket op 0x%02x not supported on %llu.", op, c->id);
|
481
|
+
return true;
|
482
|
+
}
|
483
|
+
}
|
484
|
+
if (NULL != c->req) {
|
485
|
+
mlen = c->req->mlen;
|
486
|
+
c->req->mlen = ws_decode(c->req->msg, c->req->mlen);
|
487
|
+
if (mlen <= (long)c->bcnt) {
|
488
|
+
if (debug_cat.on) {
|
489
|
+
if (ON_MSG == c->req->method) {
|
490
|
+
log_cat(&debug_cat, "WebSocket message on %llu: %s", c->id, c->req->msg);
|
491
|
+
} else {
|
492
|
+
log_cat(&debug_cat, "WebSocket binary message on %llu", c->id);
|
493
|
+
}
|
494
|
+
}
|
495
|
+
}
|
496
|
+
queue_push(&the_server.eval_queue, (void*)c->req);
|
497
|
+
if (mlen < (long)c->bcnt) {
|
498
|
+
memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
|
499
|
+
c->bcnt -= mlen;
|
500
|
+
} else {
|
501
|
+
c->bcnt = 0;
|
502
|
+
*c->buf = '\0';
|
503
|
+
c->req = NULL;
|
504
|
+
break;
|
505
|
+
}
|
506
|
+
c->req = NULL;
|
507
|
+
continue;
|
508
|
+
}
|
509
|
+
break;
|
510
|
+
}
|
511
|
+
return false;
|
512
|
+
}
|
513
|
+
|
392
514
|
// return true to remove/close connection
|
393
515
|
static bool
|
394
|
-
|
516
|
+
con_read(Con c) {
|
517
|
+
switch (c->kind) {
|
518
|
+
case CON_HTTP:
|
519
|
+
return con_http_read(c);
|
520
|
+
case CON_WS:
|
521
|
+
return con_ws_read(c);
|
522
|
+
default:
|
523
|
+
break;
|
524
|
+
}
|
525
|
+
return true;
|
526
|
+
}
|
527
|
+
|
528
|
+
// return true to remove/close connection
|
529
|
+
static bool
|
530
|
+
con_http_write(Con c) {
|
395
531
|
Text message = res_message(c->res_head);
|
396
532
|
ssize_t cnt;
|
397
533
|
|
398
534
|
c->timeout = dtime() + CON_TIMEOUT;
|
399
535
|
if (0 == c->wcnt) {
|
400
|
-
if (
|
536
|
+
if (resp_cat.on) {
|
401
537
|
char buf[4096];
|
402
538
|
char *hend = strstr(message->text, "\r\n\r\n");
|
403
539
|
|
404
540
|
if (NULL == hend) {
|
405
541
|
hend = message->text + message->len;
|
406
542
|
}
|
543
|
+
if ((long)sizeof(buf) <= hend - message->text) {
|
544
|
+
hend = message->text + sizeof(buf) - 1;
|
545
|
+
}
|
407
546
|
memcpy(buf, message->text, hend - message->text);
|
408
|
-
|
547
|
+
buf[hend - message->text] = '\0';
|
548
|
+
log_cat(&resp_cat, "%llu: %s", c->id, buf);
|
409
549
|
}
|
410
|
-
if (
|
411
|
-
log_cat(&
|
550
|
+
if (debug_cat.on) {
|
551
|
+
log_cat(&debug_cat, "response on %llu: %s", c->id, message->text);
|
412
552
|
}
|
413
553
|
}
|
414
554
|
if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
|
415
555
|
if (EAGAIN == errno) {
|
416
556
|
return false;
|
417
557
|
}
|
418
|
-
log_cat(&
|
558
|
+
log_cat(&error_cat, "Socket error @ %llu.", c->id);
|
419
559
|
|
420
560
|
return true;
|
421
561
|
}
|
@@ -436,12 +576,313 @@ con_write(Con c) {
|
|
436
576
|
return false;
|
437
577
|
}
|
438
578
|
|
579
|
+
static const char ping_msg[] = "\x89\x00";
|
580
|
+
static const char pong_msg[] = "\x8a\x00";
|
581
|
+
|
582
|
+
static bool
|
583
|
+
con_ws_write(Con c) {
|
584
|
+
Res res = c->res_head;
|
585
|
+
Text message = res_message(res);
|
586
|
+
ssize_t cnt;
|
587
|
+
|
588
|
+
if (NULL == message) {
|
589
|
+
if (res->ping) {
|
590
|
+
if (0 > (cnt = send(c->sock, ping_msg, sizeof(ping_msg) - 1, 0))) {
|
591
|
+
if (EAGAIN == errno) {
|
592
|
+
return false;
|
593
|
+
}
|
594
|
+
log_cat(&error_cat, "Socket error @ %llu.", c->id);
|
595
|
+
ws_req_close(c);
|
596
|
+
res_destroy(res);
|
597
|
+
|
598
|
+
return true;
|
599
|
+
}
|
600
|
+
} else if (res->pong) {
|
601
|
+
if (0 > (cnt = send(c->sock, pong_msg, sizeof(pong_msg) - 1, 0))) {
|
602
|
+
if (EAGAIN == errno) {
|
603
|
+
return false;
|
604
|
+
}
|
605
|
+
log_cat(&error_cat, "Socket error @ %llu.", c->id);
|
606
|
+
ws_req_close(c);
|
607
|
+
res_destroy(res);
|
608
|
+
|
609
|
+
return true;
|
610
|
+
}
|
611
|
+
} else {
|
612
|
+
ws_req_close(c);
|
613
|
+
res_destroy(res);
|
614
|
+
return true;
|
615
|
+
}
|
616
|
+
c->res_head = res->next;
|
617
|
+
if (res == c->res_tail) {
|
618
|
+
c->res_tail = NULL;
|
619
|
+
}
|
620
|
+
return false;
|
621
|
+
}
|
622
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
623
|
+
if (0 == c->wcnt) {
|
624
|
+
Text t;
|
625
|
+
|
626
|
+
if (push_cat.on) {
|
627
|
+
if (message->bin) {
|
628
|
+
log_cat(&push_cat, "%llu binary", c->id);
|
629
|
+
} else {
|
630
|
+
log_cat(&push_cat, "%llu: %s", c->id, message->text);
|
631
|
+
}
|
632
|
+
}
|
633
|
+
t = ws_expand(message);
|
634
|
+
if (t != message) {
|
635
|
+
res_set_message(res, t);
|
636
|
+
message = t;
|
637
|
+
}
|
638
|
+
}
|
639
|
+
if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
|
640
|
+
if (EAGAIN == errno) {
|
641
|
+
return false;
|
642
|
+
}
|
643
|
+
log_cat(&error_cat, "Socket error @ %llu.", c->id);
|
644
|
+
ws_req_close(c);
|
645
|
+
|
646
|
+
return true;
|
647
|
+
}
|
648
|
+
c->wcnt += cnt;
|
649
|
+
if (c->wcnt == message->len) { // finished
|
650
|
+
Res res = c->res_head;
|
651
|
+
bool done = res->close;
|
652
|
+
int pending;
|
653
|
+
|
654
|
+
c->res_head = res->next;
|
655
|
+
if (res == c->res_tail) {
|
656
|
+
c->res_tail = NULL;
|
657
|
+
}
|
658
|
+
c->wcnt = 0;
|
659
|
+
res_destroy(res);
|
660
|
+
if (1 == (pending = atomic_fetch_sub(&c->slot->pending, 1))) {
|
661
|
+
if (NULL != c->slot && Qnil != c->slot->handler && c->slot->on_empty) {
|
662
|
+
Req req = request_create(0);
|
663
|
+
|
664
|
+
req->cid = c->id;
|
665
|
+
req->method = ON_EMPTY;
|
666
|
+
req->handler_type = PUSH_HOOK;
|
667
|
+
req->handler = c->slot->handler;
|
668
|
+
queue_push(&the_server.eval_queue, (void*)req);
|
669
|
+
}
|
670
|
+
}
|
671
|
+
return done;
|
672
|
+
}
|
673
|
+
return false;
|
674
|
+
}
|
675
|
+
|
676
|
+
static bool
|
677
|
+
con_sse_write(Con c) {
|
678
|
+
Res res = c->res_head;
|
679
|
+
Text message = res_message(res);
|
680
|
+
ssize_t cnt;
|
681
|
+
|
682
|
+
if (NULL == message) {
|
683
|
+
ws_req_close(c);
|
684
|
+
res_destroy(res);
|
685
|
+
return true;
|
686
|
+
}
|
687
|
+
c->timeout = dtime() + CON_TIMEOUT *2;
|
688
|
+
if (0 == c->wcnt) {
|
689
|
+
Text t;
|
690
|
+
|
691
|
+
if (push_cat.on) {
|
692
|
+
log_cat(&push_cat, "%llu: %s", c->id, message->text);
|
693
|
+
}
|
694
|
+
t = sse_expand(message);
|
695
|
+
if (t != message) {
|
696
|
+
res_set_message(res, t);
|
697
|
+
message = t;
|
698
|
+
}
|
699
|
+
}
|
700
|
+
if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
|
701
|
+
if (EAGAIN == errno) {
|
702
|
+
return false;
|
703
|
+
}
|
704
|
+
log_cat(&error_cat, "Socket error @ %llu.", c->id);
|
705
|
+
ws_req_close(c);
|
706
|
+
|
707
|
+
return true;
|
708
|
+
}
|
709
|
+
c->wcnt += cnt;
|
710
|
+
if (c->wcnt == message->len) { // finished
|
711
|
+
Res res = c->res_head;
|
712
|
+
bool done = res->close;
|
713
|
+
int pending;
|
714
|
+
|
715
|
+
c->res_head = res->next;
|
716
|
+
if (res == c->res_tail) {
|
717
|
+
c->res_tail = NULL;
|
718
|
+
}
|
719
|
+
c->wcnt = 0;
|
720
|
+
res_destroy(res);
|
721
|
+
if (1 == (pending = atomic_fetch_sub(&c->slot->pending, 1))) {
|
722
|
+
if (NULL != c->slot && Qnil != c->slot->handler && c->slot->on_empty) {
|
723
|
+
Req req = request_create(0);
|
724
|
+
|
725
|
+
req->cid = c->id;
|
726
|
+
req->method = ON_EMPTY;
|
727
|
+
req->handler_type = PUSH_HOOK;
|
728
|
+
req->handler = c->slot->handler;
|
729
|
+
queue_push(&the_server.eval_queue, (void*)req);
|
730
|
+
}
|
731
|
+
}
|
732
|
+
return done;
|
733
|
+
}
|
734
|
+
return false;
|
735
|
+
}
|
736
|
+
|
737
|
+
static bool
|
738
|
+
con_write(Con c) {
|
739
|
+
bool remove = true;
|
740
|
+
ConKind kind = c->res_head->con_kind;
|
741
|
+
|
742
|
+
switch (c->kind) {
|
743
|
+
case CON_HTTP:
|
744
|
+
remove = con_http_write(c);
|
745
|
+
break;
|
746
|
+
case CON_WS:
|
747
|
+
remove = con_ws_write(c);
|
748
|
+
break;
|
749
|
+
case CON_SSE:
|
750
|
+
remove = con_sse_write(c);
|
751
|
+
break;
|
752
|
+
default:
|
753
|
+
break;
|
754
|
+
}
|
755
|
+
if (kind != c->kind) {
|
756
|
+
c->kind = kind;
|
757
|
+
if (CON_HTTP != kind && !remove) {
|
758
|
+
c->slot = cc_set_con(&the_server.con_cache, c);
|
759
|
+
}
|
760
|
+
}
|
761
|
+
return remove;
|
762
|
+
}
|
763
|
+
|
764
|
+
static void
|
765
|
+
process_pub_con(Pub pub) {
|
766
|
+
CSlot slot;
|
767
|
+
|
768
|
+
switch (pub->kind) {
|
769
|
+
case PUB_CLOSE:
|
770
|
+
if (NULL == (slot = cc_get_slot(&the_server.con_cache, pub->cid)) || NULL == slot->con) {
|
771
|
+
log_cat(&warn_cat, "Socket %llu already closed.", pub->cid);
|
772
|
+
pub_destroy(pub);
|
773
|
+
return;
|
774
|
+
} else {
|
775
|
+
Res res = res_create();;
|
776
|
+
|
777
|
+
if (NULL != res) {
|
778
|
+
if (NULL == slot->con->res_tail) {
|
779
|
+
slot->con->res_head = res;
|
780
|
+
} else {
|
781
|
+
slot->con->res_tail->next = res;
|
782
|
+
}
|
783
|
+
slot->con->res_tail = res;
|
784
|
+
res->con_kind = slot->con->kind;
|
785
|
+
res->close = true;
|
786
|
+
}
|
787
|
+
}
|
788
|
+
break;
|
789
|
+
case PUB_WRITE: {
|
790
|
+
if (NULL == (slot = cc_get_slot(&the_server.con_cache, pub->cid)) || NULL == slot->con) {
|
791
|
+
log_cat(&warn_cat, "Socket %llu already closed. WebSocket write failed.", pub->cid);
|
792
|
+
pub_destroy(pub);
|
793
|
+
return;
|
794
|
+
} else {
|
795
|
+
Res res = res_create();
|
796
|
+
|
797
|
+
if (NULL != res) {
|
798
|
+
if (NULL == slot->con->res_tail) {
|
799
|
+
slot->con->res_head = res;
|
800
|
+
} else {
|
801
|
+
slot->con->res_tail->next = res;
|
802
|
+
}
|
803
|
+
slot->con->res_tail = res;
|
804
|
+
res->con_kind = slot->con->kind;
|
805
|
+
res_set_message(res, pub->msg);
|
806
|
+
}
|
807
|
+
}
|
808
|
+
break;
|
809
|
+
case PUB_SUB:
|
810
|
+
// TBD handle a subscribe when implementing pub/sub
|
811
|
+
break;
|
812
|
+
case PUB_UN:
|
813
|
+
// TBD handle a subscribe when implementing pub/sub
|
814
|
+
break;
|
815
|
+
case PUB_MSG:
|
816
|
+
// TBD handle a subscribe when implementing pub/sub
|
817
|
+
break;
|
818
|
+
}
|
819
|
+
default:
|
820
|
+
break;
|
821
|
+
}
|
822
|
+
pub_destroy(pub);
|
823
|
+
}
|
824
|
+
|
825
|
+
static struct pollfd*
|
826
|
+
poll_setup(Con *ca, int ccnt, struct pollfd *pp) {
|
827
|
+
Con *cp;
|
828
|
+
Con *end = ca + MAX_SOCK;
|
829
|
+
Con c;
|
830
|
+
int i;
|
831
|
+
|
832
|
+
// The first two pollfd are for the con_queue and the pub_queue in that
|
833
|
+
// order.
|
834
|
+
pp->fd = queue_listen(&the_server.con_queue);
|
835
|
+
pp->events = POLLIN;
|
836
|
+
pp->revents = 0;
|
837
|
+
pp++;
|
838
|
+
pp->fd = queue_listen(&the_server.pub_queue);
|
839
|
+
pp->events = POLLIN;
|
840
|
+
pp->revents = 0;
|
841
|
+
pp++;
|
842
|
+
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
843
|
+
if (NULL == *cp) {
|
844
|
+
continue;
|
845
|
+
}
|
846
|
+
c = *cp;
|
847
|
+
c->pp = pp;
|
848
|
+
pp->fd = c->sock;
|
849
|
+
pp->events = 0;
|
850
|
+
switch (c->kind) {
|
851
|
+
case CON_HTTP:
|
852
|
+
if (NULL != c->res_head && NULL != res_message(c->res_head)) {
|
853
|
+
pp->events = POLLIN | POLLOUT;
|
854
|
+
} else {
|
855
|
+
pp->events = POLLIN;
|
856
|
+
}
|
857
|
+
break;
|
858
|
+
case CON_WS:
|
859
|
+
if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL != res_message(c->res_head))) {
|
860
|
+
pp->events = POLLIN | POLLOUT;
|
861
|
+
} else {
|
862
|
+
pp->events = POLLIN;
|
863
|
+
}
|
864
|
+
break;
|
865
|
+
case CON_SSE:
|
866
|
+
if (NULL != c->res_head && NULL != res_message(c->res_head)) {
|
867
|
+
pp->events = POLLOUT;
|
868
|
+
}
|
869
|
+
break;
|
870
|
+
default:
|
871
|
+
continue; // should never get here
|
872
|
+
break;
|
873
|
+
}
|
874
|
+
pp->revents = 0;
|
875
|
+
i--;
|
876
|
+
pp++;
|
877
|
+
}
|
878
|
+
return pp;
|
879
|
+
}
|
880
|
+
|
439
881
|
void*
|
440
882
|
con_loop(void *x) {
|
441
|
-
Server server = (Server)x;
|
442
883
|
Con c;
|
443
|
-
Con ca[MAX_SOCK];
|
444
|
-
struct pollfd pa[MAX_SOCK];
|
884
|
+
Con ca[MAX_SOCK + 2];
|
885
|
+
struct pollfd pa[MAX_SOCK + 2];
|
445
886
|
struct pollfd *pp;
|
446
887
|
Con *end = ca + MAX_SOCK;
|
447
888
|
Con *cp;
|
@@ -449,57 +890,47 @@ con_loop(void *x) {
|
|
449
890
|
int i;
|
450
891
|
long mcnt = 0;
|
451
892
|
double now;
|
893
|
+
Pub pub;
|
452
894
|
|
453
|
-
atomic_fetch_add(&
|
895
|
+
atomic_fetch_add(&the_server.running, 1);
|
454
896
|
memset(ca, 0, sizeof(ca));
|
455
897
|
memset(pa, 0, sizeof(pa));
|
456
|
-
while (
|
457
|
-
while (NULL != (c = (Con)queue_pop(&
|
898
|
+
while (the_server.active) {
|
899
|
+
while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
|
458
900
|
mcnt++;
|
459
901
|
ca[c->sock] = c;
|
460
902
|
ccnt++;
|
461
903
|
}
|
462
|
-
|
463
|
-
|
464
|
-
pp->events = POLLIN;
|
465
|
-
pp->revents = 0;
|
466
|
-
pp++;
|
467
|
-
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
468
|
-
if (NULL == *cp) {
|
469
|
-
continue;
|
470
|
-
}
|
471
|
-
c = *cp;
|
472
|
-
c->pp = pp;
|
473
|
-
pp->fd = c->sock;
|
474
|
-
if (NULL != c->res_head && NULL != res_message(c->res_head)) {
|
475
|
-
pp->events = POLLIN | POLLOUT;
|
476
|
-
} else {
|
477
|
-
pp->events = POLLIN;
|
478
|
-
}
|
479
|
-
pp->revents = 0;
|
480
|
-
i--;
|
481
|
-
pp++;
|
904
|
+
while (NULL != (pub = (Pub)queue_pop(&the_server.pub_queue, 0.0))) {
|
905
|
+
process_pub_con(pub);
|
482
906
|
}
|
907
|
+
pp = poll_setup(ca, ccnt, pa);
|
483
908
|
if (0 > (i = poll(pa, (nfds_t)(pp - pa), 100))) {
|
484
909
|
if (EAGAIN == errno) {
|
485
910
|
continue;
|
486
911
|
}
|
487
|
-
log_cat(&
|
912
|
+
log_cat(&error_cat, "Polling error. %s.", strerror(errno));
|
488
913
|
// Either a signal or something bad like out of memory. Might as well exit.
|
489
914
|
break;
|
490
915
|
}
|
491
916
|
now = dtime();
|
492
917
|
|
493
|
-
if (0
|
494
|
-
//
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
918
|
+
if (0 < i) {
|
919
|
+
// Check con_queue if an event is waiting.
|
920
|
+
if (0 != (pa->revents & POLLIN)) {
|
921
|
+
queue_release(&the_server.con_queue);
|
922
|
+
while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
|
923
|
+
mcnt++;
|
924
|
+
ca[c->sock] = c;
|
925
|
+
ccnt++;
|
926
|
+
}
|
927
|
+
}
|
928
|
+
// Check pub_queue if an event is waiting.
|
929
|
+
if (0 != (pa[1].revents & POLLIN)) {
|
930
|
+
queue_release(&the_server.pub_queue);
|
931
|
+
while (NULL != (pub = (Pub)queue_pop(&the_server.pub_queue, 0.0))) {
|
932
|
+
process_pub_con(pub);
|
933
|
+
}
|
503
934
|
}
|
504
935
|
}
|
505
936
|
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
@@ -522,9 +953,9 @@ con_loop(void *x) {
|
|
522
953
|
if (0 != (pp->revents & (POLLERR | POLLHUP | POLLNVAL))) {
|
523
954
|
if (0 < c->bcnt) {
|
524
955
|
if (0 != (pp->revents & (POLLHUP | POLLNVAL))) {
|
525
|
-
log_cat(&
|
956
|
+
log_cat(&error_cat, "Socket %llu closed.", c->id);
|
526
957
|
} else if (!c->closing) {
|
527
|
-
log_cat(&
|
958
|
+
log_cat(&error_cat, "Socket %llu error. %s", c->id, strerror(errno));
|
528
959
|
}
|
529
960
|
}
|
530
961
|
goto CON_RM;
|
@@ -533,6 +964,10 @@ con_loop(void *x) {
|
|
533
964
|
continue;
|
534
965
|
} else if (c->closing) {
|
535
966
|
goto CON_RM;
|
967
|
+
} else if (CON_WS == c->kind || CON_SSE == c->kind) {
|
968
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
969
|
+
ws_ping(c);
|
970
|
+
continue;
|
536
971
|
} else {
|
537
972
|
c->closing = true;
|
538
973
|
c->timeout = now + 0.5;
|
@@ -543,11 +978,16 @@ con_loop(void *x) {
|
|
543
978
|
CON_RM:
|
544
979
|
ca[c->sock] = NULL;
|
545
980
|
ccnt--;
|
546
|
-
log_cat(&
|
981
|
+
log_cat(&con_cat, "Connection %llu closed.", c->id);
|
547
982
|
con_destroy(c);
|
548
983
|
}
|
549
984
|
}
|
550
|
-
|
985
|
+
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
986
|
+
if (NULL != *cp) {
|
987
|
+
con_destroy(*cp);
|
988
|
+
}
|
989
|
+
}
|
990
|
+
atomic_fetch_sub(&the_server.running, 1);
|
551
991
|
|
552
992
|
return NULL;
|
553
993
|
}
|