nyara 0.0.1.pre.2 → 0.0.1.pre.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/accept.c +29 -33
- data/ext/event.c +218 -22
- data/ext/extconf.rb +3 -3
- data/ext/hashes.c +2 -1
- data/ext/http_parser.c +6 -0
- data/ext/inc/epoll.h +11 -9
- data/ext/inc/kqueue.h +14 -17
- data/ext/inc/str_intern.h +2 -2
- data/ext/mime.c +2 -0
- data/ext/multipart_parser.c +2 -0
- data/ext/nyara.c +2 -0
- data/ext/nyara.h +15 -9
- data/ext/request.c +88 -266
- data/ext/request.h +43 -0
- data/ext/request_parse.c +123 -0
- data/ext/route.cc +2 -0
- data/ext/url_encoded.c +66 -6
- data/hello.rb +8 -2
- data/lib/nyara/controller.rb +29 -10
- data/lib/nyara/cookie.rb +3 -2
- data/lib/nyara/nyara.rb +41 -27
- data/lib/nyara/patch_tcp_socket.rb +22 -0
- data/lib/nyara/request.rb +1 -3
- data/lib/nyara/view.rb +1 -0
- data/nyara.gemspec +1 -1
- data/rakefile +63 -42
- data/readme.md +34 -5
- data/spec/apps/connect.rb +14 -0
- data/spec/evented_io_spec.rb +23 -0
- data/spec/ext_parse_spec.rb +26 -7
- data/spec/performance/layout_render.rb +52 -0
- data/spec/performance/parse_accept_value.rb +13 -0
- data/spec/performance/parse_param.rb +18 -0
- data/spec/performance/performance_helper.rb +25 -0
- data/spec/performance_spec.rb +33 -0
- data/spec/request_delegate_spec.rb +41 -16
- data/spec/request_spec.rb +4 -7
- data/spec/spec_helper.rb +20 -10
- metadata +15 -6
- /data/lib/nyara/{config_hash.rb → hashes/config_hash.rb} +0 -0
- /data/lib/nyara/{header_hash.rb → hashes/header_hash.rb} +0 -0
- /data/lib/nyara/{param_hash.rb → hashes/param_hash.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52583810c7afb74e920b0da7336e82441471ab82
|
4
|
+
data.tar.gz: 6fbd035f24bbd10ad1ade4ffdfce186e3a0d779f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a3abf537a5027658497d171a5a593153316e60214b81c80f4f93c1e0557478f7473136b5cec6fc0daa76eedbb7650b01a284c72098a3069fa95bffd18ec468a
|
7
|
+
data.tar.gz: 8c5031033482180d9e0c1b3a4e541274abb562de5b1d04e81855c7ca556c7fcc44415abcf06a694b001f1c6a8dbd273509490a8ba45f3c58a0cf595921d92731
|
data/ext/accept.c
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
/*
|
1
|
+
/* http accpet* value parser */
|
2
2
|
|
3
3
|
#include "nyara.h"
|
4
4
|
#include <ctype.h>
|
@@ -11,42 +11,39 @@
|
|
11
11
|
// +level+ is a waste of time:
|
12
12
|
// http://stackoverflow.com/questions/13890996/http-accept-level
|
13
13
|
|
14
|
+
#define ACCEPT_MAX 1000
|
15
|
+
|
14
16
|
// sorted data structure
|
15
|
-
typedef struct {
|
16
|
-
double* qs;
|
17
|
-
long len;
|
18
|
-
long cap;
|
19
|
-
} QArray;
|
20
17
|
|
21
|
-
static
|
22
|
-
|
23
|
-
return qa;
|
24
|
-
}
|
18
|
+
static double qarray[ACCEPT_MAX];
|
19
|
+
static long qarray_len = 0;
|
25
20
|
|
26
21
|
// return inserted pos
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if (qa->qs[i] < v) {
|
35
|
-
memmove(qa->qs + i + 1, qa->qs + i, sizeof(double) * (qa->len - i));
|
22
|
+
// the sort is "stable", which doesn't swap elements with the same q
|
23
|
+
static long qarray_insert(double v) {
|
24
|
+
long i = qarray_len;
|
25
|
+
for (long j = qarray_len - 1; j >= 0; j--) {
|
26
|
+
if (qarray[j] < v) {
|
27
|
+
i = j;
|
28
|
+
} else {
|
36
29
|
break;
|
37
30
|
}
|
38
31
|
}
|
39
|
-
|
40
|
-
|
32
|
+
memmove(qarray + i + 1, qarray + i, sizeof(double) * (qarray_len - i));
|
33
|
+
qarray[i] = v;
|
34
|
+
qarray_len++;
|
41
35
|
return i;
|
42
36
|
}
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
static VALUE trim_space(volatile VALUE str) {
|
38
|
+
// why truncate:
|
39
|
+
// 1. normal user never send such long Aceept
|
40
|
+
// 2. qarray_insert is O(n^2) in worst case, can lead to ddos vulnerability if there are more than 50000 accept entries
|
41
|
+
static VALUE trim_space_and_truncate(volatile VALUE str) {
|
49
42
|
long olen = RSTRING_LEN(str);
|
43
|
+
if (olen > ACCEPT_MAX) {
|
44
|
+
// todo log this exception
|
45
|
+
olen = ACCEPT_MAX;
|
46
|
+
}
|
50
47
|
str = rb_str_new(RSTRING_PTR(str), olen);
|
51
48
|
char* s = RSTRING_PTR(str);
|
52
49
|
long len = 0;
|
@@ -80,8 +77,8 @@ static const char* find_q(const char* s, long len) {
|
|
80
77
|
return NULL;
|
81
78
|
}
|
82
79
|
|
83
|
-
// parse a segment, and
|
84
|
-
static void parse_seg(const char* s, long len, VALUE out
|
80
|
+
// parse a segment, and store in a sorted array, also updates qarray
|
81
|
+
static void parse_seg(const char* s, long len, VALUE out) {
|
85
82
|
double qval = 1;
|
86
83
|
const char* q = find_q(s, len);
|
87
84
|
if (q) {
|
@@ -97,7 +94,7 @@ static void parse_seg(const char* s, long len, VALUE out, QArray* qa) {
|
|
97
94
|
}
|
98
95
|
len = q - s;
|
99
96
|
}
|
100
|
-
long pos = qarray_insert(
|
97
|
+
long pos = qarray_insert(qval);
|
101
98
|
rb_ary_push(out, Qnil); // just to increase cap
|
102
99
|
VALUE* out_ptr = RARRAY_PTR(out);
|
103
100
|
long out_len = RARRAY_LEN(out); // note this len is +1
|
@@ -110,21 +107,20 @@ VALUE ext_parse_accept_value(VALUE _, volatile VALUE str) {
|
|
110
107
|
return rb_ary_new();
|
111
108
|
}
|
112
109
|
|
113
|
-
str =
|
110
|
+
str = trim_space_and_truncate(str);
|
114
111
|
const char* s = RSTRING_PTR(str);
|
115
112
|
long len = RSTRING_LEN(str);
|
116
113
|
volatile VALUE out = rb_ary_new();
|
117
|
-
|
114
|
+
qarray_len = 0;
|
118
115
|
while (len > 0) {
|
119
116
|
long seg_len = find_seg(s, len);
|
120
117
|
if (seg_len == 0) {
|
121
118
|
break;
|
122
119
|
}
|
123
|
-
parse_seg(s, seg_len, out
|
120
|
+
parse_seg(s, seg_len, out);
|
124
121
|
s += seg_len + 1;
|
125
122
|
len -= seg_len + 1;
|
126
123
|
}
|
127
|
-
qarray_delete(&qa);
|
128
124
|
return out;
|
129
125
|
}
|
130
126
|
|
data/ext/event.c
CHANGED
@@ -1,62 +1,167 @@
|
|
1
|
+
/* unify API for epoll and kqueue */
|
2
|
+
|
1
3
|
#include "nyara.h"
|
4
|
+
#include "request.h"
|
2
5
|
#include <sys/fcntl.h>
|
3
6
|
#include <sys/socket.h>
|
4
7
|
#include <errno.h>
|
5
8
|
#include <stdint.h>
|
6
9
|
#include <unistd.h>
|
7
10
|
|
8
|
-
#
|
9
|
-
|
11
|
+
#ifndef rb_obj_hide
|
12
|
+
extern VALUE rb_obj_hide(VALUE obj);
|
13
|
+
extern VALUE rb_obj_reveal(VALUE obj, VALUE klass);
|
14
|
+
#endif
|
15
|
+
|
16
|
+
#define ETYPE_CAN_ACCEPT 0
|
17
|
+
#define ETYPE_HANDLE_REQUEST 1
|
10
18
|
#define ETYPE_CONNECT 2
|
11
19
|
#define MAX_E 1024
|
12
20
|
static void loop_body(int fd, int etype);
|
13
21
|
static int qfd;
|
14
22
|
|
23
|
+
#define MAX_RECEIVE_DATA 65536
|
24
|
+
static char received_data[MAX_RECEIVE_DATA];
|
25
|
+
extern http_parser_settings nyara_request_parse_settings;
|
26
|
+
|
15
27
|
#ifdef HAVE_KQUEUE
|
16
28
|
#include "inc/kqueue.h"
|
17
29
|
#elif HAVE_EPOLL
|
18
30
|
#include "inc/epoll.h"
|
19
31
|
#endif
|
20
32
|
|
21
|
-
static
|
33
|
+
static VALUE fd_request_map;
|
34
|
+
static VALUE watch_request_map;
|
35
|
+
static ID id_not_found;
|
36
|
+
static VALUE sym_term_close;
|
37
|
+
static VALUE sym_writing;
|
38
|
+
static VALUE sym_reading;
|
39
|
+
static Request* curr_request;
|
40
|
+
|
41
|
+
static void _set_nonblock(int fd) {
|
22
42
|
int flags;
|
23
43
|
|
24
44
|
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
25
|
-
|
45
|
+
rb_sys_fail("fcntl(F_GETFL)");
|
26
46
|
}
|
27
47
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
28
|
-
|
48
|
+
rb_sys_fail("fcntl(F_SETFL,O_NONBLOCK)");
|
29
49
|
}
|
30
50
|
}
|
31
51
|
|
32
|
-
static VALUE
|
33
|
-
|
34
|
-
|
52
|
+
static VALUE _fiber_func(VALUE _, VALUE args) {
|
53
|
+
VALUE instance = rb_ary_pop(args);
|
54
|
+
VALUE meth = rb_ary_pop(args);
|
55
|
+
rb_apply(instance, SYM2ID(meth), args);
|
35
56
|
return Qnil;
|
36
57
|
}
|
37
58
|
|
59
|
+
static void _handle_request(VALUE request) {
|
60
|
+
Request* p;
|
61
|
+
Data_Get_Struct(request, Request, p);
|
62
|
+
curr_request = p;
|
63
|
+
|
64
|
+
// read and parse data
|
65
|
+
// NOTE we don't let http_parser invoke ruby code, because:
|
66
|
+
// 1. so the stack is shallower
|
67
|
+
// 2. Fiber.yield can pause http_parser, then the unparsed received_data is lost
|
68
|
+
long len = read(p->fd, received_data, MAX_RECEIVE_DATA);
|
69
|
+
if (len < 0) {
|
70
|
+
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
71
|
+
// this can happen when 2 events are fetched, and first event closes the fd, then second event fails
|
72
|
+
if (p->fd) {
|
73
|
+
nyara_detach_fd(p->fd);
|
74
|
+
p->fd = 0;
|
75
|
+
}
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
} else if (len) {
|
79
|
+
// note: for http_parser, len = 0 means eof reached
|
80
|
+
// but when in a fd-becomes-writable event it can also be 0
|
81
|
+
http_parser_execute(&(p->hparser), &nyara_request_parse_settings, received_data, len);
|
82
|
+
}
|
83
|
+
|
84
|
+
if (!p->parse_state) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
// ensure action
|
89
|
+
if (p->fiber == Qnil) {
|
90
|
+
volatile RouteResult result = nyara_lookup_route(p->method, p->path, p->accept);
|
91
|
+
if (RTEST(result.controller)) {
|
92
|
+
rb_ary_push(result.args, rb_class_new_instance(1, &(p->self), result.controller));
|
93
|
+
// result.args is on stack, no need to worry gc
|
94
|
+
p->fiber = rb_fiber_new(_fiber_func, result.args);
|
95
|
+
p->scope = result.scope;
|
96
|
+
p->format = result.format;
|
97
|
+
p->response_header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
98
|
+
p->response_header_extra_lines = rb_ary_new();
|
99
|
+
} else {
|
100
|
+
rb_funcall(p->self, id_not_found, 0);
|
101
|
+
nyara_detach_fd(p->fd);
|
102
|
+
p->fd = 0;
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
// resume action
|
108
|
+
VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
|
109
|
+
if (state == Qnil) { // _fiber_func always returns Qnil
|
110
|
+
// terminated (todo log raised error ?)
|
111
|
+
nyara_request_term_close(request, false);
|
112
|
+
} else if (state == sym_term_close) {
|
113
|
+
nyara_request_term_close(request, true);
|
114
|
+
} else if (state == sym_writing) {
|
115
|
+
// do nothing
|
116
|
+
} else if (state == sym_reading) {
|
117
|
+
// do nothing
|
118
|
+
} else {
|
119
|
+
// double value: sleep
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
38
123
|
// platform independent, invoked by LOOP_E()
|
39
124
|
static void loop_body(int fd, int etype) {
|
40
125
|
switch (etype) {
|
41
|
-
case
|
126
|
+
case ETYPE_CAN_ACCEPT: {
|
42
127
|
int cfd = accept(fd, NULL, NULL);
|
43
128
|
if (cfd > 0) {
|
44
|
-
|
45
|
-
ADD_E(cfd,
|
129
|
+
_set_nonblock(cfd);
|
130
|
+
ADD_E(cfd, ETYPE_HANDLE_REQUEST);
|
46
131
|
}
|
47
132
|
break;
|
48
133
|
}
|
49
|
-
case
|
50
|
-
|
134
|
+
case ETYPE_HANDLE_REQUEST: {
|
135
|
+
VALUE key = INT2FIX(fd);
|
136
|
+
volatile VALUE request = rb_hash_aref(fd_request_map, key);
|
137
|
+
if (request == Qnil) {
|
138
|
+
request = nyara_request_new(fd);
|
139
|
+
rb_hash_aset(fd_request_map, key, request);
|
140
|
+
}
|
141
|
+
_handle_request(request);
|
51
142
|
break;
|
52
143
|
}
|
53
144
|
case ETYPE_CONNECT: {
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
145
|
+
VALUE request = rb_hash_aref(watch_request_map, INT2FIX(fd));
|
146
|
+
if (request != Qnil) {
|
147
|
+
_handle_request(request);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
void nyara_detach_fd(int fd) {
|
154
|
+
VALUE request = rb_hash_delete(fd_request_map, INT2FIX(fd));
|
155
|
+
if (request != Qnil) {
|
156
|
+
Request* p;
|
157
|
+
Data_Get_Struct(request, Request, p);
|
158
|
+
VALUE* watched = RARRAY_PTR(p->watched_fds);
|
159
|
+
long watched_len = RARRAY_LEN(p->watched_fds);
|
160
|
+
for (long i = 0; i < watched_len; i++) {
|
161
|
+
rb_hash_delete(watch_request_map, watched[i]);
|
58
162
|
}
|
59
163
|
}
|
164
|
+
close(fd);
|
60
165
|
}
|
61
166
|
|
62
167
|
static VALUE ext_init_queue(VALUE _) {
|
@@ -66,8 +171,8 @@ static VALUE ext_init_queue(VALUE _) {
|
|
66
171
|
|
67
172
|
static VALUE ext_run_queue(VALUE _, VALUE v_fd) {
|
68
173
|
int fd = FIX2INT(v_fd);
|
69
|
-
|
70
|
-
ADD_E(fd,
|
174
|
+
_set_nonblock(fd);
|
175
|
+
ADD_E(fd, ETYPE_CAN_ACCEPT);
|
71
176
|
|
72
177
|
LOOP_E();
|
73
178
|
return Qnil;
|
@@ -75,15 +180,106 @@ static VALUE ext_run_queue(VALUE _, VALUE v_fd) {
|
|
75
180
|
|
76
181
|
static VALUE ext_set_nonblock(VALUE _, VALUE v_fd) {
|
77
182
|
int fd = FIX2INT(v_fd);
|
78
|
-
|
183
|
+
_set_nonblock(fd);
|
79
184
|
return Qnil;
|
80
185
|
}
|
81
186
|
|
187
|
+
static VALUE ext_fd_watch(VALUE _, VALUE v_fd) {
|
188
|
+
int fd = NUM2INT(v_fd);
|
189
|
+
rb_hash_aset(watch_request_map, v_fd, curr_request->self);
|
190
|
+
rb_ary_push(curr_request->watched_fds, v_fd);
|
191
|
+
ADD_E(fd, ETYPE_CONNECT);
|
192
|
+
return Qnil;
|
193
|
+
}
|
194
|
+
|
195
|
+
// override TCPSocket.send
|
196
|
+
// returns sent length
|
197
|
+
static VALUE ext_fd_send(VALUE _, VALUE v_fd, VALUE v_data, VALUE v_flags) {
|
198
|
+
int flags = NUM2INT(v_flags);
|
199
|
+
int fd = NUM2INT(v_fd);
|
200
|
+
char* buf = RSTRING_PTR(v_data);
|
201
|
+
long len = RSTRING_LEN(v_data);
|
202
|
+
|
203
|
+
// similar to _send_data in request.c
|
204
|
+
while(len) {
|
205
|
+
long written = send(fd, buf, len, flags);
|
206
|
+
if (written <= 0) {
|
207
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
208
|
+
rb_fiber_yield(1, &sym_writing);
|
209
|
+
continue;
|
210
|
+
} else {
|
211
|
+
rb_sys_fail("send(2)");
|
212
|
+
break;
|
213
|
+
}
|
214
|
+
} else {
|
215
|
+
buf += written;
|
216
|
+
len -= written;
|
217
|
+
if (len) {
|
218
|
+
rb_fiber_yield(1, &sym_writing);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
return LONG2NUM(RSTRING_LEN(v_data) - len);
|
224
|
+
}
|
225
|
+
|
226
|
+
// override TCPSocket.recv
|
227
|
+
// simulate blocking recv len or eof
|
228
|
+
static VALUE ext_fd_recv(VALUE _, VALUE v_fd, VALUE v_len, VALUE v_flags) {
|
229
|
+
int flags = NUM2INT(v_flags);
|
230
|
+
int fd = NUM2INT(v_fd);
|
231
|
+
long buf_len = NUM2INT(v_len); // int shall be large enough...
|
232
|
+
|
233
|
+
volatile VALUE str = rb_tainted_str_new(0, buf_len);
|
234
|
+
volatile VALUE klass = RBASIC(str)->klass;
|
235
|
+
rb_obj_hide(str);
|
236
|
+
|
237
|
+
char* s = RSTRING_PTR(str);
|
238
|
+
while(buf_len) {
|
239
|
+
long recved = recv(fd, s, buf_len, flags);
|
240
|
+
if (recved < 0) {
|
241
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
242
|
+
rb_fiber_yield(1, &sym_reading);
|
243
|
+
continue;
|
244
|
+
} else {
|
245
|
+
rb_sys_fail("recv(2)");
|
246
|
+
break;
|
247
|
+
}
|
248
|
+
} else if (recved == 0) { // reached EOF
|
249
|
+
break;
|
250
|
+
}
|
251
|
+
s += recved;
|
252
|
+
buf_len -= recved;
|
253
|
+
}
|
254
|
+
|
255
|
+
if (RBASIC(str)->klass || RSTRING_LEN(str) != NUM2INT(v_len)) {
|
256
|
+
rb_raise(rb_eRuntimeError, "buffer string modified");
|
257
|
+
}
|
258
|
+
rb_obj_reveal(str, klass);
|
259
|
+
if (buf_len) {
|
260
|
+
rb_str_set_len(str, RSTRING_LEN(str) - buf_len);
|
261
|
+
}
|
262
|
+
rb_obj_taint(str);
|
263
|
+
|
264
|
+
return str;
|
265
|
+
}
|
266
|
+
|
82
267
|
void Init_event(VALUE ext) {
|
83
|
-
|
268
|
+
fd_request_map = rb_hash_new();
|
269
|
+
rb_gc_register_mark_object(fd_request_map);
|
270
|
+
watch_request_map = rb_hash_new();
|
271
|
+
rb_gc_register_mark_object(watch_request_map);
|
272
|
+
id_not_found = rb_intern("not_found");
|
273
|
+
sym_term_close = ID2SYM(rb_intern("term_close"));
|
274
|
+
sym_writing = ID2SYM(rb_intern("writing"));
|
275
|
+
sym_reading = ID2SYM(rb_intern("reading"));
|
276
|
+
|
84
277
|
rb_define_singleton_method(ext, "init_queue", ext_init_queue, 0);
|
85
278
|
rb_define_singleton_method(ext, "run_queue", ext_run_queue, 1);
|
86
279
|
|
87
|
-
//
|
280
|
+
// fd operations
|
88
281
|
rb_define_singleton_method(ext, "set_nonblock", ext_set_nonblock, 1);
|
282
|
+
rb_define_singleton_method(ext, "fd_watch", ext_fd_watch, 1);
|
283
|
+
rb_define_singleton_method(ext, "fd_send", ext_fd_send, 3);
|
284
|
+
rb_define_singleton_method(ext, "fd_recv", ext_fd_recv, 3);
|
89
285
|
}
|
data/ext/extconf.rb
CHANGED
@@ -12,11 +12,11 @@ end
|
|
12
12
|
def tweak_cflags
|
13
13
|
mf_conf = RbConfig::MAKEFILE_CONFIG
|
14
14
|
if mf_conf['CC'] =~ /clang/
|
15
|
-
# enable c++
|
16
|
-
mf_conf['CXXFLAGS'] << ' -stdlib=libc++ -std=c++
|
15
|
+
# enable c++0x. this can not be installed on $CPPFLAGS, wtf??
|
16
|
+
mf_conf['CXXFLAGS'] << ' -stdlib=libc++ -std=c++0x'
|
17
17
|
$CFLAGS << ' $(xflags)'
|
18
18
|
else
|
19
|
-
mf_conf['CXXFLAGS'] << ' -std=c++
|
19
|
+
mf_conf['CXXFLAGS'] << ' -std=c++0x'
|
20
20
|
$CFLAGS << ' -std=c99 -Wno-declaration-after-statement $(xflags)'
|
21
21
|
end
|
22
22
|
|
data/ext/hashes.c
CHANGED
data/ext/http_parser.c
CHANGED
data/ext/inc/epoll.h
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
/* epoll event adapter */
|
2
|
+
|
1
3
|
#pragma once
|
2
4
|
|
3
5
|
#include <sys/epoll.h>
|
@@ -6,7 +8,7 @@ static struct epoll_event qevents[MAX_E];
|
|
6
8
|
|
7
9
|
static void ADD_E(int fd, uint64_t etype) {
|
8
10
|
struct epoll_event e;
|
9
|
-
e.events = EPOLLIN;
|
11
|
+
e.events = EPOLLIN | EPOLLOUT;
|
10
12
|
e.data.u64 = (etype << 32) | (uint64_t)fd;
|
11
13
|
|
12
14
|
// todo timeout
|
@@ -18,9 +20,11 @@ static void ADD_E(int fd, uint64_t etype) {
|
|
18
20
|
# endif
|
19
21
|
}
|
20
22
|
|
23
|
+
// either epoll or kqueue removes the event watch from queue when fd closed
|
24
|
+
// seems this is not required in epoll?
|
21
25
|
static void DEL_E(int fd) {
|
22
26
|
struct epoll_event e;
|
23
|
-
e.events = EPOLLIN;
|
27
|
+
e.events = EPOLLIN | EPOLLOUT;
|
24
28
|
e.data.ptr = NULL;
|
25
29
|
|
26
30
|
# ifdef NDEBUG
|
@@ -45,13 +49,11 @@ static void LOOP_E() {
|
|
45
49
|
int sz = epoll_wait(qfd, qevents, MAX_E, 100);
|
46
50
|
|
47
51
|
for (int i = 0; i < sz; i++) {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
break;
|
54
|
-
}
|
52
|
+
if (qevents[i].events & (EPOLLIN | EPOLLOUT)) {
|
53
|
+
int fd = (int)(qevents[i].data.u64 & 0xFFFFFFFF);
|
54
|
+
int etype = (int)(qevents[i].data.u64 >> 32);
|
55
|
+
loop_body(fd, etype);
|
56
|
+
break;
|
55
57
|
}
|
56
58
|
}
|
57
59
|
// execute other thread / interrupts
|
data/ext/inc/kqueue.h
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
/* kqueue event adapter */
|
2
|
+
|
1
3
|
#pragma once
|
2
4
|
|
3
5
|
#include <sys/types.h>
|
@@ -8,15 +10,13 @@
|
|
8
10
|
# include <sys/queue.h>
|
9
11
|
#endif
|
10
12
|
|
11
|
-
static void loop_body(int fd, int etype);
|
12
|
-
|
13
13
|
#define MAX_E 1024
|
14
14
|
static int qfd;
|
15
15
|
static struct kevent qevents[MAX_E];
|
16
16
|
|
17
17
|
static void ADD_E(int fd, uint64_t etype) {
|
18
18
|
struct kevent e;
|
19
|
-
EV_SET(&e, fd, EVFILT_READ, EV_ADD, 0, 0, (void*)etype);
|
19
|
+
EV_SET(&e, fd, EVFILT_READ | EVFILT_WRITE, EV_ADD, 0, 0, (void*)etype);
|
20
20
|
// todo timeout
|
21
21
|
# ifdef NDEBUG
|
22
22
|
kevent(qfd, &e, 1, NULL, 0, NULL);
|
@@ -26,9 +26,9 @@ static void ADD_E(int fd, uint64_t etype) {
|
|
26
26
|
# endif
|
27
27
|
}
|
28
28
|
|
29
|
-
static void DEL_E(int fd) {
|
29
|
+
static void DEL_E(int fd, int filter) {
|
30
30
|
struct kevent e;
|
31
|
-
EV_SET(&e, fd,
|
31
|
+
EV_SET(&e, fd, filter, EV_DELETE, 0, 0, NULL);
|
32
32
|
# ifdef NDEBUG
|
33
33
|
kevent(qfd, &e, 1, NULL, 0, NULL);
|
34
34
|
# else
|
@@ -57,18 +57,15 @@ static void LOOP_E() {
|
|
57
57
|
int sz = kevent(qfd, NULL, 0, qevents, MAX_E, &ts);
|
58
58
|
|
59
59
|
for (int i = 0; i < sz; i++) {
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
break;
|
71
|
-
}
|
60
|
+
int fd = (int)qevents[i].ident;
|
61
|
+
if (qevents[i].flags & EV_EOF) {
|
62
|
+
// EV_EOF is set if the read side of the socket is shutdown
|
63
|
+
// the event can keep flipping back to consume cpu if we don't remove it
|
64
|
+
DEL_E(fd, qevents[i].filter);
|
65
|
+
}
|
66
|
+
if (qevents[i].filter & (EVFILT_READ | EVFILT_WRITE)) {
|
67
|
+
loop_body(fd, (int)qevents[i].udata);
|
68
|
+
break;
|
72
69
|
}
|
73
70
|
}
|
74
71
|
// execute other thread / interrupts
|
data/ext/inc/str_intern.h
CHANGED
data/ext/mime.c
CHANGED
data/ext/multipart_parser.c
CHANGED
data/ext/nyara.c
CHANGED
data/ext/nyara.h
CHANGED
@@ -9,35 +9,41 @@
|
|
9
9
|
#endif
|
10
10
|
|
11
11
|
|
12
|
-
/*
|
12
|
+
/* event.c */
|
13
13
|
void Init_event(VALUE ext);
|
14
|
+
void nyara_detach_fd(int fd);
|
14
15
|
|
15
16
|
|
16
|
-
/*
|
17
|
+
/* request_parse.c */
|
18
|
+
void Init_request_parse(VALUE nyara);
|
19
|
+
|
20
|
+
|
21
|
+
/* request.c */
|
17
22
|
void Init_request(VALUE nyara, VALUE ext);
|
18
|
-
|
23
|
+
VALUE nyara_request_new(int fd);
|
24
|
+
void nyara_request_term_close(VALUE request, bool write_last_chunk);
|
19
25
|
|
20
26
|
|
21
|
-
/*
|
27
|
+
/* url_encoded.c */
|
22
28
|
void Init_url_encoded(VALUE ext);
|
23
29
|
long nyara_parse_path(VALUE path, const char*s, long len);
|
24
30
|
void nyara_parse_param(VALUE output, const char* s, long len);
|
25
31
|
|
26
32
|
|
27
|
-
/*
|
33
|
+
/* accept.c */
|
28
34
|
void Init_accept(VALUE ext);
|
29
35
|
VALUE ext_parse_accept_value(VALUE _, VALUE str);
|
30
36
|
|
31
37
|
|
32
|
-
/*
|
38
|
+
/* mime.c */
|
33
39
|
void Init_mime(VALUE ext);
|
34
40
|
VALUE ext_mime_match(VALUE _, VALUE request_accept, VALUE accept_mimes);
|
35
41
|
|
36
42
|
|
37
|
-
/*
|
43
|
+
/* hashes.c */
|
38
44
|
void Init_hashes(VALUE nyara);
|
39
45
|
|
40
|
-
// ab-cd => Ab-Cd
|
46
|
+
// "ab-cd" => "Ab-Cd"
|
41
47
|
// note str must be string created by nyara code
|
42
48
|
void nyara_headerlize(VALUE str);
|
43
49
|
int nyara_rb_hash_has_key(VALUE hash, VALUE key);
|
@@ -47,7 +53,7 @@ extern VALUE nyara_header_hash_class;
|
|
47
53
|
extern VALUE nyara_config_hash_class;
|
48
54
|
|
49
55
|
|
50
|
-
/*
|
56
|
+
/* route.c */
|
51
57
|
typedef struct {
|
52
58
|
VALUE controller;
|
53
59
|
VALUE args;
|