nyara 0.0.1.pre

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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/example/design.rb +62 -0
  3. data/example/fib.rb +15 -0
  4. data/example/hello.rb +5 -0
  5. data/example/stream.rb +10 -0
  6. data/ext/accept.c +133 -0
  7. data/ext/event.c +89 -0
  8. data/ext/extconf.rb +34 -0
  9. data/ext/hashes.c +130 -0
  10. data/ext/http-parser/AUTHORS +41 -0
  11. data/ext/http-parser/CONTRIBUTIONS +4 -0
  12. data/ext/http-parser/LICENSE-MIT +23 -0
  13. data/ext/http-parser/contrib/parsertrace.c +156 -0
  14. data/ext/http-parser/contrib/url_parser.c +44 -0
  15. data/ext/http-parser/http_parser.c +2175 -0
  16. data/ext/http-parser/http_parser.h +304 -0
  17. data/ext/http-parser/test.c +3425 -0
  18. data/ext/http_parser.c +1 -0
  19. data/ext/inc/epoll.h +60 -0
  20. data/ext/inc/kqueue.h +77 -0
  21. data/ext/inc/status_codes.inc +64 -0
  22. data/ext/inc/str_intern.h +66 -0
  23. data/ext/inc/version.inc +1 -0
  24. data/ext/mime.c +107 -0
  25. data/ext/multipart-parser-c/README.md +18 -0
  26. data/ext/multipart-parser-c/multipart_parser.c +309 -0
  27. data/ext/multipart-parser-c/multipart_parser.h +48 -0
  28. data/ext/multipart_parser.c +1 -0
  29. data/ext/nyara.c +56 -0
  30. data/ext/nyara.h +59 -0
  31. data/ext/request.c +474 -0
  32. data/ext/route.cc +325 -0
  33. data/ext/url_encoded.c +304 -0
  34. data/hello.rb +5 -0
  35. data/lib/nyara/config.rb +64 -0
  36. data/lib/nyara/config_hash.rb +51 -0
  37. data/lib/nyara/controller.rb +336 -0
  38. data/lib/nyara/cookie.rb +31 -0
  39. data/lib/nyara/cpu_counter.rb +65 -0
  40. data/lib/nyara/header_hash.rb +18 -0
  41. data/lib/nyara/mime_types.rb +612 -0
  42. data/lib/nyara/nyara.rb +82 -0
  43. data/lib/nyara/param_hash.rb +5 -0
  44. data/lib/nyara/request.rb +144 -0
  45. data/lib/nyara/route.rb +138 -0
  46. data/lib/nyara/route_entry.rb +43 -0
  47. data/lib/nyara/session.rb +104 -0
  48. data/lib/nyara/view.rb +317 -0
  49. data/lib/nyara.rb +25 -0
  50. data/nyara.gemspec +20 -0
  51. data/rakefile +91 -0
  52. data/readme.md +35 -0
  53. data/spec/ext_mime_match_spec.rb +27 -0
  54. data/spec/ext_parse_accept_value_spec.rb +29 -0
  55. data/spec/ext_parse_spec.rb +138 -0
  56. data/spec/ext_route_spec.rb +70 -0
  57. data/spec/hashes_spec.rb +71 -0
  58. data/spec/path_helper_spec.rb +77 -0
  59. data/spec/request_delegate_spec.rb +67 -0
  60. data/spec/request_spec.rb +56 -0
  61. data/spec/route_entry_spec.rb +12 -0
  62. data/spec/route_spec.rb +84 -0
  63. data/spec/session_spec.rb +66 -0
  64. data/spec/spec_helper.rb +52 -0
  65. data/spec/view_spec.rb +87 -0
  66. data/tools/bench-cookie.rb +22 -0
  67. metadata +111 -0
