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 15*1814Svbart@nginx.com #include <locale.h> 16*1814Svbart@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); 32584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 33584Salexander.borisov@nginx.com 34584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rubygems(VALUE arg); 35717Salexander.borisov@nginx.com static VALUE nxt_ruby_bundler_setup(VALUE arg); 36584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rack(VALUE arg); 37584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 38584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_env_create(VALUE arg); 391687Smax.romanov@nginx.com static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx); 40743Smax.romanov@nginx.com static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); 411687Smax.romanov@nginx.com static void *nxt_ruby_request_handler_gvl(void *req); 421687Smax.romanov@nginx.com static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx); 431738Smax.romanov@nginx.com static void *nxt_ruby_thread_create_gvl(void *rctx); 441687Smax.romanov@nginx.com static VALUE nxt_ruby_thread_func(VALUE arg); 451687Smax.romanov@nginx.com static void *nxt_ruby_unit_run(void *ctx); 461687Smax.romanov@nginx.com static void nxt_ruby_ubf(void *ctx); 471687Smax.romanov@nginx.com static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c); 481687Smax.romanov@nginx.com static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, 491687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c); 50584Salexander.borisov@nginx.com 51584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_app_run(VALUE arg); 521687Smax.romanov@nginx.com static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env); 531686Smax.romanov@nginx.com nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 541686Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t len); 551687Smax.romanov@nginx.com static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, 561687Smax.romanov@nginx.com VALUE result); 571687Smax.romanov@nginx.com static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, 581687Smax.romanov@nginx.com VALUE result, nxt_int_t status); 59743Smax.romanov@nginx.com static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); 60743Smax.romanov@nginx.com static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); 611687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, 621687Smax.romanov@nginx.com VALUE result); 631687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 641687Smax.romanov@nginx.com VALUE filepath); 651687Smax.romanov@nginx.com static void *nxt_ruby_response_write_cb(void *read_info); 661337Smax.romanov@nginx.com static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, 671337Smax.romanov@nginx.com int argc, const VALUE *argv, VALUE blockarg); 681687Smax.romanov@nginx.com static void *nxt_ruby_response_write(void *body); 69584Salexander.borisov@nginx.com 701687Smax.romanov@nginx.com static void nxt_ruby_exception_log(nxt_unit_request_info_t *req, 711687Smax.romanov@nginx.com uint32_t level, const char *desc); 72584Salexander.borisov@nginx.com 731687Smax.romanov@nginx.com static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx); 74743Smax.romanov@nginx.com static void nxt_ruby_atexit(void); 75584Salexander.borisov@nginx.com 76584Salexander.borisov@nginx.com 77584Salexander.borisov@nginx.com static uint32_t compat[] = { 78584Salexander.borisov@nginx.com NXT_VERNUM, NXT_DEBUG, 79584Salexander.borisov@nginx.com }; 80584Salexander.borisov@nginx.com 811687Smax.romanov@nginx.com static VALUE nxt_ruby_rackup; 821687Smax.romanov@nginx.com static VALUE nxt_ruby_call; 831687Smax.romanov@nginx.com 841687Smax.romanov@nginx.com static uint32_t nxt_ruby_threads; 851687Smax.romanov@nginx.com static nxt_ruby_ctx_t *nxt_ruby_ctxs; 86584Salexander.borisov@nginx.com 87743Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t nxt_app_module = { 88584Salexander.borisov@nginx.com sizeof(compat), 89584Salexander.borisov@nginx.com compat, 90584Salexander.borisov@nginx.com nxt_string("ruby"), 91612Salexander.borisov@nginx.com ruby_version, 921489St.nateldemoura@f5.com nxt_ruby_mounts, 931489St.nateldemoura@f5.com nxt_nitems(nxt_ruby_mounts), 94977Smax.romanov@gmail.com NULL, 951488St.nateldemoura@f5.com nxt_ruby_start, 96584Salexander.borisov@nginx.com }; 97584Salexander.borisov@nginx.com 981686Smax.romanov@nginx.com typedef struct { 991686Smax.romanov@nginx.com nxt_str_t string; 1001686Smax.romanov@nginx.com VALUE *v; 1011686Smax.romanov@nginx.com } nxt_ruby_string_t; 1021686Smax.romanov@nginx.com 1031686Smax.romanov@nginx.com static VALUE nxt_rb_80_str; 1041686Smax.romanov@nginx.com static VALUE nxt_rb_content_length_str; 1051686Smax.romanov@nginx.com static VALUE nxt_rb_content_type_str; 1061686Smax.romanov@nginx.com static VALUE nxt_rb_http_str; 1071686Smax.romanov@nginx.com static VALUE nxt_rb_https_str; 1081686Smax.romanov@nginx.com static VALUE nxt_rb_path_info_str; 1091686Smax.romanov@nginx.com static VALUE nxt_rb_query_string_str; 1101686Smax.romanov@nginx.com static VALUE nxt_rb_rack_url_scheme_str; 1111686Smax.romanov@nginx.com static VALUE nxt_rb_remote_addr_str; 1121686Smax.romanov@nginx.com static VALUE nxt_rb_request_method_str; 1131686Smax.romanov@nginx.com static VALUE nxt_rb_request_uri_str; 1141686Smax.romanov@nginx.com static VALUE nxt_rb_server_addr_str; 1151686Smax.romanov@nginx.com static VALUE nxt_rb_server_name_str; 1161686Smax.romanov@nginx.com static VALUE nxt_rb_server_port_str; 1171686Smax.romanov@nginx.com static VALUE nxt_rb_server_protocol_str; 1181686Smax.romanov@nginx.com 1191686Smax.romanov@nginx.com static nxt_ruby_string_t nxt_rb_strings[] = { 1201686Smax.romanov@nginx.com { nxt_string("80"), &nxt_rb_80_str }, 1211686Smax.romanov@nginx.com { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str }, 1221686Smax.romanov@nginx.com { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str }, 1231686Smax.romanov@nginx.com { nxt_string("http"), &nxt_rb_http_str }, 1241686Smax.romanov@nginx.com { nxt_string("https"), &nxt_rb_https_str }, 1251686Smax.romanov@nginx.com { nxt_string("PATH_INFO"), &nxt_rb_path_info_str }, 1261686Smax.romanov@nginx.com { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str }, 1271686Smax.romanov@nginx.com { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str }, 1281686Smax.romanov@nginx.com { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str }, 1291686Smax.romanov@nginx.com { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str }, 1301686Smax.romanov@nginx.com { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str }, 1311686Smax.romanov@nginx.com { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str }, 1321686Smax.romanov@nginx.com { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, 1331686Smax.romanov@nginx.com { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, 1341686Smax.romanov@nginx.com { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, 1351686Smax.romanov@nginx.com { nxt_null_string, NULL }, 1361686Smax.romanov@nginx.com }; 1371686Smax.romanov@nginx.com 1381686Smax.romanov@nginx.com 1391686Smax.romanov@nginx.com static int 1401686Smax.romanov@nginx.com nxt_ruby_init_strings(void) 1411686Smax.romanov@nginx.com { 1421686Smax.romanov@nginx.com VALUE v; 1431686Smax.romanov@nginx.com nxt_ruby_string_t *pstr; 1441686Smax.romanov@nginx.com 1451686Smax.romanov@nginx.com pstr = nxt_rb_strings; 1461686Smax.romanov@nginx.com 1471686Smax.romanov@nginx.com while (pstr->string.start != NULL) { 1481686Smax.romanov@nginx.com v = rb_str_new_static((char *) pstr->string.start, pstr->string.length); 1491686Smax.romanov@nginx.com 1501686Smax.romanov@nginx.com if (nxt_slow_path(v == Qnil)) { 1511686Smax.romanov@nginx.com nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'", 1521686Smax.romanov@nginx.com (int) pstr->string.length, 1531686Smax.romanov@nginx.com (char *) pstr->string.start); 1541686Smax.romanov@nginx.com 1551686Smax.romanov@nginx.com return NXT_UNIT_ERROR; 1561686Smax.romanov@nginx.com } 1571686Smax.romanov@nginx.com 1581686Smax.romanov@nginx.com *pstr->v = v; 1591686Smax.romanov@nginx.com 1601686Smax.romanov@nginx.com rb_gc_register_address(pstr->v); 1611686Smax.romanov@nginx.com 1621686Smax.romanov@nginx.com pstr++; 1631686Smax.romanov@nginx.com } 1641686Smax.romanov@nginx.com 1651686Smax.romanov@nginx.com return NXT_UNIT_OK; 1661686Smax.romanov@nginx.com } 1671686Smax.romanov@nginx.com 1681686Smax.romanov@nginx.com 1691686Smax.romanov@nginx.com static void 1701686Smax.romanov@nginx.com nxt_ruby_done_strings(void) 1711686Smax.romanov@nginx.com { 1721686Smax.romanov@nginx.com nxt_ruby_string_t *pstr; 1731686Smax.romanov@nginx.com 1741686Smax.romanov@nginx.com pstr = nxt_rb_strings; 1751686Smax.romanov@nginx.com 1761686Smax.romanov@nginx.com while (pstr->string.start != NULL) { 1771686Smax.romanov@nginx.com rb_gc_unregister_address(pstr->v); 1781686Smax.romanov@nginx.com 1791686Smax.romanov@nginx.com *pstr->v = Qnil; 1801686Smax.romanov@nginx.com 1811686Smax.romanov@nginx.com pstr++; 1821686Smax.romanov@nginx.com } 1831686Smax.romanov@nginx.com } 1841686Smax.romanov@nginx.com 185584Salexander.borisov@nginx.com 186584Salexander.borisov@nginx.com static nxt_int_t 1871488St.nateldemoura@f5.com nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) 188584Salexander.borisov@nginx.com { 1891488St.nateldemoura@f5.com int state, rc; 1901488St.nateldemoura@f5.com VALUE res; 1911687Smax.romanov@nginx.com nxt_ruby_ctx_t ruby_ctx; 1921488St.nateldemoura@f5.com nxt_unit_ctx_t *unit_ctx; 1931488St.nateldemoura@f5.com nxt_unit_init_t ruby_unit_init; 1941687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c; 1951488St.nateldemoura@f5.com nxt_ruby_rack_init_t rack_init; 1961488St.nateldemoura@f5.com nxt_common_app_conf_t *conf; 197584Salexander.borisov@nginx.com 1981258Smax.romanov@nginx.com static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; 1991258Smax.romanov@nginx.com 2001488St.nateldemoura@f5.com conf = data->app; 2011687Smax.romanov@nginx.com c = &conf->u.ruby; 2021687Smax.romanov@nginx.com 2031687Smax.romanov@nginx.com nxt_ruby_threads = c->threads; 2041488St.nateldemoura@f5.com 205*1814Svbart@nginx.com setlocale(LC_CTYPE, ""); 206*1814Svbart@nginx.com 2071258Smax.romanov@nginx.com RUBY_INIT_STACK 208584Salexander.borisov@nginx.com ruby_init(); 2091258Smax.romanov@nginx.com ruby_options(2, argv); 210584Salexander.borisov@nginx.com ruby_script("NGINX_Unit"); 211584Salexander.borisov@nginx.com 2121687Smax.romanov@nginx.com ruby_ctx.env = Qnil; 2131687Smax.romanov@nginx.com ruby_ctx.io_input = Qnil; 2141687Smax.romanov@nginx.com ruby_ctx.io_error = Qnil; 2151687Smax.romanov@nginx.com ruby_ctx.thread = Qnil; 2161687Smax.romanov@nginx.com ruby_ctx.ctx = NULL; 2171687Smax.romanov@nginx.com ruby_ctx.req = NULL; 2181687Smax.romanov@nginx.com 219584Salexander.borisov@nginx.com rack_init.task = task; 2201687Smax.romanov@nginx.com rack_init.script = &c->script; 2211687Smax.romanov@nginx.com rack_init.rctx = &ruby_ctx; 222584Salexander.borisov@nginx.com 2231686Smax.romanov@nginx.com nxt_ruby_init_strings(); 2241686Smax.romanov@nginx.com 225584Salexander.borisov@nginx.com res = rb_protect(nxt_ruby_init_basic, 226584Salexander.borisov@nginx.com (VALUE) (uintptr_t) &rack_init, &state); 227584Salexander.borisov@nginx.com if (nxt_slow_path(res == Qnil || state != 0)) { 2281687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 229584Salexander.borisov@nginx.com "Failed to init basic variables"); 230584Salexander.borisov@nginx.com return NXT_ERROR; 231584Salexander.borisov@nginx.com } 232584Salexander.borisov@nginx.com 2331687Smax.romanov@nginx.com nxt_ruby_call = Qnil; 2341687Smax.romanov@nginx.com 235584Salexander.borisov@nginx.com nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); 236584Salexander.borisov@nginx.com if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { 237584Salexander.borisov@nginx.com return NXT_ERROR; 238584Salexander.borisov@nginx.com } 239584Salexander.borisov@nginx.com 2401687Smax.romanov@nginx.com rb_gc_register_address(&nxt_ruby_rackup); 2411687Smax.romanov@nginx.com 242584Salexander.borisov@nginx.com nxt_ruby_call = rb_intern("call"); 243584Salexander.borisov@nginx.com if (nxt_slow_path(nxt_ruby_call == Qnil)) { 244584Salexander.borisov@nginx.com nxt_alert(task, "Ruby: Unable to find rack entry point"); 245584Salexander.borisov@nginx.com 2461687Smax.romanov@nginx.com goto fail; 247584Salexander.borisov@nginx.com } 248584Salexander.borisov@nginx.com 2491687Smax.romanov@nginx.com rb_gc_register_address(&nxt_ruby_call); 2501687Smax.romanov@nginx.com 2511687Smax.romanov@nginx.com ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create, 2521687Smax.romanov@nginx.com (VALUE) (uintptr_t) &ruby_ctx, &state); 2531687Smax.romanov@nginx.com if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) { 2541687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 255584Salexander.borisov@nginx.com "Failed to create 'environ' variable"); 2561687Smax.romanov@nginx.com goto fail; 257584Salexander.borisov@nginx.com } 258584Salexander.borisov@nginx.com 2591687Smax.romanov@nginx.com rc = nxt_ruby_init_threads(c); 2601687Smax.romanov@nginx.com if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 2611687Smax.romanov@nginx.com goto fail; 2621687Smax.romanov@nginx.com } 263584Salexander.borisov@nginx.com 264743Smax.romanov@nginx.com nxt_unit_default_init(task, &ruby_unit_init); 265743Smax.romanov@nginx.com 266743Smax.romanov@nginx.com ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; 2671687Smax.romanov@nginx.com ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler; 2681320Smax.romanov@nginx.com ruby_unit_init.shm_limit = conf->shm_limit; 2691687Smax.romanov@nginx.com ruby_unit_init.data = c; 2701687Smax.romanov@nginx.com ruby_unit_init.ctx_data = &ruby_ctx; 271743Smax.romanov@nginx.com 272743Smax.romanov@nginx.com unit_ctx = nxt_unit_init(&ruby_unit_init); 273743Smax.romanov@nginx.com if (nxt_slow_path(unit_ctx == NULL)) { 2741687Smax.romanov@nginx.com goto fail; 275743Smax.romanov@nginx.com } 276743Smax.romanov@nginx.com 2771687Smax.romanov@nginx.com rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, 2781687Smax.romanov@nginx.com nxt_ruby_ubf, unit_ctx); 279743Smax.romanov@nginx.com 2801687Smax.romanov@nginx.com nxt_ruby_join_threads(unit_ctx, c); 2811687Smax.romanov@nginx.com 2821687Smax.romanov@nginx.com nxt_unit_done(unit_ctx); 2831687Smax.romanov@nginx.com 2841687Smax.romanov@nginx.com nxt_ruby_ctx_done(&ruby_ctx); 285743Smax.romanov@nginx.com 286743Smax.romanov@nginx.com nxt_ruby_atexit(); 287743Smax.romanov@nginx.com 288743Smax.romanov@nginx.com exit(rc); 289743Smax.romanov@nginx.com 290584Salexander.borisov@nginx.com return NXT_OK; 2911687Smax.romanov@nginx.com 2921687Smax.romanov@nginx.com fail: 2931687Smax.romanov@nginx.com 2941687Smax.romanov@nginx.com nxt_ruby_join_threads(NULL, c); 2951687Smax.romanov@nginx.com 2961687Smax.romanov@nginx.com nxt_ruby_ctx_done(&ruby_ctx); 2971687Smax.romanov@nginx.com 2981687Smax.romanov@nginx.com nxt_ruby_atexit(); 2991687Smax.romanov@nginx.com 3001687Smax.romanov@nginx.com return NXT_ERROR; 301584Salexander.borisov@nginx.com } 302584Salexander.borisov@nginx.com 303584Salexander.borisov@nginx.com 304584Salexander.borisov@nginx.com static VALUE 305584Salexander.borisov@nginx.com nxt_ruby_init_basic(VALUE arg) 306584Salexander.borisov@nginx.com { 307584Salexander.borisov@nginx.com int state; 308584Salexander.borisov@nginx.com nxt_ruby_rack_init_t *rack_init; 309584Salexander.borisov@nginx.com 310584Salexander.borisov@nginx.com rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; 311584Salexander.borisov@nginx.com 312584Salexander.borisov@nginx.com state = rb_enc_find_index("encdb"); 313584Salexander.borisov@nginx.com if (nxt_slow_path(state == 0)) { 314584Salexander.borisov@nginx.com nxt_alert(rack_init->task, 315584Salexander.borisov@nginx.com "Ruby: Failed to find encoding index 'encdb'"); 316584Salexander.borisov@nginx.com 317584Salexander.borisov@nginx.com return Qnil; 318584Salexander.borisov@nginx.com } 319584Salexander.borisov@nginx.com 320609Salexander.borisov@nginx.com rb_funcall(rb_cObject, rb_intern("require"), 1, 321609Salexander.borisov@nginx.com rb_str_new2("enc/trans/transdb")); 322609Salexander.borisov@nginx.com 323584Salexander.borisov@nginx.com return arg; 324584Salexander.borisov@nginx.com } 325584Salexander.borisov@nginx.com 326584Salexander.borisov@nginx.com 327584Salexander.borisov@nginx.com static VALUE 328584Salexander.borisov@nginx.com nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) 329584Salexander.borisov@nginx.com { 330584Salexander.borisov@nginx.com int state; 3311687Smax.romanov@nginx.com VALUE rackup, err; 332584Salexander.borisov@nginx.com 333584Salexander.borisov@nginx.com rb_protect(nxt_ruby_require_rubygems, Qnil, &state); 334584Salexander.borisov@nginx.com if (nxt_slow_path(state != 0)) { 3351687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 336584Salexander.borisov@nginx.com "Failed to require 'rubygems' package"); 337584Salexander.borisov@nginx.com return Qnil; 338584Salexander.borisov@nginx.com } 339584Salexander.borisov@nginx.com 340717Salexander.borisov@nginx.com rb_protect(nxt_ruby_bundler_setup, Qnil, &state); 341717Salexander.borisov@nginx.com if (state != 0) { 342717Salexander.borisov@nginx.com err = rb_errinfo(); 343717Salexander.borisov@nginx.com 344717Salexander.borisov@nginx.com if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { 3451687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 346717Salexander.borisov@nginx.com "Failed to require 'bundler/setup' package"); 347717Salexander.borisov@nginx.com return Qnil; 348717Salexander.borisov@nginx.com } 349717Salexander.borisov@nginx.com 350717Salexander.borisov@nginx.com rb_set_errinfo(Qnil); 351717Salexander.borisov@nginx.com } 352717Salexander.borisov@nginx.com 353584Salexander.borisov@nginx.com rb_protect(nxt_ruby_require_rack, Qnil, &state); 354584Salexander.borisov@nginx.com if (nxt_slow_path(state != 0)) { 3551687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 356584Salexander.borisov@nginx.com "Failed to require 'rack' package"); 357584Salexander.borisov@nginx.com return Qnil; 358584Salexander.borisov@nginx.com } 359584Salexander.borisov@nginx.com 360584Salexander.borisov@nginx.com rackup = rb_protect(nxt_ruby_rack_parse_script, 361584Salexander.borisov@nginx.com (VALUE) (uintptr_t) rack_init, &state); 362584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { 3631687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 364584Salexander.borisov@nginx.com "Failed to parse rack script"); 365584Salexander.borisov@nginx.com return Qnil; 366584Salexander.borisov@nginx.com } 367584Salexander.borisov@nginx.com 368584Salexander.borisov@nginx.com if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { 369584Salexander.borisov@nginx.com nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); 370584Salexander.borisov@nginx.com return Qnil; 371584Salexander.borisov@nginx.com } 372584Salexander.borisov@nginx.com 373584Salexander.borisov@nginx.com return RARRAY_PTR(rackup)[0]; 374584Salexander.borisov@nginx.com } 375584Salexander.borisov@nginx.com 376584Salexander.borisov@nginx.com 377584Salexander.borisov@nginx.com static VALUE 378584Salexander.borisov@nginx.com nxt_ruby_require_rubygems(VALUE arg) 379584Salexander.borisov@nginx.com { 380584Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, 381584Salexander.borisov@nginx.com rb_str_new2("rubygems")); 382584Salexander.borisov@nginx.com } 383584Salexander.borisov@nginx.com 384584Salexander.borisov@nginx.com 385584Salexander.borisov@nginx.com static VALUE 386717Salexander.borisov@nginx.com nxt_ruby_bundler_setup(VALUE arg) 387717Salexander.borisov@nginx.com { 388717Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, 389717Salexander.borisov@nginx.com rb_str_new2("bundler/setup")); 390717Salexander.borisov@nginx.com } 391717Salexander.borisov@nginx.com 392717Salexander.borisov@nginx.com 393717Salexander.borisov@nginx.com static VALUE 394584Salexander.borisov@nginx.com nxt_ruby_require_rack(VALUE arg) 395584Salexander.borisov@nginx.com { 396584Salexander.borisov@nginx.com return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 397584Salexander.borisov@nginx.com } 398584Salexander.borisov@nginx.com 399584Salexander.borisov@nginx.com 400584Salexander.borisov@nginx.com static VALUE 401584Salexander.borisov@nginx.com nxt_ruby_rack_parse_script(VALUE ctx) 402584Salexander.borisov@nginx.com { 4031687Smax.romanov@nginx.com VALUE script, res, rack, builder; 404584Salexander.borisov@nginx.com nxt_ruby_rack_init_t *rack_init; 405584Salexander.borisov@nginx.com 406584Salexander.borisov@nginx.com rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; 407584Salexander.borisov@nginx.com 4081687Smax.romanov@nginx.com rack = rb_const_get(rb_cObject, rb_intern("Rack")); 4091687Smax.romanov@nginx.com builder = rb_const_get(rack, rb_intern("Builder")); 4101687Smax.romanov@nginx.com 411584Salexander.borisov@nginx.com script = rb_str_new((const char *) rack_init->script->start, 412584Salexander.borisov@nginx.com (long) rack_init->script->length); 413584Salexander.borisov@nginx.com 4141687Smax.romanov@nginx.com res = rb_funcall(builder, rb_intern("parse_file"), 1, script); 415584Salexander.borisov@nginx.com 416584Salexander.borisov@nginx.com rb_str_free(script); 417584Salexander.borisov@nginx.com 418584Salexander.borisov@nginx.com return res; 419584Salexander.borisov@nginx.com } 420584Salexander.borisov@nginx.com 421584Salexander.borisov@nginx.com 422584Salexander.borisov@nginx.com static VALUE 423584Salexander.borisov@nginx.com nxt_ruby_rack_env_create(VALUE arg) 424584Salexander.borisov@nginx.com { 4251687Smax.romanov@nginx.com int rc; 4261687Smax.romanov@nginx.com VALUE hash_env, version; 4271687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 4281687Smax.romanov@nginx.com 4291687Smax.romanov@nginx.com rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 4301687Smax.romanov@nginx.com 4311687Smax.romanov@nginx.com rc = nxt_ruby_init_io(rctx); 4321687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 4331687Smax.romanov@nginx.com return Qnil; 4341687Smax.romanov@nginx.com } 435584Salexander.borisov@nginx.com 436584Salexander.borisov@nginx.com hash_env = rb_hash_new(); 437673Svbart@nginx.com 438673Svbart@nginx.com rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 439673Svbart@nginx.com rb_str_new((const char *) nxt_server.start, 440673Svbart@nginx.com (long) nxt_server.length)); 441673Svbart@nginx.com 442584Salexander.borisov@nginx.com version = rb_ary_new(); 443584Salexander.borisov@nginx.com 444584Salexander.borisov@nginx.com rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 445584Salexander.borisov@nginx.com rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 446584Salexander.borisov@nginx.com 447584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 4481687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input); 4491687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error); 4501687Smax.romanov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), 4511687Smax.romanov@nginx.com nxt_ruby_threads > 1 ? Qtrue : Qfalse); 452584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 453584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 454584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 455584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 456584Salexander.borisov@nginx.com rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 457584Salexander.borisov@nginx.com 4581687Smax.romanov@nginx.com rctx->env = hash_env; 4591687Smax.romanov@nginx.com 4601687Smax.romanov@nginx.com rb_gc_register_address(&rctx->env); 4611687Smax.romanov@nginx.com 462584Salexander.borisov@nginx.com return hash_env; 463584Salexander.borisov@nginx.com } 464584Salexander.borisov@nginx.com 465584Salexander.borisov@nginx.com 4661687Smax.romanov@nginx.com static int 4671687Smax.romanov@nginx.com nxt_ruby_init_io(nxt_ruby_ctx_t *rctx) 4681687Smax.romanov@nginx.com { 4691687Smax.romanov@nginx.com VALUE io_input, io_error; 4701687Smax.romanov@nginx.com 4711687Smax.romanov@nginx.com io_input = nxt_ruby_stream_io_input_init(); 4721687Smax.romanov@nginx.com 4731687Smax.romanov@nginx.com rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1, 4741687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx); 4751687Smax.romanov@nginx.com if (nxt_slow_path(rctx->io_input == Qnil)) { 4761687Smax.romanov@nginx.com nxt_unit_alert(NULL, 4771687Smax.romanov@nginx.com "Ruby: Failed to create environment 'rack.input' var"); 4781687Smax.romanov@nginx.com 4791687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4801687Smax.romanov@nginx.com } 4811687Smax.romanov@nginx.com 4821687Smax.romanov@nginx.com rb_gc_register_address(&rctx->io_input); 4831687Smax.romanov@nginx.com 4841687Smax.romanov@nginx.com io_error = nxt_ruby_stream_io_error_init(); 4851687Smax.romanov@nginx.com 4861687Smax.romanov@nginx.com rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1, 4871687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx); 4881687Smax.romanov@nginx.com if (nxt_slow_path(rctx->io_error == Qnil)) { 4891687Smax.romanov@nginx.com nxt_unit_alert(NULL, 4901687Smax.romanov@nginx.com "Ruby: Failed to create environment 'rack.error' var"); 4911687Smax.romanov@nginx.com 4921687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 4931687Smax.romanov@nginx.com } 4941687Smax.romanov@nginx.com 4951687Smax.romanov@nginx.com rb_gc_register_address(&rctx->io_error); 4961687Smax.romanov@nginx.com 4971687Smax.romanov@nginx.com return NXT_UNIT_OK; 4981687Smax.romanov@nginx.com } 4991687Smax.romanov@nginx.com 5001687Smax.romanov@nginx.com 501743Smax.romanov@nginx.com static void 502743Smax.romanov@nginx.com nxt_ruby_request_handler(nxt_unit_request_info_t *req) 503584Salexander.borisov@nginx.com { 5041687Smax.romanov@nginx.com (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req); 5051687Smax.romanov@nginx.com } 5061687Smax.romanov@nginx.com 507584Salexander.borisov@nginx.com 5081687Smax.romanov@nginx.com static void * 5091687Smax.romanov@nginx.com nxt_ruby_request_handler_gvl(void *data) 5101687Smax.romanov@nginx.com { 5111687Smax.romanov@nginx.com int state; 5121687Smax.romanov@nginx.com VALUE res; 5131687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 5141687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 5151687Smax.romanov@nginx.com 5161687Smax.romanov@nginx.com req = data; 517584Salexander.borisov@nginx.com 5181687Smax.romanov@nginx.com rctx = req->ctx->data; 5191687Smax.romanov@nginx.com rctx->req = req; 5201687Smax.romanov@nginx.com 5211687Smax.romanov@nginx.com res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state); 522743Smax.romanov@nginx.com if (nxt_slow_path(res == Qnil || state != 0)) { 5231687Smax.romanov@nginx.com nxt_ruby_exception_log(req, NXT_LOG_ERR, 524584Salexander.borisov@nginx.com "Failed to run ruby script"); 5251687Smax.romanov@nginx.com 5261687Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_ERROR); 5271687Smax.romanov@nginx.com 5281687Smax.romanov@nginx.com } else { 5291687Smax.romanov@nginx.com nxt_unit_request_done(req, NXT_UNIT_OK); 530584Salexander.borisov@nginx.com } 5311687Smax.romanov@nginx.com 5321687Smax.romanov@nginx.com rctx->req = NULL; 5331687Smax.romanov@nginx.com 5341687Smax.romanov@nginx.com return NULL; 535584Salexander.borisov@nginx.com } 536584Salexander.borisov@nginx.com 537584Salexander.borisov@nginx.com 538584Salexander.borisov@nginx.com static VALUE 539584Salexander.borisov@nginx.com nxt_ruby_rack_app_run(VALUE arg) 540584Salexander.borisov@nginx.com { 5411687Smax.romanov@nginx.com int rc; 5421687Smax.romanov@nginx.com VALUE env, result; 5431687Smax.romanov@nginx.com nxt_int_t status; 5441687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 5451687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 5461687Smax.romanov@nginx.com 5471687Smax.romanov@nginx.com req = (nxt_unit_request_info_t *) arg; 548584Salexander.borisov@nginx.com 5491687Smax.romanov@nginx.com rctx = req->ctx->data; 5501687Smax.romanov@nginx.com 5511687Smax.romanov@nginx.com env = rb_hash_dup(rctx->env); 552584Salexander.borisov@nginx.com 5531687Smax.romanov@nginx.com rc = nxt_ruby_read_request(req, env); 554743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 5551687Smax.romanov@nginx.com nxt_unit_req_alert(req, 556743Smax.romanov@nginx.com "Ruby: Failed to process incoming request"); 557584Salexander.borisov@nginx.com 558584Salexander.borisov@nginx.com goto fail; 559584Salexander.borisov@nginx.com } 560584Salexander.borisov@nginx.com 561584Salexander.borisov@nginx.com result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 562584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 5631687Smax.romanov@nginx.com nxt_unit_req_error(req, 564743Smax.romanov@nginx.com "Ruby: Invalid response format from application"); 565584Salexander.borisov@nginx.com 566584Salexander.borisov@nginx.com goto fail; 567584Salexander.borisov@nginx.com } 568584Salexander.borisov@nginx.com 569584Salexander.borisov@nginx.com if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 5701687Smax.romanov@nginx.com nxt_unit_req_error(req, 571743Smax.romanov@nginx.com "Ruby: Invalid response format from application. " 572743Smax.romanov@nginx.com "Need 3 entries [Status, Headers, Body]"); 573584Salexander.borisov@nginx.com 574584Salexander.borisov@nginx.com goto fail; 575584Salexander.borisov@nginx.com } 576584Salexander.borisov@nginx.com 5771687Smax.romanov@nginx.com status = nxt_ruby_rack_result_status(req, result); 578743Smax.romanov@nginx.com if (nxt_slow_path(status < 0)) { 5791687Smax.romanov@nginx.com nxt_unit_req_error(req, 580743Smax.romanov@nginx.com "Ruby: Invalid response status from application."); 581743Smax.romanov@nginx.com 582584Salexander.borisov@nginx.com goto fail; 583584Salexander.borisov@nginx.com } 584584Salexander.borisov@nginx.com 5851687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_headers(req, result, status); 586743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 587584Salexander.borisov@nginx.com goto fail; 588584Salexander.borisov@nginx.com } 589584Salexander.borisov@nginx.com 5901687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_body(req, result); 591743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 592584Salexander.borisov@nginx.com goto fail; 593584Salexander.borisov@nginx.com } 594584Salexander.borisov@nginx.com 595584Salexander.borisov@nginx.com rb_hash_delete(env, rb_obj_id(env)); 596584Salexander.borisov@nginx.com 597584Salexander.borisov@nginx.com return result; 598584Salexander.borisov@nginx.com 599584Salexander.borisov@nginx.com fail: 600584Salexander.borisov@nginx.com 601584Salexander.borisov@nginx.com rb_hash_delete(env, rb_obj_id(env)); 602584Salexander.borisov@nginx.com 603584Salexander.borisov@nginx.com return Qnil; 604584Salexander.borisov@nginx.com } 605584Salexander.borisov@nginx.com 606584Salexander.borisov@nginx.com 607743Smax.romanov@nginx.com static int 6081687Smax.romanov@nginx.com nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) 609584Salexander.borisov@nginx.com { 6101686Smax.romanov@nginx.com VALUE name; 611967Svbart@nginx.com uint32_t i; 612743Smax.romanov@nginx.com nxt_unit_field_t *f; 613743Smax.romanov@nginx.com nxt_unit_request_t *r; 614584Salexander.borisov@nginx.com 6151687Smax.romanov@nginx.com r = req->request; 616584Salexander.borisov@nginx.com 6171686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, 618743Smax.romanov@nginx.com r->method_length); 6191686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target, 620743Smax.romanov@nginx.com r->target_length); 6211686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length); 6221686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query, 623981Svbart@nginx.com r->query_length); 6241686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version, 625743Smax.romanov@nginx.com r->version_length); 6261686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, 627743Smax.romanov@nginx.com r->remote_length); 6281686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, 6291686Smax.romanov@nginx.com r->local_length); 6301686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, 6311686Smax.romanov@nginx.com r->server_name_length); 632584Salexander.borisov@nginx.com 6331686Smax.romanov@nginx.com rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str); 634967Svbart@nginx.com 6351686Smax.romanov@nginx.com rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str, 6361686Smax.romanov@nginx.com r->tls ? nxt_rb_https_str : nxt_rb_http_str); 6371011Smax.romanov@nginx.com 638743Smax.romanov@nginx.com for (i = 0; i < r->fields_count; i++) { 639743Smax.romanov@nginx.com f = r->fields + i; 640584Salexander.borisov@nginx.com 6411686Smax.romanov@nginx.com name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length); 6421686Smax.romanov@nginx.com 6431686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length); 644584Salexander.borisov@nginx.com } 645584Salexander.borisov@nginx.com 646743Smax.romanov@nginx.com if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 647743Smax.romanov@nginx.com f = r->fields + r->content_length_field; 648584Salexander.borisov@nginx.com 6491686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str, 650743Smax.romanov@nginx.com &f->value, f->value_length); 651584Salexander.borisov@nginx.com } 652584Salexander.borisov@nginx.com 653743Smax.romanov@nginx.com if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 654743Smax.romanov@nginx.com f = r->fields + r->content_type_field; 655584Salexander.borisov@nginx.com 6561686Smax.romanov@nginx.com nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str, 657743Smax.romanov@nginx.com &f->value, f->value_length); 658584Salexander.borisov@nginx.com } 659584Salexander.borisov@nginx.com 660743Smax.romanov@nginx.com return NXT_UNIT_OK; 661584Salexander.borisov@nginx.com } 662584Salexander.borisov@nginx.com 663584Salexander.borisov@nginx.com 664743Smax.romanov@nginx.com nxt_inline void 6651686Smax.romanov@nginx.com nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 6661686Smax.romanov@nginx.com nxt_unit_sptr_t *sptr, uint32_t len) 667584Salexander.borisov@nginx.com { 668743Smax.romanov@nginx.com char *str; 669743Smax.romanov@nginx.com 670743Smax.romanov@nginx.com str = nxt_unit_sptr_get(sptr); 671584Salexander.borisov@nginx.com 6721686Smax.romanov@nginx.com rb_hash_aset(hash_env, name, rb_str_new(str, len)); 673584Salexander.borisov@nginx.com } 674584Salexander.borisov@nginx.com 675584Salexander.borisov@nginx.com 676584Salexander.borisov@nginx.com static nxt_int_t 6771687Smax.romanov@nginx.com nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result) 678584Salexander.borisov@nginx.com { 679743Smax.romanov@nginx.com VALUE status; 680584Salexander.borisov@nginx.com 681584Salexander.borisov@nginx.com status = rb_ary_entry(result, 0); 682584Salexander.borisov@nginx.com 683584Salexander.borisov@nginx.com if (TYPE(status) == T_FIXNUM) { 684743Smax.romanov@nginx.com return FIX2INT(status); 685584Salexander.borisov@nginx.com } 686584Salexander.borisov@nginx.com 687743Smax.romanov@nginx.com if (TYPE(status) == T_STRING) { 688743Smax.romanov@nginx.com return nxt_int_parse((u_char *) RSTRING_PTR(status), 689743Smax.romanov@nginx.com RSTRING_LEN(status)); 690584Salexander.borisov@nginx.com } 691584Salexander.borisov@nginx.com 6921687Smax.romanov@nginx.com nxt_unit_req_error(req, "Ruby: Invalid response 'status' " 693743Smax.romanov@nginx.com "format from application"); 694584Salexander.borisov@nginx.com 695743Smax.romanov@nginx.com return -2; 696584Salexander.borisov@nginx.com } 697584Salexander.borisov@nginx.com 698584Salexander.borisov@nginx.com 699743Smax.romanov@nginx.com typedef struct { 7001687Smax.romanov@nginx.com int rc; 7011687Smax.romanov@nginx.com uint32_t fields; 7021687Smax.romanov@nginx.com uint32_t size; 7031687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 704743Smax.romanov@nginx.com } nxt_ruby_headers_info_t; 705743Smax.romanov@nginx.com 706743Smax.romanov@nginx.com 707743Smax.romanov@nginx.com static int 7081687Smax.romanov@nginx.com nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result, 7091687Smax.romanov@nginx.com nxt_int_t status) 710584Salexander.borisov@nginx.com { 711743Smax.romanov@nginx.com int rc; 712743Smax.romanov@nginx.com VALUE headers; 713743Smax.romanov@nginx.com nxt_ruby_headers_info_t headers_info; 714743Smax.romanov@nginx.com 715743Smax.romanov@nginx.com headers = rb_ary_entry(result, 1); 716743Smax.romanov@nginx.com if (nxt_slow_path(TYPE(headers) != T_HASH)) { 7171687Smax.romanov@nginx.com nxt_unit_req_error(req, 718743Smax.romanov@nginx.com "Ruby: Invalid response 'headers' format from " 719743Smax.romanov@nginx.com "application"); 720743Smax.romanov@nginx.com 721743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 722743Smax.romanov@nginx.com } 723584Salexander.borisov@nginx.com 724743Smax.romanov@nginx.com rc = NXT_UNIT_OK; 725743Smax.romanov@nginx.com 726743Smax.romanov@nginx.com headers_info.rc = NXT_UNIT_OK; 727743Smax.romanov@nginx.com headers_info.fields = 0; 728743Smax.romanov@nginx.com headers_info.size = 0; 7291687Smax.romanov@nginx.com headers_info.req = req; 730743Smax.romanov@nginx.com 731743Smax.romanov@nginx.com rb_hash_foreach(headers, nxt_ruby_hash_info, 732743Smax.romanov@nginx.com (VALUE) (uintptr_t) &headers_info); 733743Smax.romanov@nginx.com if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { 734743Smax.romanov@nginx.com return headers_info.rc; 735743Smax.romanov@nginx.com } 736743Smax.romanov@nginx.com 7371687Smax.romanov@nginx.com rc = nxt_unit_response_init(req, status, 738743Smax.romanov@nginx.com headers_info.fields, headers_info.size); 739743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 740584Salexander.borisov@nginx.com return rc; 741584Salexander.borisov@nginx.com } 742584Salexander.borisov@nginx.com 7431687Smax.romanov@nginx.com rb_hash_foreach(headers, nxt_ruby_hash_add, 7441687Smax.romanov@nginx.com (VALUE) (uintptr_t) &headers_info); 745584Salexander.borisov@nginx.com 746584Salexander.borisov@nginx.com return rc; 747584Salexander.borisov@nginx.com } 748584Salexander.borisov@nginx.com 749584Salexander.borisov@nginx.com 750743Smax.romanov@nginx.com static int 751743Smax.romanov@nginx.com nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) 752584Salexander.borisov@nginx.com { 753743Smax.romanov@nginx.com const char *value, *value_end, *pos; 754743Smax.romanov@nginx.com nxt_ruby_headers_info_t *headers_info; 755584Salexander.borisov@nginx.com 756743Smax.romanov@nginx.com headers_info = (void *) (uintptr_t) arg; 757584Salexander.borisov@nginx.com 758584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 7591687Smax.romanov@nginx.com nxt_unit_req_error(headers_info->req, 760743Smax.romanov@nginx.com "Ruby: Wrong header entry 'key' from application"); 761584Salexander.borisov@nginx.com 762584Salexander.borisov@nginx.com goto fail; 763584Salexander.borisov@nginx.com } 764584Salexander.borisov@nginx.com 765584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 7661687Smax.romanov@nginx.com nxt_unit_req_error(headers_info->req, 767743Smax.romanov@nginx.com "Ruby: Wrong header entry 'value' from application"); 768584Salexander.borisov@nginx.com 769584Salexander.borisov@nginx.com goto fail; 770584Salexander.borisov@nginx.com } 771584Salexander.borisov@nginx.com 772584Salexander.borisov@nginx.com value = RSTRING_PTR(r_value); 773584Salexander.borisov@nginx.com value_end = value + RSTRING_LEN(r_value); 774584Salexander.borisov@nginx.com 775584Salexander.borisov@nginx.com pos = value; 776584Salexander.borisov@nginx.com 777584Salexander.borisov@nginx.com for ( ;; ) { 778584Salexander.borisov@nginx.com pos = strchr(pos, '\n'); 779584Salexander.borisov@nginx.com 780584Salexander.borisov@nginx.com if (pos == NULL) { 781584Salexander.borisov@nginx.com break; 782584Salexander.borisov@nginx.com } 783584Salexander.borisov@nginx.com 784743Smax.romanov@nginx.com headers_info->fields++; 785743Smax.romanov@nginx.com headers_info->size += RSTRING_LEN(r_key) + (pos - value); 786743Smax.romanov@nginx.com 787743Smax.romanov@nginx.com pos++; 788743Smax.romanov@nginx.com value = pos; 789743Smax.romanov@nginx.com } 790743Smax.romanov@nginx.com 791743Smax.romanov@nginx.com if (value <= value_end) { 792743Smax.romanov@nginx.com headers_info->fields++; 793743Smax.romanov@nginx.com headers_info->size += RSTRING_LEN(r_key) + (value_end - value); 794743Smax.romanov@nginx.com } 795743Smax.romanov@nginx.com 796743Smax.romanov@nginx.com return ST_CONTINUE; 797743Smax.romanov@nginx.com 798743Smax.romanov@nginx.com fail: 799743Smax.romanov@nginx.com 800743Smax.romanov@nginx.com headers_info->rc = NXT_UNIT_ERROR; 801743Smax.romanov@nginx.com 802743Smax.romanov@nginx.com return ST_STOP; 803743Smax.romanov@nginx.com } 804743Smax.romanov@nginx.com 805743Smax.romanov@nginx.com 806743Smax.romanov@nginx.com static int 807743Smax.romanov@nginx.com nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) 808743Smax.romanov@nginx.com { 8091687Smax.romanov@nginx.com int *rc; 8101687Smax.romanov@nginx.com uint32_t key_len; 8111687Smax.romanov@nginx.com const char *value, *value_end, *pos; 8121687Smax.romanov@nginx.com nxt_ruby_headers_info_t *headers_info; 813743Smax.romanov@nginx.com 8141687Smax.romanov@nginx.com headers_info = (void *) (uintptr_t) arg; 8151687Smax.romanov@nginx.com rc = &headers_info->rc; 816743Smax.romanov@nginx.com 817743Smax.romanov@nginx.com value = RSTRING_PTR(r_value); 818743Smax.romanov@nginx.com value_end = value + RSTRING_LEN(r_value); 819743Smax.romanov@nginx.com 820743Smax.romanov@nginx.com key_len = RSTRING_LEN(r_key); 821743Smax.romanov@nginx.com 822743Smax.romanov@nginx.com pos = value; 823743Smax.romanov@nginx.com 824743Smax.romanov@nginx.com for ( ;; ) { 825743Smax.romanov@nginx.com pos = strchr(pos, '\n'); 826743Smax.romanov@nginx.com 827743Smax.romanov@nginx.com if (pos == NULL) { 828743Smax.romanov@nginx.com break; 829743Smax.romanov@nginx.com } 830743Smax.romanov@nginx.com 8311687Smax.romanov@nginx.com *rc = nxt_unit_response_add_field(headers_info->req, 832743Smax.romanov@nginx.com RSTRING_PTR(r_key), key_len, 833743Smax.romanov@nginx.com value, pos - value); 834743Smax.romanov@nginx.com if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 835584Salexander.borisov@nginx.com goto fail; 836584Salexander.borisov@nginx.com } 837584Salexander.borisov@nginx.com 838584Salexander.borisov@nginx.com pos++; 839584Salexander.borisov@nginx.com value = pos; 840584Salexander.borisov@nginx.com } 841584Salexander.borisov@nginx.com 842584Salexander.borisov@nginx.com if (value <= value_end) { 8431687Smax.romanov@nginx.com *rc = nxt_unit_response_add_field(headers_info->req, 844743Smax.romanov@nginx.com RSTRING_PTR(r_key), key_len, 845743Smax.romanov@nginx.com value, value_end - value); 846743Smax.romanov@nginx.com if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 847584Salexander.borisov@nginx.com goto fail; 848584Salexander.borisov@nginx.com } 849584Salexander.borisov@nginx.com } 850584Salexander.borisov@nginx.com 851584Salexander.borisov@nginx.com return ST_CONTINUE; 852584Salexander.borisov@nginx.com 853584Salexander.borisov@nginx.com fail: 854584Salexander.borisov@nginx.com 855743Smax.romanov@nginx.com *rc = NXT_UNIT_ERROR; 856584Salexander.borisov@nginx.com 857584Salexander.borisov@nginx.com return ST_STOP; 858584Salexander.borisov@nginx.com } 859584Salexander.borisov@nginx.com 860584Salexander.borisov@nginx.com 861743Smax.romanov@nginx.com static int 8621687Smax.romanov@nginx.com nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result) 863584Salexander.borisov@nginx.com { 864743Smax.romanov@nginx.com int rc; 865743Smax.romanov@nginx.com VALUE fn, body; 866584Salexander.borisov@nginx.com 867584Salexander.borisov@nginx.com body = rb_ary_entry(result, 2); 868584Salexander.borisov@nginx.com 869584Salexander.borisov@nginx.com if (rb_respond_to(body, rb_intern("to_path"))) { 870584Salexander.borisov@nginx.com 871584Salexander.borisov@nginx.com fn = rb_funcall(body, rb_intern("to_path"), 0); 872584Salexander.borisov@nginx.com if (nxt_slow_path(TYPE(fn) != T_STRING)) { 8731687Smax.romanov@nginx.com nxt_unit_req_error(req, 874743Smax.romanov@nginx.com "Ruby: Failed to get 'body' file path from " 875743Smax.romanov@nginx.com "application"); 876584Salexander.borisov@nginx.com 877743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 878584Salexander.borisov@nginx.com } 879584Salexander.borisov@nginx.com 8801687Smax.romanov@nginx.com rc = nxt_ruby_rack_result_body_file_write(req, fn); 881743Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 882743Smax.romanov@nginx.com return rc; 883584Salexander.borisov@nginx.com } 884584Salexander.borisov@nginx.com 885584Salexander.borisov@nginx.com } else if (rb_respond_to(body, rb_intern("each"))) { 8861258Smax.romanov@nginx.com rb_block_call(body, rb_intern("each"), 0, 0, 8871687Smax.romanov@nginx.com nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req); 888584Salexander.borisov@nginx.com 889584Salexander.borisov@nginx.com } else { 8901687Smax.romanov@nginx.com nxt_unit_req_error(req, 891743Smax.romanov@nginx.com "Ruby: Invalid response 'body' format " 892743Smax.romanov@nginx.com "from application"); 893584Salexander.borisov@nginx.com 894743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 895584Salexander.borisov@nginx.com } 896584Salexander.borisov@nginx.com 897584Salexander.borisov@nginx.com if (rb_respond_to(body, rb_intern("close"))) { 898584Salexander.borisov@nginx.com rb_funcall(body, rb_intern("close"), 0); 899584Salexander.borisov@nginx.com } 900584Salexander.borisov@nginx.com 901743Smax.romanov@nginx.com return NXT_UNIT_OK; 902743Smax.romanov@nginx.com } 903743Smax.romanov@nginx.com 904743Smax.romanov@nginx.com 905743Smax.romanov@nginx.com typedef struct { 906743Smax.romanov@nginx.com int fd; 907743Smax.romanov@nginx.com off_t pos; 908743Smax.romanov@nginx.com off_t rest; 909743Smax.romanov@nginx.com } nxt_ruby_rack_file_t; 910743Smax.romanov@nginx.com 911743Smax.romanov@nginx.com 912743Smax.romanov@nginx.com static ssize_t 913743Smax.romanov@nginx.com nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) 914743Smax.romanov@nginx.com { 915743Smax.romanov@nginx.com ssize_t res; 916743Smax.romanov@nginx.com nxt_ruby_rack_file_t *file; 917743Smax.romanov@nginx.com 918743Smax.romanov@nginx.com file = read_info->data; 919743Smax.romanov@nginx.com 920743Smax.romanov@nginx.com size = nxt_min(size, (size_t) file->rest); 921743Smax.romanov@nginx.com 922743Smax.romanov@nginx.com res = pread(file->fd, dst, size, file->pos); 923743Smax.romanov@nginx.com 924743Smax.romanov@nginx.com if (res >= 0) { 925743Smax.romanov@nginx.com file->pos += res; 926743Smax.romanov@nginx.com file->rest -= res; 927743Smax.romanov@nginx.com 928743Smax.romanov@nginx.com if (size > (size_t) res) { 929743Smax.romanov@nginx.com file->rest = 0; 930743Smax.romanov@nginx.com } 931743Smax.romanov@nginx.com } 932743Smax.romanov@nginx.com 933743Smax.romanov@nginx.com read_info->eof = file->rest == 0; 934743Smax.romanov@nginx.com 935743Smax.romanov@nginx.com return res; 936584Salexander.borisov@nginx.com } 937584Salexander.borisov@nginx.com 938584Salexander.borisov@nginx.com 9391687Smax.romanov@nginx.com typedef struct { 9401687Smax.romanov@nginx.com nxt_unit_read_info_t read_info; 9411687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 9421687Smax.romanov@nginx.com } nxt_ruby_read_info_t; 9431687Smax.romanov@nginx.com 9441687Smax.romanov@nginx.com 945743Smax.romanov@nginx.com static int 9461687Smax.romanov@nginx.com nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 9471687Smax.romanov@nginx.com VALUE filepath) 948584Salexander.borisov@nginx.com { 949743Smax.romanov@nginx.com int fd, rc; 950743Smax.romanov@nginx.com struct stat finfo; 951743Smax.romanov@nginx.com nxt_ruby_rack_file_t ruby_file; 9521687Smax.romanov@nginx.com nxt_ruby_read_info_t ri; 953584Salexander.borisov@nginx.com 954743Smax.romanov@nginx.com fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); 955743Smax.romanov@nginx.com if (nxt_slow_path(fd == -1)) { 9561687Smax.romanov@nginx.com nxt_unit_req_error(req, 957743Smax.romanov@nginx.com "Ruby: Failed to open content file \"%s\": %s (%d)", 958743Smax.romanov@nginx.com RSTRING_PTR(filepath), strerror(errno), errno); 959584Salexander.borisov@nginx.com 960743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 961584Salexander.borisov@nginx.com } 962584Salexander.borisov@nginx.com 963743Smax.romanov@nginx.com rc = fstat(fd, &finfo); 964743Smax.romanov@nginx.com if (nxt_slow_path(rc == -1)) { 9651687Smax.romanov@nginx.com nxt_unit_req_error(req, 966743Smax.romanov@nginx.com "Ruby: Content file fstat(\"%s\") failed: %s (%d)", 967743Smax.romanov@nginx.com RSTRING_PTR(filepath), strerror(errno), errno); 968584Salexander.borisov@nginx.com 969743Smax.romanov@nginx.com close(fd); 970584Salexander.borisov@nginx.com 971743Smax.romanov@nginx.com return NXT_UNIT_ERROR; 972584Salexander.borisov@nginx.com } 973584Salexander.borisov@nginx.com 974743Smax.romanov@nginx.com ruby_file.fd = fd; 975743Smax.romanov@nginx.com ruby_file.pos = 0; 976743Smax.romanov@nginx.com ruby_file.rest = finfo.st_size; 977584Salexander.borisov@nginx.com 9781687Smax.romanov@nginx.com ri.read_info.read = nxt_ruby_rack_file_read; 9791687Smax.romanov@nginx.com ri.read_info.eof = ruby_file.rest == 0; 9801687Smax.romanov@nginx.com ri.read_info.buf_size = ruby_file.rest; 9811687Smax.romanov@nginx.com ri.read_info.data = &ruby_file; 9821687Smax.romanov@nginx.com ri.req = req; 983584Salexander.borisov@nginx.com 9841687Smax.romanov@nginx.com rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb, 9851687Smax.romanov@nginx.com &ri, 9861687Smax.romanov@nginx.com nxt_ruby_ubf, 9871687Smax.romanov@nginx.com req->ctx); 988584Salexander.borisov@nginx.com 989743Smax.romanov@nginx.com close(fd); 990584Salexander.borisov@nginx.com 991743Smax.romanov@nginx.com return rc; 992584Salexander.borisov@nginx.com } 993584Salexander.borisov@nginx.com 994584Salexander.borisov@nginx.com 9951687Smax.romanov@nginx.com static void * 9961687Smax.romanov@nginx.com nxt_ruby_response_write_cb(void *data) 9971687Smax.romanov@nginx.com { 9981687Smax.romanov@nginx.com int rc; 9991687Smax.romanov@nginx.com nxt_ruby_read_info_t *ri; 10001687Smax.romanov@nginx.com 10011687Smax.romanov@nginx.com ri = data; 10021687Smax.romanov@nginx.com 10031687Smax.romanov@nginx.com rc = nxt_unit_response_write_cb(ri->req, &ri->read_info); 10041687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10051687Smax.romanov@nginx.com nxt_unit_req_error(ri->req, "Ruby: Failed to write content file."); 10061687Smax.romanov@nginx.com } 10071687Smax.romanov@nginx.com 10081687Smax.romanov@nginx.com return (void *) (intptr_t) rc; 10091687Smax.romanov@nginx.com } 10101687Smax.romanov@nginx.com 10111687Smax.romanov@nginx.com 10121687Smax.romanov@nginx.com typedef struct { 10131687Smax.romanov@nginx.com VALUE body; 10141687Smax.romanov@nginx.com nxt_unit_request_info_t *req; 10151687Smax.romanov@nginx.com } nxt_ruby_write_info_t; 10161687Smax.romanov@nginx.com 10171687Smax.romanov@nginx.com 1018584Salexander.borisov@nginx.com static VALUE 10191337Smax.romanov@nginx.com nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, 10201337Smax.romanov@nginx.com const VALUE *argv, VALUE blockarg) 1021584Salexander.borisov@nginx.com { 10221687Smax.romanov@nginx.com nxt_ruby_write_info_t wi; 1023584Salexander.borisov@nginx.com 1024584Salexander.borisov@nginx.com if (TYPE(body) != T_STRING) { 1025584Salexander.borisov@nginx.com return Qnil; 1026584Salexander.borisov@nginx.com } 1027584Salexander.borisov@nginx.com 10281687Smax.romanov@nginx.com wi.body = body; 10291687Smax.romanov@nginx.com wi.req = (void *) (uintptr_t) arg; 10301687Smax.romanov@nginx.com 10311687Smax.romanov@nginx.com (void) rb_thread_call_without_gvl(nxt_ruby_response_write, 10321687Smax.romanov@nginx.com (void *) (uintptr_t) &wi, 10331687Smax.romanov@nginx.com nxt_ruby_ubf, wi.req->ctx); 1034584Salexander.borisov@nginx.com 1035584Salexander.borisov@nginx.com return Qnil; 1036584Salexander.borisov@nginx.com } 1037584Salexander.borisov@nginx.com 1038584Salexander.borisov@nginx.com 10391687Smax.romanov@nginx.com static void * 10401687Smax.romanov@nginx.com nxt_ruby_response_write(void *data) 10411687Smax.romanov@nginx.com { 10421687Smax.romanov@nginx.com int rc; 10431687Smax.romanov@nginx.com nxt_ruby_write_info_t *wi; 10441687Smax.romanov@nginx.com 10451687Smax.romanov@nginx.com wi = data; 10461687Smax.romanov@nginx.com 10471687Smax.romanov@nginx.com rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body), 10481687Smax.romanov@nginx.com RSTRING_LEN(wi->body)); 10491687Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 10501687Smax.romanov@nginx.com nxt_unit_req_error(wi->req, 10511687Smax.romanov@nginx.com "Ruby: Failed to write 'body' from application"); 10521687Smax.romanov@nginx.com } 10531687Smax.romanov@nginx.com 10541687Smax.romanov@nginx.com return (void *) (intptr_t) rc; 10551687Smax.romanov@nginx.com } 10561687Smax.romanov@nginx.com 10571687Smax.romanov@nginx.com 1058584Salexander.borisov@nginx.com static void 10591687Smax.romanov@nginx.com nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, 10601687Smax.romanov@nginx.com const char *desc) 1061584Salexander.borisov@nginx.com { 1062584Salexander.borisov@nginx.com int i; 1063584Salexander.borisov@nginx.com VALUE err, ary, eclass, msg; 1064584Salexander.borisov@nginx.com 10651687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "Ruby: %s", desc); 1066584Salexander.borisov@nginx.com 1067584Salexander.borisov@nginx.com err = rb_errinfo(); 1068743Smax.romanov@nginx.com if (nxt_slow_path(err == Qnil)) { 1069743Smax.romanov@nginx.com return; 1070743Smax.romanov@nginx.com } 1071584Salexander.borisov@nginx.com 1072743Smax.romanov@nginx.com ary = rb_funcall(err, rb_intern("backtrace"), 0); 1073743Smax.romanov@nginx.com if (nxt_slow_path(RARRAY_LEN(ary) == 0)) { 1074584Salexander.borisov@nginx.com return; 1075584Salexander.borisov@nginx.com } 1076584Salexander.borisov@nginx.com 1077584Salexander.borisov@nginx.com eclass = rb_class_name(rb_class_of(err)); 1078584Salexander.borisov@nginx.com msg = rb_funcall(err, rb_intern("message"), 0); 1079584Salexander.borisov@nginx.com 10801687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", 1081743Smax.romanov@nginx.com RSTRING_PTR(RARRAY_PTR(ary)[0]), 1082743Smax.romanov@nginx.com RSTRING_PTR(msg), RSTRING_PTR(eclass)); 1083584Salexander.borisov@nginx.com 1084584Salexander.borisov@nginx.com for (i = 1; i < RARRAY_LEN(ary); i++) { 10851687Smax.romanov@nginx.com nxt_unit_req_log(req, level, "from %s", 10861687Smax.romanov@nginx.com RSTRING_PTR(RARRAY_PTR(ary)[i])); 10871687Smax.romanov@nginx.com } 10881687Smax.romanov@nginx.com } 10891687Smax.romanov@nginx.com 1090743Smax.romanov@nginx.com 10911687Smax.romanov@nginx.com static void 10921687Smax.romanov@nginx.com nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx) 10931687Smax.romanov@nginx.com { 10941687Smax.romanov@nginx.com if (rctx->io_input != Qnil) { 10951687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->io_input); 10961687Smax.romanov@nginx.com } 10971687Smax.romanov@nginx.com 10981687Smax.romanov@nginx.com if (rctx->io_error != Qnil) { 10991687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->io_error); 11001687Smax.romanov@nginx.com } 11011687Smax.romanov@nginx.com 11021687Smax.romanov@nginx.com if (rctx->env != Qnil) { 11031687Smax.romanov@nginx.com rb_gc_unregister_address(&rctx->env); 1104584Salexander.borisov@nginx.com } 1105584Salexander.borisov@nginx.com } 1106584Salexander.borisov@nginx.com 1107584Salexander.borisov@nginx.com 1108584Salexander.borisov@nginx.com static void 1109743Smax.romanov@nginx.com nxt_ruby_atexit(void) 1110584Salexander.borisov@nginx.com { 11111687Smax.romanov@nginx.com if (nxt_ruby_rackup != Qnil) { 11121687Smax.romanov@nginx.com rb_gc_unregister_address(&nxt_ruby_rackup); 11131687Smax.romanov@nginx.com } 1114584Salexander.borisov@nginx.com 11151687Smax.romanov@nginx.com if (nxt_ruby_call != Qnil) { 11161687Smax.romanov@nginx.com rb_gc_unregister_address(&nxt_ruby_call); 11171687Smax.romanov@nginx.com } 1118584Salexander.borisov@nginx.com 11191686Smax.romanov@nginx.com nxt_ruby_done_strings(); 11201686Smax.romanov@nginx.com 1121584Salexander.borisov@nginx.com ruby_cleanup(0); 1122584Salexander.borisov@nginx.com } 11231687Smax.romanov@nginx.com 11241687Smax.romanov@nginx.com 11251687Smax.romanov@nginx.com static int 11261687Smax.romanov@nginx.com nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) 11271687Smax.romanov@nginx.com { 11281687Smax.romanov@nginx.com VALUE res; 11291687Smax.romanov@nginx.com uint32_t i; 11301687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 11311687Smax.romanov@nginx.com nxt_ruby_app_conf_t *c; 11321687Smax.romanov@nginx.com 11331687Smax.romanov@nginx.com /* Worker thread context. */ 11341687Smax.romanov@nginx.com if (!nxt_unit_is_main_ctx(ctx)) { 11351687Smax.romanov@nginx.com return NXT_UNIT_OK; 11361687Smax.romanov@nginx.com } 11371687Smax.romanov@nginx.com 11381687Smax.romanov@nginx.com c = ctx->unit->data; 11391687Smax.romanov@nginx.com 11401687Smax.romanov@nginx.com if (c->threads <= 1) { 11411687Smax.romanov@nginx.com return NXT_UNIT_OK; 11421687Smax.romanov@nginx.com } 11431687Smax.romanov@nginx.com 11441687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 11451687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 11461687Smax.romanov@nginx.com 11471687Smax.romanov@nginx.com rctx->ctx = ctx; 11481687Smax.romanov@nginx.com 11491738Smax.romanov@nginx.com res = (VALUE) rb_thread_call_with_gvl(nxt_ruby_thread_create_gvl, rctx); 11501687Smax.romanov@nginx.com 11511687Smax.romanov@nginx.com if (nxt_fast_path(res != Qnil)) { 11521687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1)); 11531687Smax.romanov@nginx.com 11541687Smax.romanov@nginx.com rctx->thread = res; 11551687Smax.romanov@nginx.com 11561687Smax.romanov@nginx.com } else { 11571687Smax.romanov@nginx.com nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1)); 11581691Smax.romanov@nginx.com 11591691Smax.romanov@nginx.com return NXT_UNIT_ERROR; 11601687Smax.romanov@nginx.com } 11611687Smax.romanov@nginx.com } 11621687Smax.romanov@nginx.com 11631687Smax.romanov@nginx.com return NXT_UNIT_OK; 11641687Smax.romanov@nginx.com } 11651687Smax.romanov@nginx.com 11661687Smax.romanov@nginx.com 11671738Smax.romanov@nginx.com static void * 11681738Smax.romanov@nginx.com nxt_ruby_thread_create_gvl(void *rctx) 11691738Smax.romanov@nginx.com { 11701738Smax.romanov@nginx.com VALUE res; 11711738Smax.romanov@nginx.com 11721738Smax.romanov@nginx.com res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx); 11731738Smax.romanov@nginx.com 11741738Smax.romanov@nginx.com return (void *) (uintptr_t) res; 11751738Smax.romanov@nginx.com } 11761738Smax.romanov@nginx.com 11771738Smax.romanov@nginx.com 11781687Smax.romanov@nginx.com static VALUE 11791687Smax.romanov@nginx.com nxt_ruby_thread_func(VALUE arg) 11801687Smax.romanov@nginx.com { 11811687Smax.romanov@nginx.com nxt_unit_ctx_t *ctx; 11821687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 11831687Smax.romanov@nginx.com 11841687Smax.romanov@nginx.com rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 11851687Smax.romanov@nginx.com 11861687Smax.romanov@nginx.com nxt_unit_debug(rctx->ctx, "worker thread start"); 11871687Smax.romanov@nginx.com 11881687Smax.romanov@nginx.com ctx = nxt_unit_ctx_alloc(rctx->ctx, rctx); 11891687Smax.romanov@nginx.com if (nxt_slow_path(ctx == NULL)) { 11901687Smax.romanov@nginx.com goto fail; 11911687Smax.romanov@nginx.com } 11921687Smax.romanov@nginx.com 11931687Smax.romanov@nginx.com (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx, 11941687Smax.romanov@nginx.com nxt_ruby_ubf, ctx); 11951687Smax.romanov@nginx.com 11961687Smax.romanov@nginx.com nxt_unit_done(ctx); 11971687Smax.romanov@nginx.com 11981687Smax.romanov@nginx.com fail: 11991687Smax.romanov@nginx.com 12001687Smax.romanov@nginx.com nxt_unit_debug(NULL, "worker thread end"); 12011687Smax.romanov@nginx.com 12021687Smax.romanov@nginx.com return Qnil; 12031687Smax.romanov@nginx.com } 12041687Smax.romanov@nginx.com 12051687Smax.romanov@nginx.com 12061687Smax.romanov@nginx.com static void * 12071687Smax.romanov@nginx.com nxt_ruby_unit_run(void *ctx) 12081687Smax.romanov@nginx.com { 12091687Smax.romanov@nginx.com return (void *) (intptr_t) nxt_unit_run(ctx); 12101687Smax.romanov@nginx.com } 12111687Smax.romanov@nginx.com 12121687Smax.romanov@nginx.com 12131687Smax.romanov@nginx.com static void 12141687Smax.romanov@nginx.com nxt_ruby_ubf(void *ctx) 12151687Smax.romanov@nginx.com { 12161687Smax.romanov@nginx.com nxt_unit_warn(ctx, "Ruby: UBF"); 12171687Smax.romanov@nginx.com } 12181687Smax.romanov@nginx.com 12191687Smax.romanov@nginx.com 12201687Smax.romanov@nginx.com static int 12211687Smax.romanov@nginx.com nxt_ruby_init_threads(nxt_ruby_app_conf_t *c) 12221687Smax.romanov@nginx.com { 12231687Smax.romanov@nginx.com int state; 12241687Smax.romanov@nginx.com uint32_t i; 12251687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 12261687Smax.romanov@nginx.com 12271687Smax.romanov@nginx.com if (c->threads <= 1) { 12281687Smax.romanov@nginx.com return NXT_UNIT_OK; 12291687Smax.romanov@nginx.com } 12301687Smax.romanov@nginx.com 12311687Smax.romanov@nginx.com nxt_ruby_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_ruby_ctx_t) 12321687Smax.romanov@nginx.com * (c->threads - 1)); 12331687Smax.romanov@nginx.com if (nxt_slow_path(nxt_ruby_ctxs == NULL)) { 12341687Smax.romanov@nginx.com nxt_unit_alert(NULL, "Failed to allocate run contexts array"); 12351687Smax.romanov@nginx.com 12361687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 12371687Smax.romanov@nginx.com } 12381687Smax.romanov@nginx.com 12391687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 12401687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 12411687Smax.romanov@nginx.com 12421687Smax.romanov@nginx.com rctx->env = Qnil; 12431687Smax.romanov@nginx.com rctx->io_input = Qnil; 12441687Smax.romanov@nginx.com rctx->io_error = Qnil; 12451687Smax.romanov@nginx.com rctx->thread = Qnil; 12461687Smax.romanov@nginx.com } 12471687Smax.romanov@nginx.com 12481687Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 12491687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 12501687Smax.romanov@nginx.com 12511687Smax.romanov@nginx.com rctx->env = rb_protect(nxt_ruby_rack_env_create, 12521687Smax.romanov@nginx.com (VALUE) (uintptr_t) rctx, &state); 12531687Smax.romanov@nginx.com if (nxt_slow_path(rctx->env == Qnil || state != 0)) { 12541687Smax.romanov@nginx.com nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 12551687Smax.romanov@nginx.com "Failed to create 'environ' variable"); 12561687Smax.romanov@nginx.com return NXT_UNIT_ERROR; 12571687Smax.romanov@nginx.com } 12581687Smax.romanov@nginx.com } 12591687Smax.romanov@nginx.com 12601687Smax.romanov@nginx.com return NXT_UNIT_OK; 12611687Smax.romanov@nginx.com } 12621687Smax.romanov@nginx.com 12631687Smax.romanov@nginx.com 12641687Smax.romanov@nginx.com static void 12651687Smax.romanov@nginx.com nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c) 12661687Smax.romanov@nginx.com { 12671687Smax.romanov@nginx.com uint32_t i; 12681687Smax.romanov@nginx.com nxt_ruby_ctx_t *rctx; 12691687Smax.romanov@nginx.com 12701687Smax.romanov@nginx.com if (nxt_ruby_ctxs == NULL) { 12711687Smax.romanov@nginx.com return; 12721687Smax.romanov@nginx.com } 12731687Smax.romanov@nginx.com 12741691Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 12751687Smax.romanov@nginx.com rctx = &nxt_ruby_ctxs[i]; 12761687Smax.romanov@nginx.com 12771687Smax.romanov@nginx.com if (rctx->thread != Qnil) { 12781687Smax.romanov@nginx.com rb_funcall(rctx->thread, rb_intern("join"), 0); 12791687Smax.romanov@nginx.com 12801687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1)); 12811687Smax.romanov@nginx.com 12821687Smax.romanov@nginx.com } else { 12831687Smax.romanov@nginx.com nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1)); 12841687Smax.romanov@nginx.com } 12851691Smax.romanov@nginx.com } 12861687Smax.romanov@nginx.com 12871691Smax.romanov@nginx.com for (i = 0; i < c->threads - 1; i++) { 12881691Smax.romanov@nginx.com nxt_ruby_ctx_done(&nxt_ruby_ctxs[i]); 12891687Smax.romanov@nginx.com } 12901687Smax.romanov@nginx.com 12911687Smax.romanov@nginx.com nxt_unit_free(ctx, nxt_ruby_ctxs); 12921687Smax.romanov@nginx.com } 1293