agoo 2.5.5 → 2.5.6
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/README.md +1 -1
- data/ext/agoo/agoo.c +4 -3
- data/ext/agoo/atomic.h +120 -0
- data/ext/agoo/bind.c +52 -52
- data/ext/agoo/bind.h +13 -13
- data/ext/agoo/con.c +499 -481
- data/ext/agoo/con.h +47 -39
- data/ext/agoo/debug.c +42 -42
- data/ext/agoo/debug.h +1 -1
- data/ext/agoo/doc.c +17 -17
- data/ext/agoo/doc.h +12 -12
- data/ext/agoo/err.c +18 -18
- data/ext/agoo/err.h +27 -27
- data/ext/agoo/error_stream.c +9 -9
- data/ext/agoo/extconf.rb +3 -0
- data/ext/agoo/gqlintro.c +43 -43
- data/ext/agoo/gqlintro.h +1 -1
- data/ext/agoo/gqlvalue.c +131 -131
- data/ext/agoo/gqlvalue.h +32 -32
- data/ext/agoo/graphql.c +158 -158
- data/ext/agoo/graphql.h +34 -33
- data/ext/agoo/hook.c +15 -14
- data/ext/agoo/hook.h +18 -14
- data/ext/agoo/http.c +14 -14
- data/ext/agoo/http.h +4 -4
- data/ext/agoo/kinds.h +5 -5
- data/ext/agoo/log.c +232 -224
- data/ext/agoo/log.h +93 -93
- data/ext/agoo/method.h +17 -17
- data/ext/agoo/page.c +88 -86
- data/ext/agoo/page.h +21 -21
- data/ext/agoo/pub.c +36 -36
- data/ext/agoo/pub.h +23 -23
- data/ext/agoo/queue.c +37 -38
- data/ext/agoo/queue.h +20 -19
- data/ext/agoo/rack_logger.c +13 -13
- data/ext/agoo/ready.c +357 -0
- data/ext/agoo/ready.h +41 -0
- data/ext/agoo/req.c +11 -11
- data/ext/agoo/req.h +30 -31
- data/ext/agoo/request.c +46 -46
- data/ext/agoo/request.h +2 -2
- data/ext/agoo/res.c +40 -18
- data/ext/agoo/res.h +14 -14
- data/ext/agoo/response.c +6 -6
- data/ext/agoo/response.h +9 -9
- data/ext/agoo/rhook.c +3 -3
- data/ext/agoo/rhook.h +1 -1
- data/ext/agoo/rlog.c +47 -42
- data/ext/agoo/rlog.h +0 -1
- data/ext/agoo/rresponse.c +33 -33
- data/ext/agoo/rresponse.h +1 -1
- data/ext/agoo/rserver.c +184 -175
- data/ext/agoo/rserver.h +2 -2
- data/ext/agoo/rupgraded.c +41 -41
- data/ext/agoo/rupgraded.h +3 -3
- data/ext/agoo/sdl.c +80 -80
- data/ext/agoo/sdl.h +1 -1
- data/ext/agoo/seg.h +2 -2
- data/ext/agoo/server.c +143 -117
- data/ext/agoo/server.h +43 -42
- data/ext/agoo/sse.c +7 -7
- data/ext/agoo/sse.h +4 -4
- data/ext/agoo/subject.c +5 -5
- data/ext/agoo/subject.h +6 -6
- data/ext/agoo/text.c +21 -21
- data/ext/agoo/text.h +14 -13
- data/ext/agoo/upgraded.c +41 -40
- data/ext/agoo/upgraded.h +41 -40
- data/ext/agoo/websocket.c +42 -42
- data/ext/agoo/websocket.h +16 -16
- data/lib/agoo/version.rb +1 -1
- data/test/static_test.rb +2 -0
- metadata +5 -5
- data/ext/agoo/log_queue.h +0 -30
- data/ext/agoo/sub.c +0 -111
- data/ext/agoo/sub.h +0 -36
data/ext/agoo/bind.h
CHANGED
@@ -11,10 +11,10 @@
|
|
11
11
|
#include "err.h"
|
12
12
|
#include "kinds.h"
|
13
13
|
|
14
|
-
struct
|
14
|
+
struct _agooCon;
|
15
15
|
|
16
|
-
typedef struct
|
17
|
-
struct
|
16
|
+
typedef struct _agooBind {
|
17
|
+
struct _agooBind *next;
|
18
18
|
int fd;
|
19
19
|
int port;
|
20
20
|
sa_family_t family;
|
@@ -22,23 +22,23 @@ typedef struct _Bind {
|
|
22
22
|
struct in_addr addr4;
|
23
23
|
struct in6_addr addr6;
|
24
24
|
};
|
25
|
-
|
26
|
-
bool (*read)(struct
|
27
|
-
bool (*write)(struct
|
28
|
-
short (*events)(struct
|
25
|
+
agooConKind kind;
|
26
|
+
bool (*read)(struct _agooCon *c);
|
27
|
+
bool (*write)(struct _agooCon *c);
|
28
|
+
short (*events)(struct _agooCon *c);
|
29
29
|
char scheme[8];
|
30
30
|
char *name; // if set then Unix file
|
31
31
|
char *key; // if set then SSL
|
32
32
|
char *cert;
|
33
33
|
char *ca;
|
34
34
|
char *id;
|
35
|
-
} *
|
35
|
+
} *agooBind;
|
36
36
|
|
37
|
-
extern
|
38
|
-
extern
|
39
|
-
extern void
|
37
|
+
extern agooBind agoo_bind_url(agooErr err, const char *url);
|
38
|
+
extern agooBind agoo_bind_port(agooErr err, int port);
|
39
|
+
extern void agoo_bind_destroy(agooBind b);
|
40
40
|
|
41
|
-
extern int
|
42
|
-
extern void
|
41
|
+
extern int agoo_bind_listen(agooErr err, agooBind b);
|
42
|
+
extern void agoo_bind_close(agooBind b);
|
43
43
|
|
44
44
|
#endif // AGOO_BIND_H
|
data/ext/agoo/con.c
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
#include "log.h"
|
16
16
|
#include "page.h"
|
17
17
|
#include "pub.h"
|
18
|
+
#include "ready.h"
|
18
19
|
#include "res.h"
|
19
20
|
#include "seg.h"
|
20
21
|
#include "server.h"
|
@@ -26,67 +27,76 @@
|
|
26
27
|
#define CON_TIMEOUT 5.0
|
27
28
|
#define INITIAL_POLL_SIZE 1024
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
typedef enum {
|
31
|
+
HEAD_AGAIN = 'A',
|
32
|
+
HEAD_ERR = 'E',
|
33
|
+
HEAD_OK = 'O',
|
34
|
+
HEAD_HANDLED = 'H',
|
35
|
+
} HeadReturn;
|
36
|
+
|
37
|
+
static bool con_ws_read(agooCon c);
|
38
|
+
static bool con_ws_write(agooCon c);
|
39
|
+
static short con_ws_events(agooCon c);
|
40
|
+
static bool con_sse_write(agooCon c);
|
41
|
+
static short con_sse_events(agooCon c);
|
42
|
+
|
43
|
+
static struct _agooBind ws_bind = {
|
44
|
+
.kind = AGOO_CON_WS,
|
37
45
|
.read = con_ws_read,
|
38
46
|
.write = con_ws_write,
|
39
47
|
.events = con_ws_events,
|
40
48
|
};
|
41
49
|
|
42
|
-
static struct
|
43
|
-
.kind =
|
50
|
+
static struct _agooBind sse_bind = {
|
51
|
+
.kind = AGOO_CON_SSE,
|
44
52
|
.read = NULL,
|
45
53
|
.write = con_sse_write,
|
46
54
|
.events = con_sse_events,
|
47
55
|
};
|
48
56
|
|
49
|
-
|
50
|
-
|
51
|
-
|
57
|
+
agooCon
|
58
|
+
agoo_con_create(agooErr err, int sock, uint64_t id, agooBind b) {
|
59
|
+
agooCon c;
|
52
60
|
|
53
|
-
if (NULL == (c = (
|
54
|
-
|
61
|
+
if (NULL == (c = (agooCon)malloc(sizeof(struct _agooCon)))) {
|
62
|
+
agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a connection.");
|
55
63
|
} else {
|
56
64
|
DEBUG_ALLOC(mem_con, c)
|
57
|
-
memset(c, 0, sizeof(struct
|
65
|
+
memset(c, 0, sizeof(struct _agooCon));
|
58
66
|
c->sock = sock;
|
59
67
|
c->id = id;
|
60
68
|
c->timeout = dtime() + CON_TIMEOUT;
|
61
69
|
c->bind = b;
|
70
|
+
c->loop = NULL;
|
62
71
|
}
|
63
72
|
return c;
|
64
73
|
}
|
65
74
|
|
66
75
|
void
|
67
|
-
|
68
|
-
atomic_fetch_sub(&
|
76
|
+
agoo_con_destroy(agooCon c) {
|
77
|
+
atomic_fetch_sub(&agoo_server.con_cnt, 1);
|
69
78
|
|
70
|
-
if (
|
71
|
-
|
79
|
+
if (AGOO_CON_WS == c->bind->kind || AGOO_CON_SSE == c->bind->kind) {
|
80
|
+
agoo_ws_req_close(c);
|
72
81
|
}
|
73
82
|
if (0 < c->sock) {
|
74
83
|
close(c->sock);
|
75
84
|
c->sock = 0;
|
76
85
|
}
|
77
86
|
if (NULL != c->req) {
|
78
|
-
|
87
|
+
agoo_req_destroy(c->req);
|
79
88
|
}
|
80
89
|
if (NULL != c->up) {
|
81
|
-
|
90
|
+
agoo_upgraded_release_con(c->up);
|
82
91
|
c->up = NULL;
|
83
92
|
}
|
93
|
+
agoo_log_cat(&agoo_con_cat, "Connection %llu closed.", (unsigned long long)c->id);
|
84
94
|
DEBUG_FREE(mem_con, c)
|
85
95
|
free(c);
|
86
96
|
}
|
87
97
|
|
88
98
|
const char*
|
89
|
-
|
99
|
+
agoo_con_header_value(const char *header, int hlen, const char *key, int *vlen) {
|
90
100
|
// Search for \r then check for \n and then the key followed by a :. Keep
|
91
101
|
// trying until the end of the header.
|
92
102
|
const char *h = header;
|
@@ -116,17 +126,19 @@ con_header_value(const char *header, int hlen, const char *key, int *vlen) {
|
|
116
126
|
return NULL;
|
117
127
|
}
|
118
128
|
|
119
|
-
static
|
120
|
-
bad_request(
|
121
|
-
|
122
|
-
const char *msg =
|
129
|
+
static HeadReturn
|
130
|
+
bad_request(agooCon c, int status, int line) {
|
131
|
+
agooRes res;
|
132
|
+
const char *msg = agoo_http_code_message(status);
|
123
133
|
|
124
|
-
if (NULL == (res =
|
125
|
-
|
134
|
+
if (NULL == (res = agoo_res_create(c))) {
|
135
|
+
agoo_log_cat(&agoo_error_cat, "memory allocation of response failed on connection %llu @ %d.",
|
136
|
+
(unsigned long long)c->id, line);
|
126
137
|
} else {
|
127
|
-
char
|
128
|
-
int
|
129
|
-
|
138
|
+
char buf[256];
|
139
|
+
int cnt = snprintf(buf, sizeof(buf),
|
140
|
+
"HTTP/1.1 %d %s\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", status, msg);
|
141
|
+
agooText message = agoo_text_create(buf, cnt);
|
130
142
|
|
131
143
|
if (NULL == c->res_tail) {
|
132
144
|
c->res_head = res;
|
@@ -135,9 +147,9 @@ bad_request(Con c, int status, int line) {
|
|
135
147
|
}
|
136
148
|
c->res_tail = res;
|
137
149
|
res->close = true;
|
138
|
-
|
150
|
+
agoo_res_set_message(res, message);
|
139
151
|
}
|
140
|
-
return
|
152
|
+
return HEAD_ERR;
|
141
153
|
}
|
142
154
|
|
143
155
|
static bool
|
@@ -145,18 +157,18 @@ should_close(const char *header, int hlen) {
|
|
145
157
|
const char *v;
|
146
158
|
int vlen = 0;
|
147
159
|
|
148
|
-
if (NULL != (v =
|
160
|
+
if (NULL != (v = agoo_con_header_value(header, hlen, "Connection", &vlen))) {
|
149
161
|
return (5 == vlen && 0 == strncasecmp("Close", v, 5));
|
150
162
|
}
|
151
163
|
return false;
|
152
164
|
}
|
153
165
|
|
154
166
|
static bool
|
155
|
-
page_response(
|
156
|
-
|
167
|
+
page_response(agooCon c, agooPage p, char *hend) {
|
168
|
+
agooRes res;
|
157
169
|
char *b;
|
158
170
|
|
159
|
-
if (NULL == (res =
|
171
|
+
if (NULL == (res = agoo_res_create(c))) {
|
160
172
|
return true;
|
161
173
|
}
|
162
174
|
if (NULL == c->res_tail) {
|
@@ -171,16 +183,16 @@ page_response(Con c, Page p, char *hend) {
|
|
171
183
|
if (res->close) {
|
172
184
|
c->closing = true;
|
173
185
|
}
|
174
|
-
|
186
|
+
agoo_res_set_message(res, p->resp);
|
175
187
|
|
176
188
|
return false;
|
177
189
|
}
|
178
190
|
|
179
191
|
// rserver
|
180
192
|
static void
|
181
|
-
push_error(
|
182
|
-
if (NULL != up &&
|
183
|
-
|
193
|
+
push_error(agooUpgraded up, const char *msg, int mlen) {
|
194
|
+
if (NULL != up && agoo_server.ctx_nil_value != up->ctx && up->on_error) {
|
195
|
+
agooReq req = agoo_req_create(mlen);
|
184
196
|
|
185
197
|
if (NULL == req) {
|
186
198
|
return;
|
@@ -188,41 +200,36 @@ push_error(Upgraded up, const char *msg, int mlen) {
|
|
188
200
|
memcpy(req->msg, msg, mlen);
|
189
201
|
req->msg[mlen] = '\0';
|
190
202
|
req->up = up;
|
191
|
-
req->method =
|
192
|
-
req->hook =
|
193
|
-
|
194
|
-
|
203
|
+
req->method = AGOO_ON_ERROR;
|
204
|
+
req->hook = agoo_hook_create(AGOO_NONE, NULL, up->ctx, PUSH_HOOK, &agoo_server.eval_queue);
|
205
|
+
agoo_upgraded_ref(up);
|
206
|
+
agoo_queue_push(&agoo_server.eval_queue, (void*)req);
|
195
207
|
}
|
196
208
|
}
|
197
209
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
char
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
long mlen;
|
213
|
-
Hook hook = NULL;
|
214
|
-
Page p;
|
215
|
-
struct _Err err = ERR_INIT;
|
216
|
-
|
210
|
+
static HeadReturn
|
211
|
+
agoo_con_header_read(agooCon c, size_t *mlenp) {
|
212
|
+
char *hend = strstr(c->buf, "\r\n\r\n");
|
213
|
+
agooMethod method;
|
214
|
+
struct _agooSeg path;
|
215
|
+
char *query = NULL;
|
216
|
+
char *qend;
|
217
|
+
char *b;
|
218
|
+
size_t clen = 0;
|
219
|
+
long mlen;
|
220
|
+
agooHook hook = NULL;
|
221
|
+
agooPage p;
|
222
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
223
|
+
|
217
224
|
if (NULL == hend) {
|
218
225
|
if (sizeof(c->buf) - 1 <= c->bcnt) {
|
219
226
|
return bad_request(c, 431, __LINE__);
|
220
227
|
}
|
221
|
-
return
|
228
|
+
return HEAD_AGAIN;
|
222
229
|
}
|
223
|
-
if (
|
230
|
+
if (agoo_req_cat.on) {
|
224
231
|
*hend = '\0';
|
225
|
-
|
232
|
+
agoo_log_cat(&agoo_req_cat, "%llu: %s", (unsigned long long)c->id, c->buf);
|
226
233
|
*hend = '\r';
|
227
234
|
}
|
228
235
|
for (b = c->buf; ' ' != *b; b++) {
|
@@ -235,7 +242,7 @@ con_header_read(Con c) {
|
|
235
242
|
if (3 != b - c->buf || 0 != strncmp("GET", c->buf, 3)) {
|
236
243
|
return bad_request(c, 400, __LINE__);
|
237
244
|
}
|
238
|
-
method =
|
245
|
+
method = AGOO_GET;
|
239
246
|
break;
|
240
247
|
case 'P': {
|
241
248
|
const char *v;
|
@@ -243,13 +250,13 @@ con_header_read(Con c) {
|
|
243
250
|
char *vend;
|
244
251
|
|
245
252
|
if (3 == b - c->buf && 0 == strncmp("PUT", c->buf, 3)) {
|
246
|
-
method =
|
253
|
+
method = AGOO_PUT;
|
247
254
|
} else if (4 == b - c->buf && 0 == strncmp("POST", c->buf, 4)) {
|
248
|
-
method =
|
255
|
+
method = AGOO_POST;
|
249
256
|
} else {
|
250
257
|
return bad_request(c, 400, __LINE__);
|
251
258
|
}
|
252
|
-
if (NULL == (v =
|
259
|
+
if (NULL == (v = agoo_con_header_value(c->buf, (int)(hend - c->buf), "Content-Length", &vlen))) {
|
253
260
|
return bad_request(c, 411, __LINE__);
|
254
261
|
}
|
255
262
|
clen = (size_t)strtoul(v, &vend, 10);
|
@@ -262,25 +269,25 @@ con_header_read(Con c) {
|
|
262
269
|
if (6 != b - c->buf || 0 != strncmp("DELETE", c->buf, 6)) {
|
263
270
|
return bad_request(c, 400, __LINE__);
|
264
271
|
}
|
265
|
-
method =
|
272
|
+
method = AGOO_DELETE;
|
266
273
|
break;
|
267
274
|
case 'H':
|
268
275
|
if (4 != b - c->buf || 0 != strncmp("HEAD", c->buf, 4)) {
|
269
276
|
return bad_request(c, 400, __LINE__);
|
270
277
|
}
|
271
|
-
method =
|
278
|
+
method = AGOO_HEAD;
|
272
279
|
break;
|
273
280
|
case 'O':
|
274
281
|
if (7 != b - c->buf || 0 != strncmp("OPTIONS", c->buf, 7)) {
|
275
282
|
return bad_request(c, 400, __LINE__);
|
276
283
|
}
|
277
|
-
method =
|
284
|
+
method = AGOO_OPTIONS;
|
278
285
|
break;
|
279
286
|
case 'C':
|
280
287
|
if (7 != b - c->buf || 0 != strncmp("CONNECT", c->buf, 7)) {
|
281
288
|
return bad_request(c, 400, __LINE__);
|
282
289
|
}
|
283
|
-
method =
|
290
|
+
method = AGOO_CONNECT;
|
284
291
|
break;
|
285
292
|
default:
|
286
293
|
return bad_request(c, 400, __LINE__);
|
@@ -311,50 +318,39 @@ con_header_read(Con c) {
|
|
311
318
|
qend = b;
|
312
319
|
}
|
313
320
|
mlen = hend - c->buf + 4 + clen;
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
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__);
|
321
|
+
*mlenp = mlen;
|
322
|
+
|
323
|
+
if (AGOO_GET == method) {
|
324
|
+
if (NULL != (p = group_get(&err, path.start, (int)(path.end - path.start)))) {
|
325
|
+
if (page_response(c, p, hend)) {
|
326
|
+
return bad_request(c, 500, __LINE__);
|
327
|
+
}
|
328
|
+
return HEAD_HANDLED;
|
325
329
|
}
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
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__);
|
330
|
+
if (agoo_server.root_first &&
|
331
|
+
NULL != (p = agoo_page_get(&err, path.start, (int)(path.end - path.start)))) {
|
332
|
+
if (page_response(c, p, hend)) {
|
333
|
+
return bad_request(c, 500, __LINE__);
|
338
334
|
}
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
335
|
+
return HEAD_HANDLED;
|
336
|
+
}
|
337
|
+
if (NULL == (hook = agoo_hook_find(agoo_server.hooks, method, &path))) {
|
338
|
+
if (NULL != (p = agoo_page_get(&err, path.start, (int)(path.end - path.start)))) {
|
339
|
+
if (page_response(c, p, hend)) {
|
340
|
+
return bad_request(c, 500, __LINE__);
|
345
341
|
}
|
342
|
+
return HEAD_HANDLED;
|
343
|
+
}
|
344
|
+
if (NULL == agoo_server.hook404) {
|
346
345
|
return bad_request(c, 404, __LINE__);
|
347
346
|
}
|
348
|
-
|
349
|
-
return bad_request(c, 500, __LINE__);
|
350
|
-
}
|
351
|
-
return -mlen;
|
347
|
+
hook = agoo_server.hook404;
|
352
348
|
}
|
349
|
+
} else if (NULL == (hook = agoo_hook_find(agoo_server.hooks, method, &path))) {
|
353
350
|
return bad_request(c, 404, __LINE__);
|
354
|
-
|
355
|
-
HOOKED:
|
351
|
+
}
|
356
352
|
// Create request and populate.
|
357
|
-
if (NULL == (c->req =
|
353
|
+
if (NULL == (c->req = agoo_req_create(mlen))) {
|
358
354
|
return bad_request(c, 413, __LINE__);
|
359
355
|
}
|
360
356
|
if ((long)c->bcnt <= mlen) {
|
@@ -367,7 +363,7 @@ HOOKED:
|
|
367
363
|
}
|
368
364
|
c->req->msg[mlen] = '\0';
|
369
365
|
c->req->method = method;
|
370
|
-
c->req->upgrade =
|
366
|
+
c->req->upgrade = AGOO_UP_NONE;
|
371
367
|
c->req->up = NULL;
|
372
368
|
c->req->path.start = c->req->msg + (path.start - c->buf);
|
373
369
|
c->req->path.len = (int)(path.end - path.start);
|
@@ -382,39 +378,39 @@ HOOKED:
|
|
382
378
|
c->req->res = NULL;
|
383
379
|
c->req->hook = hook;
|
384
380
|
|
385
|
-
return
|
381
|
+
return HEAD_OK;
|
386
382
|
}
|
387
383
|
|
388
384
|
static void
|
389
|
-
check_upgrade(
|
385
|
+
check_upgrade(agooCon c) {
|
390
386
|
const char *v;
|
391
387
|
int vlen = 0;
|
392
388
|
|
393
389
|
if (NULL == c->req) {
|
394
390
|
return;
|
395
391
|
}
|
396
|
-
if (NULL != (v =
|
392
|
+
if (NULL != (v = agoo_con_header_value(c->req->header.start, c->req->header.len, "Connection", &vlen))) {
|
397
393
|
if (NULL != strstr(v, "Upgrade")) {
|
398
|
-
if (NULL != (v =
|
394
|
+
if (NULL != (v = agoo_con_header_value(c->req->header.start, c->req->header.len, "Upgrade", &vlen))) {
|
399
395
|
if (0 == strncasecmp("WebSocket", v, vlen)) {
|
400
396
|
c->res_tail->close = false;
|
401
|
-
c->res_tail->con_kind =
|
397
|
+
c->res_tail->con_kind = AGOO_CON_WS;
|
402
398
|
return;
|
403
399
|
}
|
404
400
|
}
|
405
401
|
}
|
406
402
|
}
|
407
|
-
if (NULL != (v =
|
403
|
+
if (NULL != (v = agoo_con_header_value(c->req->header.start, c->req->header.len, "Accept", &vlen))) {
|
408
404
|
if (0 == strncasecmp("text/event-stream", v, vlen)) {
|
409
405
|
c->res_tail->close = false;
|
410
|
-
c->res_tail->con_kind =
|
406
|
+
c->res_tail->con_kind = AGOO_CON_SSE;
|
411
407
|
return;
|
412
408
|
}
|
413
409
|
}
|
414
410
|
}
|
415
411
|
|
416
412
|
bool
|
417
|
-
|
413
|
+
agoo_con_http_read(agooCon c) {
|
418
414
|
ssize_t cnt;
|
419
415
|
|
420
416
|
if (c->dead || 0 == c->sock || c->closing) {
|
@@ -430,9 +426,9 @@ con_http_read(Con c) {
|
|
430
426
|
// If nothing read then no need to complain. Just close.
|
431
427
|
if (0 < c->bcnt) {
|
432
428
|
if (0 == cnt) {
|
433
|
-
|
429
|
+
agoo_log_cat(&agoo_warn_cat, "Nothing to read. Client closed socket on connection %llu.", (unsigned long long)c->id);
|
434
430
|
} else {
|
435
|
-
|
431
|
+
agoo_log_cat(&agoo_warn_cat, "Failed to read request. %s.", strerror(errno));
|
436
432
|
}
|
437
433
|
}
|
438
434
|
return true;
|
@@ -440,39 +436,48 @@ con_http_read(Con c) {
|
|
440
436
|
c->bcnt += cnt;
|
441
437
|
while (true) {
|
442
438
|
if (NULL == c->req) {
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
439
|
+
size_t mlen;
|
440
|
+
|
441
|
+
switch (agoo_con_header_read(c, &mlen)) {
|
442
|
+
case HEAD_AGAIN:
|
443
|
+
// Try again the next time. Didn't read enough..
|
444
|
+
return false;
|
445
|
+
case HEAD_OK:
|
446
|
+
// req was created
|
447
|
+
break;
|
448
|
+
case HEAD_HANDLED:
|
449
|
+
if (mlen < c->bcnt) {
|
454
450
|
memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
|
455
451
|
c->bcnt -= mlen;
|
452
|
+
// req is NULL so try to ready the header on the next request.
|
453
|
+
continue;
|
456
454
|
} else {
|
457
455
|
c->bcnt = 0;
|
458
456
|
*c->buf = '\0';
|
457
|
+
|
459
458
|
return false;
|
460
459
|
}
|
461
|
-
|
460
|
+
break;
|
461
|
+
case HEAD_ERR:
|
462
|
+
default:
|
463
|
+
c->bcnt = 0;
|
464
|
+
*c->buf = '\0';
|
465
|
+
|
466
|
+
return false;
|
462
467
|
}
|
463
468
|
}
|
464
469
|
if (NULL != c->req) {
|
465
470
|
if (c->req->mlen <= c->bcnt) {
|
466
|
-
|
467
|
-
|
471
|
+
agooReq req;
|
472
|
+
agooRes res;
|
468
473
|
long mlen;
|
469
474
|
|
470
|
-
if (
|
471
|
-
|
475
|
+
if (agoo_debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
|
476
|
+
agoo_log_cat(&agoo_debug_cat, "request on %llu: %s", (unsigned long long)c->id, c->req->body.start);
|
472
477
|
}
|
473
|
-
if (NULL == (res =
|
478
|
+
if (NULL == (res = agoo_res_create(c))) {
|
474
479
|
c->req = NULL;
|
475
|
-
|
480
|
+
agoo_log_cat(&agoo_error_cat, "memory allocation of response failed on connection %llu.", (unsigned long long)c->id);
|
476
481
|
return bad_request(c, 500, __LINE__);
|
477
482
|
} else {
|
478
483
|
if (NULL == c->res_tail) {
|
@@ -491,7 +496,12 @@ con_http_read(Con c) {
|
|
491
496
|
check_upgrade(c);
|
492
497
|
req = c->req;
|
493
498
|
c->req = NULL;
|
494
|
-
|
499
|
+
if (req->hook->no_queue && FUNC_HOOK == req->hook->type) {
|
500
|
+
req->hook->func(req);
|
501
|
+
agoo_req_destroy(req);
|
502
|
+
} else {
|
503
|
+
agoo_queue_push(req->hook->queue, (void*)req);
|
504
|
+
}
|
495
505
|
if (mlen < (long)c->bcnt) {
|
496
506
|
memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
|
497
507
|
c->bcnt -= mlen;
|
@@ -509,7 +519,7 @@ con_http_read(Con c) {
|
|
509
519
|
}
|
510
520
|
|
511
521
|
static bool
|
512
|
-
con_ws_read(
|
522
|
+
con_ws_read(agooCon c) {
|
513
523
|
ssize_t cnt;
|
514
524
|
uint8_t *b;
|
515
525
|
uint8_t op;
|
@@ -525,13 +535,13 @@ con_ws_read(Con c) {
|
|
525
535
|
// If nothing read then no need to complain. Just close.
|
526
536
|
if (0 < c->bcnt) {
|
527
537
|
if (0 == cnt) {
|
528
|
-
|
538
|
+
agoo_log_cat(&agoo_warn_cat, "Nothing to read. Client closed socket on connection %llu.", (unsigned long long)c->id);
|
529
539
|
} else {
|
530
540
|
char msg[1024];
|
531
541
|
int len = snprintf(msg, sizeof(msg) - 1, "Failed to read WebSocket message. %s.", strerror(errno));
|
532
542
|
|
533
543
|
push_error(c->up, msg, len);
|
534
|
-
|
544
|
+
agoo_log_cat(&agoo_warn_cat, "Failed to read WebSocket message. %s.", strerror(errno));
|
535
545
|
}
|
536
546
|
}
|
537
547
|
return true;
|
@@ -543,57 +553,57 @@ con_ws_read(Con c) {
|
|
543
553
|
return false; // Try again.
|
544
554
|
}
|
545
555
|
b = (uint8_t*)c->buf;
|
546
|
-
if (0 >= (mlen =
|
556
|
+
if (0 >= (mlen = agoo_ws_calc_len(c, b, c->bcnt))) {
|
547
557
|
return (mlen < 0);
|
548
558
|
}
|
549
559
|
op = 0x0F & *b;
|
550
560
|
switch (op) {
|
551
|
-
case
|
552
|
-
case
|
553
|
-
if (
|
561
|
+
case AGOO_WS_OP_TEXT:
|
562
|
+
case AGOO_WS_OP_BIN:
|
563
|
+
if (agoo_ws_create_req(c, mlen)) {
|
554
564
|
return true;
|
555
565
|
}
|
556
566
|
break;
|
557
|
-
case
|
567
|
+
case AGOO_WS_OP_CLOSE:
|
558
568
|
return true;
|
559
|
-
case
|
569
|
+
case AGOO_WS_OP_PING:
|
560
570
|
if (mlen == (long)c->bcnt) {
|
561
|
-
|
571
|
+
agoo_ws_pong(c);
|
562
572
|
c->bcnt = 0;
|
563
573
|
}
|
564
574
|
break;
|
565
|
-
case
|
575
|
+
case AGOO_WS_OP_PONG:
|
566
576
|
// ignore
|
567
577
|
if (mlen == (long)c->bcnt) {
|
568
578
|
c->bcnt = 0;
|
569
579
|
}
|
570
580
|
break;
|
571
|
-
case
|
581
|
+
case AGOO_WS_OP_CONT:
|
572
582
|
default: {
|
573
583
|
char msg[1024];
|
574
584
|
int len = snprintf(msg, sizeof(msg) - 1, "WebSocket op 0x%02x not supported on %llu.",
|
575
585
|
op, (unsigned long long)c->id);
|
576
586
|
|
577
587
|
push_error(c->up, msg, len);
|
578
|
-
|
588
|
+
agoo_log_cat(&agoo_error_cat, "WebSocket op 0x%02x not supported on %llu.", op, (unsigned long long)c->id);
|
579
589
|
return true;
|
580
590
|
}
|
581
591
|
}
|
582
592
|
}
|
583
593
|
if (NULL != c->req) {
|
584
594
|
mlen = c->req->mlen;
|
585
|
-
c->req->mlen =
|
595
|
+
c->req->mlen = agoo_ws_decode(c->req->msg, c->req->mlen);
|
586
596
|
if (mlen <= (long)c->bcnt) {
|
587
|
-
if (
|
588
|
-
if (
|
589
|
-
|
597
|
+
if (agoo_debug_cat.on) {
|
598
|
+
if (AGOO_ON_MSG == c->req->method) {
|
599
|
+
agoo_log_cat(&agoo_debug_cat, "WebSocket message on %llu: %s", (unsigned long long)c->id, c->req->msg);
|
590
600
|
} else {
|
591
|
-
|
601
|
+
agoo_log_cat(&agoo_debug_cat, "WebSocket binary message on %llu", (unsigned long long)c->id);
|
592
602
|
}
|
593
603
|
}
|
594
604
|
}
|
595
|
-
|
596
|
-
|
605
|
+
agoo_upgraded_ref(c->up);
|
606
|
+
agoo_queue_push(&agoo_server.eval_queue, (void*)c->req);
|
597
607
|
if (mlen < (long)c->bcnt) {
|
598
608
|
memmove(c->buf, c->buf + mlen, c->bcnt - mlen);
|
599
609
|
c->bcnt -= mlen;
|
@@ -611,24 +621,18 @@ con_ws_read(Con c) {
|
|
611
621
|
return false;
|
612
622
|
}
|
613
623
|
|
614
|
-
// return
|
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
|
+
// return false to remove/close connection
|
624
625
|
bool
|
625
|
-
|
626
|
-
|
626
|
+
agoo_con_http_write(agooCon c) {
|
627
|
+
agooText message = agoo_res_message(c->res_head);
|
627
628
|
ssize_t cnt;
|
628
629
|
|
630
|
+
if (NULL == message) {
|
631
|
+
return true;
|
632
|
+
}
|
629
633
|
c->timeout = dtime() + CON_TIMEOUT;
|
630
634
|
if (0 == c->wcnt) {
|
631
|
-
if (
|
635
|
+
if (agoo_resp_cat.on) {
|
632
636
|
char buf[4096];
|
633
637
|
char *hend = strstr(message->text, "\r\n\r\n");
|
634
638
|
|
@@ -640,23 +644,23 @@ con_http_write(Con c) {
|
|
640
644
|
}
|
641
645
|
memcpy(buf, message->text, hend - message->text);
|
642
646
|
buf[hend - message->text] = '\0';
|
643
|
-
|
647
|
+
agoo_log_cat(&agoo_resp_cat, "%llu: %s", (unsigned long long)c->id, buf);
|
644
648
|
}
|
645
|
-
if (
|
646
|
-
|
649
|
+
if (agoo_debug_cat.on) {
|
650
|
+
agoo_log_cat(&agoo_debug_cat, "response on %llu: %s", (unsigned long long)c->id, message->text);
|
647
651
|
}
|
648
652
|
}
|
649
653
|
if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, MSG_DONTWAIT))) {
|
650
654
|
if (EAGAIN == errno) {
|
651
|
-
return
|
655
|
+
return true;
|
652
656
|
}
|
653
|
-
|
657
|
+
agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
|
654
658
|
|
655
|
-
return
|
659
|
+
return false;
|
656
660
|
}
|
657
661
|
c->wcnt += cnt;
|
658
662
|
if (c->wcnt == message->len) { // finished
|
659
|
-
|
663
|
+
agooRes res = c->res_head;
|
660
664
|
bool done = res->close;
|
661
665
|
|
662
666
|
c->res_head = res->next;
|
@@ -664,21 +668,20 @@ con_http_write(Con c) {
|
|
664
668
|
c->res_tail = NULL;
|
665
669
|
}
|
666
670
|
c->wcnt = 0;
|
667
|
-
|
671
|
+
agoo_res_destroy(res);
|
668
672
|
|
669
|
-
return done;
|
673
|
+
return !done;
|
670
674
|
}
|
671
|
-
|
672
|
-
return false;
|
675
|
+
return true;
|
673
676
|
}
|
674
677
|
|
675
678
|
static const char ping_msg[] = "\x89\x00";
|
676
679
|
static const char pong_msg[] = "\x8a\x00";
|
677
680
|
|
678
681
|
static bool
|
679
|
-
con_ws_write(
|
680
|
-
|
681
|
-
|
682
|
+
con_ws_write(agooCon c) {
|
683
|
+
agooRes res = c->res_head;
|
684
|
+
agooText message = agoo_res_message(res);
|
682
685
|
ssize_t cnt;
|
683
686
|
|
684
687
|
if (NULL == message) {
|
@@ -688,16 +691,16 @@ con_ws_write(Con c) {
|
|
688
691
|
int len;
|
689
692
|
|
690
693
|
if (EAGAIN == errno) {
|
691
|
-
return
|
694
|
+
return true;
|
692
695
|
}
|
693
696
|
len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
|
694
697
|
push_error(c->up, msg, len);
|
695
698
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
+
agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
|
700
|
+
agoo_ws_req_close(c);
|
701
|
+
agoo_res_destroy(res);
|
699
702
|
|
700
|
-
return
|
703
|
+
return false;
|
701
704
|
}
|
702
705
|
} else if (res->pong) {
|
703
706
|
if (0 > (cnt = send(c->sock, pong_msg, sizeof(pong_msg) - 1, 0))) {
|
@@ -705,45 +708,46 @@ con_ws_write(Con c) {
|
|
705
708
|
int len;
|
706
709
|
|
707
710
|
if (EAGAIN == errno) {
|
708
|
-
return
|
711
|
+
return true;
|
709
712
|
}
|
710
713
|
len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
|
711
714
|
push_error(c->up, msg, len);
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
+
agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
|
716
|
+
agoo_ws_req_close(c);
|
717
|
+
agoo_res_destroy(res);
|
715
718
|
|
716
|
-
return
|
719
|
+
return false;
|
717
720
|
}
|
718
721
|
} else {
|
719
|
-
|
722
|
+
agoo_ws_req_close(c);
|
720
723
|
c->res_head = res->next;
|
721
724
|
if (res == c->res_tail) {
|
722
725
|
c->res_tail = NULL;
|
723
726
|
}
|
724
|
-
|
725
|
-
|
727
|
+
agoo_res_destroy(res);
|
728
|
+
|
729
|
+
return false;
|
726
730
|
}
|
727
731
|
c->res_head = res->next;
|
728
732
|
if (res == c->res_tail) {
|
729
733
|
c->res_tail = NULL;
|
730
734
|
}
|
731
|
-
return
|
735
|
+
return true;
|
732
736
|
}
|
733
737
|
c->timeout = dtime() + CON_TIMEOUT;
|
734
738
|
if (0 == c->wcnt) {
|
735
|
-
|
739
|
+
agooText t;
|
736
740
|
|
737
|
-
if (
|
741
|
+
if (agoo_push_cat.on) {
|
738
742
|
if (message->bin) {
|
739
|
-
|
743
|
+
agoo_log_cat(&agoo_push_cat, "%llu binary", (unsigned long long)c->id);
|
740
744
|
} else {
|
741
|
-
|
745
|
+
agoo_log_cat(&agoo_push_cat, "%llu: %s", (unsigned long long)c->id, message->text);
|
742
746
|
}
|
743
747
|
}
|
744
|
-
t =
|
748
|
+
t = agoo_ws_expand(message);
|
745
749
|
if (t != message) {
|
746
|
-
|
750
|
+
agoo_res_set_message(res, t);
|
747
751
|
message = t;
|
748
752
|
}
|
749
753
|
}
|
@@ -752,18 +756,18 @@ con_ws_write(Con c) {
|
|
752
756
|
int len;
|
753
757
|
|
754
758
|
if (EAGAIN == errno) {
|
755
|
-
return
|
759
|
+
return true;
|
756
760
|
}
|
757
761
|
len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
|
758
762
|
push_error(c->up, msg, len);
|
759
|
-
|
760
|
-
|
763
|
+
agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
|
764
|
+
agoo_ws_req_close(c);
|
761
765
|
|
762
|
-
return
|
766
|
+
return false;
|
763
767
|
}
|
764
768
|
c->wcnt += cnt;
|
765
769
|
if (c->wcnt == message->len) { // finished
|
766
|
-
|
770
|
+
agooRes res = c->res_head;
|
767
771
|
bool done = res->close;
|
768
772
|
|
769
773
|
c->res_head = res->next;
|
@@ -771,34 +775,35 @@ con_ws_write(Con c) {
|
|
771
775
|
c->res_tail = NULL;
|
772
776
|
}
|
773
777
|
c->wcnt = 0;
|
774
|
-
|
778
|
+
agoo_res_destroy(res);
|
775
779
|
|
776
|
-
return done;
|
780
|
+
return !done;
|
777
781
|
}
|
778
|
-
return
|
782
|
+
return true;
|
779
783
|
}
|
780
784
|
|
781
785
|
static bool
|
782
|
-
con_sse_write(
|
783
|
-
|
784
|
-
|
786
|
+
con_sse_write(agooCon c) {
|
787
|
+
agooRes res = c->res_head;
|
788
|
+
agooText message = agoo_res_message(res);
|
785
789
|
ssize_t cnt;
|
786
790
|
|
787
791
|
if (NULL == message) {
|
788
|
-
|
789
|
-
|
790
|
-
|
792
|
+
agoo_ws_req_close(c);
|
793
|
+
agoo_res_destroy(res);
|
794
|
+
|
795
|
+
return false;
|
791
796
|
}
|
792
797
|
c->timeout = dtime() + CON_TIMEOUT *2;
|
793
798
|
if (0 == c->wcnt) {
|
794
|
-
|
799
|
+
agooText t;
|
795
800
|
|
796
|
-
if (
|
797
|
-
|
801
|
+
if (agoo_push_cat.on) {
|
802
|
+
agoo_log_cat(&agoo_push_cat, "%llu: %s", (unsigned long long)c->id, message->text);
|
798
803
|
}
|
799
|
-
t =
|
804
|
+
t = agoo_sse_expand(message);
|
800
805
|
if (t != message) {
|
801
|
-
|
806
|
+
agoo_res_set_message(res, t);
|
802
807
|
message = t;
|
803
808
|
}
|
804
809
|
}
|
@@ -807,18 +812,18 @@ con_sse_write(Con c) {
|
|
807
812
|
int len;
|
808
813
|
|
809
814
|
if (EAGAIN == errno) {
|
810
|
-
return
|
815
|
+
return true;
|
811
816
|
}
|
812
817
|
len = snprintf(msg, sizeof(msg) - 1, "Socket error @ %llu.", (unsigned long long)c->id);
|
813
818
|
push_error(c->up, msg, len);
|
814
|
-
|
815
|
-
|
819
|
+
agoo_log_cat(&agoo_error_cat, "Socket error @ %llu.", (unsigned long long)c->id);
|
820
|
+
agoo_ws_req_close(c);
|
816
821
|
|
817
|
-
return
|
822
|
+
return false;
|
818
823
|
}
|
819
824
|
c->wcnt += cnt;
|
820
825
|
if (c->wcnt == message->len) { // finished
|
821
|
-
|
826
|
+
agooRes res = c->res_head;
|
822
827
|
bool done = res->close;
|
823
828
|
|
824
829
|
c->res_head = res->next;
|
@@ -826,46 +831,22 @@ con_sse_write(Con c) {
|
|
826
831
|
c->res_tail = NULL;
|
827
832
|
}
|
828
833
|
c->wcnt = 0;
|
829
|
-
|
834
|
+
agoo_res_destroy(res);
|
830
835
|
|
831
|
-
return done;
|
836
|
+
return !done;
|
832
837
|
}
|
833
|
-
return
|
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;
|
838
|
+
return true;
|
858
839
|
}
|
859
840
|
|
860
841
|
static void
|
861
|
-
publish_pub(
|
862
|
-
|
863
|
-
const char
|
864
|
-
int
|
842
|
+
publish_pub(agooPub pub) {
|
843
|
+
agooUpgraded up;
|
844
|
+
const char *sub = pub->subject->pattern;
|
845
|
+
int cnt = 0;
|
865
846
|
|
866
|
-
for (up =
|
867
|
-
if (NULL != up->con &&
|
868
|
-
|
847
|
+
for (up = agoo_server.up_list; NULL != up; up = up->next) {
|
848
|
+
if (NULL != up->con && agoo_upgraded_match(up, sub)) {
|
849
|
+
agooRes res = agoo_res_create(up->con);
|
869
850
|
|
870
851
|
if (NULL != res) {
|
871
852
|
if (NULL == up->con->res_tail) {
|
@@ -874,8 +855,8 @@ publish_pub(Pub pub) {
|
|
874
855
|
up->con->res_tail->next = res;
|
875
856
|
}
|
876
857
|
up->con->res_tail = res;
|
877
|
-
res->con_kind =
|
878
|
-
|
858
|
+
res->con_kind = AGOO_CON_ANY;
|
859
|
+
agoo_res_set_message(res, agoo_text_dup(pub->msg));
|
879
860
|
cnt++;
|
880
861
|
}
|
881
862
|
}
|
@@ -883,45 +864,45 @@ publish_pub(Pub pub) {
|
|
883
864
|
}
|
884
865
|
|
885
866
|
static void
|
886
|
-
unsubscribe_pub(
|
867
|
+
unsubscribe_pub(agooPub pub) {
|
887
868
|
if (NULL == pub->up) {
|
888
|
-
|
869
|
+
agooUpgraded up;
|
889
870
|
|
890
|
-
for (up =
|
891
|
-
|
871
|
+
for (up = agoo_server.up_list; NULL != up; up = up->next) {
|
872
|
+
agoo_upgraded_del_subject(up, pub->subject);
|
892
873
|
}
|
893
874
|
} else {
|
894
|
-
|
875
|
+
agoo_upgraded_del_subject(pub->up, pub->subject);
|
895
876
|
}
|
896
877
|
}
|
897
878
|
|
898
879
|
static void
|
899
|
-
process_pub_con(
|
900
|
-
|
880
|
+
process_pub_con(agooPub pub) {
|
881
|
+
agooUpgraded up = pub->up;
|
901
882
|
|
902
883
|
if (NULL != up) {
|
903
884
|
int pending;
|
904
885
|
|
905
886
|
// TBD Change pending to be based on length of con queue
|
906
887
|
if (1 == (pending = atomic_fetch_sub(&up->pending, 1))) {
|
907
|
-
if (NULL != up &&
|
908
|
-
|
888
|
+
if (NULL != up && agoo_server.ctx_nil_value != up->ctx && up->on_empty) {
|
889
|
+
agooReq req = agoo_req_create(0);
|
909
890
|
|
910
891
|
req->up = up;
|
911
|
-
req->method =
|
912
|
-
req->hook =
|
913
|
-
|
914
|
-
|
892
|
+
req->method = AGOO_ON_EMPTY;
|
893
|
+
req->hook = agoo_hook_create(AGOO_NONE, NULL, up->ctx, PUSH_HOOK, &agoo_server.eval_queue);
|
894
|
+
agoo_upgraded_ref(up);
|
895
|
+
agoo_queue_push(&agoo_server.eval_queue, (void*)req);
|
915
896
|
}
|
916
897
|
}
|
917
898
|
}
|
918
899
|
switch (pub->kind) {
|
919
|
-
case
|
900
|
+
case AGOO_PUB_CLOSE:
|
920
901
|
// A close after already closed is used to decrement the reference
|
921
902
|
// count on the upgraded so it can be destroyed in the con loop
|
922
903
|
// threads.
|
923
904
|
if (NULL != up->con) {
|
924
|
-
|
905
|
+
agooRes res = agoo_res_create(up->con);
|
925
906
|
|
926
907
|
if (NULL != res) {
|
927
908
|
if (NULL == up->con->res_tail) {
|
@@ -935,11 +916,11 @@ process_pub_con(Pub pub) {
|
|
935
916
|
}
|
936
917
|
}
|
937
918
|
break;
|
938
|
-
case
|
919
|
+
case AGOO_PUB_WRITE: {
|
939
920
|
if (NULL == up->con) {
|
940
|
-
|
921
|
+
agoo_log_cat(&agoo_warn_cat, "Connection already closed. WebSocket write failed.");
|
941
922
|
} else {
|
942
|
-
|
923
|
+
agooRes res = agoo_res_create(up->con);
|
943
924
|
|
944
925
|
if (NULL != res) {
|
945
926
|
if (NULL == up->con->res_tail) {
|
@@ -948,33 +929,33 @@ process_pub_con(Pub pub) {
|
|
948
929
|
up->con->res_tail->next = res;
|
949
930
|
}
|
950
931
|
up->con->res_tail = res;
|
951
|
-
res->con_kind =
|
952
|
-
|
932
|
+
res->con_kind = AGOO_CON_ANY;
|
933
|
+
agoo_res_set_message(res, pub->msg);
|
953
934
|
}
|
954
935
|
}
|
955
936
|
break;
|
956
|
-
case
|
957
|
-
|
937
|
+
case AGOO_PUB_SUB:
|
938
|
+
agoo_upgraded_add_subject(pub->up, pub->subject);
|
958
939
|
pub->subject = NULL;
|
959
940
|
break;
|
960
|
-
case
|
941
|
+
case AGOO_PUB_UN:
|
961
942
|
unsubscribe_pub(pub);
|
962
943
|
break;
|
963
|
-
case
|
944
|
+
case AGOO_PUB_MSG:
|
964
945
|
publish_pub(pub);
|
965
946
|
break;
|
966
947
|
}
|
967
948
|
default:
|
968
949
|
break;
|
969
950
|
}
|
970
|
-
|
951
|
+
agoo_pub_destroy(pub);
|
971
952
|
}
|
972
953
|
|
973
954
|
short
|
974
|
-
|
955
|
+
agoo_con_http_events(agooCon c) {
|
975
956
|
short events = 0;
|
976
957
|
|
977
|
-
if (NULL != c->res_head && NULL !=
|
958
|
+
if (NULL != c->res_head && NULL != agoo_res_message(c->res_head)) {
|
978
959
|
events = POLLIN | POLLOUT;
|
979
960
|
} else if (!c->closing) {
|
980
961
|
events = POLLIN;
|
@@ -983,10 +964,10 @@ con_http_events(Con c) {
|
|
983
964
|
}
|
984
965
|
|
985
966
|
static short
|
986
|
-
con_ws_events(
|
967
|
+
con_ws_events(agooCon c) {
|
987
968
|
short events = 0;
|
988
969
|
|
989
|
-
if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL !=
|
970
|
+
if (NULL != c->res_head && (c->res_head->close || c->res_head->ping || NULL != agoo_res_message(c->res_head))) {
|
990
971
|
events = POLLIN | POLLOUT;
|
991
972
|
} else if (!c->closing) {
|
992
973
|
events = POLLIN;
|
@@ -995,228 +976,265 @@ con_ws_events(Con c) {
|
|
995
976
|
}
|
996
977
|
|
997
978
|
static short
|
998
|
-
con_sse_events(
|
979
|
+
con_sse_events(agooCon c) {
|
999
980
|
short events = 0;
|
1000
981
|
|
1001
|
-
if (NULL != c->res_head && NULL !=
|
982
|
+
if (NULL != c->res_head && NULL != agoo_res_message(c->res_head)) {
|
1002
983
|
events = POLLOUT;
|
1003
984
|
}
|
1004
985
|
return events;
|
1005
986
|
}
|
1006
987
|
|
1007
|
-
static struct pollfd*
|
1008
|
-
poll_setup(Con c, Queue q, struct pollfd *pp) {
|
1009
|
-
// The first two pollfd are for the con_queue and the pub_queue in that
|
1010
|
-
// order.
|
1011
|
-
pp->fd = queue_listen(&the_server.con_queue);
|
1012
|
-
pp->events = POLLIN;
|
1013
|
-
pp->revents = 0;
|
1014
|
-
pp++;
|
1015
|
-
pp->fd = queue_listen(q);
|
1016
|
-
pp->events = POLLIN;
|
1017
|
-
pp->revents = 0;
|
1018
|
-
pp++;
|
1019
|
-
for (; NULL != c; c = c->next) {
|
1020
|
-
if (c->dead || 0 == c->sock) {
|
1021
|
-
continue;
|
1022
|
-
}
|
1023
|
-
if (c->hijacked) {
|
1024
|
-
c->sock = 0;
|
1025
|
-
continue;
|
1026
|
-
}
|
1027
|
-
c->pp = pp;
|
1028
|
-
pp->fd = c->sock;
|
1029
|
-
pp->events = c->bind->events(c);
|
1030
|
-
pp->revents = 0;
|
1031
|
-
pp++;
|
1032
|
-
}
|
1033
|
-
return pp;
|
1034
|
-
}
|
1035
|
-
|
1036
988
|
static bool
|
1037
|
-
remove_dead_res(
|
1038
|
-
|
989
|
+
remove_dead_res(agooCon c) {
|
990
|
+
agooRes res;
|
1039
991
|
|
1040
992
|
while (NULL != (res = c->res_head)) {
|
1041
|
-
if (NULL ==
|
993
|
+
if (NULL == agoo_res_message(c->res_head) && !c->res_head->close && !c->res_head->ping) {
|
1042
994
|
break;
|
1043
995
|
}
|
1044
996
|
c->res_head = res->next;
|
1045
997
|
if (res == c->res_tail) {
|
1046
998
|
c->res_tail = NULL;
|
1047
999
|
}
|
1048
|
-
|
1000
|
+
agoo_res_destroy(res);
|
1049
1001
|
}
|
1050
1002
|
return NULL == c->res_head;
|
1051
1003
|
}
|
1052
1004
|
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
struct pollfd *pp;
|
1064
|
-
int ccnt = 0;
|
1065
|
-
int i;
|
1066
|
-
double now;
|
1067
|
-
Pub pub;
|
1068
|
-
|
1069
|
-
atomic_fetch_add(&the_server.running, 1);
|
1070
|
-
memset(pa, 0, size);
|
1071
|
-
while (the_server.active) {
|
1072
|
-
while (NULL != (c = (Con)queue_pop(&the_server.con_queue, 0.0))) {
|
1073
|
-
c->next = cons;
|
1074
|
-
cons = c;
|
1075
|
-
ccnt++;
|
1076
|
-
if (pend - pa < ccnt + 2) {
|
1077
|
-
size_t cnt = (pend - pa) * 2;
|
1078
|
-
|
1079
|
-
size = sizeof(struct pollfd) * cnt;
|
1080
|
-
if (NULL == (pa = (struct pollfd*)malloc(size))) {
|
1081
|
-
log_cat(&error_cat, "Out of memory.");
|
1082
|
-
log_close();
|
1083
|
-
exit(EXIT_FAILURE);
|
1084
|
-
|
1085
|
-
return NULL;
|
1086
|
-
}
|
1087
|
-
pend = pa + cnt;
|
1088
|
-
}
|
1005
|
+
static agooReadyIO
|
1006
|
+
con_ready_io(void *ctx) {
|
1007
|
+
agooCon c = (agooCon)ctx;
|
1008
|
+
|
1009
|
+
if (NULL != c->bind) {
|
1010
|
+
switch (c->bind->events(c)) {
|
1011
|
+
case POLLIN: return AGOO_READY_IN;
|
1012
|
+
case POLLOUT: return AGOO_READY_OUT;
|
1013
|
+
case POLLIN | POLLOUT: return AGOO_READY_BOTH;
|
1014
|
+
default: break;
|
1089
1015
|
}
|
1090
|
-
|
1091
|
-
|
1016
|
+
}
|
1017
|
+
return AGOO_READY_NONE;
|
1018
|
+
}
|
1019
|
+
|
1020
|
+
static bool
|
1021
|
+
con_ready_check(void *ctx, double now) {
|
1022
|
+
agooCon c = (agooCon)ctx;
|
1023
|
+
|
1024
|
+
if (c->dead || 0 == c->sock) {
|
1025
|
+
if (remove_dead_res(c)) {
|
1026
|
+
agoo_con_destroy(c);
|
1027
|
+
|
1028
|
+
return false;
|
1092
1029
|
}
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
break;
|
1030
|
+
} else if (0.0 == c->timeout || now < c->timeout) {
|
1031
|
+
return true;
|
1032
|
+
} else if (c->closing) {
|
1033
|
+
if (remove_dead_res(c)) {
|
1034
|
+
agoo_con_destroy(c);
|
1035
|
+
|
1036
|
+
return false;
|
1101
1037
|
}
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
}
|
1124
|
-
}
|
1125
|
-
}
|
1126
|
-
// Check pub_queue if an event is waiting.
|
1127
|
-
if (0 != (pa[1].revents & POLLIN)) {
|
1128
|
-
queue_release(&loop->pub_queue);
|
1129
|
-
while (NULL != (pub = (Pub)queue_pop(&loop->pub_queue, 0.0))) {
|
1130
|
-
process_pub_con(pub);
|
1131
|
-
}
|
1132
|
-
}
|
1038
|
+
} else if (AGOO_CON_WS == c->bind->kind || AGOO_CON_SSE == c->bind->kind) {
|
1039
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
1040
|
+
agoo_ws_ping(c);
|
1041
|
+
|
1042
|
+
return true;
|
1043
|
+
} else {
|
1044
|
+
c->closing = true;
|
1045
|
+
c->timeout = now + 0.5;
|
1046
|
+
|
1047
|
+
return true;
|
1048
|
+
}
|
1049
|
+
return true;
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
static bool
|
1053
|
+
con_ready_read(agooReady ready, void *ctx) {
|
1054
|
+
agooCon c = (agooCon)ctx;
|
1055
|
+
|
1056
|
+
if (NULL != c->bind->read) {
|
1057
|
+
if (!c->bind->read(c)) {
|
1058
|
+
return true;
|
1133
1059
|
}
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
if (
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
log_cat(&error_cat, "Socket %llu error. %s", (unsigned long long)c->id, strerror(errno));
|
1060
|
+
}
|
1061
|
+
return false;
|
1062
|
+
}
|
1063
|
+
|
1064
|
+
static bool
|
1065
|
+
con_ready_write(void *ctx) {
|
1066
|
+
agooCon c = (agooCon)ctx;
|
1067
|
+
|
1068
|
+
if (NULL != c->res_head) {
|
1069
|
+
agooConKind kind = c->res_head->con_kind;
|
1070
|
+
|
1071
|
+
if (NULL != c->bind->write) {
|
1072
|
+
if (c->bind->write(c)) {
|
1073
|
+
//if (kind != c->kind && AGOO_CON_ANY != kind) {
|
1074
|
+
if (AGOO_CON_ANY != kind) {
|
1075
|
+
switch (kind) {
|
1076
|
+
case AGOO_CON_WS:
|
1077
|
+
c->bind = &ws_bind;
|
1078
|
+
break;
|
1079
|
+
case AGOO_CON_SSE:
|
1080
|
+
c->bind = &sse_bind;
|
1081
|
+
break;
|
1082
|
+
default:
|
1083
|
+
break;
|
1159
1084
|
}
|
1160
1085
|
}
|
1161
|
-
|
1162
|
-
goto CON_CHECK;
|
1163
|
-
}
|
1164
|
-
CON_CHECK:
|
1165
|
-
if (c->dead || 0 == c->sock) {
|
1166
|
-
if (remove_dead_res(c)) {
|
1167
|
-
goto CON_RM;
|
1168
|
-
}
|
1169
|
-
} else if (0.0 == c->timeout || now < c->timeout) {
|
1170
|
-
prev = c;
|
1171
|
-
continue;
|
1172
|
-
} else if (c->closing) {
|
1173
|
-
if (remove_dead_res(c)) {
|
1174
|
-
goto CON_RM;
|
1175
|
-
}
|
1176
|
-
} else if (CON_WS == c->bind->kind || CON_SSE == c->bind->kind) {
|
1177
|
-
c->timeout = dtime() + CON_TIMEOUT;
|
1178
|
-
ws_ping(c);
|
1179
|
-
continue;
|
1180
|
-
} else {
|
1181
|
-
c->closing = true;
|
1182
|
-
c->timeout = now + 0.5;
|
1183
|
-
prev = c;
|
1184
|
-
continue;
|
1185
|
-
}
|
1186
|
-
prev = c;
|
1187
|
-
continue;
|
1188
|
-
CON_RM:
|
1189
|
-
if (NULL == prev) {
|
1190
|
-
cons = next;
|
1191
|
-
} else {
|
1192
|
-
prev->next = next;
|
1086
|
+
return true;
|
1193
1087
|
}
|
1194
|
-
ccnt--;
|
1195
|
-
log_cat(&con_cat, "Connection %llu closed.", (unsigned long long)c->id);
|
1196
|
-
con_destroy(c);
|
1197
1088
|
}
|
1198
1089
|
}
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1090
|
+
return false;
|
1091
|
+
}
|
1092
|
+
|
1093
|
+
static void
|
1094
|
+
con_ready_destroy(void *ctx) {
|
1095
|
+
agoo_con_destroy((agooCon)ctx);
|
1096
|
+
}
|
1097
|
+
|
1098
|
+
static struct _agooHandler con_handler = {
|
1099
|
+
.io = con_ready_io,
|
1100
|
+
.check = con_ready_check,
|
1101
|
+
.read = con_ready_read,
|
1102
|
+
.write = con_ready_write,
|
1103
|
+
.error = NULL,
|
1104
|
+
.destroy = con_ready_destroy,
|
1105
|
+
};
|
1106
|
+
|
1107
|
+
static agooReadyIO
|
1108
|
+
queue_ready_io(void *ctx) {
|
1109
|
+
return AGOO_READY_IN;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
static bool
|
1113
|
+
con_queue_ready_read(agooReady ready, void *ctx) {
|
1114
|
+
agooConLoop loop = (agooConLoop)ctx;
|
1115
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
1116
|
+
agooCon c;
|
1117
|
+
|
1118
|
+
agoo_queue_release(&agoo_server.con_queue);
|
1119
|
+
while (NULL != (c = (agooCon)agoo_queue_pop(&agoo_server.con_queue, 0.0))) {
|
1120
|
+
c->loop = loop;
|
1121
|
+
if (AGOO_ERR_OK != agoo_ready_add(&err, ready, c->sock, &con_handler, c)) {
|
1122
|
+
agoo_log_cat(&agoo_error_cat, "Failed to add connection to manager. %s", err.msg);
|
1123
|
+
agoo_err_clear(&err);
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
return true;
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
static struct _agooHandler con_queue_handler = {
|
1130
|
+
.io = queue_ready_io,
|
1131
|
+
.check = NULL,
|
1132
|
+
.read = con_queue_ready_read,
|
1133
|
+
.write = NULL,
|
1134
|
+
.error = NULL,
|
1135
|
+
.destroy = NULL,
|
1136
|
+
};
|
1137
|
+
|
1138
|
+
static bool
|
1139
|
+
pub_queue_ready_read(agooReady ready, void *ctx) {
|
1140
|
+
agooConLoop loop = (agooConLoop)ctx;
|
1141
|
+
agooPub pub;
|
1142
|
+
|
1143
|
+
agoo_queue_release(&loop->pub_queue);
|
1144
|
+
while (NULL != (pub = (agooPub)agoo_queue_pop(&loop->pub_queue, 0.0))) {
|
1145
|
+
process_pub_con(pub);
|
1202
1146
|
}
|
1203
|
-
|
1147
|
+
return true;
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
static struct _agooHandler pub_queue_handler = {
|
1151
|
+
.io = queue_ready_io,
|
1152
|
+
.check = NULL,
|
1153
|
+
.read = pub_queue_ready_read,
|
1154
|
+
.write = NULL,
|
1155
|
+
.error = NULL,
|
1156
|
+
.destroy = NULL,
|
1157
|
+
};
|
1158
|
+
|
1159
|
+
void*
|
1160
|
+
agoo_con_loop(void *x) {
|
1161
|
+
agooConLoop loop = (agooConLoop)x;
|
1162
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
1163
|
+
agooReady ready = agoo_ready_create(&err);
|
1164
|
+
agooPub pub;
|
1165
|
+
agooCon c;
|
1166
|
+
int con_queue_fd = agoo_queue_listen(&agoo_server.con_queue);
|
1167
|
+
int pub_queue_fd = agoo_queue_listen(&loop->pub_queue);
|
1204
1168
|
|
1169
|
+
if (NULL == ready) {
|
1170
|
+
agoo_log_cat(&agoo_error_cat, "Failed to create connection manager. %s", err.msg);
|
1171
|
+
exit(EXIT_FAILURE);
|
1172
|
+
return NULL;
|
1173
|
+
}
|
1174
|
+
if (AGOO_ERR_OK != agoo_ready_add(&err, ready, con_queue_fd, &con_queue_handler, loop) ||
|
1175
|
+
AGOO_ERR_OK != agoo_ready_add(&err, ready, pub_queue_fd, &pub_queue_handler, loop)) {
|
1176
|
+
agoo_log_cat(&agoo_error_cat, "Failed to add queue connection to manager. %s", err.msg);
|
1177
|
+
exit(EXIT_FAILURE);
|
1178
|
+
|
1179
|
+
return NULL;
|
1180
|
+
}
|
1181
|
+
atomic_fetch_add(&agoo_server.running, 1);
|
1182
|
+
|
1183
|
+
while (agoo_server.active) {
|
1184
|
+
while (NULL != (c = (agooCon)agoo_queue_pop(&agoo_server.con_queue, 0.0))) {
|
1185
|
+
c->loop = loop;
|
1186
|
+
if (AGOO_ERR_OK != agoo_ready_add(&err, ready, c->sock, &con_handler, c)) {
|
1187
|
+
agoo_log_cat(&agoo_error_cat, "Failed to add connection to manager. %s", err.msg);
|
1188
|
+
agoo_err_clear(&err);
|
1189
|
+
}
|
1190
|
+
}
|
1191
|
+
while (NULL != (pub = (agooPub)agoo_queue_pop(&loop->pub_queue, 0.0))) {
|
1192
|
+
process_pub_con(pub);
|
1193
|
+
}
|
1194
|
+
if (AGOO_ERR_OK != agoo_ready_go(&err, ready)) {
|
1195
|
+
agoo_log_cat(&agoo_error_cat, "IO error. %s", err.msg);
|
1196
|
+
agoo_err_clear(&err);
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
agoo_ready_destroy(ready);
|
1200
|
+
atomic_fetch_sub(&agoo_server.running, 1);
|
1201
|
+
|
1205
1202
|
return NULL;
|
1206
1203
|
}
|
1207
1204
|
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1205
|
+
agooConLoop
|
1206
|
+
agoo_conloop_create(agooErr err, int id) {
|
1207
|
+
agooConLoop loop;
|
1211
1208
|
|
1212
|
-
if (NULL == (loop = (
|
1213
|
-
|
1209
|
+
if (NULL == (loop = (agooConLoop)malloc(sizeof(struct _agooConLoop)))) {
|
1210
|
+
agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a connection thread.");
|
1214
1211
|
} else {
|
1212
|
+
int stat;
|
1213
|
+
|
1215
1214
|
//DEBUG_ALLOC(mem_con, c);
|
1216
1215
|
loop->next = NULL;
|
1217
|
-
|
1216
|
+
agoo_queue_multi_init(&loop->pub_queue, 256, true, false);
|
1218
1217
|
loop->id = id;
|
1219
|
-
|
1218
|
+
loop->res_head = NULL;
|
1219
|
+
loop->res_tail = NULL;
|
1220
|
+
if (0 != (stat = pthread_create(&loop->thread, NULL, agoo_con_loop, loop))) {
|
1221
|
+
agoo_err_set(err, stat, "Failed to create connection loop. %s", strerror(stat));
|
1222
|
+
return NULL;
|
1223
|
+
}
|
1224
|
+
pthread_mutex_init(&loop->lock, 0);
|
1220
1225
|
}
|
1221
1226
|
return loop;
|
1222
1227
|
}
|
1228
|
+
|
1229
|
+
void
|
1230
|
+
agoo_conloop_destroy(agooConLoop loop) {
|
1231
|
+
agooRes res;
|
1232
|
+
|
1233
|
+
agoo_queue_cleanup(&loop->pub_queue);
|
1234
|
+
while (NULL != (res = loop->res_head)) {
|
1235
|
+
loop->res_head = res->next;
|
1236
|
+
DEBUG_FREE(mem_res, res);
|
1237
|
+
free(res);
|
1238
|
+
}
|
1239
|
+
free(loop);
|
1240
|
+
}
|