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.
- checksums.yaml +7 -0
- data/example/design.rb +62 -0
- data/example/fib.rb +15 -0
- data/example/hello.rb +5 -0
- data/example/stream.rb +10 -0
- data/ext/accept.c +133 -0
- data/ext/event.c +89 -0
- data/ext/extconf.rb +34 -0
- data/ext/hashes.c +130 -0
- data/ext/http-parser/AUTHORS +41 -0
- data/ext/http-parser/CONTRIBUTIONS +4 -0
- data/ext/http-parser/LICENSE-MIT +23 -0
- data/ext/http-parser/contrib/parsertrace.c +156 -0
- data/ext/http-parser/contrib/url_parser.c +44 -0
- data/ext/http-parser/http_parser.c +2175 -0
- data/ext/http-parser/http_parser.h +304 -0
- data/ext/http-parser/test.c +3425 -0
- data/ext/http_parser.c +1 -0
- data/ext/inc/epoll.h +60 -0
- data/ext/inc/kqueue.h +77 -0
- data/ext/inc/status_codes.inc +64 -0
- data/ext/inc/str_intern.h +66 -0
- data/ext/inc/version.inc +1 -0
- data/ext/mime.c +107 -0
- data/ext/multipart-parser-c/README.md +18 -0
- data/ext/multipart-parser-c/multipart_parser.c +309 -0
- data/ext/multipart-parser-c/multipart_parser.h +48 -0
- data/ext/multipart_parser.c +1 -0
- data/ext/nyara.c +56 -0
- data/ext/nyara.h +59 -0
- data/ext/request.c +474 -0
- data/ext/route.cc +325 -0
- data/ext/url_encoded.c +304 -0
- data/hello.rb +5 -0
- data/lib/nyara/config.rb +64 -0
- data/lib/nyara/config_hash.rb +51 -0
- data/lib/nyara/controller.rb +336 -0
- data/lib/nyara/cookie.rb +31 -0
- data/lib/nyara/cpu_counter.rb +65 -0
- data/lib/nyara/header_hash.rb +18 -0
- data/lib/nyara/mime_types.rb +612 -0
- data/lib/nyara/nyara.rb +82 -0
- data/lib/nyara/param_hash.rb +5 -0
- data/lib/nyara/request.rb +144 -0
- data/lib/nyara/route.rb +138 -0
- data/lib/nyara/route_entry.rb +43 -0
- data/lib/nyara/session.rb +104 -0
- data/lib/nyara/view.rb +317 -0
- data/lib/nyara.rb +25 -0
- data/nyara.gemspec +20 -0
- data/rakefile +91 -0
- data/readme.md +35 -0
- data/spec/ext_mime_match_spec.rb +27 -0
- data/spec/ext_parse_accept_value_spec.rb +29 -0
- data/spec/ext_parse_spec.rb +138 -0
- data/spec/ext_route_spec.rb +70 -0
- data/spec/hashes_spec.rb +71 -0
- data/spec/path_helper_spec.rb +77 -0
- data/spec/request_delegate_spec.rb +67 -0
- data/spec/request_spec.rb +56 -0
- data/spec/route_entry_spec.rb +12 -0
- data/spec/route_spec.rb +84 -0
- data/spec/session_spec.rb +66 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/view_spec.rb +87 -0
- data/tools/bench-cookie.rb +22 -0
- 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
|
+
}
|