agoo 2.4.0 → 2.5.0
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 +12 -0
- data/ext/agoo/agoo.c +4 -3
- data/ext/agoo/bind.c +275 -0
- data/ext/agoo/bind.h +35 -0
- data/ext/agoo/con.c +22 -25
- data/ext/agoo/debug.c +2 -0
- data/ext/agoo/debug.h +1 -0
- data/ext/agoo/error_stream.c +1 -0
- data/ext/agoo/hook.c +16 -6
- data/ext/agoo/hook.h +3 -1
- data/ext/agoo/http.c +7 -7
- data/ext/agoo/http.h +3 -1
- data/ext/agoo/log.h +1 -0
- data/ext/agoo/page.c +231 -104
- data/ext/agoo/page.h +10 -38
- data/ext/agoo/request.c +5 -1
- data/ext/agoo/request.h +1 -2
- data/ext/agoo/response.c +5 -1
- data/ext/agoo/rhook.c +2 -2
- data/ext/agoo/rhook.h +1 -1
- data/ext/agoo/rserver.c +991 -0
- data/ext/agoo/rserver.h +42 -0
- data/ext/agoo/server.c +95 -978
- data/ext/agoo/server.h +16 -27
- data/ext/agoo/upgraded.c +28 -20
- data/ext/agoo/websocket.c +4 -7
- data/lib/agoo/version.rb +1 -1
- data/test/bind_test.rb +134 -0
- data/test/named_client.rb +7 -0
- data/test/named_server.rb +13 -0
- metadata +12 -2
data/ext/agoo/rserver.h
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_RSERVER_H__
|
4
|
+
#define __AGOO_RSERVER_H__
|
5
|
+
|
6
|
+
#include <pthread.h>
|
7
|
+
#include <stdbool.h>
|
8
|
+
#include <stdatomic.h>
|
9
|
+
|
10
|
+
#include <ruby.h>
|
11
|
+
|
12
|
+
#include "log.h"
|
13
|
+
#include "queue.h"
|
14
|
+
#include "sub.h"
|
15
|
+
#include "upgraded.h"
|
16
|
+
|
17
|
+
#define MAX_WORKERS 32
|
18
|
+
|
19
|
+
typedef struct _RServer {
|
20
|
+
// pub/sub (maybe common across servers,,,
|
21
|
+
struct _Queue pub_queue;
|
22
|
+
struct _SubCache sub_cache; // subscription cache
|
23
|
+
|
24
|
+
struct _Queue eval_queue;
|
25
|
+
|
26
|
+
// upgrade
|
27
|
+
pthread_mutex_t up_lock;
|
28
|
+
int max_push_pending;
|
29
|
+
Upgraded up_list;
|
30
|
+
|
31
|
+
// threads and workers
|
32
|
+
int worker_cnt;
|
33
|
+
int worker_pids[MAX_WORKERS];
|
34
|
+
VALUE *eval_threads; // Qnil terminated
|
35
|
+
} *RServer;
|
36
|
+
|
37
|
+
extern struct _RServer the_rserver;
|
38
|
+
|
39
|
+
extern void server_init(VALUE mod);
|
40
|
+
extern VALUE rserver_shutdown(VALUE self);
|
41
|
+
|
42
|
+
#endif // __AGOO_RSERVER_H__
|
data/ext/agoo/server.c
CHANGED
@@ -1,360 +1,56 @@
|
|
1
1
|
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
2
|
|
3
|
-
#include <errno.h>
|
4
3
|
#include <fcntl.h>
|
5
|
-
#include <limits.h>
|
6
4
|
#include <netdb.h>
|
7
5
|
#include <netinet/tcp.h>
|
8
6
|
#include <poll.h>
|
9
|
-
#include <signal.h>
|
10
|
-
#include <stdarg.h>
|
11
|
-
#include <stdio.h>
|
12
|
-
#include <stdlib.h>
|
13
|
-
#include <string.h>
|
14
|
-
#include <sys/select.h>
|
15
7
|
#include <sys/socket.h>
|
16
|
-
#include <sys/time.h>
|
17
8
|
#include <sys/types.h>
|
18
|
-
#include <sys/wait.h>
|
19
9
|
#include <unistd.h>
|
20
|
-
#include <stdatomic.h>
|
21
|
-
|
22
|
-
#include <ruby.h>
|
23
|
-
#include <ruby/thread.h>
|
24
|
-
#include <ruby/encoding.h>
|
25
10
|
|
26
11
|
#include "con.h"
|
27
|
-
#include "debug.h"
|
28
12
|
#include "dtime.h"
|
29
|
-
#include "err.h"
|
30
13
|
#include "http.h"
|
31
|
-
#include "
|
32
|
-
#include "
|
33
|
-
#include "request.h"
|
34
|
-
#include "server.h"
|
35
|
-
#include "sse.h"
|
36
|
-
#include "sub.h"
|
37
|
-
#include "upgraded.h"
|
38
|
-
#include "websocket.h"
|
39
|
-
|
40
|
-
extern void agoo_shutdown();
|
41
|
-
|
42
|
-
static VALUE server_mod = Qundef;
|
43
|
-
|
44
|
-
static VALUE connect_sym;
|
45
|
-
static VALUE delete_sym;
|
46
|
-
static VALUE get_sym;
|
47
|
-
static VALUE head_sym;
|
48
|
-
static VALUE options_sym;
|
49
|
-
static VALUE post_sym;
|
50
|
-
static VALUE push_env_key;
|
51
|
-
static VALUE put_sym;
|
14
|
+
#include "log.h"
|
15
|
+
#include "page.h"
|
52
16
|
|
53
|
-
|
54
|
-
|
55
|
-
static ID call_id;
|
56
|
-
static ID each_id;
|
57
|
-
static ID on_close_id;
|
58
|
-
static ID on_drained_id;
|
59
|
-
static ID on_error_id;
|
60
|
-
static ID on_message_id;
|
61
|
-
static ID on_request_id;
|
62
|
-
static ID to_i_id;
|
63
|
-
|
64
|
-
static const char err500[] = "HTTP/1.1 500 Internal Server Error\r\n";
|
17
|
+
#include "server.h"
|
65
18
|
|
66
19
|
struct _Server the_server = {false};
|
67
20
|
|
68
|
-
static void
|
69
|
-
server_mark(void *ptr) {
|
70
|
-
Upgraded up;
|
71
|
-
|
72
|
-
rb_gc_mark(rserver);
|
73
|
-
pthread_mutex_lock(&the_server.up_lock);
|
74
|
-
for (up = the_server.up_list; NULL != up; up = up->next) {
|
75
|
-
if (Qnil != up->handler) {
|
76
|
-
rb_gc_mark(up->handler);
|
77
|
-
}
|
78
|
-
if (Qnil != up->env) {
|
79
|
-
rb_gc_mark(up->env);
|
80
|
-
}
|
81
|
-
if (Qnil != up->wrap) {
|
82
|
-
rb_gc_mark(up->wrap);
|
83
|
-
}
|
84
|
-
}
|
85
|
-
pthread_mutex_unlock(&the_server.up_lock);
|
86
|
-
}
|
87
|
-
|
88
21
|
void
|
89
|
-
|
90
|
-
if (the_server.inited) {
|
91
|
-
log_cat(&info_cat, "Agoo with pid %d shutting down.", getpid());
|
92
|
-
the_server.inited = false;
|
93
|
-
if (the_server.active) {
|
94
|
-
double giveup = dtime() + 1.0;
|
95
|
-
|
96
|
-
the_server.active = false;
|
97
|
-
pthread_detach(the_server.listen_thread);
|
98
|
-
pthread_detach(the_server.con_thread);
|
99
|
-
while (0 < atomic_load(&the_server.running)) {
|
100
|
-
dsleep(0.1);
|
101
|
-
if (giveup < dtime()) {
|
102
|
-
break;
|
103
|
-
}
|
104
|
-
}
|
105
|
-
sub_cleanup(&the_server.sub_cache);
|
106
|
-
// The preferred method to of waiting for the ruby threads would
|
107
|
-
// be either a join or even a kill but since we may not have the
|
108
|
-
// gvl here that would cause a segfault. Instead we set a timeout
|
109
|
-
// and wait for the running counter to drop to zero.
|
110
|
-
if (NULL != the_server.eval_threads) {
|
111
|
-
double timeout = dtime() + 2.0;
|
112
|
-
|
113
|
-
while (dtime() < timeout) {
|
114
|
-
if (0 >= atomic_load(&the_server.running)) {
|
115
|
-
break;
|
116
|
-
}
|
117
|
-
dsleep(0.02);
|
118
|
-
}
|
119
|
-
DEBUG_FREE(mem_eval_threads, the_server.eval_threads);
|
120
|
-
free(the_server.eval_threads);
|
121
|
-
the_server.eval_threads = NULL;
|
122
|
-
}
|
123
|
-
while (NULL != the_server.hooks) {
|
124
|
-
Hook h = the_server.hooks;
|
125
|
-
|
126
|
-
the_server.hooks = h->next;
|
127
|
-
hook_destroy(h);
|
128
|
-
}
|
129
|
-
}
|
130
|
-
queue_cleanup(&the_server.con_queue);
|
131
|
-
queue_cleanup(&the_server.pub_queue);
|
132
|
-
queue_cleanup(&the_server.eval_queue);
|
133
|
-
pages_cleanup(&the_server.pages);
|
134
|
-
http_cleanup();
|
135
|
-
|
136
|
-
if (1 < the_server.worker_cnt && getpid() == *the_server.worker_pids) {
|
137
|
-
int i;
|
138
|
-
int status;
|
139
|
-
int exit_cnt = 1;
|
140
|
-
int j;
|
141
|
-
|
142
|
-
for (i = 1; i < the_server.worker_cnt; i++) {
|
143
|
-
kill(the_server.worker_pids[i], SIGKILL);
|
144
|
-
}
|
145
|
-
for (j = 0; j < 20; j++) {
|
146
|
-
for (i = 1; i < the_server.worker_cnt; i++) {
|
147
|
-
if (0 == the_server.worker_pids[i]) {
|
148
|
-
continue;
|
149
|
-
}
|
150
|
-
if (0 < waitpid(the_server.worker_pids[i], &status, WNOHANG)) {
|
151
|
-
if (WIFEXITED(status)) {
|
152
|
-
//printf("exited, status=%d for %d\n", the_server.worker_pids[i], WEXITSTATUS(status));
|
153
|
-
the_server.worker_pids[i] = 0;
|
154
|
-
exit_cnt++;
|
155
|
-
} else if (WIFSIGNALED(status)) {
|
156
|
-
//printf("*** killed by signal %d for %d\n", the_server.worker_pids[i], WTERMSIG(status));
|
157
|
-
the_server.worker_pids[i] = 0;
|
158
|
-
exit_cnt++;
|
159
|
-
}
|
160
|
-
}
|
161
|
-
}
|
162
|
-
if (the_server.worker_cnt <= exit_cnt) {
|
163
|
-
break;
|
164
|
-
}
|
165
|
-
dsleep(0.2);
|
166
|
-
}
|
167
|
-
if (exit_cnt < the_server.worker_cnt) {
|
168
|
-
printf("*-*-* Some workers did not exit.\n");
|
169
|
-
}
|
170
|
-
}
|
171
|
-
}
|
172
|
-
}
|
173
|
-
|
174
|
-
static int
|
175
|
-
configure(Err err, int port, const char *root, VALUE options) {
|
176
|
-
the_server.port = port;
|
177
|
-
the_server.pages.root = strdup(root);
|
178
|
-
the_server.thread_cnt = 0;
|
179
|
-
the_server.worker_cnt = 1;
|
180
|
-
the_server.running = 0;
|
181
|
-
the_server.fd = 0;
|
182
|
-
the_server.listen_thread = 0;
|
183
|
-
the_server.con_thread = 0;
|
184
|
-
the_server.max_push_pending = 32;
|
185
|
-
the_server.root_first = false;
|
186
|
-
if (Qnil != options) {
|
187
|
-
VALUE v;
|
188
|
-
|
189
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("thread_count"))))) {
|
190
|
-
int tc = FIX2INT(v);
|
191
|
-
|
192
|
-
if (1 <= tc || tc < 1000) {
|
193
|
-
the_server.thread_cnt = tc;
|
194
|
-
} else {
|
195
|
-
rb_raise(rb_eArgError, "thread_count must be between 1 and 1000.");
|
196
|
-
}
|
197
|
-
}
|
198
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("worker_count"))))) {
|
199
|
-
int wc = FIX2INT(v);
|
200
|
-
|
201
|
-
if (1 <= wc || wc < MAX_WORKERS) {
|
202
|
-
the_server.worker_cnt = wc;
|
203
|
-
} else {
|
204
|
-
rb_raise(rb_eArgError, "thread_count must be between 1 and %d.", MAX_WORKERS);
|
205
|
-
}
|
206
|
-
}
|
207
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("max_push_pending"))))) {
|
208
|
-
int tc = FIX2INT(v);
|
209
|
-
|
210
|
-
if (0 <= tc || tc < 1000) {
|
211
|
-
the_server.thread_cnt = tc;
|
212
|
-
} else {
|
213
|
-
rb_raise(rb_eArgError, "thread_count must be between 0 and 1000.");
|
214
|
-
}
|
215
|
-
}
|
216
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("pedantic"))))) {
|
217
|
-
the_server.pedantic = (Qtrue == v);
|
218
|
-
}
|
219
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("root_first"))))) {
|
220
|
-
the_server.root_first = (Qtrue == v);
|
221
|
-
}
|
222
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("Port"))))) {
|
223
|
-
if (rb_cInteger == rb_obj_class(v)) {
|
224
|
-
the_server.port = NUM2INT(v);
|
225
|
-
} else {
|
226
|
-
switch (rb_type(v)) {
|
227
|
-
case T_STRING:
|
228
|
-
the_server.port = atoi(StringValuePtr(v));
|
229
|
-
break;
|
230
|
-
case T_FIXNUM:
|
231
|
-
the_server.port = NUM2INT(v);
|
232
|
-
break;
|
233
|
-
default:
|
234
|
-
break;
|
235
|
-
}
|
236
|
-
}
|
237
|
-
}
|
238
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("quiet"))))) {
|
239
|
-
if (Qtrue == v) {
|
240
|
-
info_cat.on = false;
|
241
|
-
}
|
242
|
-
}
|
243
|
-
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("debug"))))) {
|
244
|
-
if (Qtrue == v) {
|
245
|
-
error_cat.on = true;
|
246
|
-
warn_cat.on = true;
|
247
|
-
info_cat.on = true;
|
248
|
-
debug_cat.on = true;
|
249
|
-
con_cat.on = true;
|
250
|
-
req_cat.on = true;
|
251
|
-
resp_cat.on = true;
|
252
|
-
eval_cat.on = true;
|
253
|
-
push_cat.on = true;
|
254
|
-
}
|
255
|
-
}
|
256
|
-
}
|
257
|
-
return ERR_OK;
|
258
|
-
}
|
259
|
-
|
260
|
-
/* Document-method: init
|
261
|
-
*
|
262
|
-
* call-seq: init(port, root, options)
|
263
|
-
*
|
264
|
-
* Configures the server that will listen on the designated _port_ and using
|
265
|
-
* the _root_ as the root of the static resources. Logging is feature based
|
266
|
-
* and not level based and the options reflect that approach.
|
267
|
-
*
|
268
|
-
* - *options* [_Hash_] server options
|
269
|
-
*
|
270
|
-
* - *:pedantic* [_true_|_false_] if true response header and status codes are checked and an exception raised if they violate the rack spec at https://github.com/rack/rack/blob/master/SPEC, https://tools.ietf.org/html/rfc3875#section-4.1.18, or https://tools.ietf.org/html/rfc7230.
|
271
|
-
*
|
272
|
-
* - *:thread_count* [_Integer_] number of ruby worker threads. Defaults to one. If zero then the _start_ function will not return but instead will proess using the thread that called _start_. Usually the default is best unless the workers are making IO calls.
|
273
|
-
*
|
274
|
-
* - *:worker_count* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
|
275
|
-
*/
|
276
|
-
static VALUE
|
277
|
-
rserver_init(int argc, VALUE *argv, VALUE self) {
|
278
|
-
struct _Err err = ERR_INIT;
|
279
|
-
int port;
|
280
|
-
const char *root;
|
281
|
-
VALUE options = Qnil;
|
282
|
-
|
283
|
-
if (argc < 2 || 3 < argc) {
|
284
|
-
rb_raise(rb_eArgError, "Wrong number of arguments to Agoo::Server.configure.");
|
285
|
-
}
|
286
|
-
port = FIX2INT(argv[0]);
|
287
|
-
rb_check_type(argv[1], T_STRING);
|
288
|
-
root = StringValuePtr(argv[1]);
|
289
|
-
if (3 <= argc) {
|
290
|
-
options = argv[2];
|
291
|
-
}
|
22
|
+
server_setup() {
|
292
23
|
memset(&the_server, 0, sizeof(struct _Server));
|
293
|
-
pages_init(
|
294
|
-
sub_init(&the_server.sub_cache);
|
295
|
-
|
296
|
-
if (ERR_OK != configure(&err, port, root, options)) {
|
297
|
-
rb_raise(rb_eArgError, "%s", err.msg);
|
298
|
-
}
|
24
|
+
pages_init();
|
299
25
|
queue_multi_init(&the_server.con_queue, 256, false, false);
|
300
|
-
queue_multi_init(&the_server.pub_queue, 256, true, false);
|
301
|
-
queue_multi_init(&the_server.eval_queue, 1024, false, true);
|
302
|
-
|
303
|
-
pthread_mutex_init(&the_server.up_lock, 0);
|
304
|
-
the_server.up_list = NULL;
|
305
|
-
|
306
|
-
the_server.inited = true;
|
307
|
-
|
308
|
-
return Qnil;
|
309
|
-
}
|
310
|
-
|
311
|
-
static void
|
312
|
-
setup_listen() {
|
313
|
-
struct sockaddr_in addr;
|
314
|
-
int optval = 1;
|
315
|
-
|
316
|
-
if (0 >= (the_server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
|
317
|
-
log_cat(&error_cat, "Server failed to open server socket. %s.", strerror(errno));
|
318
|
-
atomic_fetch_sub(&the_server.running, 1);
|
319
|
-
rb_raise(rb_eIOError, "Server failed to open server socket. %s.", strerror(errno));
|
320
|
-
}
|
321
|
-
#ifdef OSX_OS
|
322
|
-
setsockopt(the_server.fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
|
323
|
-
#endif
|
324
|
-
memset(&addr, 0, sizeof(addr));
|
325
|
-
addr.sin_family = AF_INET;
|
326
|
-
addr.sin_addr.s_addr = INADDR_ANY;
|
327
|
-
addr.sin_port = htons(the_server.port);
|
328
|
-
setsockopt(the_server.fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
329
|
-
setsockopt(the_server.fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
330
|
-
if (0 > bind(the_server.fd, (struct sockaddr*)&addr, sizeof(addr))) {
|
331
|
-
log_cat(&error_cat, "Server failed to bind server socket. %s.", strerror(errno));
|
332
|
-
atomic_fetch_sub(&the_server.running, 1);
|
333
|
-
rb_raise(rb_eIOError, "Server failed to bind server socket. %s.", strerror(errno));
|
334
|
-
}
|
335
|
-
listen(the_server.fd, 1000);
|
336
26
|
}
|
337
27
|
|
338
28
|
static void*
|
339
29
|
listen_loop(void *x) {
|
340
30
|
int optval = 1;
|
341
|
-
struct pollfd pa[
|
31
|
+
struct pollfd pa[100];
|
32
|
+
struct pollfd *p;
|
342
33
|
struct _Err err = ERR_INIT;
|
343
34
|
struct sockaddr_in client_addr;
|
344
35
|
int client_sock;
|
36
|
+
int pcnt = 0;
|
345
37
|
socklen_t alen = 0;
|
346
38
|
Con con;
|
347
39
|
int i;
|
348
40
|
uint64_t cnt = 0;
|
41
|
+
Bind b;
|
349
42
|
|
350
|
-
|
351
|
-
|
352
|
-
pa
|
353
|
-
|
43
|
+
// TBD support multiple sockets, count binds, allocate pollfd, setup
|
44
|
+
//
|
45
|
+
for (b = the_server.binds, p = pa; NULL != b; b = b->next, p++, pcnt++) {
|
46
|
+
p->fd = b->fd;
|
47
|
+
p->events = POLLIN;
|
48
|
+
p->revents = 0;
|
49
|
+
}
|
354
50
|
memset(&client_addr, 0, sizeof(client_addr));
|
355
51
|
atomic_fetch_add(&the_server.running, 1);
|
356
52
|
while (the_server.active) {
|
357
|
-
if (0 > (i = poll(pa,
|
53
|
+
if (0 > (i = poll(pa, pcnt, 100))) {
|
358
54
|
if (EAGAIN == errno) {
|
359
55
|
continue;
|
360
56
|
}
|
@@ -365,486 +61,51 @@ listen_loop(void *x) {
|
|
365
61
|
if (0 == i) { // nothing to read
|
366
62
|
continue;
|
367
63
|
}
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
64
|
+
for (b = the_server.binds, p = pa; NULL != b; b = b->next, p++) {
|
65
|
+
// TBD instead of b->port use b->id
|
66
|
+
if (0 != (p->revents & POLLIN)) {
|
67
|
+
if (0 > (client_sock = accept(p->fd, (struct sockaddr*)&client_addr, &alen))) {
|
68
|
+
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), strerror(errno));
|
69
|
+
} else if (NULL == (con = con_create(&err, client_sock, ++cnt))) {
|
70
|
+
log_cat(&error_cat, "Server with pid %d accept connection failed. %s.", getpid(), err.msg);
|
71
|
+
close(client_sock);
|
72
|
+
cnt--;
|
73
|
+
err_clear(&err);
|
74
|
+
} else {
|
377
75
|
#ifdef OSX_OS
|
378
|
-
|
76
|
+
setsockopt(client_sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
|
379
77
|
#endif
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
}
|
387
|
-
}
|
388
|
-
if (0 != (pa->revents & (POLLERR | POLLHUP | POLLNVAL))) {
|
389
|
-
if (0 != (pa->revents & (POLLHUP | POLLNVAL))) {
|
390
|
-
log_cat(&error_cat, "Agoo server with pid %d socket on port %d closed.", getpid(), the_server.port);
|
391
|
-
} else {
|
392
|
-
log_cat(&error_cat, "Agoo server with pid %d socket on port %d error.", getpid(), the_server.port);
|
393
|
-
}
|
394
|
-
the_server.active = false;
|
395
|
-
}
|
396
|
-
pa->revents = 0;
|
397
|
-
}
|
398
|
-
close(pa->fd);
|
399
|
-
atomic_fetch_sub(&the_server.running, 1);
|
400
|
-
|
401
|
-
return NULL;
|
402
|
-
}
|
403
|
-
|
404
|
-
static const char bad500[] = "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: ";
|
405
|
-
|
406
|
-
static VALUE
|
407
|
-
rescue_error(VALUE x) {
|
408
|
-
Req req = (Req)x;
|
409
|
-
volatile VALUE info = rb_errinfo();
|
410
|
-
volatile VALUE msg = rb_funcall(info, rb_intern("message"), 0);
|
411
|
-
const char *classname = rb_obj_classname(info);
|
412
|
-
const char *ms = rb_string_value_ptr(&msg);
|
413
|
-
|
414
|
-
if (NULL == req->up) {
|
415
|
-
char buf[1024];
|
416
|
-
int len = (int)(strlen(classname) + 2 + strlen(ms));
|
417
|
-
int cnt;
|
418
|
-
Text message;
|
419
|
-
|
420
|
-
if ((int)(sizeof(buf) - sizeof(bad500) + 7) <= len) {
|
421
|
-
len = sizeof(buf) - sizeof(bad500) + 7;
|
422
|
-
}
|
423
|
-
cnt = snprintf(buf, sizeof(buf), "%s%d\r\n\r\n%s: %s", bad500, len, classname, ms);
|
424
|
-
message = text_create(buf, cnt);
|
425
|
-
|
426
|
-
req->res->close = true;
|
427
|
-
res_set_message(req->res, message);
|
428
|
-
queue_wakeup(&the_server.con_queue);
|
429
|
-
} else {
|
430
|
-
// TBD should the backtrace be included in the log?
|
431
|
-
/*
|
432
|
-
volatile VALUE bt = rb_funcall(info, rb_intern("backtrace"), 0);
|
433
|
-
int blen = RARRAY_LEN(bt);
|
434
|
-
int i;
|
435
|
-
VALUE rline;
|
436
|
-
|
437
|
-
for (i = 0; i < blen; i++) {
|
438
|
-
rline = rb_ary_entry(bt, i);
|
439
|
-
}
|
440
|
-
*/
|
441
|
-
log_cat(&error_cat, "%s: %s", classname, ms);
|
442
|
-
}
|
443
|
-
return Qfalse;
|
444
|
-
}
|
445
|
-
|
446
|
-
static VALUE
|
447
|
-
handle_base_inner(void *x) {
|
448
|
-
Req req = (Req)x;
|
449
|
-
volatile VALUE rr = request_wrap(req);
|
450
|
-
volatile VALUE rres = response_new();
|
451
|
-
|
452
|
-
rb_funcall(req->handler, on_request_id, 2, rr, rres);
|
453
|
-
DATA_PTR(rr) = NULL;
|
454
|
-
|
455
|
-
res_set_message(req->res, response_text(rres));
|
456
|
-
queue_wakeup(&the_server.con_queue);
|
457
|
-
|
458
|
-
return Qfalse;
|
459
|
-
}
|
460
|
-
|
461
|
-
static void*
|
462
|
-
handle_base(void *x) {
|
463
|
-
rb_rescue2(handle_base_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
464
|
-
|
465
|
-
return NULL;
|
466
|
-
}
|
467
|
-
|
468
|
-
static int
|
469
|
-
header_cb(VALUE key, VALUE value, Text *tp) {
|
470
|
-
const char *ks = StringValuePtr(key);
|
471
|
-
int klen = (int)RSTRING_LEN(key);
|
472
|
-
const char *vs = StringValuePtr(value);
|
473
|
-
int vlen = (int)RSTRING_LEN(value);
|
474
|
-
|
475
|
-
if (the_server.pedantic) {
|
476
|
-
http_header_ok(ks, klen, vs, vlen);
|
477
|
-
}
|
478
|
-
if (0 != strcasecmp("Content-Length", ks)) {
|
479
|
-
*tp = text_append(*tp, ks, klen);
|
480
|
-
*tp = text_append(*tp, ": ", 2);
|
481
|
-
*tp = text_append(*tp, vs, vlen);
|
482
|
-
*tp = text_append(*tp, "\r\n", 2);
|
483
|
-
}
|
484
|
-
return ST_CONTINUE;
|
485
|
-
}
|
486
|
-
|
487
|
-
static VALUE
|
488
|
-
header_each_cb(VALUE kv, Text *tp) {
|
489
|
-
header_cb(rb_ary_entry(kv, 0), rb_ary_entry(kv, 1), tp);
|
490
|
-
|
491
|
-
return Qnil;
|
492
|
-
}
|
493
|
-
|
494
|
-
static VALUE
|
495
|
-
body_len_cb(VALUE v, int *sizep) {
|
496
|
-
*sizep += (int)RSTRING_LEN(v);
|
497
|
-
|
498
|
-
return Qnil;
|
499
|
-
}
|
500
|
-
|
501
|
-
static VALUE
|
502
|
-
body_append_cb(VALUE v, Text *tp) {
|
503
|
-
*tp = text_append(*tp, StringValuePtr(v), (int)RSTRING_LEN(v));
|
504
|
-
|
505
|
-
return Qnil;
|
506
|
-
}
|
507
|
-
|
508
|
-
static VALUE
|
509
|
-
handle_rack_inner(void *x) {
|
510
|
-
Req req = (Req)x;
|
511
|
-
Text t;
|
512
|
-
volatile VALUE env = request_env(req, request_wrap(req));
|
513
|
-
volatile VALUE res = rb_funcall(req->handler, call_id, 1, env);
|
514
|
-
volatile VALUE hv;
|
515
|
-
volatile VALUE bv;
|
516
|
-
int code;
|
517
|
-
const char *status_msg;
|
518
|
-
int bsize = 0;
|
519
|
-
|
520
|
-
if (req->res->con->hijacked) {
|
521
|
-
queue_wakeup(&the_server.con_queue);
|
522
|
-
return false;
|
523
|
-
}
|
524
|
-
rb_check_type(res, T_ARRAY);
|
525
|
-
if (3 != RARRAY_LEN(res)) {
|
526
|
-
rb_raise(rb_eArgError, "a rack call() response must be an array of 3 members.");
|
527
|
-
}
|
528
|
-
hv = rb_ary_entry(res, 0);
|
529
|
-
if (RUBY_T_FIXNUM == rb_type(hv)) {
|
530
|
-
code = NUM2INT(hv);
|
531
|
-
} else {
|
532
|
-
code = NUM2INT(rb_funcall(hv, to_i_id, 0));
|
533
|
-
}
|
534
|
-
status_msg = http_code_message(code);
|
535
|
-
if ('\0' == *status_msg) {
|
536
|
-
rb_raise(rb_eArgError, "invalid rack call() response status code (%d).", code);
|
537
|
-
}
|
538
|
-
hv = rb_ary_entry(res, 1);
|
539
|
-
if (!rb_respond_to(hv, each_id)) {
|
540
|
-
rb_raise(rb_eArgError, "invalid rack call() response headers does not respond to each.");
|
541
|
-
}
|
542
|
-
bv = rb_ary_entry(res, 2);
|
543
|
-
if (!rb_respond_to(bv, each_id)) {
|
544
|
-
rb_raise(rb_eArgError, "invalid rack call() response body does not respond to each.");
|
545
|
-
}
|
546
|
-
if (NULL == (t = text_allocate(1024))) {
|
547
|
-
rb_raise(rb_eArgError, "failed to allocate response.");
|
548
|
-
}
|
549
|
-
if (T_ARRAY == rb_type(bv)) {
|
550
|
-
int i;
|
551
|
-
int bcnt = (int)RARRAY_LEN(bv);
|
552
|
-
|
553
|
-
for (i = 0; i < bcnt; i++) {
|
554
|
-
bsize += (int)RSTRING_LEN(rb_ary_entry(bv, i));
|
555
|
-
}
|
556
|
-
} else {
|
557
|
-
if (HEAD == req->method) {
|
558
|
-
// Rack wraps the response in two layers, Rack::Lint and
|
559
|
-
// Rack::BodyProxy. It each is called on either with the HEAD
|
560
|
-
// method an exception is raised so the length can not be
|
561
|
-
// determined. This digs down to get the actual response so the
|
562
|
-
// length can be calculated. A very special case.
|
563
|
-
if (0 == strcmp("Rack::BodyProxy", rb_obj_classname(bv))) {
|
564
|
-
volatile VALUE body = rb_ivar_get(bv, rb_intern("@body"));
|
565
|
-
|
566
|
-
if (Qnil != body) {
|
567
|
-
body = rb_ivar_get(body, rb_intern("@body"));
|
568
|
-
}
|
569
|
-
if (Qnil != body) {
|
570
|
-
body = rb_ivar_get(body, rb_intern("@body"));
|
571
|
-
}
|
572
|
-
if (rb_respond_to(body, rb_intern("each"))) {
|
573
|
-
rb_iterate(rb_each, body, body_len_cb, (VALUE)&bsize);
|
78
|
+
fcntl(client_sock, F_SETFL, O_NONBLOCK);
|
79
|
+
setsockopt(client_sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
80
|
+
setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
81
|
+
log_cat(&con_cat, "Server with pid %d accepted connection %llu on %s [%d]",
|
82
|
+
getpid(), (unsigned long long)cnt, b->id, con->sock);
|
83
|
+
queue_push(&the_server.con_queue, (void*)con);
|
574
84
|
}
|
575
|
-
} else {
|
576
|
-
rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
|
577
|
-
}
|
578
|
-
} else {
|
579
|
-
rb_iterate(rb_each, bv, body_len_cb, (VALUE)&bsize);
|
580
|
-
}
|
581
|
-
}
|
582
|
-
switch (code) {
|
583
|
-
case 100:
|
584
|
-
case 101:
|
585
|
-
case 102:
|
586
|
-
case 204:
|
587
|
-
case 205:
|
588
|
-
case 304:
|
589
|
-
// Content-Type and Content-Length can not be present
|
590
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 %d %s\r\n", code, status_msg);
|
591
|
-
break;
|
592
|
-
default:
|
593
|
-
// Note that using simply sprintf causes an abort with travis OSX tests.
|
594
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n", code, status_msg, bsize);
|
595
|
-
break;
|
596
|
-
}
|
597
|
-
if (code < 300) {
|
598
|
-
switch (req->upgrade) {
|
599
|
-
case UP_WS:
|
600
|
-
if (CON_WS != req->res->con_kind ||
|
601
|
-
Qnil == (req->handler = rb_hash_lookup(env, push_env_key))) {
|
602
|
-
strcpy(t->text, err500);
|
603
|
-
t->len = sizeof(err500) - 1;
|
604
|
-
break;
|
605
|
-
}
|
606
|
-
req->handler_type = PUSH_HOOK;
|
607
|
-
upgraded_create(req->res->con, req->handler, request_env(req, Qnil));
|
608
|
-
t->len = snprintf(t->text, 1024, "HTTP/1.1 101 %s\r\n", status_msg);
|
609
|
-
t = ws_add_headers(req, t);
|
610
|
-
break;
|
611
|
-
case UP_SSE:
|
612
|
-
if (CON_SSE != req->res->con_kind ||
|
613
|
-
Qnil == (req->handler = rb_hash_lookup(env, push_env_key))) {
|
614
|
-
strcpy(t->text, err500);
|
615
|
-
t->len = sizeof(err500) - 1;
|
616
|
-
break;
|
617
85
|
}
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
break;
|
626
|
-
}
|
627
|
-
}
|
628
|
-
if (HEAD == req->method) {
|
629
|
-
bsize = 0;
|
630
|
-
} else {
|
631
|
-
if (T_HASH == rb_type(hv)) {
|
632
|
-
rb_hash_foreach(hv, header_cb, (VALUE)&t);
|
633
|
-
} else {
|
634
|
-
rb_iterate(rb_each, hv, header_each_cb, (VALUE)&t);
|
635
|
-
}
|
636
|
-
}
|
637
|
-
t = text_append(t, "\r\n", 2);
|
638
|
-
if (0 < bsize) {
|
639
|
-
if (T_ARRAY == rb_type(bv)) {
|
640
|
-
VALUE v;
|
641
|
-
int i;
|
642
|
-
int bcnt = (int)RARRAY_LEN(bv);
|
643
|
-
|
644
|
-
for (i = 0; i < bcnt; i++) {
|
645
|
-
v = rb_ary_entry(bv, i);
|
646
|
-
t = text_append(t, StringValuePtr(v), (int)RSTRING_LEN(v));
|
86
|
+
if (0 != (p->revents & (POLLERR | POLLHUP | POLLNVAL))) {
|
87
|
+
if (0 != (p->revents & (POLLHUP | POLLNVAL))) {
|
88
|
+
log_cat(&error_cat, "Agoo server with pid %d socket on %s closed.", getpid(), b->id);
|
89
|
+
} else {
|
90
|
+
log_cat(&error_cat, "Agoo server with pid %d socket on %s error.", getpid(), b->id);
|
91
|
+
}
|
92
|
+
the_server.active = false;
|
647
93
|
}
|
648
|
-
|
649
|
-
rb_iterate(rb_each, bv, body_append_cb, (VALUE)&t);
|
650
|
-
}
|
651
|
-
}
|
652
|
-
res_set_message(req->res, t);
|
653
|
-
queue_wakeup(&the_server.con_queue);
|
654
|
-
|
655
|
-
return Qfalse;
|
656
|
-
}
|
657
|
-
|
658
|
-
static void*
|
659
|
-
handle_rack(void *x) {
|
660
|
-
//rb_gc_disable();
|
661
|
-
rb_rescue2(handle_rack_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
662
|
-
//rb_gc_enable();
|
663
|
-
//rb_gc();
|
664
|
-
|
665
|
-
return NULL;
|
666
|
-
}
|
667
|
-
|
668
|
-
static VALUE
|
669
|
-
handle_wab_inner(void *x) {
|
670
|
-
Req req = (Req)x;
|
671
|
-
volatile VALUE rr = request_wrap(req);
|
672
|
-
volatile VALUE rres = response_new();
|
673
|
-
|
674
|
-
rb_funcall(req->handler, on_request_id, 2, rr, rres);
|
675
|
-
DATA_PTR(rr) = NULL;
|
676
|
-
|
677
|
-
res_set_message(req->res, response_text(rres));
|
678
|
-
queue_wakeup(&the_server.con_queue);
|
679
|
-
|
680
|
-
return Qfalse;
|
681
|
-
}
|
682
|
-
|
683
|
-
static void*
|
684
|
-
handle_wab(void *x) {
|
685
|
-
rb_rescue2(handle_wab_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
686
|
-
|
687
|
-
return NULL;
|
688
|
-
}
|
689
|
-
|
690
|
-
static VALUE
|
691
|
-
handle_push_inner(void *x) {
|
692
|
-
Req req = (Req)x;
|
693
|
-
|
694
|
-
switch (req->method) {
|
695
|
-
case ON_MSG:
|
696
|
-
if (req->up->on_msg) {
|
697
|
-
rb_funcall(req->handler, on_message_id, 2, req->up->wrap, rb_str_new(req->msg, req->mlen));
|
94
|
+
p->revents = 0;
|
698
95
|
}
|
699
|
-
break;
|
700
|
-
case ON_BIN:
|
701
|
-
if (req->up->on_msg) {
|
702
|
-
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
703
|
-
|
704
|
-
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
705
|
-
rb_funcall(req->handler, on_message_id, 2, req->up->wrap, rstr);
|
706
|
-
}
|
707
|
-
break;
|
708
|
-
case ON_CLOSE:
|
709
|
-
upgraded_ref(req->up);
|
710
|
-
queue_push(&the_server.pub_queue, pub_close(req->up));
|
711
|
-
rb_funcall(req->handler, on_close_id, 1, req->up->wrap);
|
712
|
-
break;
|
713
|
-
case ON_SHUTDOWN:
|
714
|
-
rb_funcall(req->handler, rb_intern("on_shutdown"), 1, req->up->wrap);
|
715
|
-
break;
|
716
|
-
case ON_ERROR:
|
717
|
-
if (req->up->on_msg) {
|
718
|
-
volatile VALUE rstr = rb_str_new(req->msg, req->mlen);
|
719
|
-
|
720
|
-
rb_enc_associate(rstr, rb_ascii8bit_encoding());
|
721
|
-
rb_funcall(req->handler, on_error_id, 2, req->up->wrap, rstr);
|
722
|
-
}
|
723
|
-
break;
|
724
|
-
case ON_EMPTY:
|
725
|
-
rb_funcall(req->handler, on_drained_id, 1, req->up->wrap);
|
726
|
-
break;
|
727
|
-
default:
|
728
|
-
break;
|
729
96
|
}
|
730
|
-
|
731
|
-
|
732
|
-
return Qfalse;
|
733
|
-
}
|
734
|
-
|
735
|
-
static void*
|
736
|
-
handle_push(void *x) {
|
737
|
-
rb_rescue2(handle_push_inner, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
|
738
|
-
return NULL;
|
739
|
-
}
|
740
|
-
|
741
|
-
static void
|
742
|
-
handle_protected(Req req, bool gvi) {
|
743
|
-
switch (req->handler_type) {
|
744
|
-
case BASE_HOOK:
|
745
|
-
if (gvi) {
|
746
|
-
rb_thread_call_with_gvl(handle_base, req);
|
747
|
-
} else {
|
748
|
-
handle_base(req);
|
749
|
-
}
|
750
|
-
break;
|
751
|
-
case RACK_HOOK:
|
752
|
-
if (gvi) {
|
753
|
-
rb_thread_call_with_gvl(handle_rack, req);
|
754
|
-
} else {
|
755
|
-
handle_rack(req);
|
756
|
-
}
|
757
|
-
break;
|
758
|
-
case WAB_HOOK:
|
759
|
-
if (gvi) {
|
760
|
-
rb_thread_call_with_gvl(handle_wab, req);
|
761
|
-
} else {
|
762
|
-
handle_wab(req);
|
763
|
-
}
|
764
|
-
break;
|
765
|
-
case PUSH_HOOK:
|
766
|
-
if (gvi) {
|
767
|
-
rb_thread_call_with_gvl(handle_push, req);
|
768
|
-
} else {
|
769
|
-
handle_push(req);
|
770
|
-
}
|
771
|
-
break;
|
772
|
-
default: {
|
773
|
-
char buf[256];
|
774
|
-
int cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n");
|
775
|
-
Text message = text_create(buf, cnt);
|
776
|
-
|
777
|
-
req->res->close = true;
|
778
|
-
res_set_message(req->res, message);
|
779
|
-
queue_wakeup(&the_server.con_queue);
|
780
|
-
break;
|
781
|
-
}
|
782
|
-
}
|
783
|
-
}
|
784
|
-
|
785
|
-
static void*
|
786
|
-
process_loop(void *ptr) {
|
787
|
-
Req req;
|
788
|
-
|
789
|
-
atomic_fetch_add(&the_server.running, 1);
|
790
|
-
while (the_server.active) {
|
791
|
-
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
792
|
-
handle_protected(req, true);
|
793
|
-
request_destroy(req);
|
794
|
-
}
|
97
|
+
for (b = the_server.binds; NULL != b; b = b->next) {
|
98
|
+
bind_close(b);
|
795
99
|
}
|
796
100
|
atomic_fetch_sub(&the_server.running, 1);
|
797
101
|
|
798
102
|
return NULL;
|
799
103
|
}
|
800
104
|
|
801
|
-
|
802
|
-
|
803
|
-
rb_thread_call_without_gvl(process_loop, NULL, RUBY_UBF_IO, NULL);
|
804
|
-
return Qnil;
|
805
|
-
}
|
806
|
-
|
807
|
-
/* Document-method: start
|
808
|
-
*
|
809
|
-
* call-seq: start()
|
810
|
-
*
|
811
|
-
* Start the server.
|
812
|
-
*/
|
813
|
-
static VALUE
|
814
|
-
server_start(VALUE self) {
|
815
|
-
VALUE *vp;
|
816
|
-
int i;
|
105
|
+
int
|
106
|
+
server_start(Err err, const char *app_name, const char *version) {
|
817
107
|
double giveup;
|
818
|
-
int pid;
|
819
|
-
|
820
|
-
*the_server.worker_pids = getpid();
|
821
|
-
setup_listen();
|
822
|
-
the_server.active = true;
|
823
108
|
|
824
|
-
for (i = 1; i < the_server.worker_cnt; i++) {
|
825
|
-
#if 0
|
826
|
-
pid = fork();
|
827
|
-
#else
|
828
|
-
{
|
829
|
-
VALUE rpid = rb_funcall(rb_cObject, rb_intern("fork"), 0);
|
830
|
-
|
831
|
-
if (Qnil == rpid) {
|
832
|
-
pid = 0;
|
833
|
-
} else {
|
834
|
-
pid = NUM2INT(rpid);
|
835
|
-
}
|
836
|
-
}
|
837
|
-
#endif
|
838
|
-
if (0 > pid) { // error, use single process
|
839
|
-
log_cat(&error_cat, "Failed to fork. %s.", strerror(errno));
|
840
|
-
break;
|
841
|
-
} else if (0 == pid) {
|
842
|
-
log_start(true);
|
843
|
-
break;
|
844
|
-
} else {
|
845
|
-
the_server.worker_pids[i] = pid;
|
846
|
-
}
|
847
|
-
}
|
848
109
|
pthread_create(&the_server.listen_thread, NULL, listen_loop, NULL);
|
849
110
|
pthread_create(&the_server.con_thread, NULL, con_loop, NULL);
|
850
111
|
|
@@ -856,214 +117,70 @@ server_start(VALUE self) {
|
|
856
117
|
dsleep(0.01);
|
857
118
|
}
|
858
119
|
if (info_cat.on) {
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
}
|
864
|
-
if (0 >= the_server.thread_cnt) {
|
865
|
-
Req req;
|
866
|
-
|
867
|
-
while (the_server.active) {
|
868
|
-
if (NULL != (req = (Req)queue_pop(&the_server.eval_queue, 0.1))) {
|
869
|
-
handle_protected(req, false);
|
870
|
-
request_destroy(req);
|
871
|
-
} else {
|
872
|
-
rb_thread_schedule();
|
873
|
-
}
|
874
|
-
|
875
|
-
}
|
876
|
-
} else {
|
877
|
-
the_server.eval_threads = (VALUE*)malloc(sizeof(VALUE) * (the_server.thread_cnt + 1));
|
878
|
-
DEBUG_ALLOC(mem_eval_threads, the_server.eval_threads);
|
879
|
-
|
880
|
-
for (i = the_server.thread_cnt, vp = the_server.eval_threads; 0 < i; i--, vp++) {
|
881
|
-
*vp = rb_thread_create(wrap_process_loop, NULL);
|
882
|
-
}
|
883
|
-
*vp = Qnil;
|
884
|
-
|
885
|
-
giveup = dtime() + 1.0;
|
886
|
-
while (dtime() < giveup) {
|
887
|
-
// The processing threads will not start until this thread
|
888
|
-
// releases ownership so do that and then see if the threads has
|
889
|
-
// been started yet.
|
890
|
-
rb_thread_schedule();
|
891
|
-
if (2 + the_server.thread_cnt <= atomic_load(&the_server.running)) {
|
892
|
-
break;
|
893
|
-
}
|
120
|
+
Bind b;
|
121
|
+
|
122
|
+
for (b = the_server.binds; NULL != b; b = b->next) {
|
123
|
+
log_cat(&info_cat, "%s %s with pid %d is listening on %s.", app_name, version, getpid(), b->id);
|
894
124
|
}
|
895
125
|
}
|
896
|
-
return
|
897
|
-
}
|
898
|
-
|
899
|
-
/* Document-method: shutdown
|
900
|
-
*
|
901
|
-
* call-seq: shutdown()
|
902
|
-
*
|
903
|
-
* Shutdown the server. Logs and queues are flushed before shutting down.
|
904
|
-
*/
|
905
|
-
static VALUE
|
906
|
-
rserver_shutdown(VALUE self) {
|
907
|
-
server_shutdown();
|
908
|
-
return Qnil;
|
126
|
+
return ERR_OK;
|
909
127
|
}
|
910
128
|
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
*
|
915
|
-
* Registers a handler for the HTTP method and path pattern specified. The
|
916
|
-
* path pattern follows glob like rules in that a single * matches a single
|
917
|
-
* token bounded by the `/` character and a double ** matches all remaining.
|
918
|
-
*/
|
919
|
-
static VALUE
|
920
|
-
handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
|
921
|
-
Hook hook;
|
922
|
-
Method meth = ALL;
|
923
|
-
const char *pat;
|
924
|
-
|
925
|
-
rb_check_type(pattern, T_STRING);
|
926
|
-
pat = StringValuePtr(pattern);
|
129
|
+
int
|
130
|
+
setup_listen(Err err) {
|
131
|
+
Bind b;
|
927
132
|
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
meth = DELETE;
|
932
|
-
} else if (get_sym == method) {
|
933
|
-
meth = GET;
|
934
|
-
} else if (head_sym == method) {
|
935
|
-
meth = HEAD;
|
936
|
-
} else if (options_sym == method) {
|
937
|
-
meth = OPTIONS;
|
938
|
-
} else if (post_sym == method) {
|
939
|
-
meth = POST;
|
940
|
-
} else if (put_sym == method) {
|
941
|
-
meth = PUT;
|
942
|
-
} else if (Qnil == method) {
|
943
|
-
meth = ALL;
|
944
|
-
} else {
|
945
|
-
rb_raise(rb_eArgError, "invalid method");
|
946
|
-
}
|
947
|
-
if (NULL == (hook = rhook_create(meth, pat, handler))) {
|
948
|
-
rb_raise(rb_eStandardError, "out of memory.");
|
949
|
-
} else {
|
950
|
-
Hook h;
|
951
|
-
Hook prev = NULL;
|
952
|
-
|
953
|
-
for (h = the_server.hooks; NULL != h; h = h->next) {
|
954
|
-
prev = h;
|
133
|
+
for (b = the_server.binds; NULL != b; b = b->next) {
|
134
|
+
if (ERR_OK != bind_listen(err, b)) {
|
135
|
+
return err->code;
|
955
136
|
}
|
956
|
-
if (NULL != prev) {
|
957
|
-
prev->next = hook;
|
958
|
-
} else {
|
959
|
-
the_server.hooks = hook;
|
960
|
-
}
|
961
|
-
rb_gc_register_address((VALUE*)&hook->handler);
|
962
|
-
}
|
963
|
-
return Qnil;
|
964
|
-
}
|
965
|
-
|
966
|
-
/* Document-method: handle_not_found
|
967
|
-
*
|
968
|
-
* call-seq: not_found_handle(handler)
|
969
|
-
*
|
970
|
-
* Registers a handler to be called when no other hook is found and no static
|
971
|
-
* file is found.
|
972
|
-
*/
|
973
|
-
static VALUE
|
974
|
-
handle_not_found(VALUE self, VALUE handler) {
|
975
|
-
if (NULL == (the_server.hook404 = rhook_create(GET, "/", handler))) {
|
976
|
-
rb_raise(rb_eStandardError, "out of memory.");
|
977
137
|
}
|
978
|
-
|
979
|
-
|
980
|
-
return Qnil;
|
981
|
-
}
|
982
|
-
|
983
|
-
/* Document-method: add_mime
|
984
|
-
*
|
985
|
-
* call-seq: add_mime(suffix, type)
|
986
|
-
*
|
987
|
-
* Adds a mime type by associating a type string with a suffix. This is used
|
988
|
-
* for static files.
|
989
|
-
*/
|
990
|
-
static VALUE
|
991
|
-
add_mime(VALUE self, VALUE suffix, VALUE type) {
|
992
|
-
mime_set(&the_server.pages, StringValuePtr(suffix), StringValuePtr(type));
|
138
|
+
the_server.active = true;
|
993
139
|
|
994
|
-
return
|
140
|
+
return ERR_OK;
|
995
141
|
}
|
996
142
|
|
997
|
-
|
998
|
-
*
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
143
|
+
void
|
144
|
+
server_shutdown(const char *app_name, void (*stop)()) {
|
145
|
+
if (the_server.inited) {
|
146
|
+
log_cat(&info_cat, "%s with pid %d shutting down.", app_name, getpid());
|
147
|
+
the_server.inited = false;
|
148
|
+
if (the_server.active) {
|
149
|
+
double giveup = dtime() + 1.0;
|
150
|
+
|
151
|
+
the_server.active = false;
|
152
|
+
pthread_detach(the_server.listen_thread);
|
153
|
+
pthread_detach(the_server.con_thread);
|
154
|
+
while (0 < atomic_load(&the_server.running)) {
|
155
|
+
dsleep(0.1);
|
156
|
+
if (giveup < dtime()) {
|
157
|
+
break;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
stop();
|
1008
161
|
|
1009
|
-
|
1010
|
-
|
162
|
+
while (NULL != the_server.hooks) {
|
163
|
+
Hook h = the_server.hooks;
|
1011
164
|
|
1012
|
-
|
1013
|
-
|
1014
|
-
int dcnt = (int)RARRAY_LEN(dirs);
|
1015
|
-
VALUE entry;
|
1016
|
-
|
1017
|
-
for (i = dcnt - 1; 0 <= i; i--) {
|
1018
|
-
entry = rb_ary_entry(dirs, i);
|
1019
|
-
if (T_STRING != rb_type(entry)) {
|
1020
|
-
entry = rb_funcall(entry, rb_intern("to_s"), 0);
|
165
|
+
the_server.hooks = h->next;
|
166
|
+
hook_destroy(h);
|
1021
167
|
}
|
1022
|
-
group_add(g, StringValuePtr(entry));
|
1023
168
|
}
|
169
|
+
while (NULL != the_server.binds) {
|
170
|
+
Bind b = the_server.binds;
|
171
|
+
|
172
|
+
the_server.binds = b->next;
|
173
|
+
bind_destroy(b);
|
174
|
+
}
|
175
|
+
queue_cleanup(&the_server.con_queue);
|
176
|
+
|
177
|
+
pages_cleanup();
|
178
|
+
http_cleanup();
|
1024
179
|
}
|
1025
|
-
return Qnil;
|
1026
180
|
}
|
1027
181
|
|
1028
|
-
/* Document-class: Agoo::Server
|
1029
|
-
*
|
1030
|
-
* An HTTP server that support the rack API as well as some other optimized
|
1031
|
-
* APIs for handling HTTP requests.
|
1032
|
-
*/
|
1033
182
|
void
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
rb_define_module_function(server_mod, "init", rserver_init, -1);
|
1038
|
-
rb_define_module_function(server_mod, "start", server_start, 0);
|
1039
|
-
rb_define_module_function(server_mod, "shutdown", rserver_shutdown, 0);
|
1040
|
-
|
1041
|
-
rb_define_module_function(server_mod, "handle", handle, 3);
|
1042
|
-
rb_define_module_function(server_mod, "handle_not_found", handle_not_found, 1);
|
1043
|
-
rb_define_module_function(server_mod, "add_mime", add_mime, 2);
|
1044
|
-
rb_define_module_function(server_mod, "path_group", path_group, 2);
|
1045
|
-
|
1046
|
-
call_id = rb_intern("call");
|
1047
|
-
each_id = rb_intern("each");
|
1048
|
-
on_close_id = rb_intern("on_close");
|
1049
|
-
on_drained_id = rb_intern("on_drained");
|
1050
|
-
on_error_id = rb_intern("on_error");
|
1051
|
-
on_message_id = rb_intern("on_message");
|
1052
|
-
on_request_id = rb_intern("on_request");
|
1053
|
-
to_i_id = rb_intern("to_i");
|
1054
|
-
|
1055
|
-
connect_sym = ID2SYM(rb_intern("CONNECT")); rb_gc_register_address(&connect_sym);
|
1056
|
-
delete_sym = ID2SYM(rb_intern("DELETE")); rb_gc_register_address(&delete_sym);
|
1057
|
-
get_sym = ID2SYM(rb_intern("GET")); rb_gc_register_address(&get_sym);
|
1058
|
-
head_sym = ID2SYM(rb_intern("HEAD")); rb_gc_register_address(&head_sym);
|
1059
|
-
options_sym = ID2SYM(rb_intern("OPTIONS")); rb_gc_register_address(&options_sym);
|
1060
|
-
post_sym = ID2SYM(rb_intern("POST")); rb_gc_register_address(&post_sym);
|
1061
|
-
put_sym = ID2SYM(rb_intern("PUT")); rb_gc_register_address(&put_sym);
|
1062
|
-
|
1063
|
-
push_env_key = rb_str_new_cstr("rack.upgrade"); rb_gc_register_address(&push_env_key);
|
1064
|
-
|
1065
|
-
rserver = Data_Wrap_Struct(rb_cObject, server_mark, NULL, strdup("dummy"));
|
1066
|
-
rb_gc_register_address(&rserver);
|
1067
|
-
|
1068
|
-
http_init();
|
183
|
+
server_bind(Bind b) {
|
184
|
+
b->next = the_server.binds;
|
185
|
+
the_server.binds = b;
|
1069
186
|
}
|