agoo 2.5.4 → 2.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/bin/agoo +3 -1
- data/ext/agoo/base64.h +3 -3
- data/ext/agoo/bind.c +1 -1
- data/ext/agoo/bind.h +3 -3
- data/ext/agoo/con.c +4 -2
- data/ext/agoo/con.h +3 -3
- data/ext/agoo/debug.c +7 -0
- data/ext/agoo/debug.h +13 -6
- data/ext/agoo/doc.c +159 -0
- data/ext/agoo/doc.h +34 -0
- data/ext/agoo/dtime.h +3 -3
- data/ext/agoo/err.h +3 -3
- data/ext/agoo/error_stream.h +3 -3
- data/ext/agoo/extconf.rb +1 -1
- data/ext/agoo/gqlintro.c +333 -0
- data/ext/agoo/gqlintro.h +10 -0
- data/ext/agoo/gqlvalue.c +1035 -0
- data/ext/agoo/gqlvalue.h +88 -0
- data/ext/agoo/graphql.c +1078 -0
- data/ext/agoo/graphql.h +198 -0
- data/ext/agoo/hook.c +2 -0
- data/ext/agoo/hook.h +4 -3
- data/ext/agoo/http.c +2 -1
- data/ext/agoo/http.h +3 -3
- data/ext/agoo/kinds.h +3 -3
- data/ext/agoo/log.h +3 -3
- data/ext/agoo/log_queue.h +3 -3
- data/ext/agoo/method.h +3 -3
- data/ext/agoo/page.h +3 -3
- data/ext/agoo/pub.h +3 -3
- data/ext/agoo/queue.h +3 -3
- data/ext/agoo/rack_logger.h +3 -3
- data/ext/agoo/req.c +2 -2
- data/ext/agoo/req.h +3 -3
- data/ext/agoo/request.h +3 -3
- data/ext/agoo/res.h +3 -3
- data/ext/agoo/response.h +3 -3
- data/ext/agoo/rhook.c +2 -2
- data/ext/agoo/rhook.h +4 -3
- data/ext/agoo/rlog.h +3 -3
- data/ext/agoo/rresponse.h +3 -3
- data/ext/agoo/rserver.c +64 -0
- data/ext/agoo/rserver.h +3 -3
- data/ext/agoo/rupgraded.c +1 -1
- data/ext/agoo/rupgraded.h +3 -3
- data/ext/agoo/sdl.c +334 -0
- data/ext/agoo/sdl.h +10 -0
- data/ext/agoo/seg.h +3 -3
- data/ext/agoo/server.c +3 -1
- data/ext/agoo/server.h +5 -4
- data/ext/agoo/sha1.h +3 -3
- data/ext/agoo/sse.h +3 -3
- data/ext/agoo/sub.h +3 -3
- data/ext/agoo/subject.h +3 -3
- data/ext/agoo/text.h +3 -3
- data/ext/agoo/upgraded.h +3 -3
- data/ext/agoo/websocket.h +3 -3
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +3 -1
- metadata +12 -12
- data/ext/agoo/foo/agoo.c +0 -109
- data/ext/agoo/foo/con.c +0 -1220
- data/ext/agoo/foo/con.h +0 -65
- data/ext/agoo/foo/page.c +0 -699
- data/ext/agoo/foo/pub.c +0 -131
- data/ext/agoo/foo/pub.h +0 -40
- data/ext/agoo/foo/rserver.c +0 -1016
- data/ext/agoo/foo/server.c +0 -303
- data/ext/agoo/foo/server.h +0 -67
- data/ext/agoo/foo/upgraded.c +0 -182
data/ext/agoo/sdl.h
ADDED
data/ext/agoo/seg.h
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_SEG_H
|
4
|
+
#define AGOO_SEG_H
|
5
5
|
|
6
6
|
typedef struct _Seg {
|
7
7
|
char *start;
|
8
8
|
char *end;
|
9
9
|
} *Seg;
|
10
10
|
|
11
|
-
#endif //
|
11
|
+
#endif // AGOO_SEG_H
|
data/ext/agoo/server.c
CHANGED
@@ -270,7 +270,8 @@ server_add_func_hook(Err err,
|
|
270
270
|
Method method,
|
271
271
|
const char *pattern,
|
272
272
|
void (*func)(Req req),
|
273
|
-
Queue queue
|
273
|
+
Queue queue,
|
274
|
+
bool quick) {
|
274
275
|
Hook h;
|
275
276
|
Hook prev = NULL;
|
276
277
|
Hook hook = hook_func_create(method, pattern, func, queue);
|
@@ -278,6 +279,7 @@ server_add_func_hook(Err err,
|
|
278
279
|
if (NULL == hook) {
|
279
280
|
return err_set(err, ERR_MEMORY, "failed to allocate memory for HTTP server Hook.");
|
280
281
|
}
|
282
|
+
hook->no_queue = quick;
|
281
283
|
for (h = the_server.hooks; NULL != h; h = h->next) {
|
282
284
|
prev = h;
|
283
285
|
}
|
data/ext/agoo/server.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_SERVER_H
|
4
|
+
#define AGOO_SERVER_H
|
5
5
|
|
6
6
|
#include <pthread.h>
|
7
7
|
#include <stdbool.h>
|
@@ -60,8 +60,9 @@ extern int server_add_func_hook(Err err,
|
|
60
60
|
Method method,
|
61
61
|
const char *pattern,
|
62
62
|
void (*func)(struct _Req *req),
|
63
|
-
Queue queue
|
63
|
+
Queue queue,
|
64
|
+
bool quick);
|
64
65
|
|
65
66
|
extern void server_publish(struct _Pub *pub);
|
66
67
|
|
67
|
-
#endif //
|
68
|
+
#endif // AGOO_SERVER_H
|
data/ext/agoo/sha1.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#ifndef
|
2
|
-
#define
|
1
|
+
#ifndef AGOO_SHA1_H
|
2
|
+
#define AGOO_SHA1_H
|
3
3
|
|
4
4
|
#include <stdint.h>
|
5
5
|
|
@@ -7,4 +7,4 @@
|
|
7
7
|
|
8
8
|
extern void sha1(const uint8_t *data, size_t len, uint8_t *digest);
|
9
9
|
|
10
|
-
#endif //
|
10
|
+
#endif // AGOO_SHA1_H
|
data/ext/agoo/sse.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_SSE_H
|
4
|
+
#define AGOO_SSE_H
|
5
5
|
|
6
6
|
struct _Req;
|
7
7
|
struct _Text;
|
@@ -9,4 +9,4 @@ struct _Text;
|
|
9
9
|
extern struct _Text* sse_upgrade(struct _Req *req, struct _Text *t);
|
10
10
|
extern struct _Text* sse_expand(struct _Text *t);
|
11
11
|
|
12
|
-
#endif //
|
12
|
+
#endif // AGOO_SSE_H
|
data/ext/agoo/sub.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_SUB_H
|
4
|
+
#define AGOO_SUB_H
|
5
5
|
|
6
6
|
#include <stdbool.h>
|
7
7
|
#include <stdint.h>
|
@@ -33,4 +33,4 @@ extern void sub_add(SubCache sc, Sub s);
|
|
33
33
|
extern Sub sub_get(SubCache sc, uint64_t cid, uint64_t sid);
|
34
34
|
extern void sub_del(SubCache sc, uint64_t cid, uint64_t sid);
|
35
35
|
|
36
|
-
#endif //
|
36
|
+
#endif // AGOO_SUB_H
|
data/ext/agoo/subject.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_SUBJECT_H
|
4
|
+
#define AGOO_SUBJECT_H
|
5
5
|
|
6
6
|
#include <stdbool.h>
|
7
7
|
|
@@ -14,4 +14,4 @@ extern Subject subject_create(const char *pattern, int plen);
|
|
14
14
|
extern void subject_destroy(Subject subject);
|
15
15
|
extern bool subject_check(Subject subj, const char *subject);
|
16
16
|
|
17
|
-
#endif //
|
17
|
+
#endif // AGOO_SUBJECT_H
|
data/ext/agoo/text.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright 2016, 2018 by Peter Ohler, All Rights Reserved
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_TEXT_H
|
4
|
+
#define AGOO_TEXT_H
|
5
5
|
|
6
6
|
#include <stdatomic.h>
|
7
7
|
#include <stdbool.h>
|
@@ -24,4 +24,4 @@ extern void text_release(Text t);
|
|
24
24
|
extern Text text_append(Text t, const char *s, int len);
|
25
25
|
extern Text text_prepend(Text t, const char *s, int len);
|
26
26
|
|
27
|
-
#endif /*
|
27
|
+
#endif /* AGOO_TEXT_H */
|
data/ext/agoo/upgraded.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_UPGRADED_H
|
4
|
+
#define AGOO_UPGRADED_H
|
5
5
|
|
6
6
|
#include <pthread.h>
|
7
7
|
#include <stdatomic.h>
|
@@ -48,4 +48,4 @@ extern void upgraded_unsubscribe(Upgraded up, const char *subject, int slen, boo
|
|
48
48
|
extern void upgraded_close(Upgraded up, bool inc_ref);
|
49
49
|
extern int upgraded_pending(Upgraded up);
|
50
50
|
|
51
|
-
#endif //
|
51
|
+
#endif // AGOO_UPGRADED_H
|
data/ext/agoo/websocket.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#ifndef
|
4
|
-
#define
|
3
|
+
#ifndef AGOO_WEBSOCKET_H
|
4
|
+
#define AGOO_WEBSOCKET_H
|
5
5
|
|
6
6
|
#define WS_OP_CONT 0x00
|
7
7
|
#define WS_OP_TEXT 0x01
|
@@ -24,4 +24,4 @@ extern void ws_req_close(Con c);
|
|
24
24
|
extern void ws_ping(Con c);
|
25
25
|
extern void ws_pong(Con c);
|
26
26
|
|
27
|
-
#endif //
|
27
|
+
#endif // AGOO_WEBSOCKET_H
|
data/lib/agoo/version.rb
CHANGED
data/lib/rack/handler/agoo.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.5.
|
4
|
+
version: 2.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -54,6 +54,8 @@ files:
|
|
54
54
|
- ext/agoo/con.h
|
55
55
|
- ext/agoo/debug.c
|
56
56
|
- ext/agoo/debug.h
|
57
|
+
- ext/agoo/doc.c
|
58
|
+
- ext/agoo/doc.h
|
57
59
|
- ext/agoo/dtime.c
|
58
60
|
- ext/agoo/dtime.h
|
59
61
|
- ext/agoo/err.c
|
@@ -61,16 +63,12 @@ files:
|
|
61
63
|
- ext/agoo/error_stream.c
|
62
64
|
- ext/agoo/error_stream.h
|
63
65
|
- ext/agoo/extconf.rb
|
64
|
-
- ext/agoo/
|
65
|
-
- ext/agoo/
|
66
|
-
- ext/agoo/
|
67
|
-
- ext/agoo/
|
68
|
-
- ext/agoo/
|
69
|
-
- ext/agoo/
|
70
|
-
- ext/agoo/foo/rserver.c
|
71
|
-
- ext/agoo/foo/server.c
|
72
|
-
- ext/agoo/foo/server.h
|
73
|
-
- ext/agoo/foo/upgraded.c
|
66
|
+
- ext/agoo/gqlintro.c
|
67
|
+
- ext/agoo/gqlintro.h
|
68
|
+
- ext/agoo/gqlvalue.c
|
69
|
+
- ext/agoo/gqlvalue.h
|
70
|
+
- ext/agoo/graphql.c
|
71
|
+
- ext/agoo/graphql.h
|
74
72
|
- ext/agoo/hook.c
|
75
73
|
- ext/agoo/hook.h
|
76
74
|
- ext/agoo/http.c
|
@@ -106,6 +104,8 @@ files:
|
|
106
104
|
- ext/agoo/rserver.h
|
107
105
|
- ext/agoo/rupgraded.c
|
108
106
|
- ext/agoo/rupgraded.h
|
107
|
+
- ext/agoo/sdl.c
|
108
|
+
- ext/agoo/sdl.h
|
109
109
|
- ext/agoo/seg.h
|
110
110
|
- ext/agoo/server.c
|
111
111
|
- ext/agoo/server.h
|
data/ext/agoo/foo/agoo.c
DELETED
@@ -1,109 +0,0 @@
|
|
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
|
-
}
|
data/ext/agoo/foo/con.c
DELETED
@@ -1,1220 +0,0 @@
|
|
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
|
-
}
|