agoo 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +61 -0
- data/ext/agoo/agoo.c +19 -0
- data/ext/agoo/con.c +515 -0
- data/ext/agoo/con.h +46 -0
- data/ext/agoo/dtime.c +52 -0
- data/ext/agoo/dtime.h +10 -0
- data/ext/agoo/err.c +78 -0
- data/ext/agoo/err.h +48 -0
- data/ext/agoo/error_stream.c +92 -0
- data/ext/agoo/error_stream.h +13 -0
- data/ext/agoo/extconf.rb +13 -0
- data/ext/agoo/hook.c +74 -0
- data/ext/agoo/hook.h +33 -0
- data/ext/agoo/http.c +617 -0
- data/ext/agoo/http.h +13 -0
- data/ext/agoo/log.c +497 -0
- data/ext/agoo/log.h +106 -0
- data/ext/agoo/log_queue.h +30 -0
- data/ext/agoo/page.c +342 -0
- data/ext/agoo/page.h +39 -0
- data/ext/agoo/queue.c +191 -0
- data/ext/agoo/queue.h +39 -0
- data/ext/agoo/request.c +563 -0
- data/ext/agoo/request.h +36 -0
- data/ext/agoo/res.c +38 -0
- data/ext/agoo/res.h +28 -0
- data/ext/agoo/response.c +271 -0
- data/ext/agoo/response.h +33 -0
- data/ext/agoo/server.c +891 -0
- data/ext/agoo/server.h +47 -0
- data/ext/agoo/text.c +66 -0
- data/ext/agoo/text.h +24 -0
- data/ext/agoo/types.h +18 -0
- data/lib/agoo.rb +9 -0
- data/lib/agoo/version.rb +5 -0
- data/test/base_handler_test.rb +170 -0
- data/test/log_test.rb +269 -0
- data/test/rack_handler_test.rb +147 -0
- data/test/static_test.rb +81 -0
- data/test/tests.rb +8 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 875dd13c45030ea940c3afe58571a260f6220ff8f09ed3fd1084957e0efa3586
|
4
|
+
data.tar.gz: d8efe96974e9299d1627373cc31be42f93b7bed3eb00c7593c32b8aa9fe954cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b66d7a3f8265fab7f84c9a78894401950adcbd7a55df412ab7ff92bc497ff825777498cf58ff1a0e3ba0b168aecec597da823ac61455f57b4f9d26ef32e1264b
|
7
|
+
data.tar.gz: d82ea3f18c07459186e64a8db4fda82e07bdb55e7506a031eebeb628c05e458eb4507fb531144fd1559f39c5543d0569a9fdc9738df47110ed4edf68045c1c83
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Peter Ohler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# agoo
|
2
|
+
|
3
|
+
[![Build Status](https://img.shields.io/travis/ohler55/agoo/master.svg)](http://travis-ci.org/ohler55/agoo?branch=master)
|
4
|
+
|
5
|
+
A High Performance HTTP Server for Ruby
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'agoo'
|
11
|
+
|
12
|
+
server = Agoo::Server.new(6464, 'root')
|
13
|
+
|
14
|
+
class MyHandler
|
15
|
+
def initialize
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(req)
|
19
|
+
[ 200, { }, [ "hello world" ] ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
handler = TellMeHandler.new
|
24
|
+
server.handle(:GET, "/hello", handler)
|
25
|
+
server.start()
|
26
|
+
```
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
```
|
30
|
+
gem install agoo
|
31
|
+
```
|
32
|
+
|
33
|
+
## What Is This?
|
34
|
+
|
35
|
+
Agoo is Japanese for a type of flying fish. This gem flies. It is a high
|
36
|
+
performance HTTP server that serves static resource at hundreds of thousands
|
37
|
+
of fetchs per second. A a simple hello world Ruby handler at over 100,000
|
38
|
+
requests per second on a desktop computer. That places Agoo at about 80 times
|
39
|
+
faster than Sinatra and 1000 times faster than Rails. In both cases the
|
40
|
+
latency was an order of magnitude lower or more. Checkout the benchmarks on <a
|
41
|
+
href="http://opo.technology/benchmarks.html#web_benchmarks">OpO
|
42
|
+
benchmarks</a>. Note that the benchmarks had to use a C program called _hose_
|
43
|
+
from the <a href="http://opo.technology/index.html">OpO</a> downloads to hit
|
44
|
+
the Agoo limits. Ruby benchmarks driver could not push Agoo hard enough.
|
45
|
+
|
46
|
+
Agoo supports the [Ruby rack API](https://rack.github.io) which allows for the
|
47
|
+
use of rack compatible gems.
|
48
|
+
|
49
|
+
## Releases
|
50
|
+
|
51
|
+
See [{file:CHANGELOG.md}](CHANGELOG.md)
|
52
|
+
|
53
|
+
## Links
|
54
|
+
|
55
|
+
- *Documentation*: http://rubydoc.info/gems/agoo
|
56
|
+
|
57
|
+
- *GitHub* *repo*: https://github.com/ohler55/agoo
|
58
|
+
|
59
|
+
- *RubyGems* *repo*: https://rubygems.org/gems/agoo
|
60
|
+
|
61
|
+
Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announcements and news about the Agoo gem.
|
data/ext/agoo/agoo.c
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <ruby.h>
|
5
|
+
|
6
|
+
#include "error_stream.h"
|
7
|
+
#include "request.h"
|
8
|
+
#include "response.h"
|
9
|
+
#include "server.h"
|
10
|
+
|
11
|
+
void
|
12
|
+
Init_agoo() {
|
13
|
+
VALUE mod = rb_define_module("Agoo");
|
14
|
+
|
15
|
+
error_stream_init(mod);
|
16
|
+
request_init(mod);
|
17
|
+
response_init(mod);
|
18
|
+
server_init(mod);
|
19
|
+
}
|
data/ext/agoo/con.c
ADDED
@@ -0,0 +1,515 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <ctype.h>
|
4
|
+
#include <string.h>
|
5
|
+
|
6
|
+
#include "con.h"
|
7
|
+
#include "dtime.h"
|
8
|
+
#include "hook.h"
|
9
|
+
#include "http.h"
|
10
|
+
#include "res.h"
|
11
|
+
#include "server.h"
|
12
|
+
|
13
|
+
#define MAX_SOCK 4096
|
14
|
+
#define CON_TIMEOUT 5.0
|
15
|
+
|
16
|
+
Con
|
17
|
+
con_create(Err err, Server server, int sock, uint64_t id) {
|
18
|
+
Con c;
|
19
|
+
|
20
|
+
if (MAX_SOCK <= sock) {
|
21
|
+
err_set(err, ERR_TOO_MANY, "Too many connections.");
|
22
|
+
return NULL;
|
23
|
+
}
|
24
|
+
if (NULL == (c = (Con)malloc(sizeof(struct _Con)))) {
|
25
|
+
err_set(err, ERR_MEMORY, "Failed to allocate memory for a connection.");
|
26
|
+
} else {
|
27
|
+
memset(c, 0, sizeof(struct _Con));
|
28
|
+
c->sock = sock;
|
29
|
+
c->iid = id;
|
30
|
+
sprintf(c->id, "%llu", (unsigned long long)id);
|
31
|
+
c->server = server;
|
32
|
+
}
|
33
|
+
return c;
|
34
|
+
}
|
35
|
+
|
36
|
+
void
|
37
|
+
con_destroy(Con c) {
|
38
|
+
if (0 < c->sock) {
|
39
|
+
close(c->sock);
|
40
|
+
c->sock = 0;
|
41
|
+
}
|
42
|
+
if (NULL != c->req) {
|
43
|
+
free(c->req);
|
44
|
+
}
|
45
|
+
free(c);
|
46
|
+
}
|
47
|
+
|
48
|
+
const char*
|
49
|
+
con_header_value(const char *header, int hlen, const char *key, int *vlen) {
|
50
|
+
// Search for \r then check for \n and then the key followed by a :. Keep
|
51
|
+
// trying until the end of the header.
|
52
|
+
const char *h = header;
|
53
|
+
const char *hend = header + hlen;
|
54
|
+
const char *value;
|
55
|
+
int klen = strlen(key);
|
56
|
+
|
57
|
+
while (h < hend) {
|
58
|
+
if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
|
59
|
+
h += klen + 1;
|
60
|
+
for (; ' ' == *h; h++) {
|
61
|
+
}
|
62
|
+
value = h;
|
63
|
+
for (; '\r' != *h && '\0' != *h; h++) {
|
64
|
+
}
|
65
|
+
*vlen = h - value;
|
66
|
+
|
67
|
+
return value;
|
68
|
+
}
|
69
|
+
for (; h < hend; h++) {
|
70
|
+
if ('\r' == *h && '\n' == *(h + 1)) {
|
71
|
+
h += 2;
|
72
|
+
break;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
return NULL;
|
77
|
+
}
|
78
|
+
|
79
|
+
static bool
|
80
|
+
bad_request(Con c, int status, int line) {
|
81
|
+
Res res;
|
82
|
+
const char *msg = http_code_message(status);
|
83
|
+
|
84
|
+
if (NULL == (res = res_create())) {
|
85
|
+
log_cat(&c->server->error_cat, "memory allocation of response failed on connection %llu @ %d.", c->iid, line);
|
86
|
+
} else {
|
87
|
+
char buf[256];
|
88
|
+
int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n", status, msg);
|
89
|
+
Text message = text_create(buf, cnt);
|
90
|
+
|
91
|
+
if (NULL == c->res_tail) {
|
92
|
+
c->res_head = res;
|
93
|
+
} else {
|
94
|
+
c->res_tail->next = res;
|
95
|
+
}
|
96
|
+
c->res_tail = res;
|
97
|
+
res->close = true;
|
98
|
+
res_set_message(res, message);
|
99
|
+
}
|
100
|
+
return true;
|
101
|
+
}
|
102
|
+
|
103
|
+
static bool
|
104
|
+
should_close(const char *header, int hlen) {
|
105
|
+
const char *v;
|
106
|
+
int vlen = 0;
|
107
|
+
|
108
|
+
if (NULL != (v = con_header_value(header, hlen, "Connection", &vlen))) {
|
109
|
+
return (5 == vlen && 0 == strncasecmp("Close", v, 5));
|
110
|
+
}
|
111
|
+
return false;
|
112
|
+
}
|
113
|
+
|
114
|
+
// Returns true if request has handled.
|
115
|
+
static bool
|
116
|
+
con_header_read(Con c) {
|
117
|
+
Server server = c->server;
|
118
|
+
char *hend = strstr(c->buf, "\r\n\r\n");
|
119
|
+
Method method;
|
120
|
+
char *path;
|
121
|
+
char *pend;
|
122
|
+
char *query = NULL;
|
123
|
+
char *qend;
|
124
|
+
char *b;
|
125
|
+
size_t clen = 0;
|
126
|
+
size_t mlen;
|
127
|
+
Hook hook = NULL;
|
128
|
+
|
129
|
+
if (NULL == hend) {
|
130
|
+
if (sizeof(c->buf) - 1 <= c->bcnt) {
|
131
|
+
return bad_request(c, 431, __LINE__);
|
132
|
+
}
|
133
|
+
return false;
|
134
|
+
}
|
135
|
+
if (server->req_cat.on) {
|
136
|
+
*hend = '\0';
|
137
|
+
log_cat(&server->req_cat, "%llu: %s", c->iid, c->buf);
|
138
|
+
*hend = '\r';
|
139
|
+
}
|
140
|
+
for (b = c->buf; ' ' != *b; b++) {
|
141
|
+
if ('\0' == *b) {
|
142
|
+
return bad_request(c, 400, __LINE__);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
switch (toupper(*c->buf)) {
|
146
|
+
case 'G':
|
147
|
+
if (3 != b - c->buf || 0 != strncmp("GET", c->buf, 3)) {
|
148
|
+
return bad_request(c, 400, __LINE__);
|
149
|
+
}
|
150
|
+
method = GET;
|
151
|
+
break;
|
152
|
+
case 'P': {
|
153
|
+
const char *v;
|
154
|
+
int vlen = 0;
|
155
|
+
char *vend;
|
156
|
+
|
157
|
+
if (3 == b - c->buf && 0 == strncmp("PUT", c->buf, 3)) {
|
158
|
+
method = PUT;
|
159
|
+
} else if (4 == b - c->buf && 0 == strncmp("POST", c->buf, 4)) {
|
160
|
+
method = POST;
|
161
|
+
} else {
|
162
|
+
return bad_request(c, 400, __LINE__);
|
163
|
+
}
|
164
|
+
if (NULL == (v = con_header_value(c->buf, hend - c->buf, "Content-Length", &vlen))) {
|
165
|
+
return bad_request(c, 411, __LINE__);
|
166
|
+
}
|
167
|
+
clen = (size_t)strtoul(v, &vend, 10);
|
168
|
+
if (vend != v + vlen) {
|
169
|
+
return bad_request(c, 411, __LINE__);
|
170
|
+
}
|
171
|
+
break;
|
172
|
+
}
|
173
|
+
case 'D':
|
174
|
+
if (6 != b - c->buf || 0 != strncmp("DELETE", c->buf, 6)) {
|
175
|
+
return bad_request(c, 400, __LINE__);
|
176
|
+
}
|
177
|
+
method = DELETE;
|
178
|
+
break;
|
179
|
+
case 'H':
|
180
|
+
if (4 != b - c->buf || 0 != strncmp("HEAD", c->buf, 4)) {
|
181
|
+
return bad_request(c, 400, __LINE__);
|
182
|
+
}
|
183
|
+
method = HEAD;
|
184
|
+
break;
|
185
|
+
case 'O':
|
186
|
+
if (7 != b - c->buf || 0 != strncmp("OPTIONS", c->buf, 7)) {
|
187
|
+
return bad_request(c, 400, __LINE__);
|
188
|
+
}
|
189
|
+
method = OPTIONS;
|
190
|
+
break;
|
191
|
+
case 'C':
|
192
|
+
if (7 != b - c->buf || 0 != strncmp("CONNECT", c->buf, 7)) {
|
193
|
+
return bad_request(c, 400, __LINE__);
|
194
|
+
}
|
195
|
+
method = CONNECT;
|
196
|
+
break;
|
197
|
+
default:
|
198
|
+
return bad_request(c, 400, __LINE__);
|
199
|
+
}
|
200
|
+
for (; ' ' == *b; b++) {
|
201
|
+
if ('\0' == *b) {
|
202
|
+
return bad_request(c, 400, __LINE__);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
path = b;
|
206
|
+
for (; ' ' != *b; b++) {
|
207
|
+
switch (*b) {
|
208
|
+
case '?':
|
209
|
+
pend = b;
|
210
|
+
query = b + 1;
|
211
|
+
break;
|
212
|
+
case '\0':
|
213
|
+
return bad_request(c, 400, __LINE__);
|
214
|
+
default:
|
215
|
+
break;
|
216
|
+
}
|
217
|
+
}
|
218
|
+
if (NULL == query) {
|
219
|
+
pend = b;
|
220
|
+
query = b;
|
221
|
+
qend = b;
|
222
|
+
} else {
|
223
|
+
qend = b;
|
224
|
+
}
|
225
|
+
if (NULL == (hook = hook_find(server->hooks, method, path, pend))) {
|
226
|
+
if (GET == method) {
|
227
|
+
struct _Err err = ERR_INIT;
|
228
|
+
Page p = page_get(&err, &server->pages, server->root, path, pend - path);
|
229
|
+
Res res;
|
230
|
+
|
231
|
+
if (NULL == p) {
|
232
|
+
return bad_request(c, 404, __LINE__);
|
233
|
+
}
|
234
|
+
if (NULL == (res = res_create())) {
|
235
|
+
return bad_request(c, 500, __LINE__);
|
236
|
+
}
|
237
|
+
if (NULL == c->res_tail) {
|
238
|
+
c->res_head = res;
|
239
|
+
} else {
|
240
|
+
c->res_tail->next = res;
|
241
|
+
}
|
242
|
+
c->res_tail = res;
|
243
|
+
|
244
|
+
b = strstr(c->buf, "\r\n");
|
245
|
+
res->close = should_close(b, hend - b);
|
246
|
+
|
247
|
+
text_ref(p->resp);
|
248
|
+
res_set_message(res, p->resp);
|
249
|
+
|
250
|
+
return true;
|
251
|
+
}
|
252
|
+
}
|
253
|
+
// Create request and populate.
|
254
|
+
mlen = hend - c->buf + 4 + clen;
|
255
|
+
if (NULL == (c->req = (Req)malloc(mlen + sizeof(struct _Req) - 8 + 1))) {
|
256
|
+
return bad_request(c, 413, __LINE__);
|
257
|
+
}
|
258
|
+
memcpy(c->req->msg, c->buf, c->bcnt);
|
259
|
+
if (c->bcnt < mlen) {
|
260
|
+
memset(c->req->msg + c->bcnt, 0, mlen - c->bcnt);
|
261
|
+
}
|
262
|
+
c->req->server = server;
|
263
|
+
c->req->method = method;
|
264
|
+
c->req->path.start = c->req->msg + (path - c->buf);
|
265
|
+
c->req->path.len = pend - path;
|
266
|
+
c->req->query.start = c->req->msg + (query - c->buf);
|
267
|
+
c->req->query.len = qend - query;
|
268
|
+
c->req->mlen = mlen;
|
269
|
+
c->req->body.start = c->req->msg + (hend - c->buf + 4);
|
270
|
+
c->req->body.len = clen;
|
271
|
+
b = strstr(b, "\r\n");
|
272
|
+
c->req->header.start = c->req->msg + (b + 2 - c->buf);
|
273
|
+
c->req->header.len = hend - b - 2;
|
274
|
+
c->req->res = NULL;
|
275
|
+
if (NULL != hook) {
|
276
|
+
c->req->handler = hook->handler;
|
277
|
+
c->req->handler_type = hook->type;
|
278
|
+
} else {
|
279
|
+
c->req->handler = Qnil;
|
280
|
+
c->req->handler_type = NO_HOOK;
|
281
|
+
}
|
282
|
+
return false;
|
283
|
+
}
|
284
|
+
|
285
|
+
// return true to remove/close connection
|
286
|
+
static bool
|
287
|
+
con_read(Con c) {
|
288
|
+
ssize_t cnt;
|
289
|
+
|
290
|
+
if (NULL != c->req) {
|
291
|
+
cnt = recv(c->sock, c->req->msg + c->bcnt, c->req->mlen - c->bcnt, 0);
|
292
|
+
} else {
|
293
|
+
cnt = recv(c->sock, c->buf + c->bcnt, sizeof(c->buf) - c->bcnt - 1, 0);
|
294
|
+
}
|
295
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
296
|
+
if (0 >= cnt) {
|
297
|
+
// If nothing read then no need to complain. Just close.
|
298
|
+
if (0 < c->bcnt) {
|
299
|
+
if (0 == cnt) {
|
300
|
+
log_cat(&c->server->warn_cat, "Nothing to read. Client closed socket %s.", c->id);
|
301
|
+
} else {
|
302
|
+
log_cat(&c->server->warn_cat, "Failed to read request. %s.", strerror(errno));
|
303
|
+
}
|
304
|
+
}
|
305
|
+
return true;
|
306
|
+
}
|
307
|
+
c->bcnt += cnt;
|
308
|
+
// Terminate with \0 for debug and strstr() check
|
309
|
+
if (NULL == c->req) {
|
310
|
+
c->buf[c->bcnt] = '\0';
|
311
|
+
if (con_header_read(c)) { // already handled
|
312
|
+
c->bcnt = 0;
|
313
|
+
*c->buf = '\0';
|
314
|
+
// TBD handle extra data in buf
|
315
|
+
|
316
|
+
return false;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
if (NULL != c->req) {
|
320
|
+
c->req->msg[c->bcnt] = '\0';
|
321
|
+
if (c->req->mlen <= c->bcnt) {
|
322
|
+
Res res;
|
323
|
+
|
324
|
+
if (c->server->debug_cat.on && NULL != c->req && NULL != c->req->body.start) {
|
325
|
+
log_cat(&c->server->debug_cat, "request on %llu: %s", c->iid, c->req->body.start);
|
326
|
+
}
|
327
|
+
if (NULL == (res = res_create())) {
|
328
|
+
c->req = NULL;
|
329
|
+
log_cat(&c->server->error_cat, "memory allocation of response failed on connection %llu.", c->iid);
|
330
|
+
return bad_request(c, 500, __LINE__);
|
331
|
+
} else {
|
332
|
+
if (NULL == c->res_tail) {
|
333
|
+
c->res_head = res;
|
334
|
+
} else {
|
335
|
+
c->res_tail->next = res;
|
336
|
+
}
|
337
|
+
c->res_tail = res;
|
338
|
+
res->close = should_close(c->req->header.start, c->req->header.len);
|
339
|
+
}
|
340
|
+
c->req->res = res;
|
341
|
+
queue_push(&c->server->eval_queue, (void*)c->req);
|
342
|
+
c->req = NULL;
|
343
|
+
c->bcnt = 0;
|
344
|
+
*c->buf = '\0';
|
345
|
+
// TBD handle extra data in buf
|
346
|
+
|
347
|
+
return false;
|
348
|
+
}
|
349
|
+
}
|
350
|
+
return false;
|
351
|
+
}
|
352
|
+
|
353
|
+
// return true to remove/close connection
|
354
|
+
static bool
|
355
|
+
con_write(Con c) {
|
356
|
+
Text message = res_message(c->res_head);
|
357
|
+
ssize_t cnt;
|
358
|
+
|
359
|
+
c->timeout = dtime() + CON_TIMEOUT;
|
360
|
+
if (0 == c->wcnt) {
|
361
|
+
if (c->server->resp_cat.on) {
|
362
|
+
char buf[4096];
|
363
|
+
char *hend = strstr(message->text, "\r\n\r\n");
|
364
|
+
|
365
|
+
if (NULL == hend) {
|
366
|
+
hend = message->text + message->len;
|
367
|
+
}
|
368
|
+
memcpy(buf, message->text, hend - message->text);
|
369
|
+
log_cat(&c->server->resp_cat, "%llu: %s", c->iid, buf);
|
370
|
+
}
|
371
|
+
if (c->server->debug_cat.on) {
|
372
|
+
log_cat(&c->server->debug_cat, "response on %llu: %s", c->iid, message->text);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
if (0 > (cnt = send(c->sock, message->text + c->wcnt, message->len - c->wcnt, 0))) {
|
376
|
+
if (EAGAIN == errno) {
|
377
|
+
return false;
|
378
|
+
}
|
379
|
+
log_cat(&c->server->error_cat, "Socket error @ %llu.", c->iid);
|
380
|
+
|
381
|
+
return true;
|
382
|
+
}
|
383
|
+
c->wcnt += cnt;
|
384
|
+
if (c->wcnt == message->len) { // finished
|
385
|
+
Res res = c->res_head;
|
386
|
+
bool done = res->close;
|
387
|
+
|
388
|
+
c->res_head = res->next;
|
389
|
+
if (res == c->res_tail) {
|
390
|
+
c->res_tail = NULL;
|
391
|
+
}
|
392
|
+
c->wcnt = 0;
|
393
|
+
res_destroy(res);
|
394
|
+
|
395
|
+
return done;
|
396
|
+
}
|
397
|
+
return false;
|
398
|
+
}
|
399
|
+
|
400
|
+
void*
|
401
|
+
con_loop(void *x) {
|
402
|
+
Server server = (Server)x;
|
403
|
+
Con c;
|
404
|
+
Con ca[MAX_SOCK];
|
405
|
+
struct pollfd pa[MAX_SOCK];
|
406
|
+
struct pollfd *pp;
|
407
|
+
Con *end = ca + MAX_SOCK;
|
408
|
+
Con *cp;
|
409
|
+
int ccnt = 0;
|
410
|
+
int i;
|
411
|
+
long mcnt = 0;
|
412
|
+
double now;
|
413
|
+
|
414
|
+
atomic_fetch_add(&server->running, 1);
|
415
|
+
memset(ca, 0, sizeof(ca));
|
416
|
+
memset(pa, 0, sizeof(pa));
|
417
|
+
while (server->active) {
|
418
|
+
while (NULL != (c = (Con)queue_pop(&server->con_queue, 0.0))) {
|
419
|
+
mcnt++;
|
420
|
+
ca[c->sock] = c;
|
421
|
+
ccnt++;
|
422
|
+
}
|
423
|
+
pp = pa;
|
424
|
+
pp->fd = queue_listen(&server->con_queue);
|
425
|
+
pp->events = POLLIN;
|
426
|
+
pp->revents = 0;
|
427
|
+
pp++;
|
428
|
+
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
429
|
+
if (NULL == *cp) {
|
430
|
+
continue;
|
431
|
+
}
|
432
|
+
c = *cp;
|
433
|
+
c->pp = pp;
|
434
|
+
pp->fd = c->sock;
|
435
|
+
if (NULL != c->res_head && NULL != res_message(c->res_head)) {
|
436
|
+
pp->events = POLLIN | POLLOUT;
|
437
|
+
} else {
|
438
|
+
pp->events = POLLIN;
|
439
|
+
}
|
440
|
+
pp->revents = 0;
|
441
|
+
i--;
|
442
|
+
pp++;
|
443
|
+
}
|
444
|
+
if (0 > (i = poll(pa, pp - pa, 100))) {
|
445
|
+
if (EAGAIN == errno) {
|
446
|
+
continue;
|
447
|
+
}
|
448
|
+
log_cat(&server->error_cat, "Polling error. %s.", strerror(errno));
|
449
|
+
// Either a signal or something bad like out of memory. Might as well exit.
|
450
|
+
break;
|
451
|
+
}
|
452
|
+
now = dtime();
|
453
|
+
|
454
|
+
if (0 == i) { // nothing to read or write
|
455
|
+
// TBD check for cons to close
|
456
|
+
continue;
|
457
|
+
}
|
458
|
+
if (0 != (pa->revents & POLLIN)) {
|
459
|
+
queue_release(&server->con_queue);
|
460
|
+
while (NULL != (c = (Con)queue_pop(&server->con_queue, 0.0))) {
|
461
|
+
mcnt++;
|
462
|
+
ca[c->sock] = c;
|
463
|
+
ccnt++;
|
464
|
+
}
|
465
|
+
}
|
466
|
+
for (i = ccnt, cp = ca; 0 < i && cp < end; cp++) {
|
467
|
+
if (NULL == *cp || 0 == (*cp)->sock || NULL == (*cp)->pp) {
|
468
|
+
continue;
|
469
|
+
}
|
470
|
+
c = *cp;
|
471
|
+
i--;
|
472
|
+
pp = c->pp;
|
473
|
+
if (0 != (pp->revents & POLLIN)) {
|
474
|
+
if (con_read(c)) {
|
475
|
+
goto CON_RM;
|
476
|
+
}
|
477
|
+
}
|
478
|
+
if (0 != (pp->revents & POLLOUT)) {
|
479
|
+
if (con_write(c)) {
|
480
|
+
goto CON_RM;
|
481
|
+
}
|
482
|
+
}
|
483
|
+
if (0 != (pp->revents & (POLLERR | POLLHUP | POLLNVAL))) {
|
484
|
+
if (0 < c->bcnt) {
|
485
|
+
if (0 != (pp->revents & (POLLHUP | POLLNVAL))) {
|
486
|
+
log_cat(&server->error_cat, "Socket %llu closed.", c->iid);
|
487
|
+
} else if (!c->closing) {
|
488
|
+
log_cat(&server->error_cat, "Socket %llu error. %s", c->iid, strerror(errno));
|
489
|
+
}
|
490
|
+
}
|
491
|
+
goto CON_RM;
|
492
|
+
}
|
493
|
+
if (0.0 == c->timeout || now < c->timeout) {
|
494
|
+
continue;
|
495
|
+
} else if (c->closing) {
|
496
|
+
goto CON_RM;
|
497
|
+
} else {
|
498
|
+
c->closing = true;
|
499
|
+
c->timeout = now + 0.5;
|
500
|
+
//wush_text_set(&c->resp, (char*)close_resp, sizeof(close_resp) - 1, false);
|
501
|
+
continue;
|
502
|
+
}
|
503
|
+
continue;
|
504
|
+
CON_RM:
|
505
|
+
ca[c->sock] = NULL;
|
506
|
+
ccnt--;
|
507
|
+
log_cat(&server->con_cat, "Connection %llu closed.", c->iid);
|
508
|
+
con_destroy(c);
|
509
|
+
}
|
510
|
+
}
|
511
|
+
atomic_fetch_sub(&server->running, 1);
|
512
|
+
|
513
|
+
return NULL;
|
514
|
+
}
|
515
|
+
|