#include "uwsgi_rack.h" extern struct uwsgi_server uwsgi; struct uwsgi_rack ur; struct uwsgi_option uwsgi_rack_options[] = { {"rails", required_argument, 0, "load a rails <= 2.x app", uwsgi_opt_set_str, &ur.rails, UWSGI_OPT_POST_BUFFERING}, {"rack", required_argument, 0, "load a rack app", uwsgi_opt_set_str, &ur.rack, UWSGI_OPT_POST_BUFFERING}, {"ruby-gc-freq", required_argument, 0, "set ruby GC frequency", uwsgi_opt_set_int, &ur.gc_freq, 0}, {"rb-gc-freq", required_argument, 0, "set ruby GC frequency", uwsgi_opt_set_int, &ur.gc_freq, 0}, {"rb-require", required_argument, 0, "import/require a ruby module/script", uwsgi_opt_add_string_list, &ur.rbrequire, 0}, {"ruby-require", required_argument, 0, "import/require a ruby module/script", uwsgi_opt_add_string_list, &ur.rbrequire, 0}, {"rbrequire", required_argument, 0, "import/require a ruby module/script", uwsgi_opt_add_string_list, &ur.rbrequire, 0}, {"rubyrequire", required_argument, 0, "import/require a ruby module/script", uwsgi_opt_add_string_list, &ur.rbrequire, 0}, {"require", required_argument, 0, "import/require a ruby module/script", uwsgi_opt_add_string_list, &ur.rbrequire, 0}, {"rbshell", optional_argument, 0, "run a ruby/irb shell", uwsgi_opt_true, &ur.rb_shell, 0}, {0, 0, 0, 0, 0, 0 ,0}, }; void uwsgi_ruby_exception(void) { VALUE lasterr = rb_gv_get("$!"); VALUE message = rb_obj_as_string(lasterr); uwsgi_log("%s\n", RSTRING_PTR(message)); if(!NIL_P(rb_errinfo())) { VALUE ary = rb_funcall(rb_errinfo(), rb_intern("backtrace"), 0); int i; for (i=0; ibuf_pos;ipost_cl;i++) { if (wsgi_req->post_buffering_buf[i] == '\n') { line = rb_str_new(wsgi_req->post_buffering_buf+wsgi_req->buf_pos, (i+1)-wsgi_req->buf_pos); wsgi_req->buf_pos = i+1; return line; } } if (wsgi_req->buf_pos < wsgi_req->post_cl) { line = rb_str_new(wsgi_req->post_buffering_buf+wsgi_req->buf_pos, wsgi_req->post_cl-wsgi_req->buf_pos); wsgi_req->buf_pos = wsgi_req->post_cl; return line; } return Qnil; } VALUE rb_uwsgi_io_each(VALUE obj, VALUE args) { struct wsgi_request *wsgi_req; Data_Get_Struct(obj, struct wsgi_request, wsgi_req); // yield strings chunks rb_raise(rb_eRuntimeError, "rack.input::each is not implemented (req %p)\n", wsgi_req); return Qnil; } VALUE rb_uwsgi_io_read(VALUE obj, VALUE args) { struct wsgi_request *wsgi_req; Data_Get_Struct(obj, struct wsgi_request, wsgi_req); VALUE chunk; int chunk_size; if (!wsgi_req->post_cl || wsgi_req->buf_pos >= wsgi_req->post_cl) { return Qnil; } if (RARRAY_LEN(args) == 0) { chunk = rb_str_new(wsgi_req->post_buffering_buf+wsgi_req->buf_pos, wsgi_req->post_cl-wsgi_req->buf_pos); wsgi_req->buf_pos += (wsgi_req->post_cl-wsgi_req->buf_pos); return chunk; } else if (RARRAY_LEN(args) > 0) { chunk_size = NUM2INT(RARRAY_PTR(args)[0]); if (wsgi_req->buf_pos+chunk_size > wsgi_req->post_cl) { chunk_size = wsgi_req->post_cl-wsgi_req->buf_pos; } if (RARRAY_LEN(args) > 1) { rb_str_cat(RARRAY_PTR(args)[1], wsgi_req->post_buffering_buf+wsgi_req->buf_pos, chunk_size); wsgi_req->buf_pos+=chunk_size; return RARRAY_PTR(args)[1]; } chunk = rb_str_new(wsgi_req->post_buffering_buf+wsgi_req->buf_pos, chunk_size); wsgi_req->buf_pos+=chunk_size; return chunk; } return Qnil; } VALUE rb_uwsgi_io_rewind(VALUE obj, VALUE args) { struct wsgi_request *wsgi_req; Data_Get_Struct(obj, struct wsgi_request, wsgi_req); if (!wsgi_req->post_cl) { return Qnil; } wsgi_req->buf_pos = 0; return Qnil; } #ifdef RUBY19 RUBY_GLOBAL_SETUP #endif VALUE uwsgi_require_file(VALUE arg) { return rb_funcall(rb_cObject, rb_intern("require"), 1, arg); } VALUE require_rack(VALUE arg) { return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); } VALUE require_rails(VALUE arg) { #ifdef RUBY19 return rb_require("./config/environment"); #else return rb_require("config/environment"); #endif } VALUE require_thin(VALUE arg) { return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("thin")); } VALUE init_rack_app(VALUE); VALUE rack_call_rpc_handler(VALUE args) { VALUE rpc_args = rb_ary_entry(args, 1); return rb_funcall2(rb_ary_entry(args, 0), rb_intern("call"), RARRAY_LEN(rpc_args), RARRAY_PTR(rpc_args)); } uint16_t uwsgi_ruby_rpc(void *func, uint8_t argc, char **argv, uint16_t argvs[], char *buffer) { uint8_t i; VALUE rb_args = rb_ary_new2(2); VALUE rb_rpc_args = rb_ary_new2(argc); VALUE ret; int error = 0; char *rv; size_t rl; rb_ary_store(rb_args, 0, (VALUE) func); for (i = 0; i < argc; i++) { rb_ary_store(rb_rpc_args, i, rb_str_new(argv[i], argvs[i])); } rb_ary_store(rb_args, 1, rb_rpc_args); ret = rb_protect(rack_call_rpc_handler, rb_args, &error); if (error) { uwsgi_ruby_exception(); return 0; } if (TYPE(ret) == T_STRING) { rv = RSTRING_PTR(ret); rl = RSTRING_LEN(ret); if (rl <= 0xffff) { memcpy(buffer, rv, rl); return rl; } } return 0; } int uwsgi_rack_init(){ struct http_status_codes *http_sc; #ifdef RUBY19 int argc = 2; char *sargv[] = { (char *) "uwsgi", (char *) "-e0" }; char **argv = sargv; #endif // filling http status codes for (http_sc = hsc; http_sc->message != NULL; http_sc++) { http_sc->message_size = (int) strlen(http_sc->message); } #ifdef RUBY19 ruby_sysinit(&argc, &argv); RUBY_INIT_STACK ruby_init(); ruby_process_options(argc, argv); #else ruby_init(); ruby_init_loadpath(); #endif ruby_show_version(); ruby_script("uwsgi"); ur.signals_protector = rb_ary_new(); ur.rpc_protector = rb_ary_new(); rb_gc_register_address(&ur.signals_protector); rb_gc_register_address(&ur.rpc_protector); #ifdef UWSGI_EMBEDDED uwsgi_rack_init_api(); #endif return 0; } VALUE uwsgi_rb_call_new(VALUE obj) { return rb_funcall(obj, rb_intern("new"), 0); } void uwsgi_rack_init_apps(void) { int error; ur.app_id = uwsgi_apps_cnt; struct uwsgi_string_list *usl = ur.rbrequire; time_t now = uwsgi_now(); while(usl) { error = 0; rb_protect( uwsgi_require_file, rb_str_new2(usl->value), &error ) ; if (error) { uwsgi_ruby_exception(); } usl = usl->next; } if (ur.rack) { ur.dispatcher = rb_protect(init_rack_app, rb_str_new2(ur.rack), &error); if (error) { uwsgi_ruby_exception(); exit(1); } if (ur.dispatcher == Qnil) { uwsgi_log("unable to find RACK entry point\n"); exit(1); } rb_gc_register_address(&ur.dispatcher); goto ready; } else if (ur.rails) { if (chdir(ur.rails)) { uwsgi_error("chdir()"); exit(1); } if (!access("config.ru", R_OK)) { uwsgi_log("!!! a config.ru file has been found in yor rails app, please use --rack instead of the old --rails !!!\n"); } uwsgi_log("loading rails app %s\n", ur.rails); rb_protect( require_rails, 0, &error ) ; if (error) { uwsgi_ruby_exception(); exit(1); } uwsgi_log("rails app %s ready\n", ur.rails); VALUE ac = rb_const_get(rb_cObject, rb_intern("ActionController")); ur.dispatcher = Qnil; if (rb_funcall(ac, rb_intern("const_defined?"), 1, ID2SYM(rb_intern("Dispatcher"))) == Qtrue) { VALUE ac_dispatcher = rb_const_get(ac, rb_intern("Dispatcher")); VALUE acd_instance_methods = rb_funcall( ac_dispatcher, rb_intern("instance_methods"), 0); VALUE acim_call = rb_funcall( acd_instance_methods, rb_intern("include?"), 1, ID2SYM(rb_intern("call"))); if (acim_call == Qfalse) { acim_call = rb_funcall( acd_instance_methods, rb_intern("include?"), 1, rb_str_new2("call")); } if (acim_call == Qtrue) { ur.dispatcher = rb_protect(uwsgi_rb_call_new, ac_dispatcher, &error); if (error) { uwsgi_ruby_exception(); exit(1); } } } if (ur.dispatcher == Qnil) { uwsgi_log("non-rack rails version detected...loading thin adapter...\n"); rb_protect( require_thin, 0, &error ) ; if (error) { uwsgi_ruby_exception(); exit(1); } VALUE thin_rack = rb_const_get(rb_cObject, rb_intern("Rack")); VALUE thin_rack_adapter = rb_const_get(thin_rack, rb_intern("Adapter")); VALUE thin_rack_adapter_rails = rb_const_get(thin_rack_adapter, rb_intern("Rails")); ur.dispatcher = rb_protect( uwsgi_rb_call_new, thin_rack_adapter_rails, &error); if (error) { uwsgi_ruby_exception(); exit(1); } } if (ur.dispatcher == Qnil) { uwsgi_log("unable to load rails dispatcher\n"); exit(1); } rb_gc_register_address(&ur.dispatcher); goto ready; } return; ready: ur.call = rb_intern("call"); if (!ur.call) { uwsgi_log("unable to find RACK entry point\n"); return; } rb_gc_register_address(&ur.call); ur.rb_uwsgi_io_class = rb_define_class("Uwsgi_IO", rb_cObject); rb_gc_register_address(&ur.rb_uwsgi_io_class); rb_define_singleton_method(ur.rb_uwsgi_io_class, "new", rb_uwsgi_io_new, 1); rb_define_method(ur.rb_uwsgi_io_class, "initialize", rb_uwsgi_io_init, -1); rb_define_method(ur.rb_uwsgi_io_class, "gets", rb_uwsgi_io_gets, 0); rb_define_method(ur.rb_uwsgi_io_class, "each", rb_uwsgi_io_each, 0); rb_define_method(ur.rb_uwsgi_io_class, "read", rb_uwsgi_io_read, -2); rb_define_method(ur.rb_uwsgi_io_class, "rewind", rb_uwsgi_io_rewind, 0); struct uwsgi_app *ua = uwsgi_add_app(ur.app_id, 7, (char*)"", 0, NULL, NULL); ua->started_at = now; ua->startup_time = uwsgi_now() - now; if (ur.gc_freq <= 1) { uwsgi_log("RACK app %d loaded in %d seconds at %p (GC frequency: AGGRESSIVE)\n", ur.app_id, (int) ua->startup_time, ur.call); } else { uwsgi_log("RACK app %d loaded in %d seconds at %p (GC frequency: %d)\n", ur.app_id, (int) ua->startup_time, ur.call, ur.gc_freq); } } VALUE call_dispatch(VALUE env) { return rb_funcall(ur.dispatcher, ur.call, 1, env); } static VALUE send_body(VALUE obj) { struct wsgi_request *wsgi_req = current_wsgi_req(); ssize_t len = 0; //uwsgi_log("sending body\n"); if (TYPE(obj) == T_STRING) { len = wsgi_req->socket->proto_write( wsgi_req, RSTRING_PTR(obj), RSTRING_LEN(obj)); } else { uwsgi_log("UNMANAGED BODY TYPE %d\n", TYPE(obj)); } wsgi_req->response_size += len; return Qnil; } VALUE body_to_path(VALUE body) { return rb_funcall( body, rb_intern("to_path"), 0); } VALUE close_body(VALUE body) { return rb_funcall( body, rb_intern("close"), 0); } VALUE iterate_body(VALUE body) { #ifdef RUBY19 return rb_block_call(body, rb_intern("each"), 0, 0, send_body, 0); #else return rb_iterate(rb_each, body, send_body, 0); #endif } VALUE send_header(VALUE obj, VALUE headers) { struct wsgi_request *wsgi_req = current_wsgi_req(); size_t len; VALUE hkey, hval; //uwsgi_log("HEADERS %d\n", TYPE(obj)); if (TYPE(obj) == T_ARRAY) { if (RARRAY_LEN(obj) >= 2) { hkey = rb_obj_as_string( RARRAY_PTR(obj)[0]); hval = rb_obj_as_string( RARRAY_PTR(obj)[1]); } else { goto clear; } } else if (TYPE(obj) == T_STRING) { hkey = obj; #ifdef RUBY19 hval = rb_hash_lookup(headers, obj); #else hval = rb_hash_aref(headers, obj); #endif } else { goto clear; } if (TYPE(hkey) != T_STRING || TYPE(hval) != T_STRING) { goto clear; } char *header_value = RSTRING_PTR(hval); size_t header_value_len = RSTRING_LEN(hval); size_t i,cnt=0; char *this_header = header_value; for(i=0;isocket->proto_write_header( wsgi_req, RSTRING_PTR(hkey), RSTRING_LEN(hkey)); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, (char *)": ", 2); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, this_header, cnt); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, (char *)"\r\n", 2); wsgi_req->headers_size += len; //uwsgi_log("(multi) --%.*s: %.*s--\n", RSTRING_LEN(hkey), RSTRING_PTR(hkey), cnt, this_header); wsgi_req->header_cnt++; this_header += cnt+1; cnt = 0; continue; } cnt++; } if (cnt > 0) { len = wsgi_req->socket->proto_write_header( wsgi_req, RSTRING_PTR(hkey), RSTRING_LEN(hkey)); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, (char *)": ", 2); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, this_header, cnt); wsgi_req->headers_size += len; len = wsgi_req->socket->proto_write_header( wsgi_req, (char *)"\r\n", 2); wsgi_req->headers_size += len; wsgi_req->header_cnt++; //uwsgi_log("--%.*s: %.*s--\n", RSTRING_LEN(hkey), RSTRING_PTR(hkey), cnt, this_header); } clear: return Qnil; } VALUE iterate_headers(VALUE headers) { #ifdef RUBY19 return rb_block_call(headers, rb_intern("each"), 0, 0, send_header, headers ); #else return rb_iterate(rb_each, headers, send_header, headers); #endif } int uwsgi_rack_request(struct wsgi_request *wsgi_req) { int error = 0; int i; VALUE env, ret, status, headers, body; struct http_status_codes *http_sc; /* Standard RACK request */ if (!wsgi_req->uh.pktsize) { uwsgi_log("Invalid RACK request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } wsgi_req->app_id = ur.app_id; uwsgi_apps[wsgi_req->app_id].requests++; env = rb_hash_new(); // fill ruby hash for(i=0;ivar_cnt;i++) { // put the var only if it is not 0 size or required (rack requirement... very inefficient) if (wsgi_req->hvec[i+1].iov_len > 0 || !uwsgi_strncmp((char *)"REQUEST_METHOD", 14, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) || !uwsgi_strncmp((char *)"SCRIPT_NAME", 11, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) || !uwsgi_strncmp((char *)"PATH_INFO", 10, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) || !uwsgi_strncmp((char *)"QUERY_STRING", 12, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) || !uwsgi_strncmp((char *)"SERVER_NAME", 11, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) || !uwsgi_strncmp((char *)"SERVER_PORT", 11, wsgi_req->hvec[i].iov_base, (int) wsgi_req->hvec[i].iov_len) ) { rb_hash_aset(env, rb_str_new(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len), rb_str_new(wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len)); //uwsgi_log("%.*s = %.*s\n", wsgi_req->hvec[i].iov_len, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i+1].iov_len, wsgi_req->hvec[i+1].iov_base); } i++; } VALUE rbv = rb_ary_new(); rb_ary_store(rbv, 0, INT2NUM(1)); rb_ary_store(rbv, 1, INT2NUM(1)); rb_hash_aset(env, rb_str_new2("rack.version"), rbv); if (wsgi_req->scheme_len > 0) { rb_hash_aset(env, rb_str_new2("rack.url_scheme"), rb_str_new(wsgi_req->scheme, wsgi_req->scheme_len)); } else if (wsgi_req->https_len > 0) { if (!strncasecmp(wsgi_req->https, "on", 2) || wsgi_req->https[0] == '1') { rb_hash_aset(env, rb_str_new2("rack.url_scheme"), rb_str_new2("https")); } else { rb_hash_aset(env, rb_str_new2("rack.url_scheme"), rb_str_new2("http")); } } else { rb_hash_aset(env, rb_str_new2("rack.url_scheme"), rb_str_new2("http")); } rb_hash_aset(env, rb_str_new2("rack.multithread"), Qfalse); rb_hash_aset(env, rb_str_new2("rack.multiprocess"), Qtrue); rb_hash_aset(env, rb_str_new2("rack.run_once"), Qfalse); VALUE dws_wr = Data_Wrap_Struct(ur.rb_uwsgi_io_class, 0, 0, wsgi_req); if (wsgi_req->async_post) { rb_hash_aset(env, rb_str_new2("rack.input"), rb_funcall( rb_const_get(rb_cObject, rb_intern("IO")), rb_intern("new"), 2, INT2NUM(fileno(wsgi_req->async_post)), rb_str_new("r",1) )); } else { rb_hash_aset(env, rb_str_new2("rack.input"), rb_funcall(ur.rb_uwsgi_io_class, rb_intern("new"), 1, dws_wr )); } rb_hash_aset(env, rb_str_new2("rack.errors"), rb_funcall( rb_const_get(rb_cObject, rb_intern("IO")), rb_intern("new"), 2, INT2NUM(2), rb_str_new("w",1) )); ret = rb_protect( call_dispatch, env, &error); if (error) { uwsgi_ruby_exception(); goto clear; } if (TYPE(ret) == T_ARRAY) { if (RARRAY_LEN(ret) != 3) { uwsgi_log("Invalid RACK response size: %d\n", RARRAY_LEN(ret)); return -1; } // manage Status status = rb_obj_as_string(RARRAY_PTR(ret)[0]); // get the status code wsgi_req->hvec[0].iov_base = wsgi_req->protocol; wsgi_req->hvec[0].iov_len = wsgi_req->protocol_len ; wsgi_req->hvec[1].iov_base = (char *) " "; wsgi_req->hvec[1].iov_len = 1 ; wsgi_req->hvec[2].iov_base = RSTRING_PTR(status); wsgi_req->hvec[2].iov_len = 3 ; wsgi_req->status = atoi(RSTRING_PTR(status)); wsgi_req->hvec[3].iov_base = (char *) " "; wsgi_req->hvec[3].iov_len = 1 ; wsgi_req->hvec[4].iov_len = 0 ; for (http_sc = hsc; http_sc->message != NULL; http_sc++) { if (!strncmp(http_sc->key, RSTRING_PTR(status), 3)) { wsgi_req->hvec[4].iov_base = (char *) http_sc->message ; wsgi_req->hvec[4].iov_len = http_sc->message_size ; break; } } wsgi_req->hvec[5].iov_base = (char *) "\r\n"; wsgi_req->hvec[5].iov_len = 2 ; if ( !(wsgi_req->headers_size = wsgi_req->socket->proto_writev_header(wsgi_req, wsgi_req->hvec, 6)) ) { uwsgi_error("writev()"); } headers = RARRAY_PTR(ret)[1] ; if (rb_respond_to( headers, rb_intern("each") )) { rb_protect( iterate_headers, headers, &error); if (error) { uwsgi_ruby_exception(); goto clear; } } if (wsgi_req->socket->proto_write(wsgi_req, (char *)"\r\n", 2) != 2) { uwsgi_error("write()"); } body = RARRAY_PTR(ret)[2] ; if (rb_respond_to( body, rb_intern("to_path") )) { VALUE sendfile_path = rb_protect( body_to_path, body, &error); if (error) { uwsgi_ruby_exception(); } else { wsgi_req->sendfile_fd = open(RSTRING_PTR(sendfile_path), O_RDONLY); wsgi_req->response_size = uwsgi_sendfile(wsgi_req); if (wsgi_req->response_size > 0) { while(wsgi_req->response_size < wsgi_req->sendfile_fd_size) { //uwsgi_log("sendfile_fd_size = %d\n", wsgi_req->sendfile_fd_size); wsgi_req->response_size += uwsgi_sendfile(wsgi_req); } } // we need to close it... close(wsgi_req->sendfile_fd); } } else if (rb_respond_to( body, rb_intern("each") )) { if (ur.unprotected) { iterate_body(body); } else { rb_protect( iterate_body, body, &error); if (error) { uwsgi_ruby_exception(); } } } if (rb_respond_to( body, rb_intern("close") )) { //uwsgi_log("calling close\n"); rb_protect( close_body, body, &error); if (error) { uwsgi_ruby_exception(); } } } else { internal_server_error(wsgi_req, (char *)"Invalid RACK response"); } clear: if (ur.gc_freq <= 1 || ur.cycles%ur.gc_freq == 0) { #ifdef UWSGI_DEBUG uwsgi_log("calling ruby GC\n"); #endif rb_gc(); } ur.cycles++; return 0; } void uwsgi_rack_after_request(struct wsgi_request *wsgi_req) { if (uwsgi.shared->options[UWSGI_OPTION_LOGGING]) log_request(wsgi_req); } void uwsgi_rack_suspend(struct wsgi_request *wsgi_req) { uwsgi_log("SUSPENDING RUBY\n"); } void uwsgi_rack_resume(struct wsgi_request *wsgi_req) { uwsgi_log("RESUMING RUBY\n"); } #ifdef RUBY19 VALUE uwsgi_call_block(VALUE body, VALUE block) { return rb_funcall(block, rb_intern("call"), 1, body ); } VALUE uwsgi_rack_patch_body_proxy_each(int argc, VALUE *argv, VALUE self) { VALUE block = Qnil; rb_scan_args(argc, argv, "0&", &block); if(!RTEST(block)) { rb_raise(rb_eArgError, "a block is required"); return Qnil; } VALUE original_body = rb_iv_get(self, "@body"); if (original_body != Qnil) { return rb_block_call(original_body, rb_intern("each"), 0, 0, uwsgi_call_block, block); } return Qnil; } VALUE uwsgi_rack_patch_body_proxy(VALUE foo) { VALUE rack = rb_const_get(rb_cObject, rb_intern("Rack")); VALUE rack_body_proxy = rb_const_get(rack, rb_intern("BodyProxy")); if (!rb_respond_to(rack_body_proxy, rb_intern("each"))) { rb_define_method(rack_body_proxy, "each", uwsgi_rack_patch_body_proxy_each, -1); return Qtrue; } return Qnil; } #endif VALUE init_rack_app( VALUE script ) { int error; #ifndef RUBY19 rb_require("rubygems"); #endif rb_protect( require_rack, 0, &error ) ; if (error) { uwsgi_ruby_exception(); return Qnil; } VALUE rack = rb_const_get(rb_cObject, rb_intern("Rack")); #ifdef RUBY19 VALUE ret = rb_protect(uwsgi_rack_patch_body_proxy, rack, &error); if (!error && ret != Qnil) { uwsgi_log("Rack::BodyProxy successfully patched for ruby 1.9.x\n"); } #endif VALUE rackup = rb_funcall( rb_const_get(rack, rb_intern("Builder")), rb_intern("parse_file"), 1, script); if (TYPE(rackup) != T_ARRAY) { uwsgi_log("unable to parse %s file\n", RSTRING_PTR(script)); return Qnil; } if (RARRAY_LEN(rackup) < 1) { uwsgi_log("invalid rack config file: %s\n", RSTRING_PTR(script)); return Qnil; } return RARRAY_PTR(rackup)[0] ; } int uwsgi_rack_magic(char *mountpoint, char *lazy) { if (!strcmp(lazy+strlen(lazy)-3, ".ru")) { ur.rack = lazy; return 1; } else if (!strcmp(lazy+strlen(lazy)-3, ".rb")) { ur.rack = lazy; return 1; } return 0; } /* int uwsgi_rack_mount_app(char *mountpoint, char *app) { if ( !strcmp(what+strlen(what)-3, ".ru") || !strcmp(what+strlen(what)-3, ".rb")) { return = uwsgi_rack_load(mountpoint, what); } return -1; } */ void uwsgi_rack_hijack(void) { } int uwsgi_rack_mule(char *opt) { int error = 0; if (uwsgi_endswith(opt, (char *)".rb")) { rb_protect( uwsgi_require_file, rb_str_new2(opt), &error ) ; if (error) { uwsgi_ruby_exception(); return 0; } return 1; } return 0; } VALUE uwsgi_rb_pfh(VALUE args) { VALUE uwsgi_rb_embedded = rb_const_get(rb_cObject, rb_intern("UWSGI")); if (rb_respond_to(uwsgi_rb_embedded, rb_intern("post_fork_hook"))) { return rb_funcall(uwsgi_rb_embedded, rb_intern("post_fork_hook"), 0); } return Qnil; } void uwsgi_rb_post_fork() { int error = 0; // call the post_fork_hook rb_protect(uwsgi_rb_pfh, 0, &error); if (error) { uwsgi_ruby_exception(); } } VALUE uwsgi_rb_mmh(VALUE args) { VALUE uwsgi_rb_embedded = rb_const_get(rb_cObject, rb_intern("UWSGI")); return rb_funcall(uwsgi_rb_embedded, rb_intern("mule_msg_hook"), 1, args); } int uwsgi_rack_mule_msg(char *message, size_t len) { int error = 0; VALUE uwsgi_rb_embedded = rb_const_get(rb_cObject, rb_intern("UWSGI")); if (rb_respond_to(uwsgi_rb_embedded, rb_intern("mule_msg_hook"))) { VALUE arg = rb_str_new(message, len); rb_protect(uwsgi_rb_mmh, arg, &error); if (error) { uwsgi_ruby_exception(); } return 1; } return 0; } VALUE rack_call_signal_handler(VALUE args) { return rb_funcall(rb_ary_entry(args, 0), rb_intern("call"), 1, rb_ary_entry(args, 1)); } int uwsgi_rack_signal_handler(uint8_t sig, void *handler) { int error = 0; VALUE rbhandler = (VALUE) handler; VALUE args = rb_ary_new2(2); rb_ary_store(args, 0, rbhandler); VALUE rbsig = INT2NUM(sig); rb_ary_store(args, 1, rbsig); rb_protect(rack_call_signal_handler, args, &error); if (error) { uwsgi_ruby_exception(); rb_gc(); return -1; } rb_gc(); return 0; } VALUE uwsgi_rb_do_spooler(VALUE args) { VALUE uwsgi_rb_embedded = rb_const_get(rb_cObject, rb_intern("UWSGI")); return rb_funcall(uwsgi_rb_embedded, rb_intern("spooler"), 1, args); } void uwsgi_ruby_add_item(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) { VALUE *spool_dict = (VALUE*) data; rb_hash_aset(*spool_dict, rb_str_new(key, keylen), rb_str_new(val, vallen)); } int uwsgi_rack_spooler(char *filename, char *buf, uint16_t len, char *body, size_t body_len) { int error = 0; VALUE uwsgi_rb_embedded = rb_const_get(rb_cObject, rb_intern("UWSGI")); if (!rb_respond_to(uwsgi_rb_embedded, rb_intern("spooler"))) { rb_gc(); return 0; } VALUE spool_dict = rb_hash_new(); if (uwsgi_hooked_parse(buf, len, uwsgi_ruby_add_item, (void *) &spool_dict)) { rb_gc(); // malformed packet, destroy it return 0; } rb_hash_aset(spool_dict, rb_str_new2("spooler_task_name"), rb_str_new2(filename)); if (body && body_len > 0) { rb_hash_aset(spool_dict, rb_str_new2("body"), rb_str_new(body, body_len)); } VALUE ret = rb_protect(uwsgi_rb_do_spooler, spool_dict, &error); if (error) { uwsgi_ruby_exception(); rb_gc(); return -1; } if (TYPE(ret) == T_FIXNUM) { rb_gc(); return NUM2INT(ret); } // error, retry rb_gc(); return -1; } struct uwsgi_plugin rack_plugin = { .name = "rack", .modifier1 = 7, .init = uwsgi_rack_init, .options = uwsgi_rack_options, .request = uwsgi_rack_request, .after_request = uwsgi_rack_after_request, .signal_handler = uwsgi_rack_signal_handler, .hijack_worker = uwsgi_rack_hijack, .post_fork = uwsgi_rb_post_fork, .spooler = uwsgi_rack_spooler, .init_apps = uwsgi_rack_init_apps, //.mount_app = uwsgi_rack_mount_app, .magic = uwsgi_rack_magic, .mule = uwsgi_rack_mule, .mule_msg = uwsgi_rack_mule_msg, .rpc = uwsgi_ruby_rpc, .suspend = uwsgi_rack_suspend, .resume = uwsgi_rack_resume, };