nyara 0.0.1.pre.5 → 0.0.1.pre.6
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/example/factorial.rb +19 -0
- data/ext/accept.c +2 -2
- data/ext/event.c +48 -23
- data/ext/extconf.rb +2 -0
- data/ext/hashes.c +28 -3
- data/ext/http-parser/http_parser.h +1 -0
- data/ext/nyara.c +20 -3
- data/ext/nyara.h +13 -2
- data/ext/request.c +90 -13
- data/ext/request.h +8 -2
- data/ext/request_parse.c +135 -6
- data/ext/route.cc +7 -10
- data/ext/test_response.c +155 -0
- data/ext/url_encoded.c +0 -5
- data/lib/nyara/config.rb +5 -0
- data/lib/nyara/controller.rb +91 -28
- data/lib/nyara/cookie.rb +7 -0
- data/lib/nyara/flash.rb +23 -0
- data/lib/nyara/hashes/header_hash.rb +2 -0
- data/lib/nyara/nyara.rb +14 -2
- data/lib/nyara/part.rb +156 -0
- data/lib/nyara/patches/array.rb +5 -0
- data/lib/nyara/patches/blank.rb +128 -0
- data/lib/nyara/patches/json.rb +15 -0
- data/lib/nyara/patches/mini_support.rb +6 -0
- data/lib/nyara/patches/string.rb +21 -0
- data/lib/nyara/patches/to_query.rb +113 -0
- data/lib/nyara/request.rb +13 -15
- data/lib/nyara/route.rb +15 -80
- data/lib/nyara/route_entry.rb +69 -2
- data/lib/nyara/session.rb +66 -21
- data/lib/nyara/test.rb +170 -0
- data/lib/nyara/view.rb +5 -6
- data/lib/nyara.rb +7 -6
- data/nyara.gemspec +2 -2
- data/rakefile +34 -4
- data/readme.md +8 -1
- data/spec/config_spec.rb +28 -0
- data/spec/cpu_counter_spec.rb +9 -0
- data/spec/evented_io_spec.rb +1 -0
- data/spec/flash_spec.rb +29 -0
- data/spec/hashes_spec.rb +8 -0
- data/spec/mini_support_spec.rb +54 -0
- data/spec/part_spec.rb +52 -0
- data/spec/path_helper_spec.rb +22 -14
- data/spec/request_delegate_spec.rb +19 -11
- data/spec/route_entry_spec.rb +55 -0
- data/spec/session_spec.rb +69 -7
- data/spec/spec_helper.rb +3 -0
- data/spec/test_spec.rb +58 -0
- data/tools/hello.rb +11 -3
- data/tools/memcheck.rb +33 -0
- data/tools/s.rb +11 -0
- metadata +23 -7
- data/example/design.rb +0 -62
- data/example/fib.rb +0 -15
- data/spec/route_spec.rb +0 -84
- /data/ext/inc/{status_codes.inc → status_codes.h} +0 -0
data/ext/request_parse.c
CHANGED
@@ -2,17 +2,92 @@
|
|
2
2
|
|
3
3
|
#include "nyara.h"
|
4
4
|
#include "request.h"
|
5
|
+
#include <ruby/re.h>
|
5
6
|
|
7
|
+
static ID id_update;
|
8
|
+
static ID id_final;
|
6
9
|
static VALUE str_accept;
|
10
|
+
static VALUE str_content_type;
|
7
11
|
static VALUE method_override_key;
|
8
12
|
static VALUE nyara_http_methods;
|
9
13
|
|
14
|
+
static int mp_header_field(multipart_parser* parser, const char* s, size_t len) {
|
15
|
+
Request* p = multipart_parser_get_data(parser);
|
16
|
+
if (p->last_part == Qnil) {
|
17
|
+
p->last_part = rb_hash_new();
|
18
|
+
}
|
19
|
+
|
20
|
+
if (p->last_field == Qnil) {
|
21
|
+
p->last_field = rb_enc_str_new(s, len, u8_encoding);
|
22
|
+
p->last_value = Qnil;
|
23
|
+
} else {
|
24
|
+
rb_str_cat(p->last_field, s, len);
|
25
|
+
}
|
26
|
+
return 0;
|
27
|
+
}
|
28
|
+
|
29
|
+
static int mp_header_value(multipart_parser* parser, const char* s, size_t len) {
|
30
|
+
Request* p = multipart_parser_get_data(parser);
|
31
|
+
if (p->last_field == Qnil) {
|
32
|
+
if (p->last_value == Qnil) {
|
33
|
+
p->parse_state = PS_ERROR;
|
34
|
+
return 1;
|
35
|
+
}
|
36
|
+
rb_str_cat(p->last_value, s, len);
|
37
|
+
} else {
|
38
|
+
nyara_headerlize(p->last_field);
|
39
|
+
p->last_value = rb_enc_str_new(s, len, u8_encoding);
|
40
|
+
rb_hash_aset(p->last_part, p->last_field, p->last_value);
|
41
|
+
p->last_field = Qnil;
|
42
|
+
}
|
43
|
+
return 0;
|
44
|
+
}
|
45
|
+
|
46
|
+
static int mp_headers_complete(multipart_parser* parser) {
|
47
|
+
static VALUE part_class = Qnil;
|
48
|
+
if (part_class == Qnil) {
|
49
|
+
VALUE nyara = rb_const_get(rb_cModule, rb_intern("Nyara"));
|
50
|
+
part_class = rb_const_get(nyara, rb_intern("Part"));
|
51
|
+
}
|
52
|
+
|
53
|
+
Request* p = multipart_parser_get_data(parser);
|
54
|
+
p->last_field = Qnil;
|
55
|
+
p->last_value = Qnil;
|
56
|
+
p->last_part = rb_class_new_instance(1, &p->last_part, part_class);
|
57
|
+
return 0;
|
58
|
+
}
|
59
|
+
|
60
|
+
static int mp_part_data(multipart_parser* parser, const char* s, size_t len) {
|
61
|
+
Request* p = multipart_parser_get_data(parser);
|
62
|
+
rb_funcall(p->last_part, id_update, 1, rb_str_new(s, len)); // no need encoding
|
63
|
+
return 0;
|
64
|
+
}
|
65
|
+
|
66
|
+
static int mp_part_data_end(multipart_parser* parser) {
|
67
|
+
Request* p = multipart_parser_get_data(parser);
|
68
|
+
rb_ary_push(p->body, rb_funcall(p->last_part, id_final, 0));
|
69
|
+
p->last_part = Qnil;
|
70
|
+
return 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
static multipart_parser_settings multipart_settings = {
|
74
|
+
.on_header_field = mp_header_field,
|
75
|
+
.on_header_value = mp_header_value,
|
76
|
+
.on_headers_complete = mp_headers_complete,
|
77
|
+
|
78
|
+
.on_part_data_begin = NULL,
|
79
|
+
.on_part_data = mp_part_data,
|
80
|
+
.on_part_data_end = mp_part_data_end,
|
81
|
+
|
82
|
+
.on_body_end = NULL
|
83
|
+
};
|
84
|
+
|
10
85
|
static int on_url(http_parser* parser, const char* s, size_t len) {
|
11
86
|
Request* p = (Request*)parser;
|
12
87
|
p->method = parser->method;
|
13
88
|
|
14
89
|
if (p->path_with_query == Qnil) {
|
15
|
-
p->path_with_query =
|
90
|
+
p->path_with_query = rb_enc_str_new(s, len, u8_encoding);
|
16
91
|
} else {
|
17
92
|
rb_str_cat(p->path_with_query, s, len);
|
18
93
|
}
|
@@ -22,7 +97,7 @@ static int on_url(http_parser* parser, const char* s, size_t len) {
|
|
22
97
|
static int on_header_field(http_parser* parser, const char* s, size_t len) {
|
23
98
|
Request* p = (Request*)parser;
|
24
99
|
if (p->last_field == Qnil) {
|
25
|
-
p->last_field =
|
100
|
+
p->last_field = rb_enc_str_new(s, len, u8_encoding);
|
26
101
|
p->last_value = Qnil;
|
27
102
|
} else {
|
28
103
|
rb_str_cat(p->last_field, s, len);
|
@@ -40,7 +115,7 @@ static int on_header_value(http_parser* parser, const char* s, size_t len) {
|
|
40
115
|
rb_str_cat(p->last_value, s, len);
|
41
116
|
} else {
|
42
117
|
nyara_headerlize(p->last_field);
|
43
|
-
p->last_value =
|
118
|
+
p->last_value = rb_enc_str_new(s, len, u8_encoding);
|
44
119
|
rb_hash_aset(p->header, p->last_field, p->last_value);
|
45
120
|
p->last_field = Qnil;
|
46
121
|
}
|
@@ -79,6 +154,39 @@ static void _parse_path_and_query(Request* p) {
|
|
79
154
|
}
|
80
155
|
}
|
81
156
|
|
157
|
+
static char* _parse_multipart_boundary(VALUE header) {
|
158
|
+
static regex_t* re = NULL;
|
159
|
+
static OnigRegion region;
|
160
|
+
if (!re) {
|
161
|
+
// rfc2046
|
162
|
+
// regexp copied from rack
|
163
|
+
const char* pattern = "\\Amultipart/.*boundary=\\\"?([^\\\";,]+)\\\"?";
|
164
|
+
onig_new(&re, (const UChar*)pattern, (const UChar*)(pattern + strlen(pattern)),
|
165
|
+
ONIG_OPTION_NONE, ONIG_ENCODING_ASCII, ONIG_SYNTAX_RUBY, NULL);
|
166
|
+
onig_region_init(®ion);
|
167
|
+
}
|
168
|
+
|
169
|
+
VALUE content_type = rb_hash_aref(header, str_content_type);
|
170
|
+
if (content_type == Qnil) {
|
171
|
+
return NULL;
|
172
|
+
}
|
173
|
+
|
174
|
+
long len = RSTRING_LEN(content_type);
|
175
|
+
char* s = RSTRING_PTR(content_type);
|
176
|
+
|
177
|
+
long matched_len = onig_match(re, (const UChar*)s, (const UChar*)(s + len), (const UChar*)s, ®ion, 0);
|
178
|
+
if (matched_len > 0) {
|
179
|
+
// multipart-parser needs a buffer to end with '\0'
|
180
|
+
long boundary_len = region.end[0] - region.beg[0];
|
181
|
+
char* boundary_bytes = ALLOC_N(char, boundary_len + 1);
|
182
|
+
memcpy(boundary_bytes, s + region.beg[0], boundary_len);
|
183
|
+
boundary_bytes[boundary_len] = '\0';
|
184
|
+
return boundary_bytes;
|
185
|
+
} else {
|
186
|
+
return NULL;
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
82
190
|
static int on_headers_complete(http_parser* parser) {
|
83
191
|
Request* p = (Request*)parser;
|
84
192
|
p->last_field = Qnil;
|
@@ -87,11 +195,28 @@ static int on_headers_complete(http_parser* parser) {
|
|
87
195
|
_parse_path_and_query(p);
|
88
196
|
p->accept = ext_parse_accept_value(Qnil, rb_hash_aref(p->header, str_accept));
|
89
197
|
p->parse_state = PS_HEADERS_COMPLETE;
|
198
|
+
|
199
|
+
char* boundary = _parse_multipart_boundary(p->header);
|
200
|
+
if (boundary) {
|
201
|
+
p->mparser = multipart_parser_init(boundary, &multipart_settings);
|
202
|
+
xfree(boundary);
|
203
|
+
multipart_parser_set_data(p->mparser, p);
|
204
|
+
p->body = rb_ary_new();
|
205
|
+
} else {
|
206
|
+
p->body = rb_enc_str_new("", 0, u8_encoding);
|
207
|
+
}
|
208
|
+
|
90
209
|
return 0;
|
91
210
|
}
|
92
211
|
|
93
212
|
static int on_body(http_parser* parser, const char* s, size_t len) {
|
94
|
-
|
213
|
+
Request* p = (Request*)parser;
|
214
|
+
if (p->mparser) {
|
215
|
+
multipart_parser_execute(p->mparser, s, len);
|
216
|
+
// todo sum total length, if too big, trigger save to tmpfile
|
217
|
+
} else {
|
218
|
+
rb_str_cat(p->body, s, len);
|
219
|
+
}
|
95
220
|
return 0;
|
96
221
|
}
|
97
222
|
|
@@ -114,9 +239,13 @@ http_parser_settings nyara_request_parse_settings = {
|
|
114
239
|
};
|
115
240
|
|
116
241
|
void Init_request_parse(VALUE nyara) {
|
117
|
-
|
242
|
+
id_update = rb_intern("update");
|
243
|
+
id_final = rb_intern("final");
|
244
|
+
str_accept = rb_enc_str_new("Accept", strlen("Accept"), u8_encoding);
|
118
245
|
rb_gc_register_mark_object(str_accept);
|
119
|
-
|
246
|
+
str_content_type = rb_enc_str_new("Content-Type", strlen("Content-Type"), u8_encoding);
|
247
|
+
rb_gc_register_mark_object(str_content_type);
|
248
|
+
method_override_key = rb_enc_str_new("_method", strlen("_method"), u8_encoding);
|
120
249
|
OBJ_FREEZE(method_override_key);
|
121
250
|
rb_const_set(nyara, rb_intern("METHOD_OVERRIDE_KEY"), method_override_key);
|
122
251
|
nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
|
data/ext/route.cc
CHANGED
@@ -4,7 +4,6 @@ extern "C" {
|
|
4
4
|
#include "nyara.h"
|
5
5
|
}
|
6
6
|
#include <ruby/re.h>
|
7
|
-
#include <ruby/encoding.h>
|
8
7
|
#include <vector>
|
9
8
|
#include <map>
|
10
9
|
#include "inc/str_intern.h"
|
@@ -52,7 +51,6 @@ typedef RouteMap::iterator MapIter;
|
|
52
51
|
static RouteMap route_map;
|
53
52
|
static OnigRegion region; // we can reuse the region without worrying thread safety
|
54
53
|
static ID id_to_s;
|
55
|
-
static rb_encoding* u8_enc;
|
56
54
|
static VALUE str_html;
|
57
55
|
static VALUE nyara_http_methods;
|
58
56
|
|
@@ -169,13 +167,13 @@ static VALUE ext_list_route(VALUE self) {
|
|
169
167
|
for (MapIter j = route_map.begin(); j != route_map.end(); j++) {
|
170
168
|
RouteEntries* route_entries = j->second;
|
171
169
|
arr = rb_ary_new();
|
172
|
-
rb_hash_aset(route_hash,
|
170
|
+
rb_hash_aset(route_hash, rb_enc_str_new(http_method_str(j->first), strlen(http_method_str(j->first)), u8_encoding), arr);
|
173
171
|
for (EntriesIter i = route_entries->begin(); i != route_entries->end(); i++) {
|
174
172
|
e = rb_ary_new();
|
175
173
|
rb_ary_push(e, i->is_sub ? Qtrue : Qfalse);
|
176
174
|
rb_ary_push(e, i->scope);
|
177
|
-
rb_ary_push(e,
|
178
|
-
rb_ary_push(e,
|
175
|
+
rb_ary_push(e, rb_enc_str_new(i->prefix, i->prefix_len, u8_encoding));
|
176
|
+
rb_ary_push(e, rb_enc_str_new(i->suffix, i->suffix_len, u8_encoding));
|
179
177
|
rb_ary_push(e, i->controller);
|
180
178
|
rb_ary_push(e, i->id);
|
181
179
|
conv = rb_ary_new();
|
@@ -191,13 +189,13 @@ static VALUE ext_list_route(VALUE self) {
|
|
191
189
|
|
192
190
|
static VALUE build_args(const char* suffix, std::vector<ID>& conv) {
|
193
191
|
volatile VALUE args = rb_ary_new();
|
194
|
-
volatile VALUE str = rb_str_new2("");
|
192
|
+
volatile VALUE str = rb_str_new2(""); // tmp for conversion, no need encoding
|
195
193
|
long last_len = 0;
|
196
194
|
for (size_t j = 0; j < conv.size(); j++) {
|
197
195
|
const char* capture_ptr = suffix + region.beg[j+1];
|
198
196
|
long capture_len = region.end[j+1] - region.beg[j+1];
|
199
197
|
if (conv[j] == id_to_s) {
|
200
|
-
rb_ary_push(args, rb_enc_str_new(capture_ptr, capture_len,
|
198
|
+
rb_ary_push(args, rb_enc_str_new(capture_ptr, capture_len, u8_encoding));
|
201
199
|
} else if (capture_len == 0) {
|
202
200
|
rb_ary_push(args, Qnil);
|
203
201
|
} else {
|
@@ -224,7 +222,7 @@ static VALUE extract_ext(const char* s, long len) {
|
|
224
222
|
return Qnil;
|
225
223
|
}
|
226
224
|
}
|
227
|
-
return
|
225
|
+
return rb_enc_str_new(s, len, u8_encoding);
|
228
226
|
}
|
229
227
|
|
230
228
|
extern "C"
|
@@ -320,8 +318,7 @@ extern "C"
|
|
320
318
|
void Init_route(VALUE nyara, VALUE ext) {
|
321
319
|
nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
|
322
320
|
id_to_s = rb_intern("to_s");
|
323
|
-
|
324
|
-
str_html = rb_str_new2("html");
|
321
|
+
str_html = rb_enc_str_new("html", strlen("html"), u8_encoding);
|
325
322
|
OBJ_FREEZE(str_html);
|
326
323
|
rb_gc_register_mark_object(str_html);
|
327
324
|
onig_region_init(®ion);
|
data/ext/test_response.c
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
/* response parse callbacks, for test helper */
|
2
|
+
|
3
|
+
#include "nyara.h"
|
4
|
+
|
5
|
+
typedef struct {
|
6
|
+
http_parser hparser;
|
7
|
+
VALUE header;
|
8
|
+
VALUE body;
|
9
|
+
VALUE last_field;
|
10
|
+
VALUE last_value;
|
11
|
+
VALUE set_cookies;
|
12
|
+
} Response;
|
13
|
+
|
14
|
+
static VALUE str_set_cookie;
|
15
|
+
static VALUE nyara_http_methods;
|
16
|
+
|
17
|
+
static int on_header_field(http_parser* parser, const char* s, size_t len) {
|
18
|
+
Response* p = (Response*)parser;
|
19
|
+
if (p->last_field == Qnil) {
|
20
|
+
p->last_field = rb_enc_str_new(s, len, u8_encoding);
|
21
|
+
p->last_value = Qnil;
|
22
|
+
} else {
|
23
|
+
rb_str_cat(p->last_field, s, len);
|
24
|
+
}
|
25
|
+
return 0;
|
26
|
+
}
|
27
|
+
|
28
|
+
static int on_header_value(http_parser* parser, const char* s, size_t len) {
|
29
|
+
Response* p = (Response*)parser;
|
30
|
+
if (p->last_field == Qnil) {
|
31
|
+
if (p->last_value == Qnil) {
|
32
|
+
// todo show where
|
33
|
+
rb_raise(rb_eRuntimeError, "parse error");
|
34
|
+
return 1;
|
35
|
+
}
|
36
|
+
rb_str_cat(p->last_value, s, len);
|
37
|
+
} else {
|
38
|
+
nyara_headerlize(p->last_field);
|
39
|
+
p->last_value = rb_enc_str_new(s, len, u8_encoding);
|
40
|
+
if (RTEST(rb_funcall(p->last_field, rb_intern("=="), 1, str_set_cookie))) {
|
41
|
+
rb_ary_push(p->set_cookies, p->last_value);
|
42
|
+
} else {
|
43
|
+
rb_hash_aset(p->header, p->last_field, p->last_value);
|
44
|
+
}
|
45
|
+
p->last_field = Qnil;
|
46
|
+
}
|
47
|
+
return 0;
|
48
|
+
}
|
49
|
+
|
50
|
+
static int on_headers_complete(http_parser* parser) {
|
51
|
+
Response* p = (Response*)parser;
|
52
|
+
p->last_field = Qnil;
|
53
|
+
p->last_value = Qnil;
|
54
|
+
return 0;
|
55
|
+
}
|
56
|
+
|
57
|
+
static int on_body(http_parser* parser, const char* s, size_t len) {
|
58
|
+
Response* p = (Response*)parser;
|
59
|
+
if (p->body == Qnil) {
|
60
|
+
p->body = rb_enc_str_new(s, len, u8_encoding);
|
61
|
+
} else {
|
62
|
+
rb_str_cat(p->body, s, len);
|
63
|
+
}
|
64
|
+
return 0;
|
65
|
+
}
|
66
|
+
|
67
|
+
static int on_message_complete(http_parser* parser) {
|
68
|
+
Response* p = (Response*)parser;
|
69
|
+
p->last_field = Qnil;
|
70
|
+
p->last_value = Qnil;
|
71
|
+
return 0;
|
72
|
+
}
|
73
|
+
|
74
|
+
static http_parser_settings response_parse_settings = {
|
75
|
+
.on_message_begin = NULL,
|
76
|
+
.on_url = NULL,
|
77
|
+
.on_status_complete = NULL,
|
78
|
+
.on_header_field = on_header_field,
|
79
|
+
.on_header_value = on_header_value,
|
80
|
+
.on_headers_complete = on_headers_complete,
|
81
|
+
.on_body = on_body,
|
82
|
+
.on_message_complete = on_message_complete
|
83
|
+
};
|
84
|
+
|
85
|
+
static void response_mark(void* pp) {
|
86
|
+
Response* p = pp;
|
87
|
+
if (p) {
|
88
|
+
rb_gc_mark_maybe(p->header);
|
89
|
+
rb_gc_mark_maybe(p->body);
|
90
|
+
rb_gc_mark_maybe(p->last_field);
|
91
|
+
rb_gc_mark_maybe(p->last_value);
|
92
|
+
rb_gc_mark_maybe(p->set_cookies);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
static VALUE response_alloc(VALUE klass) {
|
97
|
+
Response* p = ALLOC(Response);
|
98
|
+
http_parser_init(&(p->hparser), HTTP_RESPONSE);
|
99
|
+
p->header = Qnil;
|
100
|
+
p->body = Qnil;
|
101
|
+
p->last_field = Qnil;
|
102
|
+
p->last_value = Qnil;
|
103
|
+
p->set_cookies = Qnil;
|
104
|
+
// NOTE new in alloc func will crash GCC-4.2/4.6 in GC.stress mode
|
105
|
+
return Data_Wrap_Struct(klass, response_mark, xfree, p);
|
106
|
+
}
|
107
|
+
|
108
|
+
static VALUE response_initialize(VALUE self, VALUE data) {
|
109
|
+
Check_Type(data, T_STRING);
|
110
|
+
Response* p;
|
111
|
+
Data_Get_Struct(self, Response, p);
|
112
|
+
p->header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
113
|
+
p->set_cookies = rb_ary_new();
|
114
|
+
http_parser_execute(&(p->hparser), &response_parse_settings, RSTRING_PTR(data), RSTRING_LEN(data));
|
115
|
+
return self;
|
116
|
+
}
|
117
|
+
|
118
|
+
static VALUE response_header(VALUE self) {
|
119
|
+
Response* p;
|
120
|
+
Data_Get_Struct(self, Response, p);
|
121
|
+
return p->header;
|
122
|
+
}
|
123
|
+
|
124
|
+
static VALUE response_body(VALUE self) {
|
125
|
+
Response* p;
|
126
|
+
Data_Get_Struct(self, Response, p);
|
127
|
+
return p->body;
|
128
|
+
}
|
129
|
+
|
130
|
+
static VALUE response_status(VALUE self) {
|
131
|
+
Response* p;
|
132
|
+
Data_Get_Struct(self, Response, p);
|
133
|
+
return INT2FIX(p->hparser.status_code);
|
134
|
+
}
|
135
|
+
|
136
|
+
static VALUE response_set_cookies(VALUE self) {
|
137
|
+
Response* p;
|
138
|
+
Data_Get_Struct(self, Response, p);
|
139
|
+
return p->set_cookies;
|
140
|
+
}
|
141
|
+
|
142
|
+
void Init_test_response(VALUE nyara) {
|
143
|
+
str_set_cookie = rb_enc_str_new("Set-Cookie", strlen("Set-Cookie"), u8_encoding);
|
144
|
+
rb_gc_register_mark_object(str_set_cookie);
|
145
|
+
|
146
|
+
nyara_http_methods = rb_const_get(nyara, rb_intern("HTTP_METHODS"));
|
147
|
+
VALUE test = rb_define_module_under(nyara, "Test");
|
148
|
+
VALUE response = rb_define_class_under(test, "Response", rb_cObject);
|
149
|
+
rb_define_alloc_func(response, response_alloc);
|
150
|
+
rb_define_method(response, "initialize", response_initialize, 1);
|
151
|
+
rb_define_method(response, "header", response_header, 0);
|
152
|
+
rb_define_method(response, "body", response_body, 0);
|
153
|
+
rb_define_method(response, "status", response_status, 0);
|
154
|
+
rb_define_method(response, "set_cookies", response_set_cookies, 0);
|
155
|
+
}
|
data/ext/url_encoded.c
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
/* url-encoded parsing */
|
2
2
|
|
3
3
|
#include "nyara.h"
|
4
|
-
#include <ruby/encoding.h>
|
5
|
-
|
6
|
-
static rb_encoding* u8_encoding;
|
7
4
|
|
8
5
|
static char _half_octet(char c) {
|
9
6
|
// there's a faster way but not validating the range:
|
@@ -354,8 +351,6 @@ static VALUE ext_parse_cookie(VALUE self, VALUE output, VALUE str) {
|
|
354
351
|
}
|
355
352
|
|
356
353
|
void Init_url_encoded(VALUE ext) {
|
357
|
-
u8_encoding = rb_utf8_encoding();
|
358
|
-
|
359
354
|
rb_define_singleton_method(ext, "parse_param", ext_parse_param, 2);
|
360
355
|
rb_define_singleton_method(ext, "parse_cookie", ext_parse_cookie, 2);
|
361
356
|
// for test
|
data/lib/nyara/config.rb
CHANGED
data/lib/nyara/controller.rb
CHANGED
@@ -15,7 +15,7 @@ module Nyara
|
|
15
15
|
action.http_method = HTTP_METHODS[method]
|
16
16
|
action.path = path
|
17
17
|
action.set_accept_exts @formats
|
18
|
-
action.id = @curr_id
|
18
|
+
action.id = @curr_id if @curr_id
|
19
19
|
action.blk = blk
|
20
20
|
@route_entries << action
|
21
21
|
|
@@ -45,7 +45,7 @@ module Nyara
|
|
45
45
|
if tag
|
46
46
|
# todo scan class
|
47
47
|
id = tag[/\#\w++(\-\w++)*/]
|
48
|
-
@curr_id = id
|
48
|
+
@curr_id = id.to_sym
|
49
49
|
end
|
50
50
|
|
51
51
|
if opts
|
@@ -81,8 +81,8 @@ module Nyara
|
|
81
81
|
http 'PATCH', path, &blk
|
82
82
|
end
|
83
83
|
|
84
|
-
# HTTP OPTIONS
|
85
|
-
# todo generate options response for a url
|
84
|
+
# HTTP OPTIONS<br>
|
85
|
+
# todo generate options response for a url<br>
|
86
86
|
# see http://tools.ietf.org/html/rfc5789
|
87
87
|
def options path, &blk
|
88
88
|
http 'OPTIONS', path, &blk
|
@@ -104,8 +104,7 @@ module Nyara
|
|
104
104
|
end
|
105
105
|
attr_reader :controller_name
|
106
106
|
|
107
|
-
# :nodoc:
|
108
|
-
def preprocess_actions
|
107
|
+
def compile_route_entries scope # :nodoc:
|
109
108
|
raise "#{self}: no action defined" unless @route_entries
|
110
109
|
|
111
110
|
curr_id = :'#0'
|
@@ -118,68 +117,127 @@ module Nyara
|
|
118
117
|
}
|
119
118
|
next_id[]
|
120
119
|
|
120
|
+
@path_templates = {}
|
121
121
|
@route_entries.each do |e|
|
122
|
-
e.id
|
122
|
+
e.id = next_id[] if e.id.empty?
|
123
123
|
define_method e.id, &e.blk
|
124
|
+
e.compile self, scope
|
125
|
+
e.validate
|
126
|
+
@path_templates[e.id] = e.path_template
|
124
127
|
end
|
125
128
|
@route_entries
|
126
129
|
end
|
130
|
+
|
131
|
+
attr_accessor :path_templates
|
127
132
|
end
|
128
133
|
|
129
134
|
include Renderable
|
130
135
|
|
131
|
-
# :nodoc:
|
132
136
|
def self.inherited klass
|
133
137
|
# klass will also have this inherited method
|
134
138
|
# todo check class name
|
135
139
|
klass.extend ClassMethods
|
136
|
-
[:@
|
140
|
+
[:@used_ids, :@default_layout].each do |iv|
|
137
141
|
klass.instance_variable_set iv, klass.superclass.instance_variable_get(iv)
|
138
142
|
end
|
143
|
+
|
144
|
+
route_entries = klass.superclass.instance_variable_get :@route_entries
|
145
|
+
if route_entries
|
146
|
+
route_entries.map! {|e| e.dup }
|
147
|
+
klass.instance_variable_set :@route_entries, route_entries
|
148
|
+
end
|
139
149
|
end
|
140
150
|
|
141
151
|
# Path helper
|
142
|
-
def
|
152
|
+
def path_to id, *args
|
143
153
|
if args.last.is_a?(Hash)
|
144
154
|
opts = args.pop
|
145
155
|
end
|
146
156
|
|
147
|
-
r =
|
157
|
+
r = self.class.path_templates[id.to_s] % args
|
148
158
|
|
149
159
|
if opts
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
"#{CGI.escape k.to_s}=#{CGI.escape v}"
|
154
|
-
end
|
155
|
-
query.compact!
|
156
|
-
r << '?' << query.join('&') unless query.empty?
|
160
|
+
format = opts.delete :format
|
161
|
+
r << ".#{format}" if format
|
162
|
+
r << '?' << opts.to_query unless opts.empty?
|
157
163
|
end
|
158
164
|
r
|
159
165
|
end
|
160
166
|
|
161
|
-
# Url helper
|
162
|
-
# NOTE: host can include port
|
163
|
-
|
167
|
+
# Url helper<br>
|
168
|
+
# NOTE: host string can include port number<br>
|
169
|
+
# TODO: user and password?
|
170
|
+
def url_to id, *args, scheme: nil, host: nil, **opts
|
164
171
|
scheme = scheme ? scheme.sub(/\:?$/, '://') : '//'
|
165
|
-
host ||=
|
166
|
-
path =
|
172
|
+
host ||= request.host_with_port
|
173
|
+
path = path_to id, *args, opts
|
167
174
|
scheme << host << path
|
168
175
|
end
|
169
176
|
|
177
|
+
# Redirect to a url or path, terminates action<br>
|
178
|
+
# +status+ can be one of:
|
179
|
+
#
|
180
|
+
# - 300 multiple choices (e.g. offer different languages)
|
181
|
+
# - 301 moved permanently
|
182
|
+
# - 302 found (default)
|
183
|
+
# - 303 see other (e.g. for results of cgi-scripts)
|
184
|
+
# - 307 temporary redirect
|
185
|
+
#
|
186
|
+
# Caveats: there's no content in a redirect response yet, if you want one, you can configure nginx to add it
|
187
|
+
def redirect url_or_path, status=302
|
188
|
+
status = status.to_i
|
189
|
+
raise "unsupported redirect status: #{status}" unless HTTP_REDIRECT_STATUS.include?(status)
|
190
|
+
|
191
|
+
r = request
|
192
|
+
header = r.header
|
193
|
+
self.status status
|
194
|
+
|
195
|
+
uri = URI.parse url_or_path
|
196
|
+
if uri.host.nil?
|
197
|
+
uri.host = request.domain
|
198
|
+
uri.port = request.port
|
199
|
+
end
|
200
|
+
uri.scheme = r.ssl? ? 'https' : 'http'
|
201
|
+
r.header['Location'] = uri.to_s
|
202
|
+
|
203
|
+
# similar to send_header, but without content-type
|
204
|
+
Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
|
205
|
+
data = header.serialize
|
206
|
+
data.concat r.response_header_extra_lines
|
207
|
+
data << "\r\n"
|
208
|
+
Ext.request_send_data r, data.join
|
209
|
+
|
210
|
+
Fiber.yield :term_close
|
211
|
+
end
|
212
|
+
|
213
|
+
# Shortcut for +redirect url_to *xs+
|
214
|
+
def redirect_to *xs
|
215
|
+
redirect url_to(*xs)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Request extension or generated by `Accept`
|
170
219
|
def format
|
171
220
|
request.format
|
172
221
|
end
|
173
222
|
|
223
|
+
# Request header<br>
|
224
|
+
# NOTE to change response header, use +set_header+
|
174
225
|
def header
|
175
226
|
request.header
|
176
227
|
end
|
177
228
|
alias headers header
|
178
229
|
|
179
|
-
|
180
|
-
|
230
|
+
# Set response header
|
231
|
+
def set_header field, value
|
232
|
+
request.response_header[field] = value
|
181
233
|
end
|
182
234
|
|
235
|
+
# Append an extra line in reponse header
|
236
|
+
#
|
237
|
+
# :call-seq:
|
238
|
+
#
|
239
|
+
# add_header_line "X-Myheader: here we are"
|
240
|
+
#
|
183
241
|
def add_header_line h
|
184
242
|
raise 'can not modify sent header' if request.response_header.frozen?
|
185
243
|
h = h.sub /(?<![\r\n])\z/, "\r\n"
|
@@ -239,6 +297,10 @@ module Nyara
|
|
239
297
|
request.session
|
240
298
|
end
|
241
299
|
|
300
|
+
def flash
|
301
|
+
request.flash
|
302
|
+
end
|
303
|
+
|
242
304
|
# Set response status
|
243
305
|
def status n
|
244
306
|
raise ArgumentError, "unsupported status: #{n}" unless HTTP_STATUS_FIRST_LINES[n]
|
@@ -268,15 +330,16 @@ module Nyara
|
|
268
330
|
|
269
331
|
header.reverse_merge! OK_RESP_HEADER
|
270
332
|
|
271
|
-
data = header.
|
272
|
-
"#{k}: #{v}\r\n"
|
273
|
-
end
|
333
|
+
data = header.serialize
|
274
334
|
data.concat r.response_header_extra_lines
|
335
|
+
data << Session.encode_set_cookie(r.session, r.ssl?)
|
275
336
|
data << "\r\n"
|
276
337
|
Ext.request_send_data r, data.join
|
277
338
|
|
278
339
|
# forbid further modification
|
279
340
|
header.freeze
|
341
|
+
session.freeze
|
342
|
+
flash.next.freeze
|
280
343
|
end
|
281
344
|
|
282
345
|
# Send raw data, that is, not wrapped in chunked encoding<br>
|
data/lib/nyara/cookie.rb
CHANGED