nyara 0.0.1.pre → 0.0.1.pre.1
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 +2 -2
- data/ext/hashes.c +3 -2
- data/ext/mime.c +17 -28
- data/ext/multipart-parser-c/multipart_parser.c +3 -3
- data/ext/nyara.h +4 -4
- data/ext/request.c +102 -98
- data/ext/route.cc +20 -15
- data/ext/url_encoded.c +10 -10
- data/hello.rb +2 -0
- data/lib/nyara/controller.rb +7 -6
- data/lib/nyara/request.rb +5 -8
- data/nyara.gemspec +1 -1
- data/rakefile +51 -3
- data/readme.md +79 -18
- data/spec/ext_route_spec.rb +4 -4
- data/spec/request_delegate_spec.rb +1 -9
- data/spec/request_spec.rb +2 -10
- data/spec/spec_helper.rb +8 -0
- data/spec/view_spec.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 924fd91c47d5907d59fac0bef09fa645ed552188
|
4
|
+
data.tar.gz: f3f74e666325973cc8a1938eb91ce9a91102ed99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88025bd5ad58a09e1c53e239572dbbc78039a5f93de7e52475074f4da6415f6616c39b59235f24fb8e8f30bd66e3530e66f48829369edab085ea139edd837d36
|
7
|
+
data.tar.gz: 8e602eb1355aeb7a4abd81f9e7bfe5cb586f3d697eecfaba5546ae40f5aa7d6e119ea93d1937198c2805ca5bb36825cc73cae93592cb5b844a759f244b27734e
|
data/ext/accept.c
CHANGED
@@ -45,7 +45,7 @@ static void qarray_delete(QArray* qa) {
|
|
45
45
|
xfree(qa->qs);
|
46
46
|
}
|
47
47
|
|
48
|
-
static VALUE trim_space(VALUE str) {
|
48
|
+
static VALUE trim_space(volatile VALUE str) {
|
49
49
|
long olen = RSTRING_LEN(str);
|
50
50
|
str = rb_str_new(RSTRING_PTR(str), olen);
|
51
51
|
char* s = RSTRING_PTR(str);
|
@@ -105,7 +105,7 @@ static void parse_seg(const char* s, long len, VALUE out, QArray* qa) {
|
|
105
105
|
rb_ary_store(out, pos, rb_str_new(s, len));
|
106
106
|
}
|
107
107
|
|
108
|
-
VALUE ext_parse_accept_value(VALUE _, VALUE str) {
|
108
|
+
VALUE ext_parse_accept_value(VALUE _, volatile VALUE str) {
|
109
109
|
if (str == Qnil) {
|
110
110
|
return rb_ary_new();
|
111
111
|
}
|
data/ext/hashes.c
CHANGED
@@ -66,9 +66,10 @@ void nyara_headerlize(VALUE str) {
|
|
66
66
|
static VALUE header_hash_tidy_key(VALUE key) {
|
67
67
|
if (TYPE(key) == T_SYMBOL) {
|
68
68
|
key = rb_sym_to_s(key);
|
69
|
+
} else {
|
70
|
+
Check_Type(key, T_STRING);
|
71
|
+
key = rb_str_new(RSTRING_PTR(key), RSTRING_LEN(key));
|
69
72
|
}
|
70
|
-
Check_Type(key, T_STRING);
|
71
|
-
key = rb_str_new(RSTRING_PTR(key), RSTRING_LEN(key));
|
72
73
|
nyara_headerlize(key);
|
73
74
|
return key;
|
74
75
|
}
|
data/ext/mime.c
CHANGED
@@ -12,7 +12,9 @@ static const char* _strnchr(const char* s, long len, char c) {
|
|
12
12
|
return NULL;
|
13
13
|
}
|
14
14
|
|
15
|
-
|
15
|
+
// m1, m2: request
|
16
|
+
// v1, v2: action
|
17
|
+
static bool _mime_match_seg(const char* m1_ptr, long m1_len, VALUE v1, VALUE v2) {
|
16
18
|
const char* m2_ptr = _strnchr(m1_ptr, m1_len, '/');
|
17
19
|
long m2_len;
|
18
20
|
if (m2_ptr) {
|
@@ -31,21 +33,6 @@ static bool mime_match_seg(const char* m1_ptr, long m1_len, VALUE v1, VALUE v2)
|
|
31
33
|
# define EQL_STAR(s, len) (len == 1 && s[0] == '*')
|
32
34
|
# define EQL(s1, len1, s2, len2) (len1 == len2 && strncmp(s1, s2, len1) == 0)
|
33
35
|
|
34
|
-
/*
|
35
|
-
if m1 == '*'
|
36
|
-
if m2.nil? || m2 == '*'
|
37
|
-
return true
|
38
|
-
elsif m2 == v2
|
39
|
-
return true
|
40
|
-
else
|
41
|
-
return false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
return false if v1 != m1
|
45
|
-
return true if m2.nil? || m2 == '*'
|
46
|
-
m2 == v2
|
47
|
-
*/
|
48
|
-
|
49
36
|
if (EQL_STAR(m1_ptr, m1_len)) {
|
50
37
|
if (m2_len == 0 || EQL_STAR(m2_ptr, m2_len)) {
|
51
38
|
return true;
|
@@ -69,30 +56,32 @@ static bool mime_match_seg(const char* m1_ptr, long m1_len, VALUE v1, VALUE v2)
|
|
69
56
|
|
70
57
|
// for test
|
71
58
|
static VALUE ext_mime_match_seg(VALUE self, VALUE m, VALUE v1, VALUE v2) {
|
72
|
-
if (
|
59
|
+
if (_mime_match_seg(RSTRING_PTR(m), RSTRING_LEN(m), v1, v2)) {
|
73
60
|
return Qtrue;
|
74
61
|
} else {
|
75
62
|
return Qfalse;
|
76
63
|
}
|
77
64
|
}
|
78
65
|
|
79
|
-
//
|
80
|
-
|
66
|
+
// +request_accept+ is an array of mime types
|
67
|
+
// +action_accept+ is an array of split mime type and format, e.g. +[['application', 'javascript', 'js']]+
|
68
|
+
// returns matched format or nil
|
69
|
+
VALUE ext_mime_match(VALUE self, VALUE request_accept, VALUE action_accept) {
|
81
70
|
Check_Type(request_accept, T_ARRAY);
|
82
|
-
Check_Type(
|
71
|
+
Check_Type(action_accept, T_ARRAY);
|
83
72
|
|
84
|
-
VALUE*
|
85
|
-
long
|
86
|
-
VALUE* values = RARRAY_PTR(
|
87
|
-
long values_len = RARRAY_LEN(
|
73
|
+
VALUE* requests = RARRAY_PTR(request_accept);
|
74
|
+
long requests_len = RARRAY_LEN(request_accept);
|
75
|
+
VALUE* values = RARRAY_PTR(action_accept);
|
76
|
+
long values_len = RARRAY_LEN(action_accept);
|
88
77
|
|
89
|
-
for (long j = 0; j <
|
90
|
-
char* s = RSTRING_PTR(
|
91
|
-
long len = RSTRING_LEN(
|
78
|
+
for (long j = 0; j < requests_len; j++) {
|
79
|
+
char* s = RSTRING_PTR(requests[j]);
|
80
|
+
long len = RSTRING_LEN(requests[j]);
|
92
81
|
for (long i = 0; i < values_len; i++) {
|
93
82
|
Check_Type(values[i], T_ARRAY);
|
94
83
|
VALUE* arr = RARRAY_PTR(values[i]);
|
95
|
-
if (
|
84
|
+
if (_mime_match_seg(s, len, arr[0], arr[1])) {
|
96
85
|
return arr[2];
|
97
86
|
}
|
98
87
|
}
|
@@ -23,8 +23,8 @@ static void multipart_log(const char * format, ...)
|
|
23
23
|
|
24
24
|
#define NOTIFY_CB(FOR) \
|
25
25
|
do { \
|
26
|
-
if (p->settings->on_##FOR) {
|
27
|
-
if (p->settings->on_##FOR(p) != 0) {
|
26
|
+
if (p->settings->on_##FOR) { \
|
27
|
+
if (p->settings->on_##FOR(p) != 0) { \
|
28
28
|
return i; \
|
29
29
|
} \
|
30
30
|
} \
|
@@ -114,7 +114,7 @@ size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len
|
|
114
114
|
char c, cl;
|
115
115
|
int is_last = 0;
|
116
116
|
|
117
|
-
while(
|
117
|
+
while(i < len) {
|
118
118
|
c = buf[i];
|
119
119
|
is_last = (i == (len - 1));
|
120
120
|
switch (p->state) {
|
data/ext/nyara.h
CHANGED
@@ -20,8 +20,8 @@ void nyara_handle_request(int fd);
|
|
20
20
|
|
21
21
|
/* -- url encoded parse -- */
|
22
22
|
void Init_url_encoded(VALUE ext);
|
23
|
-
|
24
|
-
void nyara_parse_param(VALUE output, const char* s,
|
23
|
+
long nyara_parse_path(VALUE path, const char*s, long len);
|
24
|
+
void nyara_parse_param(VALUE output, const char* s, long len);
|
25
25
|
|
26
26
|
|
27
27
|
/* -- accept parse -- */
|
@@ -52,8 +52,8 @@ typedef struct {
|
|
52
52
|
VALUE controller;
|
53
53
|
VALUE args;
|
54
54
|
VALUE scope;
|
55
|
-
VALUE
|
55
|
+
VALUE format; // string, path extension or matched ext in config
|
56
56
|
} RouteResult;
|
57
57
|
|
58
58
|
extern void Init_route(VALUE nyara, VALUE ext);
|
59
|
-
extern RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath);
|
59
|
+
extern RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath, VALUE accept_arr);
|
data/ext/request.c
CHANGED
@@ -11,14 +11,15 @@ typedef struct {
|
|
11
11
|
multipart_parser* mparser;
|
12
12
|
enum http_method method;
|
13
13
|
VALUE header;
|
14
|
+
VALUE accept; // mime array sorted with q
|
15
|
+
VALUE format; // string ext without dot
|
14
16
|
VALUE fiber;
|
15
17
|
VALUE scope; // mapped prefix
|
18
|
+
VALUE path_with_query;
|
16
19
|
VALUE path;
|
17
|
-
VALUE
|
20
|
+
VALUE query;
|
18
21
|
VALUE last_field;
|
19
22
|
VALUE last_value;
|
20
|
-
// string if determined by url, or hash if need further check with header[Accept]
|
21
|
-
VALUE ext;
|
22
23
|
VALUE self;
|
23
24
|
int fd;
|
24
25
|
|
@@ -44,7 +45,7 @@ static VALUE fd_request_map;
|
|
44
45
|
#define MAX_RECEIVE_DATA 65536
|
45
46
|
static char received_data[MAX_RECEIVE_DATA];
|
46
47
|
|
47
|
-
static VALUE
|
48
|
+
static VALUE _fiber_func(VALUE _, VALUE args) {
|
48
49
|
VALUE instance = rb_ary_pop(args);
|
49
50
|
VALUE meth = rb_ary_pop(args);
|
50
51
|
rb_apply(instance, SYM2ID(meth), args);
|
@@ -66,49 +67,16 @@ static inline void _close_fd(int fd) {
|
|
66
67
|
close(fd);
|
67
68
|
}
|
68
69
|
|
69
|
-
// fixme assume url is always sent as whole (tcp window is large!)
|
70
70
|
static int on_url(http_parser* parser, const char* s, size_t len) {
|
71
71
|
Request* p = (Request*)parser;
|
72
72
|
p->method = parser->method;
|
73
73
|
|
74
|
-
|
75
|
-
|
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;
|
74
|
+
if (p->path_with_query == Qnil) {
|
75
|
+
p->path_with_query = rb_str_new(s, len);
|
108
76
|
} else {
|
109
|
-
|
110
|
-
return 1;
|
77
|
+
rb_str_cat(p->path_with_query, s, len);
|
111
78
|
}
|
79
|
+
return 0;
|
112
80
|
}
|
113
81
|
|
114
82
|
static int on_message_complete(http_parser* parser) {
|
@@ -160,25 +128,48 @@ static int on_header_value(http_parser* parser, const char* s, size_t len) {
|
|
160
128
|
return 0;
|
161
129
|
}
|
162
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
|
+
}
|
152
|
+
|
163
153
|
static int on_headers_complete(http_parser* parser) {
|
164
154
|
Request* p = (Request*)parser;
|
165
155
|
p->last_field = Qnil;
|
166
156
|
p->last_value = Qnil;
|
167
157
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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;
|
179
170
|
}
|
180
|
-
|
181
|
-
return
|
171
|
+
rb_funcall(p->self, id_not_found, 0);
|
172
|
+
return 1;
|
182
173
|
}
|
183
174
|
|
184
175
|
static http_parser_settings request_settings = {
|
@@ -196,13 +187,15 @@ static void request_mark(void* pp) {
|
|
196
187
|
Request* p = pp;
|
197
188
|
if (p) {
|
198
189
|
rb_gc_mark_maybe(p->header);
|
190
|
+
rb_gc_mark_maybe(p->accept);
|
191
|
+
rb_gc_mark_maybe(p->format);
|
199
192
|
rb_gc_mark_maybe(p->fiber);
|
200
193
|
rb_gc_mark_maybe(p->scope);
|
194
|
+
rb_gc_mark_maybe(p->path_with_query);
|
201
195
|
rb_gc_mark_maybe(p->path);
|
202
|
-
rb_gc_mark_maybe(p->
|
196
|
+
rb_gc_mark_maybe(p->query);
|
203
197
|
rb_gc_mark_maybe(p->last_field);
|
204
198
|
rb_gc_mark_maybe(p->last_value);
|
205
|
-
rb_gc_mark_maybe(p->ext);
|
206
199
|
rb_gc_mark_maybe(p->response_content_type);
|
207
200
|
rb_gc_mark_maybe(p->response_header);
|
208
201
|
rb_gc_mark_maybe(p->response_header_extra_lines);
|
@@ -222,15 +215,20 @@ static void request_free(void* pp) {
|
|
222
215
|
static Request* request_alloc() {
|
223
216
|
Request* p = ALLOC(Request);
|
224
217
|
http_parser_init(&(p->hparser), HTTP_REQUEST);
|
218
|
+
volatile VALUE header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
219
|
+
volatile VALUE path = rb_enc_str_new("", 0, u8_encoding);
|
220
|
+
volatile VALUE query = rb_class_new_instance(0, NULL, nyara_param_hash_class);
|
225
221
|
p->mparser = NULL;
|
226
|
-
p->header =
|
222
|
+
p->header = header;
|
223
|
+
p->accept = Qnil;
|
224
|
+
p->format = Qnil;
|
227
225
|
p->fiber = Qnil;
|
228
226
|
p->scope = Qnil;
|
229
|
-
p->
|
230
|
-
p->
|
227
|
+
p->path_with_query = Qnil;
|
228
|
+
p->path = path;
|
229
|
+
p->query = query;
|
231
230
|
p->last_field = Qnil;
|
232
231
|
p->last_value = Qnil;
|
233
|
-
p->ext = Qnil;
|
234
232
|
p->fd = 0;
|
235
233
|
p->status = 200;
|
236
234
|
p->response_content_type = Qnil;
|
@@ -248,8 +246,8 @@ static VALUE request_alloc_func(VALUE klass) {
|
|
248
246
|
invoke order:
|
249
247
|
- find/create request
|
250
248
|
- http_parser_execute
|
251
|
-
-
|
252
|
-
- on_message_complete
|
249
|
+
- on_headers_complete => 404 or create request
|
250
|
+
- on_message_complete => run action
|
253
251
|
*/
|
254
252
|
void nyara_handle_request(int fd) {
|
255
253
|
Request* p = NULL;
|
@@ -311,9 +309,24 @@ static VALUE request_path(VALUE self) {
|
|
311
309
|
return p->path;
|
312
310
|
}
|
313
311
|
|
314
|
-
static VALUE
|
312
|
+
static VALUE request_query(VALUE self) {
|
315
313
|
P;
|
316
|
-
return p->
|
314
|
+
return p->query;
|
315
|
+
}
|
316
|
+
|
317
|
+
static VALUE request_path_with_query(VALUE self) {
|
318
|
+
P;
|
319
|
+
return p->path_with_query;
|
320
|
+
}
|
321
|
+
|
322
|
+
static VALUE request_accept(VALUE self) {
|
323
|
+
P;
|
324
|
+
return p->accept;
|
325
|
+
}
|
326
|
+
|
327
|
+
static VALUE request_format(VALUE self) {
|
328
|
+
P;
|
329
|
+
return p->format == Qnil ? str_html : p->format;
|
317
330
|
}
|
318
331
|
|
319
332
|
static VALUE request_status(VALUE self) {
|
@@ -348,11 +361,6 @@ static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
|
|
348
361
|
return n;
|
349
362
|
}
|
350
363
|
|
351
|
-
static VALUE ext_request_param(VALUE _, VALUE self) {
|
352
|
-
P;
|
353
|
-
return p->param;
|
354
|
-
}
|
355
|
-
|
356
364
|
static VALUE ext_send_data(VALUE _, VALUE self, VALUE data) {
|
357
365
|
P;
|
358
366
|
char* buf = RSTRING_PTR(data);
|
@@ -397,42 +405,37 @@ static VALUE ext_handle_request(VALUE _, VALUE v_fd) {
|
|
397
405
|
|
398
406
|
// set internal attrs in the request object
|
399
407
|
static VALUE ext_set_request_attrs(VALUE _, VALUE self, VALUE attrs) {
|
400
|
-
# define ATTR(key)
|
408
|
+
# define ATTR(key) rb_hash_delete(attrs, ID2SYM(rb_intern(key)))
|
401
409
|
# define HEADER_HASH_NEW rb_class_new_instance(0, NULL, nyara_header_hash_class)
|
402
410
|
P;
|
403
411
|
|
404
|
-
|
412
|
+
VALUE method_num = ATTR("method_num");
|
413
|
+
if (method_num == Qnil) {
|
405
414
|
rb_raise(rb_eArgError, "bad method_num");
|
406
415
|
}
|
407
|
-
|
408
|
-
p->
|
409
|
-
p->
|
410
|
-
p->
|
411
|
-
p->
|
412
|
-
p->
|
413
|
-
p->
|
414
|
-
p->
|
415
|
-
p->
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
416
|
+
p->method = NUM2INT(method_num);
|
417
|
+
p->path = ATTR("path");
|
418
|
+
p->query = ATTR("query");
|
419
|
+
p->fiber = ATTR("fiber");
|
420
|
+
p->scope = ATTR("scope");
|
421
|
+
p->header = ATTR("header");
|
422
|
+
p->format = ATTR("format");
|
423
|
+
p->response_header = ATTR("response_header");
|
424
|
+
p->response_header_extra_lines = ATTR("response_header_extra_lines");
|
425
|
+
|
426
|
+
if (!RTEST(p->header)) p->header = HEADER_HASH_NEW;
|
427
|
+
if (!RTEST(p->response_header)) p->response_header = HEADER_HASH_NEW;
|
428
|
+
if (!RTEST(p->response_header_extra_lines)) p->response_header_extra_lines = rb_ary_new();
|
429
|
+
|
430
|
+
if (!RTEST(rb_funcall(attrs, rb_intern("empty?"), 0))) {
|
431
|
+
VALUE attrs_inspect = rb_funcall(attrs, rb_intern("inspect"), 0);
|
432
|
+
rb_raise(rb_eArgError, "unkown attrs: %.*s", (int)RSTRING_LEN(attrs_inspect), RSTRING_PTR(attrs_inspect));
|
420
433
|
}
|
421
434
|
return self;
|
422
435
|
# undef HEADER_HASH_NEW
|
423
436
|
# undef ATTR
|
424
437
|
}
|
425
438
|
|
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
439
|
void Init_request(VALUE nyara, VALUE ext) {
|
437
440
|
id_not_found = rb_intern("not_found");
|
438
441
|
str_html = rb_str_new2("html");
|
@@ -445,7 +448,7 @@ void Init_request(VALUE nyara, VALUE ext) {
|
|
445
448
|
fd_request_map = rb_hash_new();
|
446
449
|
rb_gc_register_mark_object(fd_request_map);
|
447
450
|
str_accept = rb_str_new2("Accept");
|
448
|
-
rb_gc_register_mark_object(
|
451
|
+
rb_gc_register_mark_object(str_accept);
|
449
452
|
|
450
453
|
// request
|
451
454
|
request_class = rb_define_class_under(nyara, "Request", rb_cObject);
|
@@ -454,7 +457,10 @@ void Init_request(VALUE nyara, VALUE ext) {
|
|
454
457
|
rb_define_method(request_class, "header", request_header, 0);
|
455
458
|
rb_define_method(request_class, "scope", request_scope, 0);
|
456
459
|
rb_define_method(request_class, "path", request_path, 0);
|
457
|
-
rb_define_method(request_class, "
|
460
|
+
rb_define_method(request_class, "query", request_query, 0);
|
461
|
+
rb_define_method(request_class, "path_with_query", request_path_with_query, 0);
|
462
|
+
rb_define_method(request_class, "accept", request_accept, 0);
|
463
|
+
rb_define_method(request_class, "format", request_format, 0);
|
458
464
|
|
459
465
|
rb_define_method(request_class, "status", request_status, 0);
|
460
466
|
rb_define_method(request_class, "response_content_type", request_response_content_type, 0);
|
@@ -464,11 +470,9 @@ void Init_request(VALUE nyara, VALUE ext) {
|
|
464
470
|
|
465
471
|
// hide internal methods in ext
|
466
472
|
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
473
|
rb_define_singleton_method(ext, "send_data", ext_send_data, 2);
|
469
474
|
rb_define_singleton_method(ext, "send_chunk", ext_send_chunk, 2);
|
470
475
|
// for test
|
471
476
|
rb_define_singleton_method(ext, "handle_request", ext_handle_request, 1);
|
472
477
|
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
478
|
}
|
data/ext/route.cc
CHANGED
@@ -6,6 +6,9 @@ extern "C" {
|
|
6
6
|
#include <vector>
|
7
7
|
#include <map>
|
8
8
|
#include "inc/str_intern.h"
|
9
|
+
#ifndef isalnum
|
10
|
+
#include <cctype>
|
11
|
+
#endif
|
9
12
|
|
10
13
|
struct RouteEntry {
|
11
14
|
// note on order: scope is supposed to be the last, but when searching, is_sub is checked first
|
@@ -220,7 +223,7 @@ static VALUE extract_ext(const char* s, long len) {
|
|
220
223
|
}
|
221
224
|
|
222
225
|
extern "C"
|
223
|
-
RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath) {
|
226
|
+
RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath, VALUE accept_arr) {
|
224
227
|
RouteResult r = {Qnil, Qnil, Qnil, Qnil};
|
225
228
|
auto map_iter = route_map.find(method_num);
|
226
229
|
if (map_iter == route_map.end()) {
|
@@ -247,8 +250,8 @@ RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath) {
|
|
247
250
|
long suffix_len = len - i->prefix_len;
|
248
251
|
if (i->suffix_len == 0) {
|
249
252
|
if (suffix_len) {
|
250
|
-
r.
|
251
|
-
if (r.
|
253
|
+
r.format = extract_ext(suffix, suffix_len);
|
254
|
+
if (r.format == Qnil) {
|
252
255
|
break;
|
253
256
|
}
|
254
257
|
}
|
@@ -260,8 +263,8 @@ RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath) {
|
|
260
263
|
(const UChar*)suffix, ®ion, 0);
|
261
264
|
if (matched_len > 0) {
|
262
265
|
if (matched_len < suffix_len) {
|
263
|
-
r.
|
264
|
-
if (r.
|
266
|
+
r.format = extract_ext(suffix + matched_len, suffix_len);
|
267
|
+
if (r.format == Qnil) {
|
265
268
|
break;
|
266
269
|
}
|
267
270
|
}
|
@@ -277,17 +280,19 @@ RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath) {
|
|
277
280
|
if (r.controller != Qnil) {
|
278
281
|
r.scope = i->scope;
|
279
282
|
|
280
|
-
if (r.
|
283
|
+
if (r.format == Qnil) {
|
281
284
|
if (i->accept_exts == Qnil) {
|
282
|
-
r.
|
285
|
+
r.format = str_html; // not configured, just plain html
|
283
286
|
} else {
|
284
|
-
|
285
|
-
r.
|
287
|
+
r.format = ext_mime_match(Qnil, accept_arr, i->accept_mimes);
|
288
|
+
if (r.format == Qnil) {
|
289
|
+
r.controller = Qnil; // reject if mime mismatch
|
290
|
+
}
|
286
291
|
}
|
287
292
|
} else {
|
288
293
|
if (i->accept_exts != Qnil) {
|
289
|
-
if (!RTEST(rb_hash_aref(i->accept_exts, r.
|
290
|
-
r.controller = Qnil; // reject if ext mismatch
|
294
|
+
if (!RTEST(rb_hash_aref(i->accept_exts, r.format))) {
|
295
|
+
r.controller = Qnil; // reject if path ext mismatch
|
291
296
|
}
|
292
297
|
}
|
293
298
|
}
|
@@ -295,14 +300,14 @@ RouteResult nyara_lookup_route(enum http_method method_num, VALUE vpath) {
|
|
295
300
|
return r;
|
296
301
|
}
|
297
302
|
|
298
|
-
static VALUE ext_lookup_route(VALUE self, VALUE method, VALUE path) {
|
303
|
+
static VALUE ext_lookup_route(VALUE self, VALUE method, VALUE path, VALUE accept_arr) {
|
299
304
|
enum http_method method_num = canonicalize_http_method(method);
|
300
|
-
volatile RouteResult r = nyara_lookup_route(method_num, path);
|
305
|
+
volatile RouteResult r = nyara_lookup_route(method_num, path, accept_arr);
|
301
306
|
volatile VALUE a = rb_ary_new();
|
302
307
|
rb_ary_push(a, r.scope);
|
303
308
|
rb_ary_push(a, r.controller);
|
304
309
|
rb_ary_push(a, r.args);
|
305
|
-
rb_ary_push(a, r.
|
310
|
+
rb_ary_push(a, r.format);
|
306
311
|
return a;
|
307
312
|
}
|
308
313
|
|
@@ -321,5 +326,5 @@ void Init_route(VALUE nyara, VALUE ext) {
|
|
321
326
|
|
322
327
|
// for test
|
323
328
|
rb_define_singleton_method(ext, "list_route", RUBY_METHOD_FUNC(ext_list_route), 0);
|
324
|
-
rb_define_singleton_method(ext, "lookup_route", RUBY_METHOD_FUNC(ext_lookup_route),
|
329
|
+
rb_define_singleton_method(ext, "lookup_route", RUBY_METHOD_FUNC(ext_lookup_route), 3);
|
325
330
|
}
|
data/ext/url_encoded.c
CHANGED
@@ -18,7 +18,7 @@ static char _half_octet(char c) {
|
|
18
18
|
}
|
19
19
|
}
|
20
20
|
|
21
|
-
static
|
21
|
+
static long _decode_url_seg(VALUE path, const char*s, long len, char stop_char) {
|
22
22
|
const char* last_s = s;
|
23
23
|
long last_len = 0;
|
24
24
|
|
@@ -29,7 +29,7 @@ static size_t _decode_url_seg(VALUE path, const char*s, size_t len, char stop_ch
|
|
29
29
|
last_len = 0;\
|
30
30
|
}
|
31
31
|
|
32
|
-
|
32
|
+
long i;
|
33
33
|
for (i = 0; i < len; i++) {
|
34
34
|
if (s[i] == '%') {
|
35
35
|
if (i + 2 >= len) {
|
@@ -71,12 +71,12 @@ static size_t _decode_url_seg(VALUE path, const char*s, size_t len, char stop_ch
|
|
71
71
|
}
|
72
72
|
|
73
73
|
// return parsed len, s + return == start of query
|
74
|
-
|
74
|
+
long nyara_parse_path(VALUE output, const char* s, long len) {
|
75
75
|
return _decode_url_seg(output, s, len, '?');
|
76
76
|
}
|
77
77
|
|
78
78
|
static VALUE ext_parse_path(VALUE self, VALUE output, VALUE input) {
|
79
|
-
|
79
|
+
long parsed = nyara_parse_path(output, RSTRING_PTR(input), RSTRING_LEN(input));
|
80
80
|
return ULONG2NUM(parsed);
|
81
81
|
}
|
82
82
|
|
@@ -227,10 +227,10 @@ static VALUE ext_parse_url_encoded_seg(VALUE self, VALUE output, VALUE kv, VALUE
|
|
227
227
|
return output;
|
228
228
|
}
|
229
229
|
|
230
|
-
void nyara_parse_param(VALUE output, const char* s,
|
230
|
+
void nyara_parse_param(VALUE output, const char* s, long len) {
|
231
231
|
// split with /[&;] */
|
232
|
-
|
233
|
-
|
232
|
+
long last_i = 0;
|
233
|
+
long i = 0;
|
234
234
|
for (; i < len; i++) {
|
235
235
|
if (s[i] == '&' || s[i] == ';') {
|
236
236
|
if (i > last_i) {
|
@@ -265,11 +265,11 @@ static VALUE _cookie_seg_str_new(const char* s, long len) {
|
|
265
265
|
static VALUE ext_parse_cookie(VALUE self, VALUE output, VALUE str) {
|
266
266
|
volatile VALUE arr = rb_ary_new();
|
267
267
|
const char* s = RSTRING_PTR(str);
|
268
|
-
|
268
|
+
long len = RSTRING_LEN(str);
|
269
269
|
|
270
270
|
// split with / *[,;] */
|
271
|
-
|
272
|
-
|
271
|
+
long last_i = 0;
|
272
|
+
long i = 0;
|
273
273
|
for (; i < len; i++) {
|
274
274
|
if (s[i] == ',' || s[i] == ';') {
|
275
275
|
// char* and len parse_seg
|
data/hello.rb
CHANGED
data/lib/nyara/controller.rb
CHANGED
@@ -14,7 +14,7 @@ module Nyara
|
|
14
14
|
action = RouteEntry.new
|
15
15
|
action.http_method = HTTP_METHODS[method]
|
16
16
|
action.path = path
|
17
|
-
action.set_accept_exts @
|
17
|
+
action.set_accept_exts @formats
|
18
18
|
action.id = @curr_id.to_sym if @curr_id
|
19
19
|
action.blk = blk
|
20
20
|
@route_entries << action
|
@@ -25,7 +25,7 @@ module Nyara
|
|
25
25
|
@curr_id = nil
|
26
26
|
@meta_exist = nil
|
27
27
|
end
|
28
|
-
@
|
28
|
+
@formats = nil
|
29
29
|
end
|
30
30
|
|
31
31
|
# Set meta data for next action
|
@@ -50,7 +50,7 @@ module Nyara
|
|
50
50
|
|
51
51
|
if opts
|
52
52
|
# todo add opts: strong param, etag, cache-control
|
53
|
-
@
|
53
|
+
@formats = opts[:formats]
|
54
54
|
end
|
55
55
|
|
56
56
|
@meta_exist = true
|
@@ -167,8 +167,8 @@ module Nyara
|
|
167
167
|
scheme << host << path
|
168
168
|
end
|
169
169
|
|
170
|
-
def
|
171
|
-
request.
|
170
|
+
def format
|
171
|
+
request.format
|
172
172
|
end
|
173
173
|
|
174
174
|
def header
|
@@ -244,7 +244,8 @@ module Nyara
|
|
244
244
|
r.response_content_type ||
|
245
245
|
header.aref_content_type ||
|
246
246
|
(r.accept and MIME_TYPES[r.accept]) ||
|
247
|
-
template_deduced_content_type
|
247
|
+
template_deduced_content_type ||
|
248
|
+
'text/html'
|
248
249
|
|
249
250
|
header.reverse_merge! OK_RESP_HEADER
|
250
251
|
|
data/lib/nyara/request.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Nyara
|
4
4
|
# request and handler
|
5
5
|
class Request
|
6
|
-
# c-ext: http_method, scope, path,
|
6
|
+
# c-ext: http_method, scope, path, query, path_with_query format, accept, header
|
7
7
|
# status, response_content_type, response_header, response_header_extra_lines
|
8
8
|
# todo: body, move all underline methods into Ext
|
9
9
|
|
@@ -77,10 +77,6 @@ module Nyara
|
|
77
77
|
header["Requested-With"] == "XMLHttpRequest"
|
78
78
|
end
|
79
79
|
|
80
|
-
def accept
|
81
|
-
@accept ||= Ext.parse_accept_value header['Accept']
|
82
|
-
end
|
83
|
-
|
84
80
|
def accept_language
|
85
81
|
@accept_language ||= Ext.parse_accept_value header['Accept-Language']
|
86
82
|
end
|
@@ -116,11 +112,12 @@ module Nyara
|
|
116
112
|
|
117
113
|
def param
|
118
114
|
@param ||= begin
|
119
|
-
|
115
|
+
q = query.dup
|
120
116
|
if form?
|
121
|
-
|
117
|
+
# todo read body, change encoding
|
118
|
+
Ext.parse_param q, body
|
122
119
|
end
|
123
|
-
|
120
|
+
q
|
124
121
|
end
|
125
122
|
end
|
126
123
|
|
data/nyara.gemspec
CHANGED
data/rakefile
CHANGED
@@ -4,6 +4,8 @@ Dir.chdir __dir__
|
|
4
4
|
|
5
5
|
status_file = "ext/inc/status_codes.inc"
|
6
6
|
version_file = "ext/inc/version.inc"
|
7
|
+
makefile = "ext/Makefile"
|
8
|
+
extconf = "ext/extconf.rb"
|
7
9
|
|
8
10
|
desc "code generate"
|
9
11
|
task :gen => [status_file, version_file]
|
@@ -42,13 +44,57 @@ file version_file => 'nyara.gemspec' do
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
desc "generate makefile"
|
48
|
+
file makefile => extconf do
|
49
|
+
Dir.chdir 'ext' do
|
50
|
+
sh 'ruby extconf.rb'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
45
54
|
desc "build ext"
|
46
|
-
task :build do
|
55
|
+
task :build => makefile do
|
47
56
|
Dir.chdir 'ext' do
|
48
57
|
sh 'make'
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
def term_color n
|
62
|
+
print "\e[38;5;#{n}m"
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset_color
|
66
|
+
print "\e[00m"
|
67
|
+
end
|
68
|
+
|
69
|
+
desc "check arity of rb_define_method/rb_define_singleton_method"
|
70
|
+
task :check_arity do
|
71
|
+
Dir.glob 'ext/*.{c,cc}' do |f|
|
72
|
+
puts "validatign #{f}"
|
73
|
+
arities = {}
|
74
|
+
data = File.read f
|
75
|
+
data.scan /^(?:static )?VALUE (\w+)\((.+)\)/ do |func, params|
|
76
|
+
arities[func] = params.count(',')
|
77
|
+
puts " scan: #{func}/#{arities[func]}"
|
78
|
+
end
|
79
|
+
data.scan /rb_define(?:_singleton)?_method\(.*?(\w+)\s*\,\s*(\d+)\)/ do |func, arity|
|
80
|
+
print " check: #{func}/#{arity} "
|
81
|
+
if arities[func].nil?
|
82
|
+
term_color 5
|
83
|
+
print "UNSCANNED"
|
84
|
+
reset_color
|
85
|
+
puts
|
86
|
+
elsif arities[func] != arity.to_i
|
87
|
+
term_color 9
|
88
|
+
print "MISMATCH #{arities[func]}"
|
89
|
+
reset_color
|
90
|
+
puts
|
91
|
+
else
|
92
|
+
puts "OK"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
52
98
|
desc "test"
|
53
99
|
task :test => :build do
|
54
100
|
sh 'rspec', '-c'
|
@@ -58,8 +104,10 @@ desc "build and test"
|
|
58
104
|
task :default => :test
|
59
105
|
|
60
106
|
desc "build and install gem"
|
61
|
-
task :gem do
|
62
|
-
|
107
|
+
task :gem => 'gen' do
|
108
|
+
Dir.glob('*.gem') do |f|
|
109
|
+
sh 'rm', f
|
110
|
+
end
|
63
111
|
sh 'gem', 'build', 'nyara.gemspec'
|
64
112
|
gem_package = Dir.glob('*.gem').first
|
65
113
|
sh 'gem', 'install', '--no-rdoc', '--no-ri', gem_package
|
data/readme.md
CHANGED
@@ -1,35 +1,96 @@
|
|
1
|
-
|
1
|
+
Not Yet Another Ruby Async web framework and server. Not on rack nor rack-compatible neither eventmachine.
|
2
|
+
|
3
|
+
- Evented IO while API remains synchrony
|
4
|
+
- Prefork production server
|
5
|
+
- Sinatra-like http method and scanf-like http path and path helper
|
6
|
+
- Request format matcher with just `case ... when`
|
7
|
+
- Easy to stream the view with `Fiber.yield`
|
8
|
+
|
9
|
+
# Getting started
|
10
|
+
|
11
|
+
Requires Ruby 2.0+, BSD/Linux/Mac OS X.
|
12
|
+
|
13
|
+
Install
|
14
|
+
|
15
|
+
```bash
|
16
|
+
gem ins --pre nyara
|
17
|
+
```
|
18
|
+
|
19
|
+
Edit a file, name it `nyahaha.rb` for example
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'nyara'
|
23
|
+
get '/' do
|
24
|
+
send_string 'hello world'
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
And start server
|
29
|
+
|
30
|
+
```bash
|
31
|
+
ruby nyahaha.rb
|
32
|
+
```
|
33
|
+
|
34
|
+
# Build from source
|
35
|
+
|
36
|
+
After cloning
|
37
|
+
|
38
|
+
```bash
|
39
|
+
git submodule update --init
|
40
|
+
bundle
|
2
41
|
rake gen
|
3
42
|
rake gem
|
43
|
+
```
|
4
44
|
|
5
45
|
# Why fast
|
6
46
|
|
7
|
-
|
47
|
+
### Solid http parsers written in C
|
48
|
+
|
49
|
+
Nyara uses two evented parsers:
|
50
|
+
|
51
|
+
- [http_parser](https://github.com/joyent/http-parser) with chunked encoding support
|
52
|
+
- [multipart-parser-c](https://github.com/iafonov/multipart-parser-c) (todo)
|
53
|
+
|
54
|
+
And implemented the following in addition:
|
55
|
+
|
56
|
+
- RFC2616 compliant `Accept*` parser
|
57
|
+
- MIME type matcher
|
58
|
+
- Url-encoded parser for path / query / request body
|
59
|
+
|
60
|
+
### Decisive routing on header complete
|
61
|
+
|
62
|
+
To support HTTP methods like PUT, DELETE for archaic browsers, a technique called **method override** is used, and the HTTP method can be overriden by a request param (usually named `_method`). In Rack the param may rest in request body, so it needs to parse the whole body before routing to a desired action. In Nyara the param is always in request path query, and routing starts when header completes. So server can do a lot of things before a huge file completely uploaded and provide better user experience.
|
63
|
+
|
64
|
+
### Thin evented IO layer built for BSD or Linux
|
65
|
+
|
66
|
+
Nyara is only for systems with kqueue or epoll (maybe iocp in the future). Manufactural event queue is a waste of time.
|
67
|
+
|
68
|
+
### Solve sequential problems with Fiber
|
69
|
+
|
70
|
+
The common caveats of an evented framework is: mutual callbacks must be used to ensure the order of operations. In eventmachine, sent data buffers are copied and chained in a deque to flush, and `close_connection_after_writing` must be called to ensure that all data are written before close.
|
71
|
+
|
72
|
+
While in Nyara, the data sending is much simpler: we send them directly, if there are remaining bytes, the action fiber is paused. When the socket is ready again, we wake up the fiber to continue the send action. So a lot of duplications, memory copy and schedule are avoided and `close` is the `close`.
|
73
|
+
|
74
|
+
### More stable memory management
|
8
75
|
|
9
|
-
|
76
|
+
To make better user experience, you may tune the server to stop GC while doing request, and start GC again after every serveral requests. But by doing so you are increasing the probability of OOM: there are cases when `malloc` or `new` fails to get memory while the GC stopped by you can release some. With C-ext this can be partly fixed with Ruby's `ALLOC` and `ALLOC_N`, which can make GC release some memory when `malloc` fails. But with C++ this becomes a bit messy: you need to redefine `new` operators.
|
10
77
|
|
11
|
-
|
12
|
-
- multipart request parser
|
78
|
+
In Nyara, the use of C++ memory allocation is limited to boot time (the use of C++ may possibly removed in the future) so your server has less chance to be quit by a silent `OutOfMemoryException`.
|
13
79
|
|
14
|
-
|
80
|
+
### Shared buffer in layout rendering
|
15
81
|
|
16
|
-
|
17
|
-
- mime type parser and content type matcher
|
18
|
-
- url-encoded parser for parsing path / query / request body
|
82
|
+
Consider you have a page with nested layout: `layout1` encloses `layout2`, and `layout2` contains `page`.
|
19
83
|
|
20
|
-
|
84
|
+
When rendering `layout2`, the output string of `page` becomes an element inside the array buffer of `layout2`, then the output of `page` is duplicated in the output of `layout2`. When rendering `layout1`, the output of `layout2` is duplicated so a string containing the output of `page` is duplicated, again.
|
21
85
|
|
22
|
-
|
86
|
+
In Nyara, nested templates of Slim, ERB or Haml share the same output buffer, so the duplication is greatly reduced.
|
23
87
|
|
24
88
|
# How fast
|
25
89
|
|
26
|
-
|
90
|
+
Performance is feature, there are specs on (TODO):
|
27
91
|
|
28
92
|
- Accept-* parse vs rack
|
29
|
-
- MIME
|
30
|
-
-
|
31
|
-
-
|
32
|
-
- layout engine vs tilt
|
93
|
+
- MIME matching vs rack
|
94
|
+
- param parse vs ruby
|
95
|
+
- layout rendering vs tilt
|
33
96
|
- evented IO vs eventmachine
|
34
|
-
- helloworld vs sinatra
|
35
|
-
- pseudo real app speed vs jruby sinatra threaded mode
|
data/spec/ext_route_spec.rb
CHANGED
@@ -50,20 +50,20 @@ module Nyara
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it '#lookup_route' do
|
53
|
-
scope, cont, args = Ext.lookup_route 'GET', '/hello'
|
53
|
+
scope, cont, args = Ext.lookup_route 'GET', '/hello', nil
|
54
54
|
assert_equal @e2.scope, scope
|
55
55
|
assert_equal @e2.controller, cont
|
56
56
|
assert_equal [:'#second'], args
|
57
57
|
|
58
|
-
scope, cont, args = Ext.lookup_route 'GET', '/hello/3world'
|
58
|
+
scope, cont, args = Ext.lookup_route 'GET', '/hello/3world', nil
|
59
59
|
assert_equal @e1.scope, scope
|
60
60
|
assert_equal @e1.controller, cont
|
61
61
|
assert_equal [3, :'#1'], args
|
62
62
|
|
63
|
-
scope, _ = Ext.lookup_route 'GET', '/world'
|
63
|
+
scope, _ = Ext.lookup_route 'GET', '/world', nil
|
64
64
|
assert_equal nil, scope
|
65
65
|
|
66
|
-
scope, _, args = Ext.lookup_route 'GET', '/a目录/2013-6-1'
|
66
|
+
scope, _, args = Ext.lookup_route 'GET', '/a目录/2013-6-1', nil
|
67
67
|
assert_equal [2013, 6, 1, :'#dir'], args
|
68
68
|
end
|
69
69
|
end
|
@@ -5,14 +5,6 @@ module Nyara
|
|
5
5
|
class DelegateController < Controller
|
6
6
|
end
|
7
7
|
|
8
|
-
before :all do
|
9
|
-
Ext.set_skip_on_url true
|
10
|
-
end
|
11
|
-
|
12
|
-
after :all do
|
13
|
-
Ext.set_skip_on_url false
|
14
|
-
end
|
15
|
-
|
16
8
|
before :each do
|
17
9
|
@client, @server = Socket.pair :UNIX, :STREAM
|
18
10
|
Ext.set_nonblock @server.fileno
|
@@ -20,7 +12,7 @@ module Nyara
|
|
20
12
|
Ext.set_request_attrs @request, {
|
21
13
|
method_num: HTTP_METHODS['GET'],
|
22
14
|
path: '/search',
|
23
|
-
|
15
|
+
query: ParamHash.new.tap{|h| h['q'] = 'nyara' },
|
24
16
|
fiber: Fiber.new{},
|
25
17
|
scope: '/scope',
|
26
18
|
header: HeaderHash.new.tap{|h| h['Accept'] = 'en-US' }
|
data/spec/request_spec.rb
CHANGED
@@ -2,14 +2,6 @@ require_relative "spec_helper"
|
|
2
2
|
|
3
3
|
module Nyara
|
4
4
|
describe Request do
|
5
|
-
before :all do
|
6
|
-
Ext.set_skip_on_url true
|
7
|
-
end
|
8
|
-
|
9
|
-
after :all do
|
10
|
-
Ext.set_skip_on_url false
|
11
|
-
end
|
12
|
-
|
13
5
|
before :each do
|
14
6
|
@server, @client = Socket.pair :UNIX, :STREAM, 0
|
15
7
|
Ext.set_nonblock @server.fileno
|
@@ -18,10 +10,10 @@ module Nyara
|
|
18
10
|
@request_attrs = {
|
19
11
|
method_num: HTTP_METHODS['GET'],
|
20
12
|
path: '/',
|
21
|
-
|
13
|
+
query: HeaderHash.new.tap{|h| h['id'] = 1 },
|
22
14
|
fiber: nil,
|
23
15
|
scope: '/',
|
24
|
-
|
16
|
+
format: 'html'
|
25
17
|
}
|
26
18
|
set_request_attrs
|
27
19
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/view_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nyara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.pre
|
4
|
+
version: 0.0.1.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zete Lui
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Fast, slim and fuzzy ruby web framework + server, based on preforked
|
14
14
|
event queue and Fiber. NO rack NOR eventmachine are used.
|