nyara 0.0.1.pre.6 → 0.0.1.pre.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/example/project.rb +11 -0
- data/example/stream.rb +6 -2
- data/ext/event.c +83 -32
- data/ext/hashes.c +6 -1
- data/ext/inc/epoll.h +1 -2
- data/ext/inc/kqueue.h +1 -2
- data/ext/nyara.h +2 -0
- data/ext/request.c +14 -9
- data/ext/test_response.c +2 -5
- data/ext/url_encoded.c +55 -1
- data/lib/nyara/config.rb +68 -17
- data/lib/nyara/controller.rb +76 -15
- data/lib/nyara/controllers/public_controller.rb +14 -0
- data/lib/nyara/cookie.rb +5 -4
- data/lib/nyara/flash.rb +2 -0
- data/lib/nyara/nyara.rb +153 -20
- data/lib/nyara/patches/to_query.rb +1 -2
- data/lib/nyara/request.rb +0 -5
- data/lib/nyara/route.rb +2 -2
- data/lib/nyara/route_entry.rb +5 -4
- data/lib/nyara/session.rb +47 -22
- data/lib/nyara/test.rb +13 -10
- data/lib/nyara/view.rb +27 -49
- data/lib/nyara/view_handlers/erb.rb +21 -0
- data/lib/nyara/view_handlers/erubis.rb +81 -0
- data/lib/nyara/view_handlers/haml.rb +17 -0
- data/lib/nyara/view_handlers/slim.rb +16 -0
- data/nyara.gemspec +3 -1
- data/readme.md +2 -2
- data/spec/apps/connect.rb +1 -1
- data/spec/config_spec.rb +76 -4
- data/spec/{test_spec.rb → integration_spec.rb} +47 -3
- data/spec/path_helper_spec.rb +1 -1
- data/spec/performance/escape.rb +10 -0
- data/spec/performance/layout.slim +14 -0
- data/spec/performance/page.slim +16 -0
- data/spec/performance_spec.rb +6 -1
- data/spec/public/empty file.html +0 -0
- data/spec/public/index.html +1 -0
- data/spec/request_delegate_spec.rb +1 -1
- data/spec/request_spec.rb +20 -0
- data/spec/route_entry_spec.rb +7 -0
- data/spec/session_spec.rb +8 -4
- data/spec/spec_helper.rb +1 -0
- data/spec/{ext_parse_spec.rb → url_encoded_spec.rb} +17 -5
- data/spec/views/edit.haml +2 -0
- data/spec/views/edit.slim +2 -0
- data/spec/views/index.liquid +0 -0
- data/spec/views/invalid_layout.liquid +0 -0
- data/spec/views/layout.erb +1 -0
- data/spec/views/show.slim +1 -0
- data/tools/foo.rb +9 -0
- data/tools/hello.rb +16 -11
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1464261ce5d4ec90868a68278e81de0124568897
|
4
|
+
data.tar.gz: 566342b6927ac0fc3f5607580a0863d18b9be432
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c322de1bd85993df20041bd8535800751abdf4b4c8152b7ed4899a0430aed90d4552a97f21839267376f78bac18cc254fd395e9124cc071db8e4b3575ba6537c
|
7
|
+
data.tar.gz: e7c16bb717db1c131069d361087a8d97e96b1282d93d002582aa10cd0bcff01741eedefa0d43ab65c18c9ec480e1c214d3f03218c4569a1bb3786194c43c46e8
|
data/example/project.rb
ADDED
data/example/stream.rb
CHANGED
data/ext/event.c
CHANGED
@@ -18,6 +18,7 @@ extern VALUE rb_obj_reveal(VALUE obj, VALUE klass);
|
|
18
18
|
#define ETYPE_CONNECT 2
|
19
19
|
#define MAX_E 1024
|
20
20
|
static void loop_body(int fd, int etype);
|
21
|
+
static void loop_check();
|
21
22
|
static int qfd = 0;
|
22
23
|
|
23
24
|
#define MAX_RECEIVE_DATA 65536 * 2
|
@@ -38,6 +39,8 @@ static VALUE sym_writing;
|
|
38
39
|
static VALUE sym_reading;
|
39
40
|
static VALUE sym_sleep;
|
40
41
|
static Request* curr_request;
|
42
|
+
static VALUE to_resume_actions;
|
43
|
+
static bool graceful_quit = false;
|
41
44
|
|
42
45
|
static VALUE _fiber_func(VALUE _, VALUE args) {
|
43
46
|
VALUE instance = rb_ary_pop(args);
|
@@ -46,6 +49,22 @@ static VALUE _fiber_func(VALUE _, VALUE args) {
|
|
46
49
|
return Qnil;
|
47
50
|
}
|
48
51
|
|
52
|
+
static void _resume_action(Request* p) {
|
53
|
+
VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
|
54
|
+
if (state == Qnil) { // _fiber_func always returns Qnil
|
55
|
+
// terminated (todo log raised error ?)
|
56
|
+
nyara_request_term_close(p->self);
|
57
|
+
} else if (state == sym_term_close) {
|
58
|
+
nyara_request_term_close(p->self);
|
59
|
+
} else if (state == sym_writing) {
|
60
|
+
// do nothing
|
61
|
+
} else if (state == sym_reading) {
|
62
|
+
// do nothing
|
63
|
+
} else if (state == sym_sleep) {
|
64
|
+
// do nothing
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
49
68
|
static void _handle_request(VALUE request) {
|
50
69
|
Request* p;
|
51
70
|
Data_Get_Struct(request, Request, p);
|
@@ -92,27 +111,19 @@ static void _handle_request(VALUE request) {
|
|
92
111
|
p->response_header_extra_lines = rb_ary_new();
|
93
112
|
nyara_request_init_env(request);
|
94
113
|
} else {
|
95
|
-
|
114
|
+
static const char* not_found = "HTTP/1.1 404 Not Found\r\nConnection: close\r\nContent-Length: 0\r\n\r\n";
|
115
|
+
static long not_found_len = 0;
|
116
|
+
if (!not_found_len) {
|
117
|
+
not_found_len = strlen(not_found);
|
118
|
+
}
|
119
|
+
nyara_send_data(p->fd, not_found, not_found_len);
|
96
120
|
nyara_detach_fd(p->fd);
|
97
121
|
p->fd = 0;
|
98
122
|
return;
|
99
123
|
}
|
100
124
|
}
|
101
125
|
|
102
|
-
|
103
|
-
VALUE state = rb_fiber_resume(p->fiber, 0, NULL);
|
104
|
-
if (state == Qnil) { // _fiber_func always returns Qnil
|
105
|
-
// terminated (todo log raised error ?)
|
106
|
-
nyara_request_term_close(request);
|
107
|
-
} else if (state == sym_term_close) {
|
108
|
-
nyara_request_term_close(request);
|
109
|
-
} else if (state == sym_writing) {
|
110
|
-
// do nothing
|
111
|
-
} else if (state == sym_reading) {
|
112
|
-
// do nothing
|
113
|
-
} else if (state == sym_sleep) {
|
114
|
-
// do nothing
|
115
|
-
}
|
126
|
+
_resume_action(p);
|
116
127
|
}
|
117
128
|
|
118
129
|
// platform independent, invoked by LOOP_E()
|
@@ -145,6 +156,45 @@ static void loop_body(int fd, int etype) {
|
|
145
156
|
}
|
146
157
|
}
|
147
158
|
|
159
|
+
static void loop_check() {
|
160
|
+
// execute other thread / interrupts
|
161
|
+
rb_thread_schedule();
|
162
|
+
|
163
|
+
// wakeup actions which finished sleeping
|
164
|
+
long len = RARRAY_LEN(to_resume_actions);
|
165
|
+
if (len) {
|
166
|
+
VALUE* ptr = RARRAY_PTR(to_resume_actions);
|
167
|
+
for (long i = 0; i < len; i++) {
|
168
|
+
VALUE request = ptr[i];
|
169
|
+
Request* p;
|
170
|
+
Data_Get_Struct(request, Request, p);
|
171
|
+
|
172
|
+
p->sleeping = false;
|
173
|
+
if (!rb_fiber_alive_p(p->fiber)) {
|
174
|
+
continue;
|
175
|
+
}
|
176
|
+
|
177
|
+
_resume_action(p);
|
178
|
+
if (qfd) {
|
179
|
+
VALUE* v_fds = RARRAY_PTR(p->watched_fds);
|
180
|
+
long v_fds_len = RARRAY_LEN(p->watched_fds);
|
181
|
+
for (long i = 0; i < v_fds_len; i++) {
|
182
|
+
ADD_E(FIX2INT(v_fds[i]), ETYPE_CONNECT);
|
183
|
+
}
|
184
|
+
ADD_E(p->fd, ETYPE_HANDLE_REQUEST);
|
185
|
+
} else {
|
186
|
+
// we are in a test, no queue
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
if (graceful_quit) {
|
192
|
+
if (RTEST(rb_funcall(fd_request_map, rb_intern("empty?"), 0))) {
|
193
|
+
_Exit(0);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
148
198
|
void nyara_detach_fd(int fd) {
|
149
199
|
VALUE request = rb_hash_delete(fd_request_map, INT2FIX(fd));
|
150
200
|
if (request != Qnil) {
|
@@ -164,8 +214,9 @@ static VALUE ext_init_queue(VALUE _) {
|
|
164
214
|
return Qnil;
|
165
215
|
}
|
166
216
|
|
167
|
-
|
168
|
-
|
217
|
+
// run queue loop with server_fd
|
218
|
+
static VALUE ext_run_queue(VALUE _, VALUE v_server_fd) {
|
219
|
+
int fd = FIX2INT(v_server_fd);
|
169
220
|
nyara_set_nonblock(fd);
|
170
221
|
ADD_E(fd, ETYPE_CAN_ACCEPT);
|
171
222
|
|
@@ -173,6 +224,15 @@ static VALUE ext_run_queue(VALUE _, VALUE v_fd) {
|
|
173
224
|
return Qnil;
|
174
225
|
}
|
175
226
|
|
227
|
+
// set graceful quit flag and do not accept server_fd anymore
|
228
|
+
static VALUE ext_graceful_quit(VALUE _, VALUE v_server_fd) {
|
229
|
+
graceful_quit = true;
|
230
|
+
int fd = FIX2INT(v_server_fd);
|
231
|
+
DEL_E(fd);
|
232
|
+
return Qnil;
|
233
|
+
}
|
234
|
+
|
235
|
+
// put request into sleep
|
176
236
|
static VALUE ext_request_sleep(VALUE _, VALUE request) {
|
177
237
|
Request* p;
|
178
238
|
Data_Get_Struct(request, Request, p);
|
@@ -192,23 +252,10 @@ static VALUE ext_request_sleep(VALUE _, VALUE request) {
|
|
192
252
|
return Qnil;
|
193
253
|
}
|
194
254
|
|
255
|
+
// NOTE this will be executed in another thread, resuming fiber in a non-main thread will stuck
|
195
256
|
static VALUE ext_request_wakeup(VALUE _, VALUE request) {
|
196
257
|
// NOTE should not use curr_request
|
197
|
-
|
198
|
-
Data_Get_Struct(request, Request, p);
|
199
|
-
|
200
|
-
p->sleeping = false;
|
201
|
-
if (!qfd) {
|
202
|
-
// we are in a test
|
203
|
-
return Qnil;
|
204
|
-
}
|
205
|
-
|
206
|
-
VALUE* v_fds = RARRAY_PTR(p->watched_fds);
|
207
|
-
long v_fds_len = RARRAY_LEN(p->watched_fds);
|
208
|
-
for (long i = 0; i < v_fds_len; i++) {
|
209
|
-
ADD_E(FIX2INT(v_fds[i]), ETYPE_CONNECT);
|
210
|
-
}
|
211
|
-
ADD_E(p->fd, ETYPE_HANDLE_REQUEST);
|
258
|
+
rb_ary_push(to_resume_actions, request);
|
212
259
|
return Qnil;
|
213
260
|
}
|
214
261
|
|
@@ -330,8 +377,12 @@ void Init_event(VALUE ext) {
|
|
330
377
|
sym_reading = ID2SYM(rb_intern("reading"));
|
331
378
|
sym_sleep = ID2SYM(rb_intern("sleep"));
|
332
379
|
|
380
|
+
to_resume_actions = rb_ary_new();
|
381
|
+
rb_gc_register_mark_object(to_resume_actions);
|
382
|
+
|
333
383
|
rb_define_singleton_method(ext, "init_queue", ext_init_queue, 0);
|
334
384
|
rb_define_singleton_method(ext, "run_queue", ext_run_queue, 1);
|
385
|
+
rb_define_singleton_method(ext, "graceful_quit", ext_graceful_quit, 1);
|
335
386
|
|
336
387
|
rb_define_singleton_method(ext, "request_sleep", ext_request_sleep, 1);
|
337
388
|
rb_define_singleton_method(ext, "request_wakeup", ext_request_wakeup, 1);
|
data/ext/hashes.c
CHANGED
@@ -112,8 +112,13 @@ static VALUE header_hash_reverse_merge_bang(VALUE self, VALUE other) {
|
|
112
112
|
}
|
113
113
|
|
114
114
|
static int header_hash_serialize_func(VALUE k, VALUE v, VALUE arr) {
|
115
|
-
long klen = RSTRING_LEN(k);
|
116
115
|
long vlen = RSTRING_LEN(v);
|
116
|
+
// deleted field
|
117
|
+
if (vlen == 0) {
|
118
|
+
return ST_CONTINUE;
|
119
|
+
}
|
120
|
+
|
121
|
+
long klen = RSTRING_LEN(k);
|
117
122
|
long capa = klen + vlen + 4;
|
118
123
|
volatile VALUE s = rb_str_buf_new(capa);
|
119
124
|
sprintf(RSTRING_PTR(s), "%.*s: %.*s\r\n", (int)klen, RSTRING_PTR(k), (int)vlen, RSTRING_PTR(v));
|
data/ext/inc/epoll.h
CHANGED
data/ext/inc/kqueue.h
CHANGED
data/ext/nyara.h
CHANGED
@@ -24,6 +24,7 @@ void Init_request(VALUE nyara, VALUE ext);
|
|
24
24
|
VALUE nyara_request_new(int fd);
|
25
25
|
void nyara_request_init_env(VALUE request);
|
26
26
|
void nyara_request_term_close(VALUE request);
|
27
|
+
bool nyara_send_data(int fd, const char* s, long len);
|
27
28
|
|
28
29
|
|
29
30
|
/* test_response.c */
|
@@ -34,6 +35,7 @@ void Init_test_response(VALUE nyara);
|
|
34
35
|
void Init_url_encoded(VALUE ext);
|
35
36
|
long nyara_parse_path(VALUE path, const char*s, long len);
|
36
37
|
void nyara_parse_param(VALUE output, const char* s, long len);
|
38
|
+
VALUE ext_parse_cookie(VALUE self, VALUE output, VALUE str);
|
37
39
|
|
38
40
|
|
39
41
|
/* accept.c */
|
data/ext/request.c
CHANGED
@@ -106,20 +106,25 @@ VALUE nyara_request_new(int fd) {
|
|
106
106
|
}
|
107
107
|
|
108
108
|
void nyara_request_init_env(VALUE self) {
|
109
|
-
static VALUE cookie_mod = Qnil;
|
110
109
|
static VALUE session_mod = Qnil;
|
111
110
|
static VALUE flash_class = Qnil;
|
111
|
+
static VALUE str_cookie = Qnil;
|
112
112
|
static ID id_decode = 0;
|
113
|
-
if (
|
113
|
+
if (session_mod == Qnil) {
|
114
114
|
VALUE nyara = rb_const_get(rb_cModule, rb_intern("Nyara"));
|
115
|
-
cookie_mod = rb_const_get(nyara, rb_intern("Cookie"));
|
116
115
|
session_mod = rb_const_get(nyara, rb_intern("Session"));
|
117
116
|
flash_class = rb_const_get(nyara, rb_intern("Flash"));
|
117
|
+
str_cookie = rb_enc_str_new("Cookie", strlen("Cookie"), u8_encoding);
|
118
|
+
rb_gc_register_mark_object(str_cookie);
|
118
119
|
id_decode = rb_intern("decode");
|
119
120
|
}
|
120
121
|
|
121
122
|
P;
|
122
|
-
p->cookie =
|
123
|
+
p->cookie = rb_class_new_instance(0, NULL, nyara_param_hash_class);
|
124
|
+
VALUE cookie = rb_hash_aref(p->header, str_cookie);
|
125
|
+
if (cookie != Qnil) {
|
126
|
+
ext_parse_cookie(Qnil, p->cookie, cookie);
|
127
|
+
}
|
123
128
|
p->session = rb_funcall(session_mod, id_decode, 1, p->cookie);
|
124
129
|
p->flash = rb_class_new_instance(1, &p->session, flash_class);
|
125
130
|
}
|
@@ -232,7 +237,7 @@ static VALUE ext_request_set_status(VALUE _, VALUE self, VALUE n) {
|
|
232
237
|
}
|
233
238
|
|
234
239
|
// return true if success
|
235
|
-
|
240
|
+
bool nyara_send_data(int fd, const char* buf, long len) {
|
236
241
|
while(len) {
|
237
242
|
long written = write(fd, buf, len);
|
238
243
|
if (written <= 0) {
|
@@ -256,7 +261,7 @@ static VALUE ext_request_send_data(VALUE _, VALUE self, VALUE data) {
|
|
256
261
|
P;
|
257
262
|
char* buf = RSTRING_PTR(data);
|
258
263
|
long len = RSTRING_LEN(data);
|
259
|
-
|
264
|
+
nyara_send_data(p->fd, buf, len);
|
260
265
|
return Qnil;
|
261
266
|
}
|
262
267
|
|
@@ -273,9 +278,9 @@ static VALUE ext_request_send_chunk(VALUE _, VALUE self, VALUE str) {
|
|
273
278
|
rb_raise(rb_eRuntimeError, "fail to format chunk length for len: %ld", len);
|
274
279
|
}
|
275
280
|
bool success = \
|
276
|
-
|
277
|
-
|
278
|
-
|
281
|
+
nyara_send_data(p->fd, pre_buf, pre_len) &&
|
282
|
+
nyara_send_data(p->fd, RSTRING_PTR(str), len) &&
|
283
|
+
nyara_send_data(p->fd, "\r\n", 2);
|
279
284
|
|
280
285
|
if (!success) {
|
281
286
|
rb_sys_fail("write(2)");
|
data/ext/test_response.c
CHANGED
@@ -56,11 +56,7 @@ static int on_headers_complete(http_parser* parser) {
|
|
56
56
|
|
57
57
|
static int on_body(http_parser* parser, const char* s, size_t len) {
|
58
58
|
Response* p = (Response*)parser;
|
59
|
-
|
60
|
-
p->body = rb_enc_str_new(s, len, u8_encoding);
|
61
|
-
} else {
|
62
|
-
rb_str_cat(p->body, s, len);
|
63
|
-
}
|
59
|
+
rb_str_cat(p->body, s, len);
|
64
60
|
return 0;
|
65
61
|
}
|
66
62
|
|
@@ -111,6 +107,7 @@ static VALUE response_initialize(VALUE self, VALUE data) {
|
|
111
107
|
Data_Get_Struct(self, Response, p);
|
112
108
|
p->header = rb_class_new_instance(0, NULL, nyara_header_hash_class);
|
113
109
|
p->set_cookies = rb_ary_new();
|
110
|
+
p->body = rb_enc_str_new("", 0, u8_encoding);
|
114
111
|
http_parser_execute(&(p->hparser), &response_parse_settings, RSTRING_PTR(data), RSTRING_LEN(data));
|
115
112
|
return self;
|
116
113
|
}
|
data/ext/url_encoded.c
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
/* url-encoded parsing */
|
2
2
|
|
3
3
|
#include "nyara.h"
|
4
|
+
#include <ctype.h>
|
4
5
|
|
5
6
|
static char _half_octet(char c) {
|
6
7
|
// there's a faster way but not validating the range:
|
@@ -319,7 +320,7 @@ static VALUE _cookie_seg_str_new(const char* s, long len) {
|
|
319
320
|
return rb_enc_str_new(s, len, u8_encoding);
|
320
321
|
}
|
321
322
|
|
322
|
-
|
323
|
+
VALUE ext_parse_cookie(VALUE self, VALUE output, VALUE str) {
|
323
324
|
volatile VALUE arr = rb_ary_new();
|
324
325
|
const char* s = RSTRING_PTR(str);
|
325
326
|
long len = RSTRING_LEN(str);
|
@@ -350,9 +351,62 @@ static VALUE ext_parse_cookie(VALUE self, VALUE output, VALUE str) {
|
|
350
351
|
return output;
|
351
352
|
}
|
352
353
|
|
354
|
+
static bool _should_escape(char c) {
|
355
|
+
return !isalnum(c) && c != '_' && c != '.' && c != '-';
|
356
|
+
}
|
357
|
+
|
358
|
+
// prereq: n always < 16
|
359
|
+
static char _hex_char(unsigned char n) {
|
360
|
+
if (n < 10) {
|
361
|
+
return '0' + n;
|
362
|
+
} else {
|
363
|
+
return 'A' + (n - 10);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
static void _concat_char(VALUE s, char c, bool ispath) {
|
368
|
+
static char buf[3] = {'%', 0, 0};
|
369
|
+
static char plus[1] = {'+'};
|
370
|
+
|
371
|
+
if (ispath) {
|
372
|
+
if (_should_escape(c) && c != '+' && c != '/') {
|
373
|
+
buf[1] = _hex_char((unsigned char)c / 16);
|
374
|
+
buf[2] = _hex_char((unsigned char)c % 16);
|
375
|
+
rb_str_cat(s, buf, 3);
|
376
|
+
} else {
|
377
|
+
rb_str_cat(s, &c, 1);
|
378
|
+
}
|
379
|
+
} else {
|
380
|
+
if (c == ' ') {
|
381
|
+
rb_str_cat(s, plus, 1);
|
382
|
+
} else if (_should_escape(c)) {
|
383
|
+
buf[1] = _hex_char((unsigned char)c / 16);
|
384
|
+
buf[2] = _hex_char((unsigned char)c % 16);
|
385
|
+
rb_str_cat(s, buf, 3);
|
386
|
+
} else {
|
387
|
+
rb_str_cat(s, &c, 1);
|
388
|
+
}
|
389
|
+
}
|
390
|
+
}
|
391
|
+
|
392
|
+
// escape for uri path ('/', '+' are not changed) or component ('/', '+' are changed)
|
393
|
+
static VALUE ext_escape(VALUE _, VALUE s, VALUE v_ispath) {
|
394
|
+
Check_Type(s, T_STRING);
|
395
|
+
long len = RSTRING_LEN(s);
|
396
|
+
const char* ptr = RSTRING_PTR(s);
|
397
|
+
volatile VALUE res = rb_str_buf_new(len);
|
398
|
+
bool ispath = RTEST(v_ispath);
|
399
|
+
for (long i = 0; i < len; i++) {
|
400
|
+
_concat_char(res, ptr[i], ispath);
|
401
|
+
}
|
402
|
+
rb_enc_associate(res, u8_encoding);
|
403
|
+
return res;
|
404
|
+
}
|
405
|
+
|
353
406
|
void Init_url_encoded(VALUE ext) {
|
354
407
|
rb_define_singleton_method(ext, "parse_param", ext_parse_param, 2);
|
355
408
|
rb_define_singleton_method(ext, "parse_cookie", ext_parse_cookie, 2);
|
409
|
+
rb_define_singleton_method(ext, "escape", ext_escape, 2);
|
356
410
|
// for test
|
357
411
|
rb_define_singleton_method(ext, "parse_url_encoded_seg", ext_parse_url_encoded_seg, 3);
|
358
412
|
rb_define_singleton_method(ext, "parse_path", ext_parse_path, 2);
|
data/lib/nyara/config.rb
CHANGED
@@ -1,32 +1,83 @@
|
|
1
1
|
module Nyara
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
2
|
+
# options:
|
3
|
+
#
|
4
|
+
# [env] environment, default is +'development'+
|
5
|
+
# [port] listen port number
|
6
|
+
# [workers] number of workers
|
7
|
+
# [host] host name used in `url_to` helper
|
8
|
+
# [root] root path, default is +Dir.pwd+
|
9
|
+
# [views] views (templates) directory, relative to root, default is +"views"+
|
10
|
+
# [public] static files directory, relative to root, default is +"public"+
|
11
|
+
# [x_send_file] header field name for X-Sendfile or X-Accel-Redirect, see Nyara::Controller#send_file for details
|
12
|
+
# [session] see Nyara::Session for sub options
|
13
|
+
# [prefer_erb] use ERB instead of ERubis for +.erb+ templates
|
7
14
|
Config = ConfigHash.new
|
8
15
|
class << Config
|
16
|
+
# clear all settings
|
9
17
|
def reset
|
10
18
|
clear
|
11
19
|
Route.clear
|
12
20
|
end
|
13
21
|
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
# init and check configures
|
23
|
+
def init
|
24
|
+
self['env'] ||= 'development'
|
17
25
|
|
18
|
-
|
19
|
-
n = n.to_i
|
26
|
+
n = (self['port'] || 3000).to_i
|
20
27
|
assert n >= 0 && n <= 65535
|
21
|
-
|
22
|
-
end
|
28
|
+
self['port'] = n
|
23
29
|
|
24
|
-
|
25
|
-
n = n.to_i
|
30
|
+
n = (self['workers'] || self['worker'] || ((CpuCounter.count + 1)/ 2)).to_i
|
26
31
|
assert n > 0 && n < 1000
|
27
|
-
|
32
|
+
self['workers'] = n
|
33
|
+
|
34
|
+
unless self['root']
|
35
|
+
set :root, Dir.pwd
|
36
|
+
end
|
37
|
+
self['root'] = File.expand_path self['root']
|
38
|
+
|
39
|
+
self['views'] = project_path(self['views'] || 'views')
|
40
|
+
self['public'] = project_path(self['public'] || 'public')
|
41
|
+
|
42
|
+
if self['public']
|
43
|
+
map '/', PublicController
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# get absoute path under project path <br>
|
48
|
+
# if +strict+, return nil if path is not under the dir
|
49
|
+
def project_path path, strict=true
|
50
|
+
path_under 'root', path, strict
|
51
|
+
end
|
52
|
+
|
53
|
+
# get absoute path under public path <br>
|
54
|
+
# if +strict+, return nil if path is not under the dir
|
55
|
+
def public_path path, strict=true
|
56
|
+
path_under 'public', path, strict
|
57
|
+
end
|
58
|
+
|
59
|
+
# get absoute path under views path <br>
|
60
|
+
# if +strict+, return nil if path is not under the dir
|
61
|
+
def views_path path, strict=true
|
62
|
+
path_under 'views', path, strict
|
63
|
+
end
|
64
|
+
|
65
|
+
# get path under the dir configured +Nyara.config[key]+ <br>
|
66
|
+
# if +strict+, return nil if path is not under the dir
|
67
|
+
def path_under key, path, strict=true
|
68
|
+
dir = self[key]
|
69
|
+
path = File.expand_path File.join(dir, path)
|
70
|
+
if !strict or path.start_with?(dir)
|
71
|
+
path
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# pass requests under a prefix to a controller
|
76
|
+
def map prefix, controller
|
77
|
+
Route.register_controller prefix, controller
|
28
78
|
end
|
29
79
|
|
80
|
+
# get environment
|
30
81
|
def env
|
31
82
|
self['env'].to_s
|
32
83
|
end
|
@@ -47,8 +98,8 @@ module Nyara
|
|
47
98
|
alias set []=
|
48
99
|
alias get []
|
49
100
|
|
50
|
-
def assert expr
|
51
|
-
raise ArgumentError unless expr
|
101
|
+
def assert expr # :nodoc:
|
102
|
+
raise ArgumentError, "expect #{expr.inspect} to be true", caller[1..-1] unless expr
|
52
103
|
end
|
53
104
|
|
54
105
|
# todo env aware configure
|
data/lib/nyara/controller.rb
CHANGED
@@ -93,13 +93,13 @@ module Nyara
|
|
93
93
|
# +++
|
94
94
|
|
95
95
|
# Set default layout
|
96
|
-
def
|
96
|
+
def set_default_layout l
|
97
97
|
@default_layout = l
|
98
98
|
end
|
99
99
|
attr_reader :default_layout
|
100
100
|
|
101
101
|
# Set controller name, so you can use a shorter name to reference the controller in path helper
|
102
|
-
def
|
102
|
+
def set_controller_name n
|
103
103
|
@controller_name = n
|
104
104
|
end
|
105
105
|
attr_reader :controller_name
|
@@ -204,6 +204,7 @@ module Nyara
|
|
204
204
|
Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
|
205
205
|
data = header.serialize
|
206
206
|
data.concat r.response_header_extra_lines
|
207
|
+
data << Session.encode_set_cookie(r.session, r.ssl?)
|
207
208
|
data << "\r\n"
|
208
209
|
Ext.request_send_data r, data.join
|
209
210
|
|
@@ -338,8 +339,8 @@ module Nyara
|
|
338
339
|
|
339
340
|
# forbid further modification
|
340
341
|
header.freeze
|
341
|
-
session.freeze
|
342
|
-
flash.next.freeze
|
342
|
+
r.session.freeze
|
343
|
+
r.flash.next.freeze
|
343
344
|
end
|
344
345
|
|
345
346
|
# Send raw data, that is, not wrapped in chunked encoding<br>
|
@@ -359,20 +360,77 @@ module Nyara
|
|
359
360
|
end
|
360
361
|
alias send_string send_chunk
|
361
362
|
|
362
|
-
#
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
363
|
+
# Set aproppriate headers and send the file<br>
|
364
|
+
# :call-seq:
|
365
|
+
#
|
366
|
+
# send_file '/home/www/no-virus-inside.exe', disposition: 'attachment'
|
367
|
+
#
|
368
|
+
# options are:
|
369
|
+
#
|
370
|
+
# [disposition] 'inline' by default, if set to 'attachment', the file is presented as a download item in browser.
|
371
|
+
# [x_send_file] if not false/nil, it is considered to be behind a web server.
|
372
|
+
# Then the app sends file with only header configures,
|
373
|
+
# which proxies the actual action to the web server,
|
374
|
+
# which can take the advantage of system calls and reduce transfered data,
|
375
|
+
# thus faster.
|
376
|
+
# [filename] name for the downloaded file, will use basename of +file+ if not set.
|
377
|
+
# [content_type] defaults to the MIME type matching +file+ or +filename+.
|
378
|
+
#
|
379
|
+
# To configure for lighttpd and apache2 mod_xsendfile (https://tn123.org/mod_xsendfile/):
|
380
|
+
#
|
381
|
+
# configure do
|
382
|
+
# set :x_send_file, 'X-Sendfile'
|
383
|
+
# end
|
384
|
+
#
|
385
|
+
# To configure for nginx (http://wiki.nginx.org/XSendfile):
|
386
|
+
#
|
387
|
+
# configure do
|
388
|
+
# set :x_send_file, 'X-Accel-Redirect'
|
389
|
+
# end
|
390
|
+
#
|
391
|
+
# To disable x_send_file while configured:
|
392
|
+
#
|
393
|
+
# send_file '/some/file', x_send_file: false
|
394
|
+
#
|
395
|
+
# To enable x_send_file while not configured:
|
396
|
+
#
|
397
|
+
# send_file '/some/file', x_send_file: 'X-Sendfile'
|
398
|
+
#
|
399
|
+
def send_file file, disposition: 'inline', x_send_file: Config['x_send_file'], filename: nil, content_type: nil
|
400
|
+
header = request.response_header
|
401
|
+
|
402
|
+
unless header['Content-Type']
|
403
|
+
unless content_type
|
404
|
+
extname = File.extname(file)
|
405
|
+
extname = File.extname(filename) if extname.blank? and filename
|
406
|
+
content_type = MIME_TYPES[extname] || 'application/octet-stream'
|
407
|
+
end
|
408
|
+
header['Content-Type'] = content_type
|
409
|
+
end
|
410
|
+
|
411
|
+
disposition = disposition.to_s
|
412
|
+
if disposition != 'inline'
|
413
|
+
if disposition != 'attachment'
|
414
|
+
raise ArgumentError, "disposition should be inline or attachment, but got #{disposition.inspect}"
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
filename ||= File.basename file
|
419
|
+
header['Content-Disposition'] = "#{disposition}; filename=#{Ext.escape filename, true}"
|
420
|
+
|
421
|
+
header['Transfer-Encoding'] = '' # delete it
|
422
|
+
|
423
|
+
if x_send_file
|
424
|
+
header[x_send_file] = file # todo escape name?
|
368
425
|
send_header unless request.response_header.frozen?
|
369
426
|
else
|
427
|
+
# todo nonblock read file?
|
370
428
|
data = File.binread file
|
371
|
-
header['Content-
|
429
|
+
header['Content-Length'] = data.bytesize
|
372
430
|
send_header unless request.response_header.frozen?
|
373
431
|
send_data data
|
374
432
|
end
|
375
|
-
Fiber.yield :term_close
|
433
|
+
Fiber.yield :term_close
|
376
434
|
end
|
377
435
|
|
378
436
|
# Resume action after +seconds+
|
@@ -382,10 +440,10 @@ module Nyara
|
|
382
440
|
|
383
441
|
# NOTE request_wake requires request as param, so this method can not be generalized to Fiber.sleep
|
384
442
|
|
385
|
-
Ext.request_sleep
|
443
|
+
Ext.request_sleep request # place sleep actions before wake
|
386
444
|
Thread.new do
|
387
|
-
sleep seconds
|
388
|
-
Ext.request_wakeup
|
445
|
+
Kernel.sleep seconds
|
446
|
+
Ext.request_wakeup request
|
389
447
|
end
|
390
448
|
Fiber.yield :sleep # see event.c for the handler
|
391
449
|
end
|
@@ -400,6 +458,9 @@ module Nyara
|
|
400
458
|
# # with template source, set content type to +text/html+ if not given
|
401
459
|
# render erb: "<%= 1 + 1 %>"
|
402
460
|
#
|
461
|
+
# # layout can be string or array
|
462
|
+
# render 'index', ['inner_layout', 'outer_layout']
|
463
|
+
#
|
403
464
|
# For steam rendering, see #stream
|
404
465
|
def render view_path=nil, layout: self.class.default_layout, locals: nil, **opts
|
405
466
|
view = View.new self, view_path, layout, locals, opts
|