@@ -0,0 +1,48 @@
1
+ /* Based on node-formidable by Felix Geisendörfer
2
+ * Igor Afonov - afonov@gmail.com - 2012
3
+ * MIT License - http://www.opensource.org/licenses/mit-license.php
4
+ */
5
+ #ifndef _multipart_parser_h
6
+ #define _multipart_parser_h
7
+
8
+ #ifdef __cplusplus
9
+ extern "C"
10
+ {
11
+ #endif
12
+
13
+ #include <stdlib.h>
14
+ #include <ctype.h>
15
+
16
+ typedef struct multipart_parser multipart_parser;
17
+ typedef struct multipart_parser_settings multipart_parser_settings;
18
+ typedef struct multipart_parser_state multipart_parser_state;
19
+
20
+ typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length);
21
+ typedef int (*multipart_notify_cb) (multipart_parser*);
22
+
23
+ struct multipart_parser_settings {
24
+ multipart_data_cb on_header_field;
25
+ multipart_data_cb on_header_value;
26
+ multipart_data_cb on_part_data;
27
+
28
+ multipart_notify_cb on_part_data_begin;
29
+ multipart_notify_cb on_headers_complete;
30
+ multipart_notify_cb on_part_data_end;
31
+ multipart_notify_cb on_body_end;
32
+ };
33
+
34
+ multipart_parser* multipart_parser_init
35
+ (const char *boundary, const multipart_parser_settings* settings);
36
+
37
+ void multipart_parser_free(multipart_parser* p);
38
+
39
+ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len);
40
+
41
+ void multipart_parser_set_data(multipart_parser* p, void* data);
42
+ void * multipart_parser_get_data(multipart_parser* p);
43
+
44
+ #ifdef __cplusplus
45
+ } /* extern "C" */
46
+ #endif
47
+
48
+ #endif
@@ -0,0 +1 @@
1
+ #include "multipart-parser-c/multipart_parser.c"
data/ext/nyara.c ADDED
@@ -0,0 +1,56 @@
1
+ #include "nyara.h"
2
+ #include <ruby/io.h>
3
+ #include <sys/socket.h>
4
+ #include <sys/resource.h>
5
+
6
+ static void set_fd_limit(int nofiles) {
7
+ struct rlimit rlim;
8
+ getrlimit (RLIMIT_NOFILE, &rlim);
9
+ if (nofiles >= 0) {
10
+ rlim.rlim_cur = nofiles;
11
+ if ((unsigned int)nofiles > rlim.rlim_max)
12
+ rlim.rlim_max = nofiles;
13
+ setrlimit (RLIMIT_NOFILE, &rlim);
14
+ }
15
+ }
16
+
17
+ void Init_nyara() {
18
+ set_fd_limit(20000);
19
+
20
+ VALUE nyara = rb_define_module("Nyara");
21
+
22
+ // utils: hashes
23
+ Init_hashes(nyara);
24
+
25
+ // utils: method map
26
+ volatile VALUE method_map = rb_class_new_instance(0, NULL, nyara_param_hash_class);
27
+ rb_const_set(nyara, rb_intern("HTTP_METHODS"), method_map);
28
+ VALUE tmp_key = Qnil;
29
+ # define METHOD_STR2NUM(n, name, string) \
30
+ tmp_key = rb_str_new2(#string);\
31
+ OBJ_FREEZE(tmp_key);\
32
+ rb_hash_aset(method_map, tmp_key, INT2FIX(n));
33
+ HTTP_METHOD_MAP(METHOD_STR2NUM);
34
+ # undef METHOD_STR2NUM
35
+ OBJ_FREEZE(method_map);
36
+
37
+ // utils: status codes
38
+ volatile VALUE status_map = rb_hash_new();
39
+ rb_const_set(nyara, rb_intern("HTTP_STATUS_CODES"), status_map);
40
+ VALUE tmp_value = Qnil;
41
+ # define STATUS_DESC(status, desc) \
42
+ tmp_value = rb_str_new2(desc);\
43
+ OBJ_FREEZE(tmp_value);\
44
+ rb_hash_aset(status_map, INT2FIX(status), tmp_value);
45
+ HTTP_STATUS_CODES(STATUS_DESC);
46
+ # undef STATUS_DESC
47
+ OBJ_FREEZE(status_map);
48
+
49
+ VALUE ext = rb_define_module_under(nyara, "Ext");
50
+ Init_accept(ext);
51
+ Init_mime(ext);
52
+ Init_request(nyara, ext);
53
+ Init_event(ext);
54
+ Init_route(nyara, ext);
55
+ Init_url_encoded(ext);
56
+ }
data/ext/nyara.h ADDED
@@ -0,0 +1,59 @@
1
+ #pragma once
2
+ #include <ruby.h>
3
+ #include <stdbool.h>
4
+ #include <http_parser.h>
5
+ #include "inc/status_codes.inc"
6
+
7
+ #ifdef DEBUG
8
+ #undef NDEBUG
9
+ #endif
10
+
11
+
12
+ /* -- event -- */
13
+ void Init_event(VALUE ext);
14
+
15
+
16
+ /* -- request & response class -- */
17
+ void Init_request(VALUE nyara, VALUE ext);
18
+ void nyara_handle_request(int fd);
19
+
20
+
21
+ /* -- url encoded parse -- */
22
+ void Init_url_encoded(VALUE ext);
23
+ size_t nyara_parse_path(VALUE path, const char*s, size_t len);
24
+ void nyara_parse_param(VALUE output, const char* s, size_t len);
25
+
26
+
27
+ /* -- accept parse -- */
28
+ void Init_accept(VALUE ext);
29
+ VALUE ext_parse_accept_value(VALUE _, VALUE str);
30
+
31
+
32
+ /* -- mime parse and match -- */
33
+ void Init_mime(VALUE ext);
34
+ VALUE ext_mime_match(VALUE _, VALUE request_accept, VALUE accept_mimes);
35
+
36
+
37
+ /* -- hashes -- */
38
+ void Init_hashes(VALUE nyara);
39
+
40
+ // ab-cd => Ab-Cd
41
+ // note str must be string created by nyara code
42
+ void nyara_headerlize(VALUE str);
43
+ int nyara_rb_hash_has_key(VALUE hash, VALUE key);
44
+
45
+ extern VALUE nyara_param_hash_class;
46
+ extern VALUE nyara_header_hash_class;
47
+ extern VALUE nyara_config_hash_class;
48
+
49
+
50
+ /* -- route -- */
51
+ typedef struct {
52
+ VALUE controller;
53
+ VALUE args;
54
+ VALUE scope;
55
+ VALUE ext; // maybe string or map
56
+ } RouteResult;
57
+
58
+ extern void Init_route(VALUE nyara, VALUE ext);
59
+ extern RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath);
data/ext/request.c ADDED
@@ -0,0 +1,474 @@
1
+ #include "nyara.h"
2
+ #include <ruby/encoding.h>
3
+ #include <multipart_parser.h>
4
+ #include <errno.h>
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 fiber;
15
+ VALUE scope; // mapped prefix
16
+ VALUE path;
17
+ VALUE param;
18
+ VALUE last_field;
19
+ VALUE last_value;
20
+ // string if determined by url, or hash if need further check with header[Accept]
21
+ VALUE ext;
22
+ VALUE self;
23
+ int fd;
24
+
25
+ // response
26
+ int status;
27
+ VALUE response_content_type;
28
+ VALUE response_header;
29
+ VALUE response_header_extra_lines;
30
+ } Request;
31
+
32
+ // typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
33
+ // typedef int (*http_cb) (http_parser*);
34
+
35
+ static ID id_not_found;
36
+ static VALUE str_html;
37
+ static rb_encoding* u8_encoding;
38
+ static VALUE request_class;
39
+ static VALUE method_override_key;
40
+ static VALUE str_accept;
41
+ static VALUE nyara_http_methods;
42
+
43
+ static VALUE fd_request_map;
44
+ #define MAX_RECEIVE_DATA 65536
45
+ static char received_data[MAX_RECEIVE_DATA];
46
+
47
+ static VALUE fiber_func(VALUE _, VALUE args) {
48
+ VALUE instance = rb_ary_pop(args);
49
+ VALUE meth = rb_ary_pop(args);
50
+ rb_apply(instance, SYM2ID(meth), args);
51
+ return Qnil;
52
+ }
53
+
54
+ static void _upcase_method(VALUE str) {
55
+ char* s = RSTRING_PTR(str);
56
+ long len = RSTRING_LEN(str);
57
+ for (long i = 0; i < len; i++) {
58
+ if (s[i] >= 'a' && s[i] <= 'z') {
59
+ s[i] = 'A' + (s[i] - 'a');
60
+ }
61
+ }
62
+ }
63
+
64
+ static inline void _close_fd(int fd) {
65
+ rb_hash_delete(fd_request_map, INT2FIX(fd));
66
+ close(fd);
67
+ }
68
+
69
+ // fixme assume url is always sent as whole (tcp window is large!)
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
+ // matching raw path is bad idea, for example: %a0 and %A0 are different strings but same route
75
+ p->path = rb_enc_str_new("", 0, u8_encoding);
76
+ size_t query_i = nyara_parse_path(p->path, s, len);
77
+ p->param = rb_class_new_instance(0, NULL, nyara_param_hash_class);
78
+ if (query_i < len) {
79
+ nyara_parse_param(p->param, s + query_i, len - query_i);
80
+
81
+ // do method override with _method=xxx in query
82
+ if (p->method == HTTP_POST) {
83
+ VALUE meth = rb_hash_aref(p->param, method_override_key);
84
+ if (TYPE(meth) == T_STRING) {
85
+ _upcase_method(meth);
86
+ VALUE meth_num = rb_hash_aref(nyara_http_methods, meth);
87
+ if (meth_num != Qnil) {
88
+ p->method = FIX2INT(meth_num);
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ volatile RouteResult result = nyara_lookup_route(p->method, p->path);
95
+ if (RTEST(result.controller)) {
96
+ {
97
+ VALUE instance = rb_class_new_instance(1, &(p->self), result.controller);
98
+ rb_ary_push(result.args, instance);
99
+ }
100
+ // result.args is on stack, no need to worry gc
101
+ p->fiber = rb_fiber_new(fiber_func, result.args);
102
+ p->scope = result.scope;
103
+ p->header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
104
+ p->ext = result.ext;
105
+ p->response_header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
106
+ p->response_header_extra_lines = rb_ary_new();
107
+ return 0;
108
+ } else {
109
+ rb_funcall(p->self, id_not_found, 0);
110
+ return 1;
111
+ }
112
+ }
113
+
114
+ static int on_message_complete(http_parser* parser) {
115
+ Request* p = (Request*)parser;
116
+ if (p->fiber == Qnil) {
117
+ return 1;
118
+ } else {
119
+ VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
120
+ if (state == Qnil) { // terminated (todo check raise error)
121
+ if (p->status == 200) {
122
+ write(p->fd, "0\r\n\r\n", 5);
123
+ }
124
+ _close_fd(p->fd);
125
+ p->fd = 0;
126
+ } else if (SYM2ID(state) == rb_intern("term_close")) {
127
+ write(p->fd, "0\r\n\r\n", 5);
128
+ _close_fd(p->fd);
129
+ p->fd = 0;
130
+ }
131
+ return 0;
132
+ }
133
+ }
134
+
135
+ static int on_header_field(http_parser* parser, const char* s, size_t len) {
136
+ Request* p = (Request*)parser;
137
+ if (p->last_field == Qnil) {
138
+ p->last_field = rb_str_new(s, len);
139
+ p->last_value = Qnil;
140
+ } else {
141
+ rb_str_cat(p->last_field, s, len);
142
+ }
143
+ return 0;
144
+ }
145
+
146
+ static int on_header_value(http_parser* parser, const char* s, size_t len) {
147
+ Request* p = (Request*)parser;
148
+ if (p->last_field == Qnil) {
149
+ if (p->last_value == Qnil) {
150
+ rb_bug("on_header_value called when neither last_field nor last_value exist");
151
+ return 1;
152
+ }
153
+ rb_str_cat(p->last_value, s, len);
154
+ } else {
155
+ nyara_headerlize(p->last_field);
156
+ p->last_value = rb_str_new(s, len);
157
+ rb_hash_aset(p->header, p->last_field, p->last_value);
158
+ p->last_field = Qnil;
159
+ }
160
+ return 0;
161
+ }
162
+
163
+ static int on_headers_complete(http_parser* parser) {
164
+ Request* p = (Request*)parser;
165
+ p->last_field = Qnil;
166
+ p->last_value = Qnil;
167
+
168
+ if (TYPE(p->ext) != T_STRING) {
169
+ VALUE accept = rb_hash_aref(p->header, str_accept);
170
+ if (RTEST(accept)) {
171
+ accept = ext_parse_accept_value(Qnil, accept);
172
+ rb_iv_set(p->self, "@accept", accept);
173
+ p->ext = ext_mime_match(Qnil, accept, p->ext);
174
+ }
175
+ if (p->ext == Qnil) {
176
+ rb_funcall(p->self, id_not_found, 0);
177
+ return 1;
178
+ }
179
+ }
180
+
181
+ return 0;
182
+ }
183
+
184
+ static http_parser_settings request_settings = {
185
+ .on_message_begin = NULL,
186
+ .on_url = on_url,
187
+ .on_status_complete = NULL,
188
+ .on_header_field = on_header_field,
189
+ .on_header_value = on_header_value,
190
+ .on_headers_complete = on_headers_complete,
191
+ .on_body = NULL, // data_cb
192
+ .on_message_complete = on_message_complete
193
+ };
194
+
195
+ static void request_mark(void* pp) {
196
+ Request* p = pp;
197
+ if (p) {
198
+ rb_gc_mark_maybe(p->header);
199
+ rb_gc_mark_maybe(p->fiber);
200
+ rb_gc_mark_maybe(p->scope);
201
+ rb_gc_mark_maybe(p->path);
202
+ rb_gc_mark_maybe(p->param);
203
+ rb_gc_mark_maybe(p->last_field);
204
+ rb_gc_mark_maybe(p->last_value);
205
+ rb_gc_mark_maybe(p->ext);
206
+ rb_gc_mark_maybe(p->response_content_type);
207
+ rb_gc_mark_maybe(p->response_header);
208
+ rb_gc_mark_maybe(p->response_header_extra_lines);
209
+ }
210
+ }
211
+
212
+ static void request_free(void* pp) {
213
+ Request* p = pp;
214
+ if (p) {
215
+ if (p->fd) {
216
+ _close_fd(p->fd);
217
+ }
218
+ xfree(p);
219
+ }
220
+ }
221
+
222
+ static Request* request_alloc() {
223
+ Request* p = ALLOC(Request);
224
+ http_parser_init(&(p->hparser), HTTP_REQUEST);
225
+ p->mparser = NULL;
226
+ p->header = Qnil;
227
+ p->fiber = Qnil;
228
+ p->scope = Qnil;
229
+ p->path = Qnil;
230
+ p->param = Qnil;
231
+ p->last_field = Qnil;
232
+ p->last_value = Qnil;
233
+ p->ext = Qnil;
234
+ p->fd = 0;
235
+ p->status = 200;
236
+ p->response_content_type = Qnil;
237
+ p->response_header = Qnil;
238
+ p->response_header_extra_lines = Qnil;
239
+ p->self = Data_Wrap_Struct(request_class, request_mark, request_free, p);
240
+ return p;
241
+ }
242
+
243
+ static VALUE request_alloc_func(VALUE klass) {
244
+ return request_alloc()->self;
245
+ }
246
+
247
+ /* client entrance
248
+ invoke order:
249
+ - find/create request
250
+ - http_parser_execute
251
+ - on_url
252
+ - on_message_complete
253
+ */
254
+ void nyara_handle_request(int fd) {
255
+ Request* p = NULL;
256
+ bool first_time = false;
257
+
258
+ {
259
+ VALUE v_fd = INT2FIX(fd);
260
+ VALUE request = rb_hash_aref(fd_request_map, v_fd);
261
+ if (request == Qnil) {
262
+ p = request_alloc();
263
+ p->fd = fd;
264
+ rb_hash_aset(fd_request_map, v_fd, p->self);
265
+ first_time = true;
266
+ } else {
267
+ Data_Get_Struct(request, Request, p);
268
+ }
269
+ }
270
+
271
+ long len = read(fd, received_data, MAX_RECEIVE_DATA);
272
+ if (len < 0) {
273
+ if (errno != EAGAIN) {
274
+ // todo log the bug
275
+ if (p->fd) {
276
+ _close_fd(p->fd);
277
+ p->fd = 0;
278
+ }
279
+ }
280
+ } else {
281
+ if (first_time && !len) {
282
+ // todo log this exception
283
+ return;
284
+ }
285
+ // note: when len == 0, means eof reached, that also informs http_parser the eof
286
+ http_parser_execute(&(p->hparser), &request_settings, received_data, len);
287
+ }
288
+ }
289
+
290
+ #define P \
291
+ Request* p;\
292
+ Data_Get_Struct(self, Request, p);
293
+
294
+ static VALUE request_http_method(VALUE self) {
295
+ P;
296
+ return rb_str_new2(http_method_str(p->method));
297
+ }
298
+
299
+ static VALUE request_header(VALUE self) {
300
+ P;
301
+ return p->header;
302
+ }
303
+
304
+ static VALUE request_scope(VALUE self) {
305
+ P;
306
+ return p->scope;
307
+ }
308
+
309
+ static VALUE request_path(VALUE self) {
310
+ P;
311
+ return p->path;
312
+ }
313
+
314
+ static VALUE request_matched_accept(VALUE _, VALUE self) {
315
+ P;
316
+ return p->ext == Qnil ? str_html : p->ext;
317
+ }
318
+
319
+ static VALUE request_status(VALUE self) {
320
+ P;
321
+ return INT2FIX(p->status);
322
+ }
323
+
324
+ static VALUE request_response_content_type(VALUE self) {
325
+ P;
326
+ return p->response_content_type;
327
+ }
328
+
329
+ static VALUE request_response_content_type_eq(VALUE self, VALUE ct) {
330
+ P;
331
+ p->response_content_type = ct;
332
+ return self;
333
+ }
334
+
335
+ static VALUE request_response_header(VALUE self) {
336
+ P;
337
+ return p->response_header;
338
+ }
339
+
340
+ static VALUE request_response_header_extra_lines(VALUE self) {
341
+ P;
342
+ return p->response_header_extra_lines;
343
+ }
344
+
345
+ static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
346
+ P;
347
+ p->status = NUM2INT(n);
348
+ return n;
349
+ }
350
+
351
+ static VALUE ext_request_param(VALUE _, VALUE self) {
352
+ P;
353
+ return p->param;
354
+ }
355
+
356
+ static VALUE ext_send_data(VALUE _, VALUE self, VALUE data) {
357
+ P;
358
+ char* buf = RSTRING_PTR(data);
359
+ long len = RSTRING_LEN(data);
360
+
361
+ while(len) {
362
+ long written = write(p->fd, buf, len);
363
+ if (written == 0)
364
+ return Qnil;
365
+ if (written == -1) {
366
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
367
+ // todo enqueue data and set state
368
+ }
369
+ return Qnil;
370
+ }
371
+ buf += written;
372
+ len -= written;
373
+ }
374
+ return Qnil;
375
+ }
376
+
377
+ static VALUE ext_send_chunk(VALUE _, VALUE self, VALUE str) {
378
+ long len = RSTRING_LEN(str);
379
+ if (!len) {
380
+ return Qnil;
381
+ }
382
+ // todo len overflow?
383
+ P;
384
+ long res = dprintf(p->fd, "%lx\r\n%.*s\r\n", len, (int)len, RSTRING_PTR(str));
385
+ if (res < 0) {
386
+ rb_raise(rb_eRuntimeError, "%s", strerror(errno));
387
+ }
388
+ return Qnil;
389
+ }
390
+
391
+ // for test: find or create a request with a fd
392
+ static VALUE ext_handle_request(VALUE _, VALUE v_fd) {
393
+ int fd = FIX2INT(v_fd);
394
+ nyara_handle_request(fd);
395
+ return rb_hash_aref(fd_request_map, v_fd);
396
+ }
397
+
398
+ // set internal attrs in the request object
399
+ static VALUE ext_set_request_attrs(VALUE _, VALUE self, VALUE attrs) {
400
+ # define ATTR(key) rb_hash_aref(attrs, ID2SYM(rb_intern(key)))
401
+ # define HEADER_HASH_NEW rb_class_new_instance(0, NULL, nyara_header_hash_class)
402
+ P;
403
+
404
+ if (ATTR("method_num") == Qnil) {
405
+ rb_raise(rb_eArgError, "bad method_num");
406
+ }
407
+
408
+ p->method = NUM2INT(ATTR("method_num"));
409
+ p->path = ATTR("path");
410
+ p->param = ATTR("param");
411
+ p->fiber = ATTR("fiber");
412
+ p->scope = ATTR("scope");
413
+ p->header = RTEST(ATTR("header")) ? ATTR("header") : HEADER_HASH_NEW;
414
+ p->ext = ATTR("ext");
415
+ p->response_header = RTEST(ATTR("response_header")) ? ATTR("response_header") : HEADER_HASH_NEW;
416
+ if (RTEST(ATTR("response_header_extra_lines"))) {
417
+ p->response_header_extra_lines = ATTR("response_header_extra_lines");
418
+ } else {
419
+ p->response_header_extra_lines = rb_ary_new();
420
+ }
421
+ return self;
422
+ # undef HEADER_HASH_NEW
423
+ # undef ATTR
424
+ }
425
+
426
+ // skip on_url so we can focus on testing request
427
+ static VALUE ext_set_skip_on_url(VALUE _, VALUE pred) {
428
+ if (RTEST(pred)) {
429
+ request_settings.on_url = NULL;
430
+ } else {
431
+ request_settings.on_url = on_url;
432
+ }
433
+ return Qnil;
434
+ }
435
+
436
+ void Init_request(VALUE nyara, VALUE ext) {
437
+ id_not_found = rb_intern("not_found");
438
+ str_html = rb_str_new2("html");
439
+ OBJ_FREEZE(str_html);
440
+ rb_gc_register_mark_object(str_html);
441
+ u8_encoding = rb_utf8_encoding();
442
+ method_override_key = rb_str_new2("_method");
443
+ rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
444
+ nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
445
+ fd_request_map = rb_hash_new();
446
+ rb_gc_register_mark_object(fd_request_map);
447
+ str_accept = rb_str_new2("Accept");
448
+ rb_gc_register_mark_object(fd_request_map);
449
+
450
+ // request
451
+ request_class = rb_define_class_under(nyara, "Request", rb_cObject);
452
+ rb_define_alloc_func(request_class, request_alloc_func);
453
+ rb_define_method(request_class, "http_method", request_http_method, 0);
454
+ rb_define_method(request_class, "header", request_header, 0);
455
+ rb_define_method(request_class, "scope", request_scope, 0);
456
+ rb_define_method(request_class, "path", request_path, 0);
457
+ rb_define_method(request_class, "matched_accept", request_matched_accept, 0);
458
+
459
+ rb_define_method(request_class, "status", request_status, 0);
460
+ rb_define_method(request_class, "response_content_type", request_response_content_type, 0);
461
+ rb_define_method(request_class, "response_content_type=", request_response_content_type_eq, 1);
462
+ rb_define_method(request_class, "response_header", request_response_header, 0);
463
+ rb_define_method(request_class, "response_header_extra_lines", request_response_header_extra_lines, 0);
464
+
465
+ // hide internal methods in ext
466
+ rb_define_singleton_method(ext, "request_set_status", ext_request_set_status, 2);
467
+ rb_define_singleton_method(ext, "request_param", ext_request_param, 1);
468
+ rb_define_singleton_method(ext, "send_data", ext_send_data, 2);
469
+ rb_define_singleton_method(ext, "send_chunk", ext_send_chunk, 2);
470
+ // for test
471
+ rb_define_singleton_method(ext, "handle_request", ext_handle_request, 1);
472
+ rb_define_singleton_method(ext, "set_request_attrs", ext_set_request_attrs, 2);
473
+ rb_define_singleton_method(ext, "set_skip_on_url", ext_set_skip_on_url, 1);
474
+ }