nyara 0.0.1.pre.2 → 0.0.1.pre.3
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.
- 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
data/ext/request.c
CHANGED
@@ -1,187 +1,17 @@
|
|
1
|
+
/* request parsing and request object */
|
2
|
+
|
1
3
|
#include "nyara.h"
|
2
4
|
#include <ruby/encoding.h>
|
3
|
-
#include
|
4
|
-
|
5
|
-
#ifndef write
|
6
|
-
#include <unistd.h>
|
7
|
-
#endif
|
8
|
-
|
9
|
-
typedef struct {
|
10
|
-
http_parser hparser;
|
11
|
-
multipart_parser* mparser;
|
12
|
-
enum http_method method;
|
13
|
-
VALUE header;
|
14
|
-
VALUE accept; // mime array sorted with q
|
15
|
-
VALUE format; // string ext without dot
|
16
|
-
VALUE fiber;
|
17
|
-
VALUE scope; // mapped prefix
|
18
|
-
VALUE path_with_query;
|
19
|
-
VALUE path;
|
20
|
-
VALUE query;
|
21
|
-
VALUE last_field;
|
22
|
-
VALUE last_value;
|
23
|
-
VALUE self;
|
24
|
-
int fd;
|
25
|
-
|
26
|
-
// response
|
27
|
-
int status;
|
28
|
-
VALUE response_content_type;
|
29
|
-
VALUE response_header;
|
30
|
-
VALUE response_header_extra_lines;
|
31
|
-
} Request;
|
32
|
-
|
33
|
-
// typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
34
|
-
// typedef int (*http_cb) (http_parser*);
|
35
|
-
|
36
|
-
static ID id_not_found;
|
5
|
+
#include "request.h"
|
6
|
+
|
37
7
|
static VALUE str_html;
|
38
8
|
static rb_encoding* u8_encoding;
|
39
9
|
static VALUE request_class;
|
40
|
-
static VALUE
|
41
|
-
static VALUE str_accept;
|
42
|
-
static VALUE nyara_http_methods;
|
43
|
-
|
44
|
-
static VALUE fd_request_map;
|
45
|
-
#define MAX_RECEIVE_DATA 65536
|
46
|
-
static char received_data[MAX_RECEIVE_DATA];
|
47
|
-
|
48
|
-
static VALUE _fiber_func(VALUE _, VALUE args) {
|
49
|
-
VALUE instance = rb_ary_pop(args);
|
50
|
-
VALUE meth = rb_ary_pop(args);
|
51
|
-
rb_apply(instance, SYM2ID(meth), args);
|
52
|
-
return Qnil;
|
53
|
-
}
|
54
|
-
|
55
|
-
static void _upcase_method(VALUE str) {
|
56
|
-
char* s = RSTRING_PTR(str);
|
57
|
-
long len = RSTRING_LEN(str);
|
58
|
-
for (long i = 0; i < len; i++) {
|
59
|
-
if (s[i] >= 'a' && s[i] <= 'z') {
|
60
|
-
s[i] = 'A' + (s[i] - 'a');
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
static inline void _close_fd(int fd) {
|
66
|
-
rb_hash_delete(fd_request_map, INT2FIX(fd));
|
67
|
-
close(fd);
|
68
|
-
}
|
69
|
-
|
70
|
-
static int on_url(http_parser* parser, const char* s, size_t len) {
|
71
|
-
Request* p = (Request*)parser;
|
72
|
-
p->method = parser->method;
|
73
|
-
|
74
|
-
if (p->path_with_query == Qnil) {
|
75
|
-
p->path_with_query = rb_str_new(s, len);
|
76
|
-
} else {
|
77
|
-
rb_str_cat(p->path_with_query, s, len);
|
78
|
-
}
|
79
|
-
return 0;
|
80
|
-
}
|
81
|
-
|
82
|
-
static int on_message_complete(http_parser* parser) {
|
83
|
-
Request* p = (Request*)parser;
|
84
|
-
if (p->fiber == Qnil) {
|
85
|
-
return 1;
|
86
|
-
} else {
|
87
|
-
VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
|
88
|
-
if (state == Qnil) { // terminated (todo check raise error)
|
89
|
-
if (p->status == 200) {
|
90
|
-
write(p->fd, "0\r\n\r\n", 5);
|
91
|
-
}
|
92
|
-
_close_fd(p->fd);
|
93
|
-
p->fd = 0;
|
94
|
-
} else if (SYM2ID(state) == rb_intern("term_close")) {
|
95
|
-
write(p->fd, "0\r\n\r\n", 5);
|
96
|
-
_close_fd(p->fd);
|
97
|
-
p->fd = 0;
|
98
|
-
}
|
99
|
-
return 0;
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
static int on_header_field(http_parser* parser, const char* s, size_t len) {
|
104
|
-
Request* p = (Request*)parser;
|
105
|
-
if (p->last_field == Qnil) {
|
106
|
-
p->last_field = rb_str_new(s, len);
|
107
|
-
p->last_value = Qnil;
|
108
|
-
} else {
|
109
|
-
rb_str_cat(p->last_field, s, len);
|
110
|
-
}
|
111
|
-
return 0;
|
112
|
-
}
|
113
|
-
|
114
|
-
static int on_header_value(http_parser* parser, const char* s, size_t len) {
|
115
|
-
Request* p = (Request*)parser;
|
116
|
-
if (p->last_field == Qnil) {
|
117
|
-
if (p->last_value == Qnil) {
|
118
|
-
rb_bug("on_header_value called when neither last_field nor last_value exist");
|
119
|
-
return 1;
|
120
|
-
}
|
121
|
-
rb_str_cat(p->last_value, s, len);
|
122
|
-
} else {
|
123
|
-
nyara_headerlize(p->last_field);
|
124
|
-
p->last_value = rb_str_new(s, len);
|
125
|
-
rb_hash_aset(p->header, p->last_field, p->last_value);
|
126
|
-
p->last_field = Qnil;
|
127
|
-
}
|
128
|
-
return 0;
|
129
|
-
}
|
130
|
-
|
131
|
-
// may override POST by _method in query
|
132
|
-
static void _parse_path_and_query(Request* p) {
|
133
|
-
char* s = RSTRING_PTR(p->path_with_query);
|
134
|
-
long len = RSTRING_LEN(p->path_with_query);
|
135
|
-
long query_i = nyara_parse_path(p->path, s, len);
|
136
|
-
if (query_i < len) {
|
137
|
-
nyara_parse_param(p->query, s + query_i, len - query_i);
|
138
|
-
|
139
|
-
// do method override with _method=xxx in query
|
140
|
-
if (p->method == HTTP_POST) {
|
141
|
-
VALUE meth = rb_hash_aref(p->query, method_override_key);
|
142
|
-
if (TYPE(meth) == T_STRING) {
|
143
|
-
_upcase_method(meth);
|
144
|
-
VALUE meth_num = rb_hash_aref(nyara_http_methods, meth);
|
145
|
-
if (meth_num != Qnil) {
|
146
|
-
p->method = FIX2INT(meth_num);
|
147
|
-
}
|
148
|
-
}
|
149
|
-
}
|
150
|
-
}
|
151
|
-
}
|
10
|
+
static VALUE sym_writing;
|
152
11
|
|
153
|
-
|
154
|
-
Request* p
|
155
|
-
|
156
|
-
p->last_value = Qnil;
|
157
|
-
|
158
|
-
_parse_path_and_query(p);
|
159
|
-
p->accept = ext_parse_accept_value(Qnil, rb_hash_aref(p->header, str_accept));
|
160
|
-
volatile RouteResult result = nyara_lookup_route(p->method, p->path, p->accept);
|
161
|
-
if (RTEST(result.controller)) {
|
162
|
-
rb_ary_push(result.args, rb_class_new_instance(1, &(p->self), result.controller));
|
163
|
-
// result.args is on stack, no need to worry gc
|
164
|
-
p->fiber = rb_fiber_new(_fiber_func, result.args);
|
165
|
-
p->scope = result.scope;
|
166
|
-
p->format = result.format;
|
167
|
-
p->response_header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
168
|
-
p->response_header_extra_lines = rb_ary_new();
|
169
|
-
return 0;
|
170
|
-
}
|
171
|
-
rb_funcall(p->self, id_not_found, 0);
|
172
|
-
return 1;
|
173
|
-
}
|
174
|
-
|
175
|
-
static http_parser_settings request_settings = {
|
176
|
-
.on_message_begin = NULL,
|
177
|
-
.on_url = on_url,
|
178
|
-
.on_status_complete = NULL,
|
179
|
-
.on_header_field = on_header_field,
|
180
|
-
.on_header_value = on_header_value,
|
181
|
-
.on_headers_complete = on_headers_complete,
|
182
|
-
.on_body = NULL, // data_cb
|
183
|
-
.on_message_complete = on_message_complete
|
184
|
-
};
|
12
|
+
#define P \
|
13
|
+
Request* p;\
|
14
|
+
Data_Get_Struct(self, Request, p);
|
185
15
|
|
186
16
|
static void request_mark(void* pp) {
|
187
17
|
Request* p = pp;
|
@@ -199,6 +29,7 @@ static void request_mark(void* pp) {
|
|
199
29
|
rb_gc_mark_maybe(p->response_content_type);
|
200
30
|
rb_gc_mark_maybe(p->response_header);
|
201
31
|
rb_gc_mark_maybe(p->response_header_extra_lines);
|
32
|
+
rb_gc_mark_maybe(p->watched_fds);
|
202
33
|
}
|
203
34
|
}
|
204
35
|
|
@@ -206,19 +37,25 @@ static void request_free(void* pp) {
|
|
206
37
|
Request* p = pp;
|
207
38
|
if (p) {
|
208
39
|
if (p->fd) {
|
209
|
-
|
40
|
+
nyara_detach_fd(p->fd);
|
210
41
|
}
|
211
42
|
xfree(p);
|
212
43
|
}
|
213
44
|
}
|
214
45
|
|
215
|
-
static Request*
|
46
|
+
static Request* _request_alloc() {
|
216
47
|
Request* p = ALLOC(Request);
|
217
48
|
http_parser_init(&(p->hparser), HTTP_REQUEST);
|
49
|
+
p->mparser = NULL;
|
50
|
+
|
51
|
+
p->method = HTTP_GET;
|
52
|
+
p->fd = 0;
|
53
|
+
p->parse_state = 0;
|
54
|
+
p->status = 200;
|
55
|
+
|
218
56
|
volatile VALUE header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
219
57
|
volatile VALUE path = rb_enc_str_new("", 0, u8_encoding);
|
220
58
|
volatile VALUE query = rb_class_new_instance(0, NULL, nyara_param_hash_class);
|
221
|
-
p->mparser = NULL;
|
222
59
|
p->header = header;
|
223
60
|
p->accept = Qnil;
|
224
61
|
p->format = Qnil;
|
@@ -229,66 +66,36 @@ static Request* request_alloc() {
|
|
229
66
|
p->query = query;
|
230
67
|
p->last_field = Qnil;
|
231
68
|
p->last_value = Qnil;
|
232
|
-
p->fd = 0;
|
233
|
-
p->status = 200;
|
234
69
|
p->response_content_type = Qnil;
|
235
70
|
p->response_header = Qnil;
|
236
71
|
p->response_header_extra_lines = Qnil;
|
72
|
+
|
73
|
+
volatile VALUE watched_fds = rb_ary_new();
|
74
|
+
p->watched_fds = watched_fds;
|
75
|
+
|
237
76
|
p->self = Data_Wrap_Struct(request_class, request_mark, request_free, p);
|
238
77
|
return p;
|
239
78
|
}
|
240
79
|
|
241
|
-
|
242
|
-
|
80
|
+
VALUE nyara_request_new(int fd) {
|
81
|
+
Request* p = _request_alloc();
|
82
|
+
p->fd = fd;
|
83
|
+
return p->self;
|
243
84
|
}
|
244
85
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
Request* p = NULL;
|
254
|
-
bool first_time = false;
|
255
|
-
|
256
|
-
{
|
257
|
-
VALUE v_fd = INT2FIX(fd);
|
258
|
-
VALUE request = rb_hash_aref(fd_request_map, v_fd);
|
259
|
-
if (request == Qnil) {
|
260
|
-
p = request_alloc();
|
261
|
-
p->fd = fd;
|
262
|
-
rb_hash_aset(fd_request_map, v_fd, p->self);
|
263
|
-
first_time = true;
|
264
|
-
} else {
|
265
|
-
Data_Get_Struct(request, Request, p);
|
266
|
-
}
|
267
|
-
}
|
268
|
-
|
269
|
-
long len = read(fd, received_data, MAX_RECEIVE_DATA);
|
270
|
-
if (len < 0) {
|
271
|
-
if (errno != EAGAIN) {
|
272
|
-
// todo log the bug
|
273
|
-
if (p->fd) {
|
274
|
-
_close_fd(p->fd);
|
275
|
-
p->fd = 0;
|
276
|
-
}
|
277
|
-
}
|
278
|
-
} else {
|
279
|
-
if (first_time && !len) {
|
280
|
-
// todo log this exception
|
281
|
-
return;
|
282
|
-
}
|
283
|
-
// note: when len == 0, means eof reached, that also informs http_parser the eof
|
284
|
-
http_parser_execute(&(p->hparser), &request_settings, received_data, len);
|
86
|
+
void nyara_request_term_close(VALUE self, bool write_last_chunk) {
|
87
|
+
P;
|
88
|
+
if (write_last_chunk || p->status == 200) {
|
89
|
+
// usually this succeeds, while not, it doesn't matter cause we are closing it
|
90
|
+
# pragma GCC diagnostic push
|
91
|
+
# pragma GCC diagnostic ignored "-Wunused-result"
|
92
|
+
write(p->fd, "0\r\n\r\n", 5);
|
93
|
+
# pragma GCC diagnostic pop
|
285
94
|
}
|
95
|
+
nyara_detach_fd(p->fd);
|
96
|
+
p->fd = 0;
|
286
97
|
}
|
287
98
|
|
288
|
-
#define P \
|
289
|
-
Request* p;\
|
290
|
-
Data_Get_Struct(self, Request, p);
|
291
|
-
|
292
99
|
static VALUE request_http_method(VALUE self) {
|
293
100
|
P;
|
294
101
|
return rb_str_new2(http_method_str(p->method));
|
@@ -361,50 +168,72 @@ static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
|
|
361
168
|
return n;
|
362
169
|
}
|
363
170
|
|
364
|
-
|
365
|
-
|
366
|
-
char* buf = RSTRING_PTR(data);
|
367
|
-
long len = RSTRING_LEN(data);
|
368
|
-
|
171
|
+
// return true if success
|
172
|
+
static bool _send_data(int fd, const char* buf, long len) {
|
369
173
|
while(len) {
|
370
|
-
long written = write(
|
371
|
-
if (written
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
174
|
+
long written = write(fd, buf, len);
|
175
|
+
if (written <= 0) {
|
176
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
177
|
+
rb_fiber_yield(1, &sym_writing);
|
178
|
+
} else {
|
179
|
+
return false;
|
180
|
+
}
|
181
|
+
} else {
|
182
|
+
buf += written;
|
183
|
+
len -= written;
|
184
|
+
if (len) {
|
185
|
+
rb_fiber_yield(1, &sym_writing);
|
376
186
|
}
|
377
|
-
return Qnil;
|
378
187
|
}
|
379
|
-
buf += written;
|
380
|
-
len -= written;
|
381
188
|
}
|
189
|
+
return true;
|
190
|
+
}
|
191
|
+
|
192
|
+
static VALUE ext_request_send_data(VALUE _, VALUE self, VALUE data) {
|
193
|
+
P;
|
194
|
+
char* buf = RSTRING_PTR(data);
|
195
|
+
long len = RSTRING_LEN(data);
|
196
|
+
_send_data(p->fd, buf, len);
|
382
197
|
return Qnil;
|
383
198
|
}
|
384
199
|
|
385
|
-
static VALUE
|
200
|
+
static VALUE ext_request_send_chunk(VALUE _, VALUE self, VALUE str) {
|
386
201
|
long len = RSTRING_LEN(str);
|
387
202
|
if (!len) {
|
388
203
|
return Qnil;
|
389
204
|
}
|
390
|
-
// todo len overflow?
|
391
205
|
P;
|
392
|
-
|
393
|
-
|
394
|
-
|
206
|
+
|
207
|
+
char pre_buf[20]; // enough space to hold a long + 2 chars
|
208
|
+
long pre_len = sprintf(pre_buf, "%lx\r\n", len);
|
209
|
+
if (pre_len <= 0) {
|
210
|
+
rb_raise(rb_eRuntimeError, "fail to format chunk length for len: %ld", len);
|
211
|
+
}
|
212
|
+
bool success = \
|
213
|
+
_send_data(p->fd, pre_buf, pre_len) &&
|
214
|
+
_send_data(p->fd, RSTRING_PTR(str), len) &&
|
215
|
+
_send_data(p->fd, "\r\n", 2);
|
216
|
+
|
217
|
+
if (!success) {
|
218
|
+
rb_sys_fail("write(2)");
|
395
219
|
}
|
220
|
+
|
396
221
|
return Qnil;
|
397
222
|
}
|
398
223
|
|
399
224
|
// for test: find or create a request with a fd
|
400
|
-
static VALUE
|
401
|
-
|
402
|
-
|
403
|
-
|
225
|
+
static VALUE ext_request_new(VALUE _) {
|
226
|
+
return _request_alloc()->self;
|
227
|
+
}
|
228
|
+
|
229
|
+
static VALUE ext_request_set_fd(VALUE _, VALUE self, VALUE vfd) {
|
230
|
+
P;
|
231
|
+
p->fd = NUM2INT(vfd);
|
232
|
+
return Qnil;
|
404
233
|
}
|
405
234
|
|
406
235
|
// set internal attrs in the request object
|
407
|
-
static VALUE
|
236
|
+
static VALUE ext_request_set_attrs(VALUE _, VALUE self, VALUE attrs) {
|
408
237
|
# define ATTR(key) rb_hash_delete(attrs, ID2SYM(rb_intern(key)))
|
409
238
|
# define HEADER_HASH_NEW rb_class_new_instance(0, NULL, nyara_header_hash_class)
|
410
239
|
P;
|
@@ -437,22 +266,14 @@ static VALUE ext_set_request_attrs(VALUE _, VALUE self, VALUE attrs) {
|
|
437
266
|
}
|
438
267
|
|
439
268
|
void Init_request(VALUE nyara, VALUE ext) {
|
440
|
-
id_not_found = rb_intern("not_found");
|
441
269
|
str_html = rb_str_new2("html");
|
442
270
|
OBJ_FREEZE(str_html);
|
443
271
|
rb_gc_register_mark_object(str_html);
|
444
272
|
u8_encoding = rb_utf8_encoding();
|
445
|
-
|
446
|
-
rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
|
447
|
-
nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
|
448
|
-
fd_request_map = rb_hash_new();
|
449
|
-
rb_gc_register_mark_object(fd_request_map);
|
450
|
-
str_accept = rb_str_new2("Accept");
|
451
|
-
rb_gc_register_mark_object(str_accept);
|
273
|
+
sym_writing = ID2SYM(rb_intern("writing"));
|
452
274
|
|
453
275
|
// request
|
454
276
|
request_class = rb_define_class_under(nyara, "Request", rb_cObject);
|
455
|
-
rb_define_alloc_func(request_class, request_alloc_func);
|
456
277
|
rb_define_method(request_class, "http_method", request_http_method, 0);
|
457
278
|
rb_define_method(request_class, "header", request_header, 0);
|
458
279
|
rb_define_method(request_class, "scope", request_scope, 0);
|
@@ -470,9 +291,10 @@ void Init_request(VALUE nyara, VALUE ext) {
|
|
470
291
|
|
471
292
|
// hide internal methods in ext
|
472
293
|
rb_define_singleton_method(ext, "request_set_status", ext_request_set_status, 2);
|
473
|
-
rb_define_singleton_method(ext, "
|
474
|
-
rb_define_singleton_method(ext, "
|
294
|
+
rb_define_singleton_method(ext, "request_send_data", ext_request_send_data, 2);
|
295
|
+
rb_define_singleton_method(ext, "request_send_chunk", ext_request_send_chunk, 2);
|
475
296
|
// for test
|
476
|
-
rb_define_singleton_method(ext, "
|
477
|
-
rb_define_singleton_method(ext, "
|
297
|
+
rb_define_singleton_method(ext, "request_new", ext_request_new, 0);
|
298
|
+
rb_define_singleton_method(ext, "request_set_fd", ext_request_set_fd, 2);
|
299
|
+
rb_define_singleton_method(ext, "request_set_attrs", ext_request_set_attrs, 2);
|
478
300
|
}
|
data/ext/request.h
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "nyara.h"
|
4
|
+
#include <multipart_parser.h>
|
5
|
+
#include <errno.h>
|
6
|
+
#ifndef write
|
7
|
+
#include <unistd.h>
|
8
|
+
#endif
|
9
|
+
|
10
|
+
enum ParseState {
|
11
|
+
PS_INIT, PS_HEADERS_COMPLETE, PS_MESSAGE_COMPLETE, PS_ERROR
|
12
|
+
};
|
13
|
+
|
14
|
+
typedef struct {
|
15
|
+
http_parser hparser;
|
16
|
+
multipart_parser* mparser;
|
17
|
+
|
18
|
+
enum http_method method;
|
19
|
+
int fd;
|
20
|
+
enum ParseState parse_state;
|
21
|
+
int status; // response status
|
22
|
+
|
23
|
+
VALUE self;
|
24
|
+
|
25
|
+
// request
|
26
|
+
VALUE header;
|
27
|
+
VALUE accept; // mime array sorted with q
|
28
|
+
VALUE format; // string ext without dot
|
29
|
+
VALUE fiber;
|
30
|
+
VALUE scope; // mapped prefix
|
31
|
+
VALUE path_with_query;
|
32
|
+
VALUE path;
|
33
|
+
VALUE query;
|
34
|
+
VALUE last_field;
|
35
|
+
VALUE last_value;
|
36
|
+
|
37
|
+
// response
|
38
|
+
VALUE response_content_type;
|
39
|
+
VALUE response_header;
|
40
|
+
VALUE response_header_extra_lines;
|
41
|
+
|
42
|
+
VALUE watched_fds;
|
43
|
+
} Request;
|
data/ext/request_parse.c
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
/* request parse callbacks */
|
2
|
+
|
3
|
+
#include "nyara.h"
|
4
|
+
#include "request.h"
|
5
|
+
|
6
|
+
static VALUE str_accept;
|
7
|
+
static VALUE method_override_key;
|
8
|
+
static VALUE nyara_http_methods;
|
9
|
+
|
10
|
+
static int on_url(http_parser* parser, const char* s, size_t len) {
|
11
|
+
Request* p = (Request*)parser;
|
12
|
+
p->method = parser->method;
|
13
|
+
|
14
|
+
if (p->path_with_query == Qnil) {
|
15
|
+
p->path_with_query = rb_str_new(s, len);
|
16
|
+
} else {
|
17
|
+
rb_str_cat(p->path_with_query, s, len);
|
18
|
+
}
|
19
|
+
return 0;
|
20
|
+
}
|
21
|
+
|
22
|
+
static int on_header_field(http_parser* parser, const char* s, size_t len) {
|
23
|
+
Request* p = (Request*)parser;
|
24
|
+
if (p->last_field == Qnil) {
|
25
|
+
p->last_field = rb_str_new(s, len);
|
26
|
+
p->last_value = Qnil;
|
27
|
+
} else {
|
28
|
+
rb_str_cat(p->last_field, s, len);
|
29
|
+
}
|
30
|
+
return 0;
|
31
|
+
}
|
32
|
+
|
33
|
+
static int on_header_value(http_parser* parser, const char* s, size_t len) {
|
34
|
+
Request* p = (Request*)parser;
|
35
|
+
if (p->last_field == Qnil) {
|
36
|
+
if (p->last_value == Qnil) {
|
37
|
+
p->parse_state = PS_ERROR;
|
38
|
+
return 1;
|
39
|
+
}
|
40
|
+
rb_str_cat(p->last_value, s, len);
|
41
|
+
} else {
|
42
|
+
nyara_headerlize(p->last_field);
|
43
|
+
p->last_value = rb_str_new(s, len);
|
44
|
+
rb_hash_aset(p->header, p->last_field, p->last_value);
|
45
|
+
p->last_field = Qnil;
|
46
|
+
}
|
47
|
+
return 0;
|
48
|
+
}
|
49
|
+
|
50
|
+
static void _upcase_method(VALUE str) {
|
51
|
+
char* s = RSTRING_PTR(str);
|
52
|
+
long len = RSTRING_LEN(str);
|
53
|
+
for (long i = 0; i < len; i++) {
|
54
|
+
if (s[i] >= 'a' && s[i] <= 'z') {
|
55
|
+
s[i] = 'A' + (s[i] - 'a');
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
// may override POST by _method in query
|
61
|
+
static void _parse_path_and_query(Request* p) {
|
62
|
+
char* s = RSTRING_PTR(p->path_with_query);
|
63
|
+
long len = RSTRING_LEN(p->path_with_query);
|
64
|
+
long query_i = nyara_parse_path(p->path, s, len);
|
65
|
+
if (query_i < len) {
|
66
|
+
nyara_parse_param(p->query, s + query_i, len - query_i);
|
67
|
+
|
68
|
+
// do method override with _method=xxx in query
|
69
|
+
if (p->method == HTTP_POST) {
|
70
|
+
VALUE meth = rb_hash_aref(p->query, method_override_key);
|
71
|
+
if (TYPE(meth) == T_STRING) {
|
72
|
+
_upcase_method(meth);
|
73
|
+
VALUE meth_num = rb_hash_aref(nyara_http_methods, meth);
|
74
|
+
if (meth_num != Qnil) {
|
75
|
+
p->method = FIX2INT(meth_num);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
static int on_headers_complete(http_parser* parser) {
|
83
|
+
Request* p = (Request*)parser;
|
84
|
+
p->last_field = Qnil;
|
85
|
+
p->last_value = Qnil;
|
86
|
+
|
87
|
+
_parse_path_and_query(p);
|
88
|
+
p->accept = ext_parse_accept_value(Qnil, rb_hash_aref(p->header, str_accept));
|
89
|
+
p->parse_state = PS_HEADERS_COMPLETE;
|
90
|
+
return 0;
|
91
|
+
}
|
92
|
+
|
93
|
+
static int on_body(http_parser* parser, const char* s, size_t len) {
|
94
|
+
// todo
|
95
|
+
return 0;
|
96
|
+
}
|
97
|
+
|
98
|
+
static int on_message_complete(http_parser* parser) {
|
99
|
+
Request* p = (Request*)parser;
|
100
|
+
p->parse_state = PS_MESSAGE_COMPLETE;
|
101
|
+
return 0;
|
102
|
+
}
|
103
|
+
|
104
|
+
// used in event.c
|
105
|
+
http_parser_settings nyara_request_parse_settings = {
|
106
|
+
.on_message_begin = NULL,
|
107
|
+
.on_url = on_url,
|
108
|
+
.on_status_complete = NULL,
|
109
|
+
.on_header_field = on_header_field,
|
110
|
+
.on_header_value = on_header_value,
|
111
|
+
.on_headers_complete = on_headers_complete,
|
112
|
+
.on_body = on_body,
|
113
|
+
.on_message_complete = on_message_complete
|
114
|
+
};
|
115
|
+
|
116
|
+
void Init_request_parse(VALUE nyara) {
|
117
|
+
str_accept = rb_str_new2("Accept");
|
118
|
+
rb_gc_register_mark_object(str_accept);
|
119
|
+
method_override_key = rb_str_new2("_method");
|
120
|
+
OBJ_FREEZE(method_override_key);
|
121
|
+
rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
|
122
|
+
nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
|
123
|
+
}
|