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