1584Salexander.borisov@nginx.com /* 2584Salexander.borisov@nginx.com * Copyright (C) Alexander Borisov 3584Salexander.borisov@nginx.com * Copyright (C) NGINX, Inc. 4584Salexander.borisov@nginx.com */ 5584Salexander.borisov@nginx.com 6584Salexander.borisov@nginx.com #include <ruby/nxt_ruby.h> 7584Salexander.borisov@nginx.com 8743Smax.romanov@nginx.com #include <nxt_unit.h> 9743Smax.romanov@nginx.com #include <nxt_unit_request.h> 101532St.nateldemoura@f5.com 111687Smax.romanov@nginx.com #include <ruby/thread.h> 121687Smax.romanov@nginx.com 131532St.nateldemoura@f5.com #include NXT_RUBY_MOUNTS_H 14743Smax.romanov@nginx.com 151814Svbart@nginx.com #include <locale.h> 161814Svbart@nginx.com 17584Salexander.borisov@nginx.com 18584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 19584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MINOR 3 20584Salexander.borisov@nginx.com 21584Salexander.borisov@nginx.com 22584Salexander.borisov@nginx.com typedef struct { 231687Smax.romanov@nginx.com nxt_task_t *task; 241687Smax.romanov@nginx.com nxt_str_t *script; 251687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 26584Salexander.borisov@nginx.com } nxt_ruby_rack_init_t; 27584Salexander.borisov@nginx.com 28584Salexander.borisov@nginx.com 291488St.nateldemoura@f5.com static nxt_int_t nxt_ruby_start(nxt_task_t *task, 301488St.nateldemoura@f5.com nxt_process_data_t *data); 31584Salexander.borisov@nginx.com static VALUE nxt_ruby_init_basic(VALUE arg); 32*2087Sz.hong@f5.com static VALUE nxt_ruby_script_basename(nxt_str_t *script); 331910So.canty@f5.com 341910So.canty@f5.com static VALUE nxt_ruby_hook_procs_load(VALUE path); 351910So.canty@f5.com static VALUE nxt_ruby_hook_register(VALUE arg); 361910So.canty@f5.com static VALUE nxt_ruby_hook_call(VALUE name); 371910So.canty@f5.com 38584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 39584Salexander.borisov@nginx.com 40584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rubygems(VALUE arg); 41717Salexander.borisov@nginx.com static VALUE nxt_ruby_bundler_setup(VALUE arg); 42584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rack(VALUE arg); 43584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 44584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_env_create(VALUE arg); 451687Smax.romanov@nginx.com static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx); 46743Smax.romanov@nginx.com static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); 471687Smax.romanov@nginx.com static void *nxt_ruby_request_handler_gvl(void *req); 481687Smax.romanov@nginx.com static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx); 491738Smax.romanov@nginx.com static void *nxt_ruby_thread_create_gvl(void *rctx); 501687Smax.romanov@nginx.com static VALUE nxt_ruby_thread_func(VALUE arg); 511687Smax.romanov@nginx.com static void *nxt_ruby_unit_run(void *ctx); 521687Smax.romanov@nginx.com static void nxt_ruby_ubf(void *ctx); 53*2087Sz.hong@f5.com static int nxt_ruby_init_threads(VALUE script, nxt_ruby_app_conf_t *c); 541687Smax.romanov@nginx.com static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, 551687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c); 56584Salexander.borisov@nginx.com 57584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_app_run(VALUE arg); 581687Smax.romanov@nginx.com static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env); 591686Smax.romanov@nginx.com nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 601686Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t len); 611687Smax.romanov@nginx.com static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, 621687Smax.romanov@nginx.com VALUE result); 631687Smax.romanov@nginx.com static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, 641687Smax.romanov@nginx.com VALUE result, nxt_int_t status); 65743Smax.romanov@nginx.com static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); 66743Smax.romanov@nginx.com static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); 671687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, 681687Smax.romanov@nginx.com VALUE result); 691687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 701687Smax.romanov@nginx.com VALUE filepath); 711687Smax.romanov@nginx.com static void *nxt_ruby_response_write_cb(void *read_info); 721337Smax.romanov@nginx.com static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, 731337Smax.romanov@nginx.com int argc, const VALUE *argv, VALUE blockarg); 741687Smax.romanov@nginx.com static void *nxt_ruby_response_write(void *body); 75584Salexander.borisov@nginx.com 761687Smax.romanov@nginx.com static void nxt_ruby_exception_log(nxt_unit_request_info_t *req, 771687Smax.romanov@nginx.com uint32_t level, const char *desc); 78584Salexander.borisov@nginx.com 791687Smax.romanov@nginx.com static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx); 80743Smax.romanov@nginx.com static void nxt_ruby_atexit(void); 81584Salexander.borisov@nginx.com 82584Salexander.borisov@nginx.com 83584Salexander.borisov@nginx.com static uint32_t compat[] = { 84584Salexander.borisov@nginx.com NXT_VERNUM, NXT_DEBUG, 85584Salexander.borisov@nginx.com }; 86584Salexander.borisov@nginx.com 871910So.canty@f5.com static VALUE nxt_ruby_hook_procs; 881687Smax.romanov@nginx.com static VALUE nxt_ruby_rackup; 891687Smax.romanov@nginx.com static VALUE nxt_ruby_call; 901687Smax.romanov@nginx.com 911687Smax.romanov@nginx.com static uint32_t nxt_ruby_threads; 921687Smax.romanov@nginx.com static nxt_ruby_ctx_t *nxt_ruby_ctxs; 93584Salexander.borisov@nginx.com 94743Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t nxt_app_module = { 95584Salexander.borisov@nginx.com sizeof(compat), 96584Salexander.borisov@nginx.com compat, 97584Salexander.borisov@nginx.com nxt_string("ruby"), 98612Salexander.borisov@nginx.com ruby_version, 991489St.nateldemoura@f5.com nxt_ruby_mounts, 1001489St.nateldemoura@f5.com nxt_nitems(nxt_ruby_mounts), 101977Smax.romanov@gmail.com NULL, 1021488St.nateldemoura@f5.com nxt_ruby_start, 103584Salexander.borisov@nginx.com }; 104584Salexander.borisov@nginx.com 1051686Smax.romanov@nginx.com typedef struct { 1061686Smax.romanov@nginx.com nxt_str_t string; 1071686Smax.romanov@nginx.com VALUE *v; 1081686Smax.romanov@nginx.com } nxt_ruby_string_t; 1091686Smax.romanov@nginx.com 1101686Smax.romanov@nginx.com static VALUE nxt_rb_80_str; 1111686Smax.romanov@nginx.com static VALUE nxt_rb_content_length_str; 1121686Smax.romanov@nginx.com static VALUE nxt_rb_content_type_str; 1131686Smax.romanov@nginx.com static VALUE nxt_rb_http_str; 1141686Smax.romanov@nginx.com static VALUE nxt_rb_https_str; 1151686Smax.romanov@nginx.com static VALUE nxt_rb_path_info_str; 1161686Smax.romanov@nginx.com static VALUE nxt_rb_query_string_str; 1171686Smax.romanov@nginx.com static VALUE nxt_rb_rack_url_scheme_str; 1181686Smax.romanov@nginx.com static VALUE nxt_rb_remote_addr_str; 1191686Smax.romanov@nginx.com static VALUE nxt_rb_request_method_str; 1201686Smax.romanov@nginx.com static VALUE nxt_rb_request_uri_str; 1211686Smax.romanov@nginx.com static VALUE nxt_rb_server_addr_str; 1221686Smax.romanov@nginx.com static VALUE nxt_rb_server_name_str; 1231686Smax.romanov@nginx.com static VALUE nxt_rb_server_port_str; 1241686Smax.romanov@nginx.com static VALUE nxt_rb_server_protocol_str; 1251910So.canty@f5.com static VALUE nxt_rb_on_worker_boot; 1261910So.canty@f5.com static VALUE nxt_rb_on_worker_shutdown; 1271910So.canty@f5.com static VALUE nxt_rb_on_thread_boot; 1281910So.canty@f5.com static VALUE nxt_rb_on_thread_shutdown; 1291686Smax.romanov@nginx.com 1301686Smax.romanov@nginx.com static nxt_ruby_string_t nxt_rb_strings[] = { 1311686Smax.romanov@nginx.com { nxt_string("80"), &nxt_rb_80_str }, 1321686Smax.romanov@nginx.com { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str }, 1331686Smax.romanov@nginx.com { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str }, 1341686Smax.romanov@nginx.com { nxt_string("http"), &nxt_rb_http_str }, 1351686Smax.romanov@nginx.com { nxt_string("https"), &nxt_rb_https_str }, 1361686Smax.romanov@nginx.com { nxt_string("PATH_INFO"), &nxt_rb_path_info_str }, 1371686Smax.romanov@nginx.com { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str }, 1381686Smax.romanov@nginx.com { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str }, 1391686Smax.romanov@nginx.com { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str }, 1401686Smax.romanov@nginx.com { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str }, 1411686Smax.romanov@nginx.com { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str }, 1421686Smax.romanov@nginx.com { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str }, 1431686Smax.romanov@nginx.com { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, 1441686Smax.romanov@nginx.com { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, 1451686Smax.romanov@nginx.com { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, 1461910So.canty@f5.com { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot }, 1471910So.canty@f5.com { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown }, 1481910So.canty@f5.com { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot }, 1491910So.canty@f5.com { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown }, 1501686Smax.romanov@nginx.com { nxt_null_string, NULL }, 1511686Smax.romanov@nginx.com }; 1521686Smax.romanov@nginx.com 1531686Smax.romanov@nginx.com 1541686Smax.romanov@nginx.com static int 1551686Smax.romanov@nginx.com nxt_ruby_init_strings(void) 1561686Smax.romanov@nginx.com { 1571686Smax.romanov@nginx.com VALUE v; 1581686Smax.romanov@nginx.com nxt_ruby_string_t *pstr; 1591686Smax.romanov@nginx.com 1601686Smax.romanov@nginx.com pstr = nxt_rb_strings; 1611686Smax.romanov@nginx.com 1621686Smax.romanov@nginx.com while (pstr->string.start != NULL) { 1631686Smax.romanov@nginx.com v = rb_str_new_static((char *) pstr->string.start, pstr->string.length); 1641686Smax.romanov@nginx.com 1651686Smax.romanov@nginx.com if (nxt_slow_path(v == Qnil)) { 1661686Smax.romanov@nginx.com nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'", 1671686Smax.romanov@nginx.com (int) pstr->string.length, 1681686Smax.romanov@nginx.com (char *) pstr->string.start); 1691686Smax.romanov@nginx.com 1701686Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1711686Smax.romanov@nginx.com } 1721686Smax.romanov@nginx.com 1731686Smax.romanov@nginx.com *pstr->v = v; 1741686Smax.romanov@nginx.com 1751686Smax.romanov@nginx.com rb_gc_register_address(pstr->v); 1761686Smax.romanov@nginx.com 1771686Smax.romanov@nginx.com pstr++; 1781686Smax.romanov@nginx.com } 1791686Smax.romanov@nginx.com 1801686Smax.romanov@nginx.com return NXT_UNIT_OK; 1811686Smax.romanov@nginx.com } 1821686Smax.romanov@nginx.com 1831686Smax.romanov@nginx.com 1841686Smax.romanov@nginx.com static void 1851686Smax.romanov@nginx.com nxt_ruby_done_strings(void) 1861686Smax.romanov@nginx.com { 1871686Smax.romanov@nginx.com nxt_ruby_string_t *pstr; 1881686Smax.romanov@nginx.com 1891686Smax.romanov@nginx.com pstr = nxt_rb_strings; 1901686Smax.romanov@nginx.com 1911686Smax.romanov@nginx.com while (pstr->string.start != NULL) { 1921686Smax.romanov@nginx.com rb_gc_unregister_address(pstr->v); 1931686Smax.romanov@nginx.com 1941686Smax.romanov@nginx.com *pstr->v = Qnil; 1951686Smax.romanov@nginx.com 1961686Smax.romanov@nginx.com pstr++; 1971686Smax.romanov@nginx.com } 1981686Smax.romanov@nginx.com } 1991686Smax.romanov@nginx.com 200584Salexander.borisov@nginx.com 2011910So.canty@f5.com static VALUE 2021910So.canty@f5.com nxt_ruby_hook_procs_load(VALUE path) 2031910So.canty@f5.com { 2041910So.canty@f5.com VALUE module, file, file_obj; 2051910So.canty@f5.com 2061910So.canty@f5.com module = rb_define_module("Unit"); 2071910So.canty@f5.com 2081910So.canty@f5.com nxt_ruby_hook_procs = rb_hash_new(); 2091910So.canty@f5.com 2101910So.canty@f5.com rb_gc_register_address(&nxt_ruby_hook_procs); 2111910So.canty@f5.com 2121910So.canty@f5.com rb_define_module_function(module, "on_worker_boot", 2131910So.canty@f5.com &nxt_ruby_hook_register, 0); 2141910So.canty@f5.com rb_define_module_function(module, "on_worker_shutdown", 2151910So.canty@f5.com &nxt_ruby_hook_register, 0); 2161910So.canty@f5.com rb_define_module_function(module, "on_thread_boot", 2171910So.canty@f5.com &nxt_ruby_hook_register, 0); 2181910So.canty@f5.com rb_define_module_function(module, "on_thread_shutdown", 2191910So.canty@f5.com &nxt_ruby_hook_register, 0); 2201910So.canty@f5.com 2211910So.canty@f5.com file = rb_const_get(rb_cObject, rb_intern("File")); 2221910So.canty@f5.com file_obj = rb_funcall(file, rb_intern("read"), 1, path); 2231910So.canty@f5.com 2241910So.canty@f5.com return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path, 2251910So.canty@f5.com INT2NUM(1)); 2261910So.canty@f5.com } 2271910So.canty@f5.com 2281910So.canty@f5.com 2291910So.canty@f5.com static VALUE 2301910So.canty@f5.com nxt_ruby_hook_register(VALUE arg) 2311910So.canty@f5.com { 2321910So.canty@f5.com VALUE kernel, callee, callee_str; 2331910So.canty@f5.com 2341910So.canty@f5.com rb_need_block(); 2351910So.canty@f5.com 2361910So.canty@f5.com kernel = rb_const_get(rb_cObject, rb_intern("Kernel")); 2371910So.canty@f5.com callee = rb_funcall(kernel, rb_intern("__callee__"), 0); 2381910So.canty@f5.com callee_str = rb_funcall(callee, rb_intern("to_s"), 0); 2391910So.canty@f5.com 2401910So.canty@f5.com rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc()); 2411910So.canty@f5.com 2421910So.canty@f5.com return Qnil; 2431910So.canty@f5.com } 2441910So.canty@f5.com 2451910So.canty@f5.com 2461910So.canty@f5.com static VALUE 2471910So.canty@f5.com nxt_ruby_hook_call(VALUE name) 2481910So.canty@f5.com { 2491910So.canty@f5.com VALUE proc; 2501910So.canty@f5.com 2511910So.canty@f5.com proc = rb_hash_lookup(nxt_ruby_hook_procs, name); 2521910So.canty@f5.com if (proc == Qnil) { 2531910So.canty@f5.com return Qnil; 2541910So.canty@f5.com } 2551910So.canty@f5.com 2561910So.canty@f5.com return rb_funcall(proc, rb_intern("call"), 0); 2571910So.canty@f5.com } 2581910So.canty@f5.com 2591910So.canty@f5.com 260584Salexander.borisov@nginx.com static nxt_int_t 2611488St.nateldemoura@f5.com nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) 262584Salexander.borisov@nginx.com { 2631488St.nateldemoura@f5.com int state, rc; 264*2087Sz.hong@f5.com VALUE res, path, script; 2651687Smax.romanov@nginx.com nxt_ruby_ctx_t ruby_ctx; 2661488St.nateldemoura@f5.com nxt_unit_ctx_t *unit_ctx; 2671488St.nateldemoura@f5.com nxt_unit_init_t ruby_unit_init; 2681687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c; 2691488St.nateldemoura@f5.com nxt_ruby_rack_init_t rack_init; 2701488St.nateldemoura@f5.com nxt_common_app_conf_t *conf; 271584Salexander.borisov@nginx.com 2721258Smax.romanov@nginx.com static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; 2731258Smax.romanov@nginx.com 2741488St.nateldemoura@f5.com conf = data->app; 2751687Smax.romanov@nginx.com c = &conf->u.ruby; 2761687Smax.romanov@nginx.com 2771687Smax.romanov@nginx.com nxt_ruby_threads = c->threads; 2781488St.nateldemoura@f5.com 2791814Svbart@nginx.com setlocale(LC_CTYPE, ""); 2801814Svbart@nginx.com 2811258Smax.romanov@nginx.com RUBY_INIT_STACK 282584Salexander.borisov@nginx.com ruby_init(); 2831258Smax.romanov@nginx.com ruby_options(2, argv); 284584Salexander.borisov@nginx.com ruby_script("NGINX_Unit"); 285584Salexander.borisov@nginx.com 286*2087Sz.hong@f5.com script = nxt_ruby_script_basename(&c->script); 287*2087Sz.hong@f5.com 2881687Smax.romanov@nginx.com ruby_ctx.env = Qnil; 289*2087Sz.hong@f5.com ruby_ctx.script = script; 2901687Smax.romanov@nginx.com ruby_ctx.io_input = Qnil; 2911687Smax.romanov@nginx.com ruby_ctx.io_error = Qnil; 2921687Smax.romanov@nginx.com ruby_ctx.thread = Qnil; 2931687Smax.romanov@nginx.com ruby_ctx.ctx = NULL; 2941687Smax.romanov@nginx.com ruby_ctx.req = NULL; 2951687Smax.romanov@nginx.com 296584Salexander.borisov@nginx.com rack_init.task = task; 2971687Smax.romanov@nginx.com rack_init.script = &c->script; 2981687Smax.romanov@nginx.com rack_init.rctx = &ruby_ctx; 299584Salexander.borisov@nginx.com 3001686Smax.romanov@nginx.com nxt_ruby_init_strings(); 3011686Smax.romanov@nginx.com 302584Salexander.borisov@nginx.com res = rb_protect(nxt_ruby_init_basic, 303584Salexander.borisov@nginx.com (VALUE) (uintptr_t) &rack_init, &state); 304584Salexander.borisov@nginx.com if (nxt_slow_path(res == Qnil || state != 0)) { 3051687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 306584Salexander.borisov@nginx.com "Failed to init basic variables"); 307584Salexander.borisov@nginx.com return NXT_ERROR; 308584Salexander.borisov@nginx.com } 309584Salexander.borisov@nginx.com 3101687Smax.romanov@nginx.com nxt_ruby_call = Qnil; 3111910So.canty@f5.com nxt_ruby_hook_procs = Qnil; 3121910So.canty@f5.com 3131910So.canty@f5.com if (c->hooks.start != NULL) { 3141910So.canty@f5.com path = rb_str_new((const char *) c->hooks.start, 3151910So.canty@f5.com (long) c->hooks.length); 3161910So.canty@f5.com 3171910So.canty@f5.com rb_protect(nxt_ruby_hook_procs_load, path, &state); 3181910So.canty@f5.com rb_str_free(path); 3191910So.canty@f5.com if (nxt_slow_path(state != 0)) { 3201910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 3211910So.canty@f5.com "Failed to setup hooks"); 3221910So.canty@f5.com return NXT_ERROR; 3231910So.canty@f5.com } 3241910So.canty@f5.com } 3251910So.canty@f5.com 3261910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 3271910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state); 3281910So.canty@f5.com if (nxt_slow_path(state != 0)) { 3291910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 3301910So.canty@f5.com "Failed to call on_worker_boot()"); 3311910So.canty@f5.com return NXT_ERROR; 3321910So.canty@f5.com } 3331910So.canty@f5.com } 3341687Smax.romanov@nginx.com 335584Salexander.borisov@nginx.com nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); 336584Salexander.borisov@nginx.com if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { 337584Salexander.borisov@nginx.com return NXT_ERROR; 338584Salexander.borisov@nginx.com } 339584Salexander.borisov@nginx.com 3401687Smax.romanov@nginx.com rb_gc_register_address(&nxt_ruby_rackup); 3411687Smax.romanov@nginx.com 342584Salexander.borisov@nginx.com nxt_ruby_call = rb_intern("call"); 343584Salexander.borisov@nginx.com if (nxt_slow_path(nxt_ruby_call == Qnil)) { 344584Salexander.borisov@nginx.com nxt_alert(task, "Ruby: Unable to find rack entry point"); 345584Salexander.borisov@nginx.com 3461687Smax.romanov@nginx.com goto fail; 347584Salexander.borisov@nginx.com } 348584Salexander.borisov@nginx.com 3491687Smax.romanov@nginx.com rb_gc_register_address(&nxt_ruby_call); 3501687Smax.romanov@nginx.com 3511687Smax.romanov@nginx.com ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create, 3521687Smax.romanov@nginx.com (VALUE) (uintptr_t) &ruby_ctx, &state); 3531687Smax.romanov@nginx.com if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) { 3541687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 355584Salexander.borisov@nginx.com "Failed to create 'environ' variable"); 3561687Smax.romanov@nginx.com goto fail; 357584Salexander.borisov@nginx.com } 358584Salexander.borisov@nginx.com 359*2087Sz.hong@f5.com rc = nxt_ruby_init_threads(script, c); 3601687Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 3611687Smax.romanov@nginx.com goto fail; 3621687Smax.romanov@nginx.com } 363584Salexander.borisov@nginx.com 3641980Smax.romanov@nginx.com nxt_unit_default_init(task, &ruby_unit_init, conf); 365743Smax.romanov@nginx.com 366743Smax.romanov@nginx.com ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; 3671687Smax.romanov@nginx.com ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler; 3681687Smax.romanov@nginx.com ruby_unit_init.data = c; 3691687Smax.romanov@nginx.com ruby_unit_init.ctx_data = &ruby_ctx; 370743Smax.romanov@nginx.com 371743Smax.romanov@nginx.com unit_ctx = nxt_unit_init(&ruby_unit_init); 372743Smax.romanov@nginx.com if (nxt_slow_path(unit_ctx == NULL)) { 3731687Smax.romanov@nginx.com goto fail; 374743Smax.romanov@nginx.com } 375743Smax.romanov@nginx.com 3761910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 3771910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); 3781910So.canty@f5.com if (nxt_slow_path(state != 0)) { 3791910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 3801910So.canty@f5.com "Failed to call on_thread_boot()"); 3811910So.canty@f5.com } 3821910So.canty@f5.com } 3831910So.canty@f5.com 3841687Smax.romanov@nginx.com rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, 3851687Smax.romanov@nginx.com nxt_ruby_ubf, unit_ctx); 386743Smax.romanov@nginx.com 3871910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 3881910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); 3891910So.canty@f5.com if (nxt_slow_path(state != 0)) { 3901910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 3911910So.canty@f5.com "Failed to call on_thread_shutdown()"); 3921910So.canty@f5.com } 3931910So.canty@f5.com } 3941910So.canty@f5.com 3951687Smax.romanov@nginx.com nxt_ruby_join_threads(unit_ctx, c); 3961687Smax.romanov@nginx.com 3971910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 3981910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state); 3991910So.canty@f5.com if (nxt_slow_path(state != 0)) { 4001910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 4011910So.canty@f5.com "Failed to call on_worker_shutdown()"); 4021910So.canty@f5.com } 4031910So.canty@f5.com } 4041910So.canty@f5.com 4051687Smax.romanov@nginx.com nxt_unit_done(unit_ctx); 4061687Smax.romanov@nginx.com 4071687Smax.romanov@nginx.com nxt_ruby_ctx_done(&ruby_ctx); 408743Smax.romanov@nginx.com 409743Smax.romanov@nginx.com nxt_ruby_atexit(); 410743Smax.romanov@nginx.com 411743Smax.romanov@nginx.com exit(rc); 412743Smax.romanov@nginx.com 413584Salexander.borisov@nginx.com return NXT_OK; 4141687Smax.romanov@nginx.com 4151687Smax.romanov@nginx.com fail: 4161687Smax.romanov@nginx.com 4171687Smax.romanov@nginx.com nxt_ruby_join_threads(NULL, c); 4181687Smax.romanov@nginx.com 4191687Smax.romanov@nginx.com nxt_ruby_ctx_done(&ruby_ctx); 4201687Smax.romanov@nginx.com 4211687Smax.romanov@nginx.com nxt_ruby_atexit(); 4221687Smax.romanov@nginx.com 4231687Smax.romanov@nginx.com return NXT_ERROR; 424584Salexander.borisov@nginx.com } 425584Salexander.borisov@nginx.com 426584Salexander.borisov@nginx.com 427584Salexander.borisov@nginx.com static VALUE 428*2087Sz.hong@f5.com nxt_ruby_script_basename(nxt_str_t *script) 429*2087Sz.hong@f5.com { 430*2087Sz.hong@f5.com size_t len; 431*2087Sz.hong@f5.com u_char *p, *last; 432*2087Sz.hong@f5.com 433*2087Sz.hong@f5.com last = NULL; 434*2087Sz.hong@f5.com p = script->start + script->length; 435*2087Sz.hong@f5.com 436*2087Sz.hong@f5.com while (p > script->start) { 437*2087Sz.hong@f5.com 438*2087Sz.hong@f5.com if (p[-1] == '/') { 439*2087Sz.hong@f5.com last = p; 440*2087Sz.hong@f5.com break; 441*2087Sz.hong@f5.com } 442*2087Sz.hong@f5.com 443*2087Sz.hong@f5.com p--; 444*2087Sz.hong@f5.com } 445*2087Sz.hong@f5.com 446*2087Sz.hong@f5.com if (last != NULL) { 447*2087Sz.hong@f5.com len = script->length - (last - script->start); 448*2087Sz.hong@f5.com 449*2087Sz.hong@f5.com } else { 450*2087Sz.hong@f5.com last = script->start; 451*2087Sz.hong@f5.com len = script->length; 452*2087Sz.hong@f5.com } 453*2087Sz.hong@f5.com 454*2087Sz.hong@f5.com return rb_str_new((const char *) last, len); 455*2087Sz.hong@f5.com } 456*2087Sz.hong@f5.com 457*2087Sz.hong@f5.com 458*2087Sz.hong@f5.com static VALUE 459584Salexander.borisov@nginx.com nxt_ruby_init_basic(VALUE arg) 460584Salexander.borisov@nginx.com { 461584Salexander.borisov@nginx.com int state; 462584Salexander.borisov@nginx.com nxt_ruby_rack_init_t *rack_init; 463584Salexander.borisov@nginx.com 464584Salexander.borisov@nginx.com rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; 465584Salexander.borisov@nginx.com 466584Salexander.borisov@nginx.com state = rb_enc_find_index("encdb"); 467584Salexander.borisov@nginx.com if (nxt_slow_path(state == 0)) { 468584Salexander.borisov@nginx.com nxt_alert(rack_init->task, 469584Salexander.borisov@nginx.com "Ruby: Failed to find encoding index 'encdb'"); 470584Salexander.borisov@nginx.com 471584Salexander.borisov@nginx.com return Qnil; 472584Salexander.borisov@nginx.com } 473584Salexander.borisov@nginx.com 474609Salexander.borisov@nginx.com rb_funcall(rb_cObject, rb_intern("require"), 1, 475609Salexander.borisov@nginx.com rb_str_new2("enc/trans/transdb")); 476609Salexander.borisov@nginx.com 477584Salexander.borisov@nginx.com return arg; 478584Salexander.borisov@nginx.com } 479584Salexander.borisov@nginx.com 480584Salexander.borisov@nginx.com 481584Salexander.borisov@nginx.com static VALUE 482584Salexander.borisov@nginx.com nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) 483584Salexander.borisov@nginx.com { 484584Salexander.borisov@nginx.com int state; 4851687Smax.romanov@nginx.com VALUE rackup, err; 486584Salexander.borisov@nginx.com 487584Salexander.borisov@nginx.com rb_protect(nxt_ruby_require_rubygems, Qnil, &state); 488584Salexander.borisov@nginx.com if (nxt_slow_path(state != 0)) { 4891687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 490584Salexander.borisov@nginx.com "Failed to require 'rubygems' package"); 491584Salexander.borisov@nginx.com return Qnil; 492584Salexander.borisov@nginx.com } 493584Salexander.borisov@nginx.com 494717Salexander.borisov@nginx.com rb_protect(nxt_ruby_bundler_setup, Qnil, &state); 495717Salexander.borisov@nginx.com if (state != 0) { 496717Salexander.borisov@nginx.com err = rb_errinfo(); 497717Salexander.borisov@nginx.com 498717Salexander.borisov@nginx.com if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { 4991687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 500717Salexander.borisov@nginx.com "Failed to require 'bundler/setup' package"); 501717Salexander.borisov@nginx.com return Qnil; 502717Salexander.borisov@nginx.com } 503717Salexander.borisov@nginx.com 504717Salexander.borisov@nginx.com rb_set_errinfo(Qnil); 505717Salexander.borisov@nginx.com } 506717Salexander.borisov@nginx.com 507584Salexander.borisov@nginx.com rb_protect(nxt_ruby_require_rack, Qnil, &state); 508584Salexander.borisov@nginx.com if (nxt_slow_path(state != 0)) { 5091687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 510584Salexander.borisov@nginx.com "Failed to require 'rack' package"); 511584Salexander.borisov@nginx.com return Qnil; 512584Salexander.borisov@nginx.com } 513584Salexander.borisov@nginx.com 514584Salexander.borisov@nginx.com rackup = rb_protect(nxt_ruby_rack_parse_script, 515584Salexander.borisov@nginx.com (VALUE) (uintptr_t) rack_init, &state); 516584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { 5171687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 518584Salexander.borisov@nginx.com "Failed to parse rack script"); 519584Salexander.borisov@nginx.com return Qnil; 520584Salexander.borisov@nginx.com } 521584Salexander.borisov@nginx.com 522584Salexander.borisov@nginx.com if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { 523584Salexander.borisov@nginx.com nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); 524584Salexander.borisov@nginx.com return Qnil; 525584Salexander.borisov@nginx.com } 526584Salexander.borisov@nginx.com 527584Salexander.borisov@nginx.com return RARRAY_PTR(rackup)[0]; 528584Salexander.borisov@nginx.com } 529584Salexander.borisov@nginx.com 530584Salexander.borisov@nginx.com 531584Salexander.borisov@nginx.com static VALUE 532584Salexander.borisov@nginx.com nxt_ruby_require_rubygems(VALUE arg) 533584Salexander.borisov@nginx.com { 534584Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, 535584Salexander.borisov@nginx.com rb_str_new2("rubygems")); 536584Salexander.borisov@nginx.com } 537584Salexander.borisov@nginx.com 538584Salexander.borisov@nginx.com 539584Salexander.borisov@nginx.com static VALUE 540717Salexander.borisov@nginx.com nxt_ruby_bundler_setup(VALUE arg) 541717Salexander.borisov@nginx.com { 542717Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, 543717Salexander.borisov@nginx.com rb_str_new2("bundler/setup")); 544717Salexander.borisov@nginx.com } 545717Salexander.borisov@nginx.com 546717Salexander.borisov@nginx.com 547717Salexander.borisov@nginx.com static VALUE 548584Salexander.borisov@nginx.com nxt_ruby_require_rack(VALUE arg) 549584Salexander.borisov@nginx.com { 550584Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 551584Salexander.borisov@nginx.com } 552584Salexander.borisov@nginx.com 553584Salexander.borisov@nginx.com 554584Salexander.borisov@nginx.com static VALUE 555584Salexander.borisov@nginx.com nxt_ruby_rack_parse_script(VALUE ctx) 556584Salexander.borisov@nginx.com { 5571687Smax.romanov@nginx.com VALUE script, res, rack, builder; 558584Salexander.borisov@nginx.com nxt_ruby_rack_init_t *rack_init; 559584Salexander.borisov@nginx.com 560584Salexander.borisov@nginx.com rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; 561584Salexander.borisov@nginx.com 5621687Smax.romanov@nginx.com rack = rb_const_get(rb_cObject, rb_intern("Rack")); 5631687Smax.romanov@nginx.com builder = rb_const_get(rack, rb_intern("Builder")); 5641687Smax.romanov@nginx.com 565584Salexander.borisov@nginx.com script = rb_str_new((const char *) rack_init->script->start, 566584Salexander.borisov@nginx.com (long) rack_init->script->length); 567584Salexander.borisov@nginx.com 5681687Smax.romanov@nginx.com res = rb_funcall(builder, rb_intern("parse_file"), 1, script); 569584Salexander.borisov@nginx.com 570584Salexander.borisov@nginx.com rb_str_free(script); 571584Salexander.borisov@nginx.com 572584Salexander.borisov@nginx.com return res; 573584Salexander.borisov@nginx.com } 574584Salexander.borisov@nginx.com 575584Salexander.borisov@nginx.com 576584Salexander.borisov@nginx.com static VALUE 577584Salexander.borisov@nginx.com nxt_ruby_rack_env_create(VALUE arg) 578584Salexander.borisov@nginx.com { 5791687Smax.romanov@nginx.com int rc; 5801687Smax.romanov@nginx.com VALUE hash_env, version; 5811687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 5821687Smax.romanov@nginx.com 5831687Smax.romanov@nginx.com rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 5841687Smax.romanov@nginx.com 5851687Smax.romanov@nginx.com rc = nxt_ruby_init_io(rctx); 5861687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 5871687Smax.romanov@nginx.com return Qnil; 5881687Smax.romanov@nginx.com } 589584Salexander.borisov@nginx.com 590584Salexander.borisov@nginx.com hash_env = rb_hash_new(); 591673Svbart@nginx.com 592673Svbart@nginx.com rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 593673Svbart@nginx.com rb_str_new((const char *) nxt_server.start, 594673Svbart@nginx.com (long) nxt_server.length)); 595673Svbart@nginx.com 596584Salexander.borisov@nginx.com version = rb_ary_new(); 597584Salexander.borisov@nginx.com 598584Salexander.borisov@nginx.com rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 599584Salexander.borisov@nginx.com rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 600584Salexander.borisov@nginx.com 601*2087Sz.hong@f5.com rb_hash_aset(hash_env, rb_str_new2("SCRIPT_NAME"), rctx->script); 602584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 6031687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input); 6041687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error); 6051687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), 6061687Smax.romanov@nginx.com nxt_ruby_threads > 1 ? Qtrue : Qfalse); 607584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 608584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 609584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 610584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 611584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 612584Salexander.borisov@nginx.com 6131687Smax.romanov@nginx.com rctx->env = hash_env; 6141687Smax.romanov@nginx.com 6151687Smax.romanov@nginx.com rb_gc_register_address(&rctx->env); 6161687Smax.romanov@nginx.com 617584Salexander.borisov@nginx.com return hash_env; 618584Salexander.borisov@nginx.com } 619584Salexander.borisov@nginx.com 620584Salexander.borisov@nginx.com 6211687Smax.romanov@nginx.com static int 6221687Smax.romanov@nginx.com nxt_ruby_init_io(nxt_ruby_ctx_t *rctx) 6231687Smax.romanov@nginx.com { 6241687Smax.romanov@nginx.com VALUE io_input, io_error; 6251687Smax.romanov@nginx.com 6261687Smax.romanov@nginx.com io_input = nxt_ruby_stream_io_input_init(); 6271687Smax.romanov@nginx.com 6281687Smax.romanov@nginx.com rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1, 6291687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx); 6301687Smax.romanov@nginx.com if (nxt_slow_path(rctx->io_input == Qnil)) { 6311687Smax.romanov@nginx.com nxt_unit_alert(NULL, 6321687Smax.romanov@nginx.com "Ruby: Failed to create environment 'rack.input' var"); 6331687Smax.romanov@nginx.com 6341687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 6351687Smax.romanov@nginx.com } 6361687Smax.romanov@nginx.com 6371687Smax.romanov@nginx.com rb_gc_register_address(&rctx->io_input); 6381687Smax.romanov@nginx.com 6391687Smax.romanov@nginx.com io_error = nxt_ruby_stream_io_error_init(); 6401687Smax.romanov@nginx.com 6411687Smax.romanov@nginx.com rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1, 6421687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx); 6431687Smax.romanov@nginx.com if (nxt_slow_path(rctx->io_error == Qnil)) { 6441687Smax.romanov@nginx.com nxt_unit_alert(NULL, 6451687Smax.romanov@nginx.com "Ruby: Failed to create environment 'rack.error' var"); 6461687Smax.romanov@nginx.com 6471687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 6481687Smax.romanov@nginx.com } 6491687Smax.romanov@nginx.com 6501687Smax.romanov@nginx.com rb_gc_register_address(&rctx->io_error); 6511687Smax.romanov@nginx.com 6521687Smax.romanov@nginx.com return NXT_UNIT_OK; 6531687Smax.romanov@nginx.com } 6541687Smax.romanov@nginx.com 6551687Smax.romanov@nginx.com 656743Smax.romanov@nginx.com static void 657743Smax.romanov@nginx.com nxt_ruby_request_handler(nxt_unit_request_info_t *req) 658584Salexander.borisov@nginx.com { 6591687Smax.romanov@nginx.com (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req); 6601687Smax.romanov@nginx.com } 6611687Smax.romanov@nginx.com 662584Salexander.borisov@nginx.com 6631687Smax.romanov@nginx.com static void * 6641687Smax.romanov@nginx.com nxt_ruby_request_handler_gvl(void *data) 6651687Smax.romanov@nginx.com { 6661687Smax.romanov@nginx.com int state; 6671687Smax.romanov@nginx.com VALUE res; 6681687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 6691687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 6701687Smax.romanov@nginx.com 6711687Smax.romanov@nginx.com req = data; 672584Salexander.borisov@nginx.com 6731687Smax.romanov@nginx.com rctx = req->ctx->data; 6741687Smax.romanov@nginx.com rctx->req = req; 6751687Smax.romanov@nginx.com 6761687Smax.romanov@nginx.com res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state); 677743Smax.romanov@nginx.com if (nxt_slow_path(res == Qnil || state != 0)) { 6781687Smax.romanov@nginx.com nxt_ruby_exception_log(req, NXT_LOG_ERR, 679584Salexander.borisov@nginx.com "Failed to run ruby script"); 6801687Smax.romanov@nginx.com 6811687Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 6821687Smax.romanov@nginx.com 6831687Smax.romanov@nginx.com } else { 6841687Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_OK); 685584Salexander.borisov@nginx.com } 6861687Smax.romanov@nginx.com 6871687Smax.romanov@nginx.com rctx->req = NULL; 6881687Smax.romanov@nginx.com 6891687Smax.romanov@nginx.com return NULL; 690584Salexander.borisov@nginx.com } 691584Salexander.borisov@nginx.com 692584Salexander.borisov@nginx.com 693584Salexander.borisov@nginx.com static VALUE 694584Salexander.borisov@nginx.com nxt_ruby_rack_app_run(VALUE arg) 695584Salexander.borisov@nginx.com { 6961687Smax.romanov@nginx.com int rc; 6971687Smax.romanov@nginx.com VALUE env, result; 6981687Smax.romanov@nginx.com nxt_int_t status; 6991687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 7001687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 7011687Smax.romanov@nginx.com 7021687Smax.romanov@nginx.com req = (nxt_unit_request_info_t *) arg; 703584Salexander.borisov@nginx.com 7041687Smax.romanov@nginx.com rctx = req->ctx->data; 7051687Smax.romanov@nginx.com 7061687Smax.romanov@nginx.com env = rb_hash_dup(rctx->env); 707584Salexander.borisov@nginx.com 7081687Smax.romanov@nginx.com rc = nxt_ruby_read_request(req, env); 709743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 7101687Smax.romanov@nginx.com nxt_unit_req_alert(req, 711743Smax.romanov@nginx.com "Ruby: Failed to process incoming request"); 712584Salexander.borisov@nginx.com 713584Salexander.borisov@nginx.com goto fail; 714584Salexander.borisov@nginx.com } 715584Salexander.borisov@nginx.com 716584Salexander.borisov@nginx.com result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 717584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 7181687Smax.romanov@nginx.com nxt_unit_req_error(req, 719743Smax.romanov@nginx.com "Ruby: Invalid response format from application"); 720584Salexander.borisov@nginx.com 721584Salexander.borisov@nginx.com goto fail; 722584Salexander.borisov@nginx.com } 723584Salexander.borisov@nginx.com 724584Salexander.borisov@nginx.com if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 7251687Smax.romanov@nginx.com nxt_unit_req_error(req, 726743Smax.romanov@nginx.com "Ruby: Invalid response format from application. " 727743Smax.romanov@nginx.com "Need 3 entries [Status, Headers, Body]"); 728584Salexander.borisov@nginx.com 729584Salexander.borisov@nginx.com goto fail; 730584Salexander.borisov@nginx.com } 731584Salexander.borisov@nginx.com 7321687Smax.romanov@nginx.com status = nxt_ruby_rack_result_status(req, result); 733743Smax.romanov@nginx.com if (nxt_slow_path(status < 0)) { 7341687Smax.romanov@nginx.com nxt_unit_req_error(req, 735743Smax.romanov@nginx.com "Ruby: Invalid response status from application."); 736743Smax.romanov@nginx.com 737584Salexander.borisov@nginx.com goto fail; 738584Salexander.borisov@nginx.com } 739584Salexander.borisov@nginx.com 7401687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_headers(req, result, status); 741743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 742584Salexander.borisov@nginx.com goto fail; 743584Salexander.borisov@nginx.com } 744584Salexander.borisov@nginx.com 7451687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_body(req, result); 746743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 747584Salexander.borisov@nginx.com goto fail; 748584Salexander.borisov@nginx.com } 749584Salexander.borisov@nginx.com 750584Salexander.borisov@nginx.com rb_hash_delete(env, rb_obj_id(env)); 751584Salexander.borisov@nginx.com 752584Salexander.borisov@nginx.com return result; 753584Salexander.borisov@nginx.com 754584Salexander.borisov@nginx.com fail: 755584Salexander.borisov@nginx.com 756584Salexander.borisov@nginx.com rb_hash_delete(env, rb_obj_id(env)); 757584Salexander.borisov@nginx.com 758584Salexander.borisov@nginx.com return Qnil; 759584Salexander.borisov@nginx.com } 760584Salexander.borisov@nginx.com 761584Salexander.borisov@nginx.com 762743Smax.romanov@nginx.com static int 7631687Smax.romanov@nginx.com nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) 764584Salexander.borisov@nginx.com { 7651686Smax.romanov@nginx.com VALUE name; 766967Svbart@nginx.com uint32_t i; 767743Smax.romanov@nginx.com nxt_unit_field_t *f; 768743Smax.romanov@nginx.com nxt_unit_request_t *r; 769584Salexander.borisov@nginx.com 7701687Smax.romanov@nginx.com r = req->request; 771584Salexander.borisov@nginx.com 7721686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, 773743Smax.romanov@nginx.com r->method_length); 7741686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target, 775743Smax.romanov@nginx.com r->target_length); 7761686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length); 7771686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query, 778981Svbart@nginx.com r->query_length); 7791686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version, 780743Smax.romanov@nginx.com r->version_length); 7811686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, 782743Smax.romanov@nginx.com r->remote_length); 7831686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, 7841686Smax.romanov@nginx.com r->local_length); 7851686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, 7861686Smax.romanov@nginx.com r->server_name_length); 787584Salexander.borisov@nginx.com 7881686Smax.romanov@nginx.com rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str); 789967Svbart@nginx.com 7901686Smax.romanov@nginx.com rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str, 7911686Smax.romanov@nginx.com r->tls ? nxt_rb_https_str : nxt_rb_http_str); 7921011Smax.romanov@nginx.com 793743Smax.romanov@nginx.com for (i = 0; i < r->fields_count; i++) { 794743Smax.romanov@nginx.com f = r->fields + i; 795584Salexander.borisov@nginx.com 7961686Smax.romanov@nginx.com name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length); 7971686Smax.romanov@nginx.com 7981686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length); 799584Salexander.borisov@nginx.com } 800584Salexander.borisov@nginx.com 801743Smax.romanov@nginx.com if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 802743Smax.romanov@nginx.com f = r->fields + r->content_length_field; 803584Salexander.borisov@nginx.com 8041686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str, 805743Smax.romanov@nginx.com &f->value, f->value_length); 806584Salexander.borisov@nginx.com } 807584Salexander.borisov@nginx.com 808743Smax.romanov@nginx.com if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 809743Smax.romanov@nginx.com f = r->fields + r->content_type_field; 810584Salexander.borisov@nginx.com 8111686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str, 812743Smax.romanov@nginx.com &f->value, f->value_length); 813584Salexander.borisov@nginx.com } 814584Salexander.borisov@nginx.com 815743Smax.romanov@nginx.com return NXT_UNIT_OK; 816584Salexander.borisov@nginx.com } 817584Salexander.borisov@nginx.com 818584Salexander.borisov@nginx.com 819743Smax.romanov@nginx.com nxt_inline void 8201686Smax.romanov@nginx.com nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 8211686Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t len) 822584Salexander.borisov@nginx.com { 823743Smax.romanov@nginx.com char *str; 824743Smax.romanov@nginx.com 825743Smax.romanov@nginx.com str = nxt_unit_sptr_get(sptr); 826584Salexander.borisov@nginx.com 8271686Smax.romanov@nginx.com rb_hash_aset(hash_env, name, rb_str_new(str, len)); 828584Salexander.borisov@nginx.com } 829584Salexander.borisov@nginx.com 830584Salexander.borisov@nginx.com 831584Salexander.borisov@nginx.com static nxt_int_t 8321687Smax.romanov@nginx.com nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result) 833584Salexander.borisov@nginx.com { 834743Smax.romanov@nginx.com VALUE status; 835584Salexander.borisov@nginx.com 836584Salexander.borisov@nginx.com status = rb_ary_entry(result, 0); 837584Salexander.borisov@nginx.com 838584Salexander.borisov@nginx.com if (TYPE(status) == T_FIXNUM) { 839743Smax.romanov@nginx.com return FIX2INT(status); 840584Salexander.borisov@nginx.com } 841584Salexander.borisov@nginx.com 842743Smax.romanov@nginx.com if (TYPE(status) == T_STRING) { 843743Smax.romanov@nginx.com return nxt_int_parse((u_char *) RSTRING_PTR(status), 844743Smax.romanov@nginx.com RSTRING_LEN(status)); 845584Salexander.borisov@nginx.com } 846584Salexander.borisov@nginx.com 8471687Smax.romanov@nginx.com nxt_unit_req_error(req, "Ruby: Invalid response 'status' " 848743Smax.romanov@nginx.com "format from application"); 849584Salexander.borisov@nginx.com 850743Smax.romanov@nginx.com return -2; 851584Salexander.borisov@nginx.com } 852584Salexander.borisov@nginx.com 853584Salexander.borisov@nginx.com 854743Smax.romanov@nginx.com typedef struct { 8551687Smax.romanov@nginx.com int rc; 8561687Smax.romanov@nginx.com uint32_t fields; 8571687Smax.romanov@nginx.com uint32_t size; 8581687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 859743Smax.romanov@nginx.com } nxt_ruby_headers_info_t; 860743Smax.romanov@nginx.com 861743Smax.romanov@nginx.com 862743Smax.romanov@nginx.com static int 8631687Smax.romanov@nginx.com nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result, 8641687Smax.romanov@nginx.com nxt_int_t status) 865584Salexander.borisov@nginx.com { 866743Smax.romanov@nginx.com int rc; 867743Smax.romanov@nginx.com VALUE headers; 868743Smax.romanov@nginx.com nxt_ruby_headers_info_t headers_info; 869743Smax.romanov@nginx.com 870743Smax.romanov@nginx.com headers = rb_ary_entry(result, 1); 871743Smax.romanov@nginx.com if (nxt_slow_path(TYPE(headers) != T_HASH)) { 8721687Smax.romanov@nginx.com nxt_unit_req_error(req, 873743Smax.romanov@nginx.com "Ruby: Invalid response 'headers' format from " 874743Smax.romanov@nginx.com "application"); 875743Smax.romanov@nginx.com 876743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 877743Smax.romanov@nginx.com } 878584Salexander.borisov@nginx.com 879743Smax.romanov@nginx.com rc = NXT_UNIT_OK; 880743Smax.romanov@nginx.com 881743Smax.romanov@nginx.com headers_info.rc = NXT_UNIT_OK; 882743Smax.romanov@nginx.com headers_info.fields = 0; 883743Smax.romanov@nginx.com headers_info.size = 0; 8841687Smax.romanov@nginx.com headers_info.req = req; 885743Smax.romanov@nginx.com 886743Smax.romanov@nginx.com rb_hash_foreach(headers, nxt_ruby_hash_info, 887743Smax.romanov@nginx.com (VALUE) (uintptr_t) &headers_info); 888743Smax.romanov@nginx.com if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { 889743Smax.romanov@nginx.com return headers_info.rc; 890743Smax.romanov@nginx.com } 891743Smax.romanov@nginx.com 8921687Smax.romanov@nginx.com rc = nxt_unit_response_init(req, status, 893743Smax.romanov@nginx.com headers_info.fields, headers_info.size); 894743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 895584Salexander.borisov@nginx.com return rc; 896584Salexander.borisov@nginx.com } 897584Salexander.borisov@nginx.com 8981687Smax.romanov@nginx.com rb_hash_foreach(headers, nxt_ruby_hash_add, 8991687Smax.romanov@nginx.com (VALUE) (uintptr_t) &headers_info); 900584Salexander.borisov@nginx.com 901584Salexander.borisov@nginx.com return rc; 902584Salexander.borisov@nginx.com } 903584Salexander.borisov@nginx.com 904584Salexander.borisov@nginx.com 905743Smax.romanov@nginx.com static int 906743Smax.romanov@nginx.com nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) 907584Salexander.borisov@nginx.com { 908743Smax.romanov@nginx.com const char *value, *value_end, *pos; 909743Smax.romanov@nginx.com nxt_ruby_headers_info_t *headers_info; 910584Salexander.borisov@nginx.com 911743Smax.romanov@nginx.com headers_info = (void *) (uintptr_t) arg; 912584Salexander.borisov@nginx.com 913584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 9141687Smax.romanov@nginx.com nxt_unit_req_error(headers_info->req, 915743Smax.romanov@nginx.com "Ruby: Wrong header entry 'key' from application"); 916584Salexander.borisov@nginx.com 917584Salexander.borisov@nginx.com goto fail; 918584Salexander.borisov@nginx.com } 919584Salexander.borisov@nginx.com 920584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 9211687Smax.romanov@nginx.com nxt_unit_req_error(headers_info->req, 922743Smax.romanov@nginx.com "Ruby: Wrong header entry 'value' from application"); 923584Salexander.borisov@nginx.com 924584Salexander.borisov@nginx.com goto fail; 925584Salexander.borisov@nginx.com } 926584Salexander.borisov@nginx.com 927584Salexander.borisov@nginx.com value = RSTRING_PTR(r_value); 928584Salexander.borisov@nginx.com value_end = value + RSTRING_LEN(r_value); 929584Salexander.borisov@nginx.com 930584Salexander.borisov@nginx.com pos = value; 931584Salexander.borisov@nginx.com 932584Salexander.borisov@nginx.com for ( ;; ) { 933584Salexander.borisov@nginx.com pos = strchr(pos, '\n'); 934584Salexander.borisov@nginx.com 935584Salexander.borisov@nginx.com if (pos == NULL) { 936584Salexander.borisov@nginx.com break; 937584Salexander.borisov@nginx.com } 938584Salexander.borisov@nginx.com 939743Smax.romanov@nginx.com headers_info->fields++; 940743Smax.romanov@nginx.com headers_info->size += RSTRING_LEN(r_key) + (pos - value); 941743Smax.romanov@nginx.com 942743Smax.romanov@nginx.com pos++; 943743Smax.romanov@nginx.com value = pos; 944743Smax.romanov@nginx.com } 945743Smax.romanov@nginx.com 946743Smax.romanov@nginx.com if (value <= value_end) { 947743Smax.romanov@nginx.com headers_info->fields++; 948743Smax.romanov@nginx.com headers_info->size += RSTRING_LEN(r_key) + (value_end - value); 949743Smax.romanov@nginx.com } 950743Smax.romanov@nginx.com 951743Smax.romanov@nginx.com return ST_CONTINUE; 952743Smax.romanov@nginx.com 953743Smax.romanov@nginx.com fail: 954743Smax.romanov@nginx.com 955743Smax.romanov@nginx.com headers_info->rc = NXT_UNIT_ERROR; 956743Smax.romanov@nginx.com 957743Smax.romanov@nginx.com return ST_STOP; 958743Smax.romanov@nginx.com } 959743Smax.romanov@nginx.com 960743Smax.romanov@nginx.com 961743Smax.romanov@nginx.com static int 962743Smax.romanov@nginx.com nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) 963743Smax.romanov@nginx.com { 9641687Smax.romanov@nginx.com int *rc; 9651687Smax.romanov@nginx.com uint32_t key_len; 9661687Smax.romanov@nginx.com const char *value, *value_end, *pos; 9671687Smax.romanov@nginx.com nxt_ruby_headers_info_t *headers_info; 968743Smax.romanov@nginx.com 9691687Smax.romanov@nginx.com headers_info = (void *) (uintptr_t) arg; 9701687Smax.romanov@nginx.com rc = &headers_info->rc; 971743Smax.romanov@nginx.com 972743Smax.romanov@nginx.com value = RSTRING_PTR(r_value); 973743Smax.romanov@nginx.com value_end = value + RSTRING_LEN(r_value); 974743Smax.romanov@nginx.com 975743Smax.romanov@nginx.com key_len = RSTRING_LEN(r_key); 976743Smax.romanov@nginx.com 977743Smax.romanov@nginx.com pos = value; 978743Smax.romanov@nginx.com 979743Smax.romanov@nginx.com for ( ;; ) { 980743Smax.romanov@nginx.com pos = strchr(pos, '\n'); 981743Smax.romanov@nginx.com 982743Smax.romanov@nginx.com if (pos == NULL) { 983743Smax.romanov@nginx.com break; 984743Smax.romanov@nginx.com } 985743Smax.romanov@nginx.com 9861687Smax.romanov@nginx.com *rc = nxt_unit_response_add_field(headers_info->req, 987743Smax.romanov@nginx.com RSTRING_PTR(r_key), key_len, 988743Smax.romanov@nginx.com value, pos - value); 989743Smax.romanov@nginx.com if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 990584Salexander.borisov@nginx.com goto fail; 991584Salexander.borisov@nginx.com } 992584Salexander.borisov@nginx.com 993584Salexander.borisov@nginx.com pos++; 994584Salexander.borisov@nginx.com value = pos; 995584Salexander.borisov@nginx.com } 996584Salexander.borisov@nginx.com 997584Salexander.borisov@nginx.com if (value <= value_end) { 9981687Smax.romanov@nginx.com *rc = nxt_unit_response_add_field(headers_info->req, 999743Smax.romanov@nginx.com RSTRING_PTR(r_key), key_len, 1000743Smax.romanov@nginx.com value, value_end - value); 1001743Smax.romanov@nginx.com if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 1002584Salexander.borisov@nginx.com goto fail; 1003584Salexander.borisov@nginx.com } 1004584Salexander.borisov@nginx.com } 1005584Salexander.borisov@nginx.com 1006584Salexander.borisov@nginx.com return ST_CONTINUE; 1007584Salexander.borisov@nginx.com 1008584Salexander.borisov@nginx.com fail: 1009584Salexander.borisov@nginx.com 1010743Smax.romanov@nginx.com *rc = NXT_UNIT_ERROR; 1011584Salexander.borisov@nginx.com 1012584Salexander.borisov@nginx.com return ST_STOP; 1013584Salexander.borisov@nginx.com } 1014584Salexander.borisov@nginx.com 1015584Salexander.borisov@nginx.com 1016743Smax.romanov@nginx.com static int 10171687Smax.romanov@nginx.com nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result) 1018584Salexander.borisov@nginx.com { 1019743Smax.romanov@nginx.com int rc; 1020743Smax.romanov@nginx.com VALUE fn, body; 1021584Salexander.borisov@nginx.com 1022584Salexander.borisov@nginx.com body = rb_ary_entry(result, 2); 1023584Salexander.borisov@nginx.com 1024584Salexander.borisov@nginx.com if (rb_respond_to(body, rb_intern("to_path"))) { 1025584Salexander.borisov@nginx.com 1026584Salexander.borisov@nginx.com fn = rb_funcall(body, rb_intern("to_path"), 0); 1027584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(fn) != T_STRING)) { 10281687Smax.romanov@nginx.com nxt_unit_req_error(req, 1029743Smax.romanov@nginx.com "Ruby: Failed to get 'body' file path from " 1030743Smax.romanov@nginx.com "application"); 1031584Salexander.borisov@nginx.com 1032743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1033584Salexander.borisov@nginx.com } 1034584Salexander.borisov@nginx.com 10351687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_body_file_write(req, fn); 1036743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1037743Smax.romanov@nginx.com return rc; 1038584Salexander.borisov@nginx.com } 1039584Salexander.borisov@nginx.com 1040584Salexander.borisov@nginx.com } else if (rb_respond_to(body, rb_intern("each"))) { 10411258Smax.romanov@nginx.com rb_block_call(body, rb_intern("each"), 0, 0, 10421687Smax.romanov@nginx.com nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req); 1043584Salexander.borisov@nginx.com 1044584Salexander.borisov@nginx.com } else { 10451687Smax.romanov@nginx.com nxt_unit_req_error(req, 1046743Smax.romanov@nginx.com "Ruby: Invalid response 'body' format " 1047743Smax.romanov@nginx.com "from application"); 1048584Salexander.borisov@nginx.com 1049743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1050584Salexander.borisov@nginx.com } 1051584Salexander.borisov@nginx.com 1052584Salexander.borisov@nginx.com if (rb_respond_to(body, rb_intern("close"))) { 1053584Salexander.borisov@nginx.com rb_funcall(body, rb_intern("close"), 0); 1054584Salexander.borisov@nginx.com } 1055584Salexander.borisov@nginx.com 1056743Smax.romanov@nginx.com return NXT_UNIT_OK; 1057743Smax.romanov@nginx.com } 1058743Smax.romanov@nginx.com 1059743Smax.romanov@nginx.com 1060743Smax.romanov@nginx.com typedef struct { 1061743Smax.romanov@nginx.com int fd; 1062743Smax.romanov@nginx.com off_t pos; 1063743Smax.romanov@nginx.com off_t rest; 1064743Smax.romanov@nginx.com } nxt_ruby_rack_file_t; 1065743Smax.romanov@nginx.com 1066743Smax.romanov@nginx.com 1067743Smax.romanov@nginx.com static ssize_t 1068743Smax.romanov@nginx.com nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) 1069743Smax.romanov@nginx.com { 1070743Smax.romanov@nginx.com ssize_t res; 1071743Smax.romanov@nginx.com nxt_ruby_rack_file_t *file; 1072743Smax.romanov@nginx.com 1073743Smax.romanov@nginx.com file = read_info->data; 1074743Smax.romanov@nginx.com 1075743Smax.romanov@nginx.com size = nxt_min(size, (size_t) file->rest); 1076743Smax.romanov@nginx.com 1077743Smax.romanov@nginx.com res = pread(file->fd, dst, size, file->pos); 1078743Smax.romanov@nginx.com 1079743Smax.romanov@nginx.com if (res >= 0) { 1080743Smax.romanov@nginx.com file->pos += res; 1081743Smax.romanov@nginx.com file->rest -= res; 1082743Smax.romanov@nginx.com 1083743Smax.romanov@nginx.com if (size > (size_t) res) { 1084743Smax.romanov@nginx.com file->rest = 0; 1085743Smax.romanov@nginx.com } 1086743Smax.romanov@nginx.com } 1087743Smax.romanov@nginx.com 1088743Smax.romanov@nginx.com read_info->eof = file->rest == 0; 1089743Smax.romanov@nginx.com 1090743Smax.romanov@nginx.com return res; 1091584Salexander.borisov@nginx.com } 1092584Salexander.borisov@nginx.com 1093584Salexander.borisov@nginx.com 10941687Smax.romanov@nginx.com typedef struct { 10951687Smax.romanov@nginx.com nxt_unit_read_info_t read_info; 10961687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 10971687Smax.romanov@nginx.com } nxt_ruby_read_info_t; 10981687Smax.romanov@nginx.com 10991687Smax.romanov@nginx.com 1100743Smax.romanov@nginx.com static int 11011687Smax.romanov@nginx.com nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 11021687Smax.romanov@nginx.com VALUE filepath) 1103584Salexander.borisov@nginx.com { 1104743Smax.romanov@nginx.com int fd, rc; 1105743Smax.romanov@nginx.com struct stat finfo; 1106743Smax.romanov@nginx.com nxt_ruby_rack_file_t ruby_file; 11071687Smax.romanov@nginx.com nxt_ruby_read_info_t ri; 1108584Salexander.borisov@nginx.com 1109743Smax.romanov@nginx.com fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); 1110743Smax.romanov@nginx.com if (nxt_slow_path(fd == -1)) { 11111687Smax.romanov@nginx.com nxt_unit_req_error(req, 1112743Smax.romanov@nginx.com "Ruby: Failed to open content file \"%s\": %s (%d)", 1113743Smax.romanov@nginx.com RSTRING_PTR(filepath), strerror(errno), errno); 1114584Salexander.borisov@nginx.com 1115743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1116584Salexander.borisov@nginx.com } 1117584Salexander.borisov@nginx.com 1118743Smax.romanov@nginx.com rc = fstat(fd, &finfo); 1119743Smax.romanov@nginx.com if (nxt_slow_path(rc == -1)) { 11201687Smax.romanov@nginx.com nxt_unit_req_error(req, 1121743Smax.romanov@nginx.com "Ruby: Content file fstat(\"%s\") failed: %s (%d)", 1122743Smax.romanov@nginx.com RSTRING_PTR(filepath), strerror(errno), errno); 1123584Salexander.borisov@nginx.com 1124743Smax.romanov@nginx.com close(fd); 1125584Salexander.borisov@nginx.com 1126743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1127584Salexander.borisov@nginx.com } 1128584Salexander.borisov@nginx.com 1129743Smax.romanov@nginx.com ruby_file.fd = fd; 1130743Smax.romanov@nginx.com ruby_file.pos = 0; 1131743Smax.romanov@nginx.com ruby_file.rest = finfo.st_size; 1132584Salexander.borisov@nginx.com 11331687Smax.romanov@nginx.com ri.read_info.read = nxt_ruby_rack_file_read; 11341687Smax.romanov@nginx.com ri.read_info.eof = ruby_file.rest == 0; 11351687Smax.romanov@nginx.com ri.read_info.buf_size = ruby_file.rest; 11361687Smax.romanov@nginx.com ri.read_info.data = &ruby_file; 11371687Smax.romanov@nginx.com ri.req = req; 1138584Salexander.borisov@nginx.com 11391687Smax.romanov@nginx.com rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb, 11401687Smax.romanov@nginx.com &ri, 11411687Smax.romanov@nginx.com nxt_ruby_ubf, 11421687Smax.romanov@nginx.com req->ctx); 1143584Salexander.borisov@nginx.com 1144743Smax.romanov@nginx.com close(fd); 1145584Salexander.borisov@nginx.com 1146743Smax.romanov@nginx.com return rc; 1147584Salexander.borisov@nginx.com } 1148584Salexander.borisov@nginx.com 1149584Salexander.borisov@nginx.com 11501687Smax.romanov@nginx.com static void * 11511687Smax.romanov@nginx.com nxt_ruby_response_write_cb(void *data) 11521687Smax.romanov@nginx.com { 11531687Smax.romanov@nginx.com int rc; 11541687Smax.romanov@nginx.com nxt_ruby_read_info_t *ri; 11551687Smax.romanov@nginx.com 11561687Smax.romanov@nginx.com ri = data; 11571687Smax.romanov@nginx.com 11581687Smax.romanov@nginx.com rc = nxt_unit_response_write_cb(ri->req, &ri->read_info); 11591687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 11601687Smax.romanov@nginx.com nxt_unit_req_error(ri->req, "Ruby: Failed to write content file."); 11611687Smax.romanov@nginx.com } 11621687Smax.romanov@nginx.com 11631687Smax.romanov@nginx.com return (void *) (intptr_t) rc; 11641687Smax.romanov@nginx.com } 11651687Smax.romanov@nginx.com 11661687Smax.romanov@nginx.com 11671687Smax.romanov@nginx.com typedef struct { 11681687Smax.romanov@nginx.com VALUE body; 11691687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 11701687Smax.romanov@nginx.com } nxt_ruby_write_info_t; 11711687Smax.romanov@nginx.com 11721687Smax.romanov@nginx.com 1173584Salexander.borisov@nginx.com static VALUE 11741337Smax.romanov@nginx.com nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, 11751337Smax.romanov@nginx.com const VALUE *argv, VALUE blockarg) 1176584Salexander.borisov@nginx.com { 11771687Smax.romanov@nginx.com nxt_ruby_write_info_t wi; 1178584Salexander.borisov@nginx.com 1179584Salexander.borisov@nginx.com if (TYPE(body) != T_STRING) { 1180584Salexander.borisov@nginx.com return Qnil; 1181584Salexander.borisov@nginx.com } 1182584Salexander.borisov@nginx.com 11831687Smax.romanov@nginx.com wi.body = body; 11841687Smax.romanov@nginx.com wi.req = (void *) (uintptr_t) arg; 11851687Smax.romanov@nginx.com 11861687Smax.romanov@nginx.com (void) rb_thread_call_without_gvl(nxt_ruby_response_write, 11871687Smax.romanov@nginx.com (void *) (uintptr_t) &wi, 11881687Smax.romanov@nginx.com nxt_ruby_ubf, wi.req->ctx); 1189584Salexander.borisov@nginx.com 1190584Salexander.borisov@nginx.com return Qnil; 1191584Salexander.borisov@nginx.com } 1192584Salexander.borisov@nginx.com 1193584Salexander.borisov@nginx.com 11941687Smax.romanov@nginx.com static void * 11951687Smax.romanov@nginx.com nxt_ruby_response_write(void *data) 11961687Smax.romanov@nginx.com { 11971687Smax.romanov@nginx.com int rc; 11981687Smax.romanov@nginx.com nxt_ruby_write_info_t *wi; 11991687Smax.romanov@nginx.com 12001687Smax.romanov@nginx.com wi = data; 12011687Smax.romanov@nginx.com 12021687Smax.romanov@nginx.com rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body), 12031687Smax.romanov@nginx.com RSTRING_LEN(wi->body)); 12041687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 12051687Smax.romanov@nginx.com nxt_unit_req_error(wi->req, 12061687Smax.romanov@nginx.com "Ruby: Failed to write 'body' from application"); 12071687Smax.romanov@nginx.com } 12081687Smax.romanov@nginx.com 12091687Smax.romanov@nginx.com return (void *) (intptr_t) rc; 12101687Smax.romanov@nginx.com } 12111687Smax.romanov@nginx.com 12121687Smax.romanov@nginx.com 1213584Salexander.borisov@nginx.com static void 12141687Smax.romanov@nginx.com nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, 12151687Smax.romanov@nginx.com const char *desc) 1216584Salexander.borisov@nginx.com { 1217584Salexander.borisov@nginx.com int i; 1218584Salexander.borisov@nginx.com VALUE err, ary, eclass, msg; 1219584Salexander.borisov@nginx.com 12201687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "Ruby: %s", desc); 1221584Salexander.borisov@nginx.com 1222584Salexander.borisov@nginx.com err = rb_errinfo(); 1223743Smax.romanov@nginx.com if (nxt_slow_path(err == Qnil)) { 1224743Smax.romanov@nginx.com return; 1225743Smax.romanov@nginx.com } 1226584Salexander.borisov@nginx.com 12271905So.canty@f5.com eclass = rb_class_name(rb_class_of(err)); 12281905So.canty@f5.com 12291905So.canty@f5.com msg = rb_funcall(err, rb_intern("message"), 0); 1230743Smax.romanov@nginx.com ary = rb_funcall(err, rb_intern("backtrace"), 0); 12311905So.canty@f5.com 12321905So.canty@f5.com if (RARRAY_LEN(ary) == 0) { 12331905So.canty@f5.com nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg), 12341905So.canty@f5.com RSTRING_PTR(eclass)); 12351905So.canty@f5.com 1236584Salexander.borisov@nginx.com return; 1237584Salexander.borisov@nginx.com } 1238584Salexander.borisov@nginx.com 12391687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", 1240743Smax.romanov@nginx.com RSTRING_PTR(RARRAY_PTR(ary)[0]), 1241743Smax.romanov@nginx.com RSTRING_PTR(msg), RSTRING_PTR(eclass)); 1242584Salexander.borisov@nginx.com 1243584Salexander.borisov@nginx.com for (i = 1; i < RARRAY_LEN(ary); i++) { 12441687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "from %s", 12451687Smax.romanov@nginx.com RSTRING_PTR(RARRAY_PTR(ary)[i])); 12461687Smax.romanov@nginx.com } 12471687Smax.romanov@nginx.com } 12481687Smax.romanov@nginx.com 1249743Smax.romanov@nginx.com 12501687Smax.romanov@nginx.com static void 12511687Smax.romanov@nginx.com nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx) 12521687Smax.romanov@nginx.com { 12531687Smax.romanov@nginx.com if (rctx->io_input != Qnil) { 12541687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->io_input); 12551687Smax.romanov@nginx.com } 12561687Smax.romanov@nginx.com 12571687Smax.romanov@nginx.com if (rctx->io_error != Qnil) { 12581687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->io_error); 12591687Smax.romanov@nginx.com } 12601687Smax.romanov@nginx.com 12611687Smax.romanov@nginx.com if (rctx->env != Qnil) { 12621687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->env); 1263584Salexander.borisov@nginx.com } 1264584Salexander.borisov@nginx.com } 1265584Salexander.borisov@nginx.com 1266584Salexander.borisov@nginx.com 1267584Salexander.borisov@nginx.com static void 1268743Smax.romanov@nginx.com nxt_ruby_atexit(void) 1269584Salexander.borisov@nginx.com { 12701687Smax.romanov@nginx.com if (nxt_ruby_rackup != Qnil) { 12711687Smax.romanov@nginx.com rb_gc_unregister_address(&nxt_ruby_rackup); 12721687Smax.romanov@nginx.com } 1273584Salexander.borisov@nginx.com 12741687Smax.romanov@nginx.com if (nxt_ruby_call != Qnil) { 12751687Smax.romanov@nginx.com rb_gc_unregister_address(&nxt_ruby_call); 12761687Smax.romanov@nginx.com } 1277584Salexander.borisov@nginx.com 12781910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 12791910So.canty@f5.com rb_gc_unregister_address(&nxt_ruby_hook_procs); 12801910So.canty@f5.com } 12811910So.canty@f5.com 12821686Smax.romanov@nginx.com nxt_ruby_done_strings(); 12831686Smax.romanov@nginx.com 1284584Salexander.borisov@nginx.com ruby_cleanup(0); 1285584Salexander.borisov@nginx.com } 12861687Smax.romanov@nginx.com 12871687Smax.romanov@nginx.com 12881687Smax.romanov@nginx.com static int 12891687Smax.romanov@nginx.com nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) 12901687Smax.romanov@nginx.com { 12911687Smax.romanov@nginx.com VALUE res; 12921687Smax.romanov@nginx.com uint32_t i; 12931687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 12941687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c; 12951687Smax.romanov@nginx.com 12961687Smax.romanov@nginx.com c = ctx->unit->data; 12971687Smax.romanov@nginx.com 12981687Smax.romanov@nginx.com if (c->threads <= 1) { 12991687Smax.romanov@nginx.com return NXT_UNIT_OK; 13001687Smax.romanov@nginx.com } 13011687Smax.romanov@nginx.com 13021687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 13031687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 13041687Smax.romanov@nginx.com 13051687Smax.romanov@nginx.com rctx->ctx = ctx; 13061687Smax.romanov@nginx.com 13071738Smax.romanov@nginx.com res = (VALUE) rb_thread_call_with_gvl(nxt_ruby_thread_create_gvl, rctx); 13081687Smax.romanov@nginx.com 13091687Smax.romanov@nginx.com if (nxt_fast_path(res != Qnil)) { 13101687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); 13111687Smax.romanov@nginx.com 13121687Smax.romanov@nginx.com rctx->thread = res; 13131687Smax.romanov@nginx.com 13141687Smax.romanov@nginx.com } else { 13151687Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1)); 13161691Smax.romanov@nginx.com 13171691Smax.romanov@nginx.com return NXT_UNIT_ERROR; 13181687Smax.romanov@nginx.com } 13191687Smax.romanov@nginx.com } 13201687Smax.romanov@nginx.com 13211687Smax.romanov@nginx.com return NXT_UNIT_OK; 13221687Smax.romanov@nginx.com } 13231687Smax.romanov@nginx.com 13241687Smax.romanov@nginx.com 13251738Smax.romanov@nginx.com static void * 13261738Smax.romanov@nginx.com nxt_ruby_thread_create_gvl(void *rctx) 13271738Smax.romanov@nginx.com { 13281738Smax.romanov@nginx.com VALUE res; 13291738Smax.romanov@nginx.com 13301738Smax.romanov@nginx.com res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx); 13311738Smax.romanov@nginx.com 13321738Smax.romanov@nginx.com return (void *) (uintptr_t) res; 13331738Smax.romanov@nginx.com } 13341738Smax.romanov@nginx.com 13351738Smax.romanov@nginx.com 13361687Smax.romanov@nginx.com static VALUE 13371687Smax.romanov@nginx.com nxt_ruby_thread_func(VALUE arg) 13381687Smax.romanov@nginx.com { 13391910So.canty@f5.com int state; 13401687Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 13411687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 13421687Smax.romanov@nginx.com 13431687Smax.romanov@nginx.com rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 13441687Smax.romanov@nginx.com 13451687Smax.romanov@nginx.com nxt_unit_debug(rctx->ctx, "worker thread start"); 13461687Smax.romanov@nginx.com 13471687Smax.romanov@nginx.com ctx = nxt_unit_ctx_alloc(rctx->ctx, rctx); 13481687Smax.romanov@nginx.com if (nxt_slow_path(ctx == NULL)) { 13491687Smax.romanov@nginx.com goto fail; 13501687Smax.romanov@nginx.com } 13511687Smax.romanov@nginx.com 13521910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 13531910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); 13541910So.canty@f5.com if (nxt_slow_path(state != 0)) { 13551910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 13561910So.canty@f5.com "Failed to call on_thread_boot()"); 13571910So.canty@f5.com } 13581910So.canty@f5.com } 13591910So.canty@f5.com 13601687Smax.romanov@nginx.com (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx, 13611687Smax.romanov@nginx.com nxt_ruby_ubf, ctx); 13621687Smax.romanov@nginx.com 13631910So.canty@f5.com if (nxt_ruby_hook_procs != Qnil) { 13641910So.canty@f5.com rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); 13651910So.canty@f5.com if (nxt_slow_path(state != 0)) { 13661910So.canty@f5.com nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 13671910So.canty@f5.com "Failed to call on_thread_shutdown()"); 13681910So.canty@f5.com } 13691910So.canty@f5.com } 13701910So.canty@f5.com 13711687Smax.romanov@nginx.com nxt_unit_done(ctx); 13721687Smax.romanov@nginx.com 13731687Smax.romanov@nginx.com fail: 13741687Smax.romanov@nginx.com 13751687Smax.romanov@nginx.com nxt_unit_debug(NULL, "worker thread end"); 13761687Smax.romanov@nginx.com 13771687Smax.romanov@nginx.com return Qnil; 13781687Smax.romanov@nginx.com } 13791687Smax.romanov@nginx.com 13801687Smax.romanov@nginx.com 13811687Smax.romanov@nginx.com static void * 13821687Smax.romanov@nginx.com nxt_ruby_unit_run(void *ctx) 13831687Smax.romanov@nginx.com { 13841687Smax.romanov@nginx.com return (void *) (intptr_t) nxt_unit_run(ctx); 13851687Smax.romanov@nginx.com } 13861687Smax.romanov@nginx.com 13871687Smax.romanov@nginx.com 13881687Smax.romanov@nginx.com static void 13891687Smax.romanov@nginx.com nxt_ruby_ubf(void *ctx) 13901687Smax.romanov@nginx.com { 13911687Smax.romanov@nginx.com nxt_unit_warn(ctx, "Ruby: UBF"); 13921687Smax.romanov@nginx.com } 13931687Smax.romanov@nginx.com 13941687Smax.romanov@nginx.com 13951687Smax.romanov@nginx.com static int 1396*2087Sz.hong@f5.com nxt_ruby_init_threads(VALUE script, nxt_ruby_app_conf_t *c) 13971687Smax.romanov@nginx.com { 13981687Smax.romanov@nginx.com int state; 13991687Smax.romanov@nginx.com uint32_t i; 14001687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 14011687Smax.romanov@nginx.com 14021687Smax.romanov@nginx.com if (c->threads <= 1) { 14031687Smax.romanov@nginx.com return NXT_UNIT_OK; 14041687Smax.romanov@nginx.com } 14051687Smax.romanov@nginx.com 14061687Smax.romanov@nginx.com nxt_ruby_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_ruby_ctx_t) 14071687Smax.romanov@nginx.com * (c->threads - 1)); 14081687Smax.romanov@nginx.com if (nxt_slow_path(nxt_ruby_ctxs == NULL)) { 14091687Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate run contexts array"); 14101687Smax.romanov@nginx.com 14111687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 14121687Smax.romanov@nginx.com } 14131687Smax.romanov@nginx.com 14141687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 14151687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 14161687Smax.romanov@nginx.com 14171687Smax.romanov@nginx.com rctx->env = Qnil; 1418*2087Sz.hong@f5.com rctx->script = script; 14191687Smax.romanov@nginx.com rctx->io_input = Qnil; 14201687Smax.romanov@nginx.com rctx->io_error = Qnil; 14211687Smax.romanov@nginx.com rctx->thread = Qnil; 14221687Smax.romanov@nginx.com } 14231687Smax.romanov@nginx.com 14241687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 14251687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 14261687Smax.romanov@nginx.com 14271687Smax.romanov@nginx.com rctx->env = rb_protect(nxt_ruby_rack_env_create, 14281687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx, &state); 14291687Smax.romanov@nginx.com if (nxt_slow_path(rctx->env == Qnil || state != 0)) { 14301687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 14311687Smax.romanov@nginx.com "Failed to create 'environ' variable"); 14321687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 14331687Smax.romanov@nginx.com } 14341687Smax.romanov@nginx.com } 14351687Smax.romanov@nginx.com 14361687Smax.romanov@nginx.com return NXT_UNIT_OK; 14371687Smax.romanov@nginx.com } 14381687Smax.romanov@nginx.com 14391687Smax.romanov@nginx.com 14401687Smax.romanov@nginx.com static void 14411687Smax.romanov@nginx.com nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c) 14421687Smax.romanov@nginx.com { 14431687Smax.romanov@nginx.com uint32_t i; 14441687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 14451687Smax.romanov@nginx.com 14461687Smax.romanov@nginx.com if (nxt_ruby_ctxs == NULL) { 14471687Smax.romanov@nginx.com return; 14481687Smax.romanov@nginx.com } 14491687Smax.romanov@nginx.com 14501691Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 14511687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 14521687Smax.romanov@nginx.com 14531687Smax.romanov@nginx.com if (rctx->thread != Qnil) { 14541687Smax.romanov@nginx.com rb_funcall(rctx->thread, rb_intern("join"), 0); 14551687Smax.romanov@nginx.com 14561687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); 14571687Smax.romanov@nginx.com 14581687Smax.romanov@nginx.com } else { 14591687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1)); 14601687Smax.romanov@nginx.com } 14611691Smax.romanov@nginx.com } 14621687Smax.romanov@nginx.com 14631691Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 14641691Smax.romanov@nginx.com nxt_ruby_ctx_done(&nxt_ruby_ctxs[i]); 14651687Smax.romanov@nginx.com } 14661687Smax.romanov@nginx.com 14671687Smax.romanov@nginx.com nxt_unit_free(ctx, nxt_ruby_ctxs); 14681687Smax.romanov@nginx.com } 1469