xref: /unit/src/ruby/nxt_ruby.c (revision 2609:f2a0806125dc)
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);
321910So.canty@f5.com 
331910So.canty@f5.com static VALUE nxt_ruby_hook_procs_load(VALUE path);
341910So.canty@f5.com static VALUE nxt_ruby_hook_register(VALUE arg);
351910So.canty@f5.com static VALUE nxt_ruby_hook_call(VALUE name);
361910So.canty@f5.com 
37584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
38584Salexander.borisov@nginx.com 
39584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rubygems(VALUE arg);
40717Salexander.borisov@nginx.com static VALUE nxt_ruby_bundler_setup(VALUE arg);
41584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rack(VALUE arg);
42584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
43584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_env_create(VALUE arg);
441687Smax.romanov@nginx.com static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx);
45743Smax.romanov@nginx.com static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
461687Smax.romanov@nginx.com static void *nxt_ruby_request_handler_gvl(void *req);
471687Smax.romanov@nginx.com static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx);
481738Smax.romanov@nginx.com static void *nxt_ruby_thread_create_gvl(void *rctx);
491687Smax.romanov@nginx.com static VALUE nxt_ruby_thread_func(VALUE arg);
501687Smax.romanov@nginx.com static void *nxt_ruby_unit_run(void *ctx);
511687Smax.romanov@nginx.com static void nxt_ruby_ubf(void *ctx);
522163Salx.manpages@gmail.com static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c);
531687Smax.romanov@nginx.com static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx,
541687Smax.romanov@nginx.com     nxt_ruby_app_conf_t *c);
55584Salexander.borisov@nginx.com 
56584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_app_run(VALUE arg);
571687Smax.romanov@nginx.com static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env);
581686Smax.romanov@nginx.com nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name,
591686Smax.romanov@nginx.com     nxt_unit_sptr_t *sptr, uint32_t len);
601687Smax.romanov@nginx.com static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req,
611687Smax.romanov@nginx.com     VALUE result);
621687Smax.romanov@nginx.com static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req,
631687Smax.romanov@nginx.com     VALUE result, nxt_int_t status);
64743Smax.romanov@nginx.com static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg);
65743Smax.romanov@nginx.com static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg);
661687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req,
671687Smax.romanov@nginx.com     VALUE result);
681687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req,
691687Smax.romanov@nginx.com     VALUE filepath);
701687Smax.romanov@nginx.com static void *nxt_ruby_response_write_cb(void *read_info);
711337Smax.romanov@nginx.com static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg,
721337Smax.romanov@nginx.com     int argc, const VALUE *argv, VALUE blockarg);
731687Smax.romanov@nginx.com static void *nxt_ruby_response_write(void *body);
74584Salexander.borisov@nginx.com 
751687Smax.romanov@nginx.com static void nxt_ruby_exception_log(nxt_unit_request_info_t *req,
761687Smax.romanov@nginx.com     uint32_t level, const char *desc);
77584Salexander.borisov@nginx.com 
781687Smax.romanov@nginx.com static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx);
79743Smax.romanov@nginx.com static void nxt_ruby_atexit(void);
80584Salexander.borisov@nginx.com 
81584Salexander.borisov@nginx.com 
82584Salexander.borisov@nginx.com static uint32_t  compat[] = {
83584Salexander.borisov@nginx.com     NXT_VERNUM, NXT_DEBUG,
84584Salexander.borisov@nginx.com };
85584Salexander.borisov@nginx.com 
861910So.canty@f5.com static VALUE  nxt_ruby_hook_procs;
871687Smax.romanov@nginx.com static VALUE  nxt_ruby_rackup;
881687Smax.romanov@nginx.com static VALUE  nxt_ruby_call;
891687Smax.romanov@nginx.com 
901687Smax.romanov@nginx.com static uint32_t        nxt_ruby_threads;
911687Smax.romanov@nginx.com static nxt_ruby_ctx_t  *nxt_ruby_ctxs;
92584Salexander.borisov@nginx.com 
93743Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t  nxt_app_module = {
94584Salexander.borisov@nginx.com     sizeof(compat),
95584Salexander.borisov@nginx.com     compat,
96584Salexander.borisov@nginx.com     nxt_string("ruby"),
97612Salexander.borisov@nginx.com     ruby_version,
981489St.nateldemoura@f5.com     nxt_ruby_mounts,
991489St.nateldemoura@f5.com     nxt_nitems(nxt_ruby_mounts),
100977Smax.romanov@gmail.com     NULL,
1011488St.nateldemoura@f5.com     nxt_ruby_start,
102584Salexander.borisov@nginx.com };
103584Salexander.borisov@nginx.com 
1041686Smax.romanov@nginx.com typedef struct {
1051686Smax.romanov@nginx.com     nxt_str_t  string;
1061686Smax.romanov@nginx.com     VALUE      *v;
1071686Smax.romanov@nginx.com } nxt_ruby_string_t;
1081686Smax.romanov@nginx.com 
1091686Smax.romanov@nginx.com static VALUE  nxt_rb_80_str;
1101686Smax.romanov@nginx.com static VALUE  nxt_rb_content_length_str;
1111686Smax.romanov@nginx.com static VALUE  nxt_rb_content_type_str;
1121686Smax.romanov@nginx.com static VALUE  nxt_rb_http_str;
1131686Smax.romanov@nginx.com static VALUE  nxt_rb_https_str;
1141686Smax.romanov@nginx.com static VALUE  nxt_rb_path_info_str;
1151686Smax.romanov@nginx.com static VALUE  nxt_rb_query_string_str;
1161686Smax.romanov@nginx.com static VALUE  nxt_rb_rack_url_scheme_str;
1171686Smax.romanov@nginx.com static VALUE  nxt_rb_remote_addr_str;
1181686Smax.romanov@nginx.com static VALUE  nxt_rb_request_method_str;
1191686Smax.romanov@nginx.com static VALUE  nxt_rb_request_uri_str;
1201686Smax.romanov@nginx.com static VALUE  nxt_rb_server_addr_str;
1211686Smax.romanov@nginx.com static VALUE  nxt_rb_server_name_str;
1221686Smax.romanov@nginx.com static VALUE  nxt_rb_server_port_str;
1231686Smax.romanov@nginx.com static VALUE  nxt_rb_server_protocol_str;
1241910So.canty@f5.com static VALUE  nxt_rb_on_worker_boot;
1251910So.canty@f5.com static VALUE  nxt_rb_on_worker_shutdown;
1261910So.canty@f5.com static VALUE  nxt_rb_on_thread_boot;
1271910So.canty@f5.com static VALUE  nxt_rb_on_thread_shutdown;
1281686Smax.romanov@nginx.com 
1291686Smax.romanov@nginx.com static nxt_ruby_string_t nxt_rb_strings[] = {
1301686Smax.romanov@nginx.com     { nxt_string("80"), &nxt_rb_80_str },
1311686Smax.romanov@nginx.com     { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str },
1321686Smax.romanov@nginx.com     { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str },
1331686Smax.romanov@nginx.com     { nxt_string("http"), &nxt_rb_http_str },
1341686Smax.romanov@nginx.com     { nxt_string("https"), &nxt_rb_https_str },
1351686Smax.romanov@nginx.com     { nxt_string("PATH_INFO"), &nxt_rb_path_info_str },
1361686Smax.romanov@nginx.com     { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str },
1371686Smax.romanov@nginx.com     { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str },
1381686Smax.romanov@nginx.com     { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str },
1391686Smax.romanov@nginx.com     { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str },
1401686Smax.romanov@nginx.com     { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str },
1411686Smax.romanov@nginx.com     { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str },
1421686Smax.romanov@nginx.com     { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str },
1431686Smax.romanov@nginx.com     { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str },
1441686Smax.romanov@nginx.com     { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str },
1451910So.canty@f5.com     { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot },
1461910So.canty@f5.com     { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown },
1471910So.canty@f5.com     { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot },
1481910So.canty@f5.com     { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown },
1491686Smax.romanov@nginx.com     { nxt_null_string, NULL },
1501686Smax.romanov@nginx.com };
1511686Smax.romanov@nginx.com 
1521686Smax.romanov@nginx.com 
1531686Smax.romanov@nginx.com static int
nxt_ruby_init_strings(void)1541686Smax.romanov@nginx.com nxt_ruby_init_strings(void)
1551686Smax.romanov@nginx.com {
1561686Smax.romanov@nginx.com     VALUE              v;
1571686Smax.romanov@nginx.com     nxt_ruby_string_t  *pstr;
1581686Smax.romanov@nginx.com 
1591686Smax.romanov@nginx.com     pstr = nxt_rb_strings;
1601686Smax.romanov@nginx.com 
1611686Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
1621686Smax.romanov@nginx.com         v = rb_str_new_static((char *) pstr->string.start, pstr->string.length);
1631686Smax.romanov@nginx.com 
1641686Smax.romanov@nginx.com         if (nxt_slow_path(v == Qnil)) {
1651686Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'",
1661686Smax.romanov@nginx.com                            (int) pstr->string.length,
1671686Smax.romanov@nginx.com                            (char *) pstr->string.start);
1681686Smax.romanov@nginx.com 
1691686Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
1701686Smax.romanov@nginx.com         }
1711686Smax.romanov@nginx.com 
1721686Smax.romanov@nginx.com         *pstr->v = v;
1731686Smax.romanov@nginx.com 
1741686Smax.romanov@nginx.com         rb_gc_register_address(pstr->v);
1751686Smax.romanov@nginx.com 
1761686Smax.romanov@nginx.com         pstr++;
1771686Smax.romanov@nginx.com     }
1781686Smax.romanov@nginx.com 
1791686Smax.romanov@nginx.com     return NXT_UNIT_OK;
1801686Smax.romanov@nginx.com }
1811686Smax.romanov@nginx.com 
1821686Smax.romanov@nginx.com 
1831686Smax.romanov@nginx.com static void
nxt_ruby_done_strings(void)1841686Smax.romanov@nginx.com nxt_ruby_done_strings(void)
1851686Smax.romanov@nginx.com {
1861686Smax.romanov@nginx.com     nxt_ruby_string_t  *pstr;
1871686Smax.romanov@nginx.com 
1881686Smax.romanov@nginx.com     pstr = nxt_rb_strings;
1891686Smax.romanov@nginx.com 
1901686Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
1911686Smax.romanov@nginx.com         rb_gc_unregister_address(pstr->v);
1921686Smax.romanov@nginx.com 
1931686Smax.romanov@nginx.com         *pstr->v = Qnil;
1941686Smax.romanov@nginx.com 
1951686Smax.romanov@nginx.com         pstr++;
1961686Smax.romanov@nginx.com     }
1971686Smax.romanov@nginx.com }
1981686Smax.romanov@nginx.com 
199584Salexander.borisov@nginx.com 
2001910So.canty@f5.com static VALUE
nxt_ruby_hook_procs_load(VALUE path)2011910So.canty@f5.com nxt_ruby_hook_procs_load(VALUE path)
2021910So.canty@f5.com {
2031910So.canty@f5.com     VALUE  module, file, file_obj;
2041910So.canty@f5.com 
2051910So.canty@f5.com     module = rb_define_module("Unit");
2061910So.canty@f5.com 
2071910So.canty@f5.com     nxt_ruby_hook_procs = rb_hash_new();
2081910So.canty@f5.com 
2091910So.canty@f5.com     rb_gc_register_address(&nxt_ruby_hook_procs);
2101910So.canty@f5.com 
2111910So.canty@f5.com     rb_define_module_function(module, "on_worker_boot",
2121910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2131910So.canty@f5.com     rb_define_module_function(module, "on_worker_shutdown",
2141910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2151910So.canty@f5.com     rb_define_module_function(module, "on_thread_boot",
2161910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2171910So.canty@f5.com     rb_define_module_function(module, "on_thread_shutdown",
2181910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2191910So.canty@f5.com 
2201910So.canty@f5.com     file = rb_const_get(rb_cObject, rb_intern("File"));
2211910So.canty@f5.com     file_obj = rb_funcall(file, rb_intern("read"), 1, path);
2221910So.canty@f5.com 
2231910So.canty@f5.com     return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path,
2241910So.canty@f5.com                       INT2NUM(1));
2251910So.canty@f5.com }
2261910So.canty@f5.com 
2271910So.canty@f5.com 
2281910So.canty@f5.com static VALUE
nxt_ruby_hook_register(VALUE arg)2291910So.canty@f5.com nxt_ruby_hook_register(VALUE arg)
2301910So.canty@f5.com {
2311910So.canty@f5.com     VALUE  kernel, callee, callee_str;
2321910So.canty@f5.com 
2331910So.canty@f5.com     rb_need_block();
2341910So.canty@f5.com 
2351910So.canty@f5.com     kernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
2361910So.canty@f5.com     callee = rb_funcall(kernel, rb_intern("__callee__"), 0);
2371910So.canty@f5.com     callee_str = rb_funcall(callee, rb_intern("to_s"), 0);
2381910So.canty@f5.com 
2391910So.canty@f5.com     rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc());
2401910So.canty@f5.com 
2411910So.canty@f5.com     return Qnil;
2421910So.canty@f5.com }
2431910So.canty@f5.com 
2441910So.canty@f5.com 
2451910So.canty@f5.com static VALUE
nxt_ruby_hook_call(VALUE name)2461910So.canty@f5.com nxt_ruby_hook_call(VALUE name)
2471910So.canty@f5.com {
2481910So.canty@f5.com     VALUE  proc;
2491910So.canty@f5.com 
2501910So.canty@f5.com     proc = rb_hash_lookup(nxt_ruby_hook_procs, name);
2511910So.canty@f5.com     if (proc == Qnil) {
2521910So.canty@f5.com         return Qnil;
2531910So.canty@f5.com     }
2541910So.canty@f5.com 
2551910So.canty@f5.com     return rb_funcall(proc, rb_intern("call"), 0);
2561910So.canty@f5.com }
2571910So.canty@f5.com 
2581910So.canty@f5.com 
259584Salexander.borisov@nginx.com static nxt_int_t
nxt_ruby_start(nxt_task_t * task,nxt_process_data_t * data)2601488St.nateldemoura@f5.com nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
261584Salexander.borisov@nginx.com {
2621488St.nateldemoura@f5.com     int                    state, rc;
2632163Salx.manpages@gmail.com     VALUE                  res, path;
2641687Smax.romanov@nginx.com     nxt_ruby_ctx_t         ruby_ctx;
2651488St.nateldemoura@f5.com     nxt_unit_ctx_t         *unit_ctx;
2661488St.nateldemoura@f5.com     nxt_unit_init_t        ruby_unit_init;
2671687Smax.romanov@nginx.com     nxt_ruby_app_conf_t    *c;
2681488St.nateldemoura@f5.com     nxt_ruby_rack_init_t   rack_init;
2691488St.nateldemoura@f5.com     nxt_common_app_conf_t  *conf;
270584Salexander.borisov@nginx.com 
2711258Smax.romanov@nginx.com     static char  *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" };
2721258Smax.romanov@nginx.com 
2732183Sa.clayton@f5.com     signal(SIGINT, SIG_IGN);
2742183Sa.clayton@f5.com 
2751488St.nateldemoura@f5.com     conf = data->app;
2761687Smax.romanov@nginx.com     c = &conf->u.ruby;
2771687Smax.romanov@nginx.com 
2781687Smax.romanov@nginx.com     nxt_ruby_threads = c->threads;
2791488St.nateldemoura@f5.com 
2801814Svbart@nginx.com     setlocale(LC_CTYPE, "");
2811814Svbart@nginx.com 
2821258Smax.romanov@nginx.com     RUBY_INIT_STACK
283584Salexander.borisov@nginx.com     ruby_init();
2841258Smax.romanov@nginx.com     ruby_options(2, argv);
285584Salexander.borisov@nginx.com     ruby_script("NGINX_Unit");
286584Salexander.borisov@nginx.com 
2871687Smax.romanov@nginx.com     ruby_ctx.env = Qnil;
2881687Smax.romanov@nginx.com     ruby_ctx.io_input = Qnil;
2891687Smax.romanov@nginx.com     ruby_ctx.io_error = Qnil;
2901687Smax.romanov@nginx.com     ruby_ctx.thread = Qnil;
2911687Smax.romanov@nginx.com     ruby_ctx.ctx = NULL;
2921687Smax.romanov@nginx.com     ruby_ctx.req = NULL;
2931687Smax.romanov@nginx.com 
294584Salexander.borisov@nginx.com     rack_init.task = task;
2951687Smax.romanov@nginx.com     rack_init.script = &c->script;
2961687Smax.romanov@nginx.com     rack_init.rctx = &ruby_ctx;
297584Salexander.borisov@nginx.com 
2981686Smax.romanov@nginx.com     nxt_ruby_init_strings();
2991686Smax.romanov@nginx.com 
300584Salexander.borisov@nginx.com     res = rb_protect(nxt_ruby_init_basic,
301584Salexander.borisov@nginx.com                      (VALUE) (uintptr_t) &rack_init, &state);
302584Salexander.borisov@nginx.com     if (nxt_slow_path(res == Qnil || state != 0)) {
3031687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
304584Salexander.borisov@nginx.com                                "Failed to init basic variables");
305584Salexander.borisov@nginx.com         return NXT_ERROR;
306584Salexander.borisov@nginx.com     }
307584Salexander.borisov@nginx.com 
3081687Smax.romanov@nginx.com     nxt_ruby_call = Qnil;
3091910So.canty@f5.com     nxt_ruby_hook_procs = Qnil;
3101910So.canty@f5.com 
3111910So.canty@f5.com     if (c->hooks.start != NULL) {
3121910So.canty@f5.com         path = rb_str_new((const char *) c->hooks.start,
3131910So.canty@f5.com                           (long) c->hooks.length);
3141910So.canty@f5.com 
3151910So.canty@f5.com         rb_protect(nxt_ruby_hook_procs_load, path, &state);
3161910So.canty@f5.com         rb_str_free(path);
3171910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3181910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
3191910So.canty@f5.com                                    "Failed to setup hooks");
3201910So.canty@f5.com             return NXT_ERROR;
3211910So.canty@f5.com         }
3221910So.canty@f5.com     }
3231910So.canty@f5.com 
3241910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3251910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state);
3261910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3271910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3281910So.canty@f5.com                                    "Failed to call on_worker_boot()");
3291910So.canty@f5.com             return NXT_ERROR;
3301910So.canty@f5.com         }
3311910So.canty@f5.com     }
3321687Smax.romanov@nginx.com 
333584Salexander.borisov@nginx.com     nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init);
334584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_rackup == Qnil)) {
335584Salexander.borisov@nginx.com         return NXT_ERROR;
336584Salexander.borisov@nginx.com     }
337584Salexander.borisov@nginx.com 
3381687Smax.romanov@nginx.com     rb_gc_register_address(&nxt_ruby_rackup);
3391687Smax.romanov@nginx.com 
340584Salexander.borisov@nginx.com     nxt_ruby_call = rb_intern("call");
341584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_call == Qnil)) {
342584Salexander.borisov@nginx.com         nxt_alert(task, "Ruby: Unable to find rack entry point");
343584Salexander.borisov@nginx.com 
3441687Smax.romanov@nginx.com         goto fail;
345584Salexander.borisov@nginx.com     }
346584Salexander.borisov@nginx.com 
3471687Smax.romanov@nginx.com     rb_gc_register_address(&nxt_ruby_call);
3481687Smax.romanov@nginx.com 
3491687Smax.romanov@nginx.com     ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create,
3501687Smax.romanov@nginx.com                               (VALUE) (uintptr_t) &ruby_ctx, &state);
3511687Smax.romanov@nginx.com     if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) {
3521687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
353584Salexander.borisov@nginx.com                                "Failed to create 'environ' variable");
3541687Smax.romanov@nginx.com         goto fail;
355584Salexander.borisov@nginx.com     }
356584Salexander.borisov@nginx.com 
3572163Salx.manpages@gmail.com     rc = nxt_ruby_init_threads(c);
3581687Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
3591687Smax.romanov@nginx.com         goto fail;
3601687Smax.romanov@nginx.com     }
361584Salexander.borisov@nginx.com 
3621980Smax.romanov@nginx.com     nxt_unit_default_init(task, &ruby_unit_init, conf);
363743Smax.romanov@nginx.com 
364743Smax.romanov@nginx.com     ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler;
3651687Smax.romanov@nginx.com     ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler;
3661687Smax.romanov@nginx.com     ruby_unit_init.data = c;
3671687Smax.romanov@nginx.com     ruby_unit_init.ctx_data = &ruby_ctx;
368743Smax.romanov@nginx.com 
369743Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&ruby_unit_init);
370743Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
3711687Smax.romanov@nginx.com         goto fail;
372743Smax.romanov@nginx.com     }
373743Smax.romanov@nginx.com 
3741910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3751910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state);
3761910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3771910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3781910So.canty@f5.com                                    "Failed to call on_thread_boot()");
3791910So.canty@f5.com         }
3801910So.canty@f5.com     }
3811910So.canty@f5.com 
3822164Sz.hong@f5.com     rc = (intptr_t) rb_thread_call_without_gvl2(nxt_ruby_unit_run, unit_ctx,
3832164Sz.hong@f5.com                                                 nxt_ruby_ubf, unit_ctx);
384743Smax.romanov@nginx.com 
3851910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3861910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state);
3871910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3881910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3891910So.canty@f5.com                                    "Failed to call on_thread_shutdown()");
3901910So.canty@f5.com         }
3911910So.canty@f5.com     }
3921910So.canty@f5.com 
3931687Smax.romanov@nginx.com     nxt_ruby_join_threads(unit_ctx, c);
3941687Smax.romanov@nginx.com 
3951910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3961910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state);
3971910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3981910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3991910So.canty@f5.com                                    "Failed to call on_worker_shutdown()");
4001910So.canty@f5.com         }
4011910So.canty@f5.com     }
4021910So.canty@f5.com 
4031687Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
4041687Smax.romanov@nginx.com 
4051687Smax.romanov@nginx.com     nxt_ruby_ctx_done(&ruby_ctx);
406743Smax.romanov@nginx.com 
407743Smax.romanov@nginx.com     nxt_ruby_atexit();
408743Smax.romanov@nginx.com 
409743Smax.romanov@nginx.com     exit(rc);
410743Smax.romanov@nginx.com 
411584Salexander.borisov@nginx.com     return NXT_OK;
4121687Smax.romanov@nginx.com 
4131687Smax.romanov@nginx.com fail:
4141687Smax.romanov@nginx.com 
4151687Smax.romanov@nginx.com     nxt_ruby_join_threads(NULL, c);
4161687Smax.romanov@nginx.com 
4171687Smax.romanov@nginx.com     nxt_ruby_ctx_done(&ruby_ctx);
4181687Smax.romanov@nginx.com 
4191687Smax.romanov@nginx.com     nxt_ruby_atexit();
4201687Smax.romanov@nginx.com 
4211687Smax.romanov@nginx.com     return NXT_ERROR;
422584Salexander.borisov@nginx.com }
423584Salexander.borisov@nginx.com 
424584Salexander.borisov@nginx.com 
425584Salexander.borisov@nginx.com static VALUE
nxt_ruby_init_basic(VALUE arg)426584Salexander.borisov@nginx.com nxt_ruby_init_basic(VALUE arg)
427584Salexander.borisov@nginx.com {
428584Salexander.borisov@nginx.com     int                   state;
429584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
430584Salexander.borisov@nginx.com 
431584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg;
432584Salexander.borisov@nginx.com 
433584Salexander.borisov@nginx.com     state = rb_enc_find_index("encdb");
434584Salexander.borisov@nginx.com     if (nxt_slow_path(state == 0)) {
435584Salexander.borisov@nginx.com         nxt_alert(rack_init->task,
436584Salexander.borisov@nginx.com                   "Ruby: Failed to find encoding index 'encdb'");
437584Salexander.borisov@nginx.com 
438584Salexander.borisov@nginx.com         return Qnil;
439584Salexander.borisov@nginx.com     }
440584Salexander.borisov@nginx.com 
441609Salexander.borisov@nginx.com     rb_funcall(rb_cObject, rb_intern("require"), 1,
442609Salexander.borisov@nginx.com                rb_str_new2("enc/trans/transdb"));
443609Salexander.borisov@nginx.com 
444584Salexander.borisov@nginx.com     return arg;
445584Salexander.borisov@nginx.com }
446584Salexander.borisov@nginx.com 
447584Salexander.borisov@nginx.com 
448584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_init(nxt_ruby_rack_init_t * rack_init)449584Salexander.borisov@nginx.com nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
450584Salexander.borisov@nginx.com {
451584Salexander.borisov@nginx.com     int    state;
4521687Smax.romanov@nginx.com     VALUE  rackup, err;
453584Salexander.borisov@nginx.com 
454584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rubygems, Qnil, &state);
455584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
4561687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
457584Salexander.borisov@nginx.com                                "Failed to require 'rubygems' package");
458584Salexander.borisov@nginx.com         return Qnil;
459584Salexander.borisov@nginx.com     }
460584Salexander.borisov@nginx.com 
461717Salexander.borisov@nginx.com     rb_protect(nxt_ruby_bundler_setup, Qnil, &state);
462717Salexander.borisov@nginx.com     if (state != 0) {
463717Salexander.borisov@nginx.com         err = rb_errinfo();
464717Salexander.borisov@nginx.com 
465717Salexander.borisov@nginx.com         if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) {
4661687Smax.romanov@nginx.com             nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
467717Salexander.borisov@nginx.com                                    "Failed to require 'bundler/setup' package");
468717Salexander.borisov@nginx.com             return Qnil;
469717Salexander.borisov@nginx.com         }
470717Salexander.borisov@nginx.com 
471717Salexander.borisov@nginx.com         rb_set_errinfo(Qnil);
472717Salexander.borisov@nginx.com     }
473717Salexander.borisov@nginx.com 
474584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rack, Qnil, &state);
475584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
4761687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
477584Salexander.borisov@nginx.com                                "Failed to require 'rack' package");
478584Salexander.borisov@nginx.com         return Qnil;
479584Salexander.borisov@nginx.com     }
480584Salexander.borisov@nginx.com 
481584Salexander.borisov@nginx.com     rackup = rb_protect(nxt_ruby_rack_parse_script,
482584Salexander.borisov@nginx.com                         (VALUE) (uintptr_t) rack_init, &state);
4832209Sz.hong@f5.com 
4842209Sz.hong@f5.com     if (nxt_slow_path(state != 0)) {
4851687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
486584Salexander.borisov@nginx.com                                "Failed to parse rack script");
487584Salexander.borisov@nginx.com         return Qnil;
488584Salexander.borisov@nginx.com     }
489584Salexander.borisov@nginx.com 
4902209Sz.hong@f5.com     if (TYPE(rackup) != T_ARRAY) {
4912209Sz.hong@f5.com         return rackup;
4922209Sz.hong@f5.com     }
4932209Sz.hong@f5.com 
494584Salexander.borisov@nginx.com     if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) {
4952210Sa.clayton@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, "Invalid rack config file");
496584Salexander.borisov@nginx.com         return Qnil;
497584Salexander.borisov@nginx.com     }
498584Salexander.borisov@nginx.com 
499584Salexander.borisov@nginx.com     return RARRAY_PTR(rackup)[0];
500584Salexander.borisov@nginx.com }
501584Salexander.borisov@nginx.com 
502584Salexander.borisov@nginx.com 
503584Salexander.borisov@nginx.com static VALUE
nxt_ruby_require_rubygems(VALUE arg)504584Salexander.borisov@nginx.com nxt_ruby_require_rubygems(VALUE arg)
505584Salexander.borisov@nginx.com {
506584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1,
507584Salexander.borisov@nginx.com                       rb_str_new2("rubygems"));
508584Salexander.borisov@nginx.com }
509584Salexander.borisov@nginx.com 
510584Salexander.borisov@nginx.com 
511584Salexander.borisov@nginx.com static VALUE
nxt_ruby_bundler_setup(VALUE arg)512717Salexander.borisov@nginx.com nxt_ruby_bundler_setup(VALUE arg)
513717Salexander.borisov@nginx.com {
514717Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1,
515717Salexander.borisov@nginx.com                       rb_str_new2("bundler/setup"));
516717Salexander.borisov@nginx.com }
517717Salexander.borisov@nginx.com 
518717Salexander.borisov@nginx.com 
519717Salexander.borisov@nginx.com static VALUE
nxt_ruby_require_rack(VALUE arg)520584Salexander.borisov@nginx.com nxt_ruby_require_rack(VALUE arg)
521584Salexander.borisov@nginx.com {
522584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack"));
523584Salexander.borisov@nginx.com }
524584Salexander.borisov@nginx.com 
525584Salexander.borisov@nginx.com 
526584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_parse_script(VALUE ctx)527584Salexander.borisov@nginx.com nxt_ruby_rack_parse_script(VALUE ctx)
528584Salexander.borisov@nginx.com {
5291687Smax.romanov@nginx.com     VALUE                 script, res, rack, builder;
530584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
531584Salexander.borisov@nginx.com 
532584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx;
533584Salexander.borisov@nginx.com 
5341687Smax.romanov@nginx.com     rack = rb_const_get(rb_cObject, rb_intern("Rack"));
5351687Smax.romanov@nginx.com     builder = rb_const_get(rack, rb_intern("Builder"));
5361687Smax.romanov@nginx.com 
537584Salexander.borisov@nginx.com     script = rb_str_new((const char *) rack_init->script->start,
538584Salexander.borisov@nginx.com                         (long) rack_init->script->length);
539584Salexander.borisov@nginx.com 
5401687Smax.romanov@nginx.com     res = rb_funcall(builder, rb_intern("parse_file"), 1, script);
541584Salexander.borisov@nginx.com 
542584Salexander.borisov@nginx.com     rb_str_free(script);
543584Salexander.borisov@nginx.com 
544584Salexander.borisov@nginx.com     return res;
545584Salexander.borisov@nginx.com }
546584Salexander.borisov@nginx.com 
547584Salexander.borisov@nginx.com 
548584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_env_create(VALUE arg)549584Salexander.borisov@nginx.com nxt_ruby_rack_env_create(VALUE arg)
550584Salexander.borisov@nginx.com {
5511687Smax.romanov@nginx.com     int             rc;
5521687Smax.romanov@nginx.com     VALUE           hash_env, version;
5531687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
5541687Smax.romanov@nginx.com 
5551687Smax.romanov@nginx.com     rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg;
5561687Smax.romanov@nginx.com 
5571687Smax.romanov@nginx.com     rc = nxt_ruby_init_io(rctx);
5581687Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
5591687Smax.romanov@nginx.com         return Qnil;
5601687Smax.romanov@nginx.com     }
561584Salexander.borisov@nginx.com 
562584Salexander.borisov@nginx.com     hash_env = rb_hash_new();
563673Svbart@nginx.com 
564673Svbart@nginx.com     rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"),
565673Svbart@nginx.com                  rb_str_new((const char *) nxt_server.start,
566673Svbart@nginx.com                             (long) nxt_server.length));
567673Svbart@nginx.com 
568584Salexander.borisov@nginx.com     version = rb_ary_new();
569584Salexander.borisov@nginx.com 
570584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR));
571584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
572584Salexander.borisov@nginx.com 
5732163Salx.manpages@gmail.com     rb_hash_aset(hash_env, rb_str_new2("SCRIPT_NAME"), rb_str_new("", 0));
574584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.version"), version);
5751687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input);
5761687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error);
5771687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multithread"),
5781687Smax.romanov@nginx.com                  nxt_ruby_threads > 1 ? Qtrue : Qfalse);
579584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue);
580584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse);
581584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse);
582584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil);
583584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil);
584584Salexander.borisov@nginx.com 
5851687Smax.romanov@nginx.com     rctx->env = hash_env;
5861687Smax.romanov@nginx.com 
5871687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->env);
5881687Smax.romanov@nginx.com 
589584Salexander.borisov@nginx.com     return hash_env;
590584Salexander.borisov@nginx.com }
591584Salexander.borisov@nginx.com 
592584Salexander.borisov@nginx.com 
5931687Smax.romanov@nginx.com static int
nxt_ruby_init_io(nxt_ruby_ctx_t * rctx)5941687Smax.romanov@nginx.com nxt_ruby_init_io(nxt_ruby_ctx_t *rctx)
5951687Smax.romanov@nginx.com {
5961687Smax.romanov@nginx.com     VALUE  io_input, io_error;
5971687Smax.romanov@nginx.com 
5981687Smax.romanov@nginx.com     io_input = nxt_ruby_stream_io_input_init();
5991687Smax.romanov@nginx.com 
6001687Smax.romanov@nginx.com     rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1,
6011687Smax.romanov@nginx.com                                    (VALUE) (uintptr_t) rctx);
6021687Smax.romanov@nginx.com     if (nxt_slow_path(rctx->io_input == Qnil)) {
6031687Smax.romanov@nginx.com         nxt_unit_alert(NULL,
6041687Smax.romanov@nginx.com                        "Ruby: Failed to create environment 'rack.input' var");
6051687Smax.romanov@nginx.com 
6061687Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
6071687Smax.romanov@nginx.com     }
6081687Smax.romanov@nginx.com 
6091687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->io_input);
6101687Smax.romanov@nginx.com 
6111687Smax.romanov@nginx.com     io_error = nxt_ruby_stream_io_error_init();
6121687Smax.romanov@nginx.com 
6131687Smax.romanov@nginx.com     rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1,
6141687Smax.romanov@nginx.com                                    (VALUE) (uintptr_t) rctx);
6151687Smax.romanov@nginx.com     if (nxt_slow_path(rctx->io_error == Qnil)) {
6161687Smax.romanov@nginx.com         nxt_unit_alert(NULL,
6171687Smax.romanov@nginx.com                        "Ruby: Failed to create environment 'rack.error' var");
6181687Smax.romanov@nginx.com 
6191687Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
6201687Smax.romanov@nginx.com     }
6211687Smax.romanov@nginx.com 
6221687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->io_error);
6231687Smax.romanov@nginx.com 
6241687Smax.romanov@nginx.com     return NXT_UNIT_OK;
6251687Smax.romanov@nginx.com }
6261687Smax.romanov@nginx.com 
6271687Smax.romanov@nginx.com 
628743Smax.romanov@nginx.com static void
nxt_ruby_request_handler(nxt_unit_request_info_t * req)629743Smax.romanov@nginx.com nxt_ruby_request_handler(nxt_unit_request_info_t *req)
630584Salexander.borisov@nginx.com {
6311687Smax.romanov@nginx.com     (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req);
6321687Smax.romanov@nginx.com }
6331687Smax.romanov@nginx.com 
634584Salexander.borisov@nginx.com 
6351687Smax.romanov@nginx.com static void *
nxt_ruby_request_handler_gvl(void * data)6361687Smax.romanov@nginx.com nxt_ruby_request_handler_gvl(void *data)
6371687Smax.romanov@nginx.com {
6381687Smax.romanov@nginx.com     int                      state;
6391687Smax.romanov@nginx.com     VALUE                    res;
6401687Smax.romanov@nginx.com     nxt_ruby_ctx_t           *rctx;
6411687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
6421687Smax.romanov@nginx.com 
6431687Smax.romanov@nginx.com     req = data;
644584Salexander.borisov@nginx.com 
6451687Smax.romanov@nginx.com     rctx = req->ctx->data;
6461687Smax.romanov@nginx.com     rctx->req = req;
6471687Smax.romanov@nginx.com 
6481687Smax.romanov@nginx.com     res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state);
649743Smax.romanov@nginx.com     if (nxt_slow_path(res == Qnil || state != 0)) {
6501687Smax.romanov@nginx.com         nxt_ruby_exception_log(req, NXT_LOG_ERR,
651584Salexander.borisov@nginx.com                                "Failed to run ruby script");
6521687Smax.romanov@nginx.com 
6531687Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_ERROR);
6541687Smax.romanov@nginx.com 
6551687Smax.romanov@nginx.com     } else {
6561687Smax.romanov@nginx.com         nxt_unit_request_done(req, NXT_UNIT_OK);
657584Salexander.borisov@nginx.com     }
6581687Smax.romanov@nginx.com 
6591687Smax.romanov@nginx.com     rctx->req = NULL;
6601687Smax.romanov@nginx.com 
6611687Smax.romanov@nginx.com     return NULL;
662584Salexander.borisov@nginx.com }
663584Salexander.borisov@nginx.com 
664584Salexander.borisov@nginx.com 
665584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_app_run(VALUE arg)666584Salexander.borisov@nginx.com nxt_ruby_rack_app_run(VALUE arg)
667584Salexander.borisov@nginx.com {
6681687Smax.romanov@nginx.com     int                      rc;
6691687Smax.romanov@nginx.com     VALUE                    env, result;
6701687Smax.romanov@nginx.com     nxt_int_t                status;
6711687Smax.romanov@nginx.com     nxt_ruby_ctx_t           *rctx;
6721687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
6731687Smax.romanov@nginx.com 
6741687Smax.romanov@nginx.com     req = (nxt_unit_request_info_t *) arg;
675584Salexander.borisov@nginx.com 
6761687Smax.romanov@nginx.com     rctx = req->ctx->data;
6771687Smax.romanov@nginx.com 
6781687Smax.romanov@nginx.com     env = rb_hash_dup(rctx->env);
679584Salexander.borisov@nginx.com 
6801687Smax.romanov@nginx.com     rc = nxt_ruby_read_request(req, env);
681743Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
6821687Smax.romanov@nginx.com         nxt_unit_req_alert(req,
683743Smax.romanov@nginx.com                            "Ruby: Failed to process incoming request");
684584Salexander.borisov@nginx.com 
685584Salexander.borisov@nginx.com         goto fail;
686584Salexander.borisov@nginx.com     }
687584Salexander.borisov@nginx.com 
688584Salexander.borisov@nginx.com     result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env);
689584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(result) != T_ARRAY)) {
6901687Smax.romanov@nginx.com         nxt_unit_req_error(req,
691743Smax.romanov@nginx.com                            "Ruby: Invalid response format from application");
692584Salexander.borisov@nginx.com 
693584Salexander.borisov@nginx.com         goto fail;
694584Salexander.borisov@nginx.com     }
695584Salexander.borisov@nginx.com 
696584Salexander.borisov@nginx.com     if (nxt_slow_path(RARRAY_LEN(result) != 3)) {
6971687Smax.romanov@nginx.com         nxt_unit_req_error(req,
698743Smax.romanov@nginx.com                            "Ruby: Invalid response format from application. "
699743Smax.romanov@nginx.com                            "Need 3 entries [Status, Headers, Body]");
700584Salexander.borisov@nginx.com 
701584Salexander.borisov@nginx.com         goto fail;
702584Salexander.borisov@nginx.com     }
703584Salexander.borisov@nginx.com 
7041687Smax.romanov@nginx.com     status = nxt_ruby_rack_result_status(req, result);
705743Smax.romanov@nginx.com     if (nxt_slow_path(status < 0)) {
7061687Smax.romanov@nginx.com         nxt_unit_req_error(req,
707743Smax.romanov@nginx.com                            "Ruby: Invalid response status from application.");
708743Smax.romanov@nginx.com 
709584Salexander.borisov@nginx.com         goto fail;
710584Salexander.borisov@nginx.com     }
711584Salexander.borisov@nginx.com 
7121687Smax.romanov@nginx.com     rc = nxt_ruby_rack_result_headers(req, result, status);
713743Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
714584Salexander.borisov@nginx.com         goto fail;
715584Salexander.borisov@nginx.com     }
716584Salexander.borisov@nginx.com 
7171687Smax.romanov@nginx.com     rc = nxt_ruby_rack_result_body(req, result);
718743Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
719584Salexander.borisov@nginx.com         goto fail;
720584Salexander.borisov@nginx.com     }
721584Salexander.borisov@nginx.com 
722584Salexander.borisov@nginx.com     rb_hash_delete(env, rb_obj_id(env));
723584Salexander.borisov@nginx.com 
724584Salexander.borisov@nginx.com     return result;
725584Salexander.borisov@nginx.com 
726584Salexander.borisov@nginx.com fail:
727584Salexander.borisov@nginx.com 
728584Salexander.borisov@nginx.com     rb_hash_delete(env, rb_obj_id(env));
729584Salexander.borisov@nginx.com 
730584Salexander.borisov@nginx.com     return Qnil;
731584Salexander.borisov@nginx.com }
732584Salexander.borisov@nginx.com 
733584Salexander.borisov@nginx.com 
734743Smax.romanov@nginx.com static int
nxt_ruby_read_request(nxt_unit_request_info_t * req,VALUE hash_env)7351687Smax.romanov@nginx.com nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env)
736584Salexander.borisov@nginx.com {
7371686Smax.romanov@nginx.com     VALUE               name;
738967Svbart@nginx.com     uint32_t            i;
739743Smax.romanov@nginx.com     nxt_unit_field_t    *f;
740743Smax.romanov@nginx.com     nxt_unit_request_t  *r;
741584Salexander.borisov@nginx.com 
7421687Smax.romanov@nginx.com     r = req->request;
743584Salexander.borisov@nginx.com 
7441686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method,
745743Smax.romanov@nginx.com                       r->method_length);
7461686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target,
747743Smax.romanov@nginx.com                       r->target_length);
7481686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length);
7491686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query,
750981Svbart@nginx.com                       r->query_length);
7511686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version,
752743Smax.romanov@nginx.com                       r->version_length);
7531686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote,
754743Smax.romanov@nginx.com                       r->remote_length);
7552208Sa.clayton@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local_addr,
7562208Sa.clayton@nginx.com                       r->local_addr_length);
7571686Smax.romanov@nginx.com     nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name,
7581686Smax.romanov@nginx.com                       r->server_name_length);
759584Salexander.borisov@nginx.com 
7601686Smax.romanov@nginx.com     rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str);
761967Svbart@nginx.com 
7621686Smax.romanov@nginx.com     rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str,
7631686Smax.romanov@nginx.com                  r->tls ? nxt_rb_https_str : nxt_rb_http_str);
7641011Smax.romanov@nginx.com 
765743Smax.romanov@nginx.com     for (i = 0; i < r->fields_count; i++) {
766743Smax.romanov@nginx.com         f = r->fields + i;
767584Salexander.borisov@nginx.com 
7681686Smax.romanov@nginx.com         name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length);
7691686Smax.romanov@nginx.com 
7701686Smax.romanov@nginx.com         nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length);
771584Salexander.borisov@nginx.com     }
772584Salexander.borisov@nginx.com 
773743Smax.romanov@nginx.com     if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
774743Smax.romanov@nginx.com         f = r->fields + r->content_length_field;
775584Salexander.borisov@nginx.com 
7761686Smax.romanov@nginx.com         nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str,
777743Smax.romanov@nginx.com                           &f->value, f->value_length);
778584Salexander.borisov@nginx.com     }
779584Salexander.borisov@nginx.com 
780743Smax.romanov@nginx.com     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
781743Smax.romanov@nginx.com         f = r->fields + r->content_type_field;
782584Salexander.borisov@nginx.com 
7831686Smax.romanov@nginx.com         nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str,
784743Smax.romanov@nginx.com                           &f->value, f->value_length);
785584Salexander.borisov@nginx.com     }
786584Salexander.borisov@nginx.com 
787743Smax.romanov@nginx.com     return NXT_UNIT_OK;
788584Salexander.borisov@nginx.com }
789584Salexander.borisov@nginx.com 
790584Salexander.borisov@nginx.com 
791743Smax.romanov@nginx.com nxt_inline void
nxt_ruby_add_sptr(VALUE hash_env,VALUE name,nxt_unit_sptr_t * sptr,uint32_t len)7921686Smax.romanov@nginx.com nxt_ruby_add_sptr(VALUE hash_env, VALUE name,
7931686Smax.romanov@nginx.com     nxt_unit_sptr_t *sptr, uint32_t len)
794584Salexander.borisov@nginx.com {
795743Smax.romanov@nginx.com     char  *str;
796743Smax.romanov@nginx.com 
797743Smax.romanov@nginx.com     str = nxt_unit_sptr_get(sptr);
798584Salexander.borisov@nginx.com 
7991686Smax.romanov@nginx.com     rb_hash_aset(hash_env, name, rb_str_new(str, len));
800584Salexander.borisov@nginx.com }
801584Salexander.borisov@nginx.com 
802584Salexander.borisov@nginx.com 
803584Salexander.borisov@nginx.com static nxt_int_t
nxt_ruby_rack_result_status(nxt_unit_request_info_t * req,VALUE result)8041687Smax.romanov@nginx.com nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result)
805584Salexander.borisov@nginx.com {
806743Smax.romanov@nginx.com     VALUE   status;
807584Salexander.borisov@nginx.com 
808584Salexander.borisov@nginx.com     status = rb_ary_entry(result, 0);
809584Salexander.borisov@nginx.com 
810584Salexander.borisov@nginx.com     if (TYPE(status) == T_FIXNUM) {
811743Smax.romanov@nginx.com         return FIX2INT(status);
812584Salexander.borisov@nginx.com     }
813584Salexander.borisov@nginx.com 
814743Smax.romanov@nginx.com     if (TYPE(status) == T_STRING) {
815743Smax.romanov@nginx.com         return nxt_int_parse((u_char *) RSTRING_PTR(status),
816743Smax.romanov@nginx.com                              RSTRING_LEN(status));
817584Salexander.borisov@nginx.com     }
818584Salexander.borisov@nginx.com 
8191687Smax.romanov@nginx.com     nxt_unit_req_error(req, "Ruby: Invalid response 'status' "
820743Smax.romanov@nginx.com                        "format from application");
821584Salexander.borisov@nginx.com 
822743Smax.romanov@nginx.com     return -2;
823584Salexander.borisov@nginx.com }
824584Salexander.borisov@nginx.com 
825584Salexander.borisov@nginx.com 
826743Smax.romanov@nginx.com typedef struct {
8271687Smax.romanov@nginx.com     int                      rc;
8281687Smax.romanov@nginx.com     uint32_t                 fields;
8291687Smax.romanov@nginx.com     uint32_t                 size;
8301687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
831743Smax.romanov@nginx.com } nxt_ruby_headers_info_t;
832743Smax.romanov@nginx.com 
833743Smax.romanov@nginx.com 
834743Smax.romanov@nginx.com static int
nxt_ruby_rack_result_headers(nxt_unit_request_info_t * req,VALUE result,nxt_int_t status)8351687Smax.romanov@nginx.com nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result,
8361687Smax.romanov@nginx.com     nxt_int_t status)
837584Salexander.borisov@nginx.com {
838743Smax.romanov@nginx.com     int                      rc;
839743Smax.romanov@nginx.com     VALUE                    headers;
840743Smax.romanov@nginx.com     nxt_ruby_headers_info_t  headers_info;
841743Smax.romanov@nginx.com 
842743Smax.romanov@nginx.com     headers = rb_ary_entry(result, 1);
843743Smax.romanov@nginx.com     if (nxt_slow_path(TYPE(headers) != T_HASH)) {
8441687Smax.romanov@nginx.com         nxt_unit_req_error(req,
845743Smax.romanov@nginx.com                            "Ruby: Invalid response 'headers' format from "
846743Smax.romanov@nginx.com                            "application");
847743Smax.romanov@nginx.com 
848743Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
849743Smax.romanov@nginx.com     }
850584Salexander.borisov@nginx.com 
851743Smax.romanov@nginx.com     rc = NXT_UNIT_OK;
852743Smax.romanov@nginx.com 
853743Smax.romanov@nginx.com     headers_info.rc = NXT_UNIT_OK;
854743Smax.romanov@nginx.com     headers_info.fields = 0;
855743Smax.romanov@nginx.com     headers_info.size = 0;
8561687Smax.romanov@nginx.com     headers_info.req = req;
857743Smax.romanov@nginx.com 
858743Smax.romanov@nginx.com     rb_hash_foreach(headers, nxt_ruby_hash_info,
859743Smax.romanov@nginx.com                     (VALUE) (uintptr_t) &headers_info);
860743Smax.romanov@nginx.com     if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) {
861743Smax.romanov@nginx.com         return headers_info.rc;
862743Smax.romanov@nginx.com     }
863743Smax.romanov@nginx.com 
8641687Smax.romanov@nginx.com     rc = nxt_unit_response_init(req, status,
865743Smax.romanov@nginx.com                                 headers_info.fields, headers_info.size);
866743Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
867584Salexander.borisov@nginx.com         return rc;
868584Salexander.borisov@nginx.com     }
869584Salexander.borisov@nginx.com 
8701687Smax.romanov@nginx.com     rb_hash_foreach(headers, nxt_ruby_hash_add,
8711687Smax.romanov@nginx.com                     (VALUE) (uintptr_t) &headers_info);
872584Salexander.borisov@nginx.com 
873584Salexander.borisov@nginx.com     return rc;
874584Salexander.borisov@nginx.com }
875584Salexander.borisov@nginx.com 
876584Salexander.borisov@nginx.com 
877743Smax.romanov@nginx.com static int
nxt_ruby_hash_info(VALUE r_key,VALUE r_value,VALUE arg)878743Smax.romanov@nginx.com nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg)
879584Salexander.borisov@nginx.com {
880743Smax.romanov@nginx.com     const char               *value, *value_end, *pos;
881743Smax.romanov@nginx.com     nxt_ruby_headers_info_t  *headers_info;
882584Salexander.borisov@nginx.com 
883743Smax.romanov@nginx.com     headers_info = (void *) (uintptr_t) arg;
884584Salexander.borisov@nginx.com 
885584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(r_key) != T_STRING)) {
8861687Smax.romanov@nginx.com         nxt_unit_req_error(headers_info->req,
887743Smax.romanov@nginx.com                            "Ruby: Wrong header entry 'key' from application");
888584Salexander.borisov@nginx.com 
889584Salexander.borisov@nginx.com         goto fail;
890584Salexander.borisov@nginx.com     }
891584Salexander.borisov@nginx.com 
8922608Sa.clayton@nginx.com     if (nxt_slow_path(TYPE(r_value) != T_STRING && TYPE(r_value) != T_ARRAY)) {
8931687Smax.romanov@nginx.com         nxt_unit_req_error(headers_info->req,
894743Smax.romanov@nginx.com                            "Ruby: Wrong header entry 'value' from application");
895584Salexander.borisov@nginx.com 
896584Salexander.borisov@nginx.com         goto fail;
897584Salexander.borisov@nginx.com     }
898584Salexander.borisov@nginx.com 
8992608Sa.clayton@nginx.com     if (TYPE(r_value) == T_ARRAY) {
9002608Sa.clayton@nginx.com         int     i;
9012608Sa.clayton@nginx.com         int     arr_len = RARRAY_LEN(r_value);
9022608Sa.clayton@nginx.com         VALUE   item;
9032608Sa.clayton@nginx.com         size_t  len = 0;
9042608Sa.clayton@nginx.com 
9052608Sa.clayton@nginx.com         for (i = 0; i < arr_len; i++) {
9062608Sa.clayton@nginx.com             item = rb_ary_entry(r_value, i);
9072608Sa.clayton@nginx.com             if (TYPE(item) != T_STRING) {
9082608Sa.clayton@nginx.com                 nxt_unit_req_error(headers_info->req,
9092608Sa.clayton@nginx.com                                    "Ruby: Wrong header entry in 'value' array "
9102608Sa.clayton@nginx.com                                    "from application");
9112608Sa.clayton@nginx.com                 goto fail;
9122608Sa.clayton@nginx.com             }
9132608Sa.clayton@nginx.com 
9142608Sa.clayton@nginx.com             len += RSTRING_LEN(item) + 2;   /* +2 for '; ' */
9152608Sa.clayton@nginx.com         }
9162608Sa.clayton@nginx.com 
917*2609Sa.clayton@nginx.com         if (arr_len > 0) {
918*2609Sa.clayton@nginx.com             len -= 2;
919*2609Sa.clayton@nginx.com         }
920*2609Sa.clayton@nginx.com 
9212608Sa.clayton@nginx.com         headers_info->fields++;
922*2609Sa.clayton@nginx.com         headers_info->size += RSTRING_LEN(r_key) + len;
9232608Sa.clayton@nginx.com 
9242608Sa.clayton@nginx.com         return ST_CONTINUE;
9252608Sa.clayton@nginx.com     }
9262608Sa.clayton@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
nxt_ruby_hash_add(VALUE r_key,VALUE r_value,VALUE arg)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 
9722608Sa.clayton@nginx.com     key_len = RSTRING_LEN(r_key);
9732608Sa.clayton@nginx.com 
9742608Sa.clayton@nginx.com     if (TYPE(r_value) == T_ARRAY) {
9752608Sa.clayton@nginx.com         int     i;
9762608Sa.clayton@nginx.com         int     arr_len = RARRAY_LEN(r_value);
9772608Sa.clayton@nginx.com         char    *field, *p;
9782608Sa.clayton@nginx.com         VALUE   item;
9792608Sa.clayton@nginx.com         size_t  len = 0;
9802608Sa.clayton@nginx.com 
9812608Sa.clayton@nginx.com         for (i = 0; i < arr_len; i++) {
9822608Sa.clayton@nginx.com             item = rb_ary_entry(r_value, i);
9832608Sa.clayton@nginx.com 
9842608Sa.clayton@nginx.com             len += RSTRING_LEN(item) + 2;   /* +2 for '; ' */
9852608Sa.clayton@nginx.com         }
9862608Sa.clayton@nginx.com 
9872608Sa.clayton@nginx.com         field = nxt_unit_malloc(NULL, len);
9882608Sa.clayton@nginx.com         if (field == NULL) {
9892608Sa.clayton@nginx.com             goto fail;
9902608Sa.clayton@nginx.com         }
9912608Sa.clayton@nginx.com 
9922608Sa.clayton@nginx.com         p = field;
9932608Sa.clayton@nginx.com 
9942608Sa.clayton@nginx.com         for (i = 0; i < arr_len; i++) {
9952608Sa.clayton@nginx.com             item = rb_ary_entry(r_value, i);
9962608Sa.clayton@nginx.com 
9972608Sa.clayton@nginx.com             p = nxt_cpymem(p, RSTRING_PTR(item), RSTRING_LEN(item));
9982608Sa.clayton@nginx.com             p = nxt_cpymem(p, "; ", 2);
9992608Sa.clayton@nginx.com         }
10002608Sa.clayton@nginx.com 
1001*2609Sa.clayton@nginx.com         if (arr_len > 0) {
1002*2609Sa.clayton@nginx.com             len -= 2;
1003*2609Sa.clayton@nginx.com         }
10042608Sa.clayton@nginx.com 
10052608Sa.clayton@nginx.com         *rc = nxt_unit_response_add_field(headers_info->req,
10062608Sa.clayton@nginx.com                                           RSTRING_PTR(r_key), key_len,
10072608Sa.clayton@nginx.com                                           field, len);
10082608Sa.clayton@nginx.com         nxt_unit_free(NULL, field);
10092608Sa.clayton@nginx.com 
10102608Sa.clayton@nginx.com         if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
10112608Sa.clayton@nginx.com             goto fail;
10122608Sa.clayton@nginx.com         }
10132608Sa.clayton@nginx.com 
10142608Sa.clayton@nginx.com         return ST_CONTINUE;
10152608Sa.clayton@nginx.com     }
10162608Sa.clayton@nginx.com 
1017743Smax.romanov@nginx.com     value = RSTRING_PTR(r_value);
1018743Smax.romanov@nginx.com     value_end = value + RSTRING_LEN(r_value);
1019743Smax.romanov@nginx.com 
1020743Smax.romanov@nginx.com     pos = value;
1021743Smax.romanov@nginx.com 
1022743Smax.romanov@nginx.com     for ( ;; ) {
1023743Smax.romanov@nginx.com         pos = strchr(pos, '\n');
1024743Smax.romanov@nginx.com 
1025743Smax.romanov@nginx.com         if (pos == NULL) {
1026743Smax.romanov@nginx.com             break;
1027743Smax.romanov@nginx.com         }
1028743Smax.romanov@nginx.com 
10291687Smax.romanov@nginx.com         *rc = nxt_unit_response_add_field(headers_info->req,
1030743Smax.romanov@nginx.com                                           RSTRING_PTR(r_key), key_len,
1031743Smax.romanov@nginx.com                                           value, pos - value);
1032743Smax.romanov@nginx.com         if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
1033584Salexander.borisov@nginx.com             goto fail;
1034584Salexander.borisov@nginx.com         }
1035584Salexander.borisov@nginx.com 
1036584Salexander.borisov@nginx.com         pos++;
1037584Salexander.borisov@nginx.com         value = pos;
1038584Salexander.borisov@nginx.com     }
1039584Salexander.borisov@nginx.com 
1040584Salexander.borisov@nginx.com     if (value <= value_end) {
10411687Smax.romanov@nginx.com         *rc = nxt_unit_response_add_field(headers_info->req,
1042743Smax.romanov@nginx.com                                           RSTRING_PTR(r_key), key_len,
1043743Smax.romanov@nginx.com                                           value, value_end - value);
1044743Smax.romanov@nginx.com         if (nxt_slow_path(*rc != NXT_UNIT_OK)) {
1045584Salexander.borisov@nginx.com             goto fail;
1046584Salexander.borisov@nginx.com         }
1047584Salexander.borisov@nginx.com     }
1048584Salexander.borisov@nginx.com 
1049584Salexander.borisov@nginx.com     return ST_CONTINUE;
1050584Salexander.borisov@nginx.com 
1051584Salexander.borisov@nginx.com fail:
1052584Salexander.borisov@nginx.com 
1053743Smax.romanov@nginx.com     *rc = NXT_UNIT_ERROR;
1054584Salexander.borisov@nginx.com 
1055584Salexander.borisov@nginx.com     return ST_STOP;
1056584Salexander.borisov@nginx.com }
1057584Salexander.borisov@nginx.com 
1058584Salexander.borisov@nginx.com 
1059743Smax.romanov@nginx.com static int
nxt_ruby_rack_result_body(nxt_unit_request_info_t * req,VALUE result)10601687Smax.romanov@nginx.com nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result)
1061584Salexander.borisov@nginx.com {
1062743Smax.romanov@nginx.com     int    rc;
1063743Smax.romanov@nginx.com     VALUE  fn, body;
1064584Salexander.borisov@nginx.com 
1065584Salexander.borisov@nginx.com     body = rb_ary_entry(result, 2);
1066584Salexander.borisov@nginx.com 
1067584Salexander.borisov@nginx.com     if (rb_respond_to(body, rb_intern("to_path"))) {
1068584Salexander.borisov@nginx.com 
1069584Salexander.borisov@nginx.com         fn = rb_funcall(body, rb_intern("to_path"), 0);
1070584Salexander.borisov@nginx.com         if (nxt_slow_path(TYPE(fn) != T_STRING)) {
10711687Smax.romanov@nginx.com             nxt_unit_req_error(req,
1072743Smax.romanov@nginx.com                                "Ruby: Failed to get 'body' file path from "
1073743Smax.romanov@nginx.com                                "application");
1074584Salexander.borisov@nginx.com 
1075743Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
1076584Salexander.borisov@nginx.com         }
1077584Salexander.borisov@nginx.com 
10781687Smax.romanov@nginx.com         rc = nxt_ruby_rack_result_body_file_write(req, fn);
1079743Smax.romanov@nginx.com         if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1080743Smax.romanov@nginx.com             return rc;
1081584Salexander.borisov@nginx.com         }
1082584Salexander.borisov@nginx.com 
1083584Salexander.borisov@nginx.com     } else if (rb_respond_to(body, rb_intern("each"))) {
10841258Smax.romanov@nginx.com         rb_block_call(body, rb_intern("each"), 0, 0,
10851687Smax.romanov@nginx.com                       nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req);
1086584Salexander.borisov@nginx.com 
1087584Salexander.borisov@nginx.com     } else {
10881687Smax.romanov@nginx.com         nxt_unit_req_error(req,
1089743Smax.romanov@nginx.com                            "Ruby: Invalid response 'body' format "
1090743Smax.romanov@nginx.com                            "from application");
1091584Salexander.borisov@nginx.com 
1092743Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1093584Salexander.borisov@nginx.com     }
1094584Salexander.borisov@nginx.com 
1095584Salexander.borisov@nginx.com     if (rb_respond_to(body, rb_intern("close"))) {
1096584Salexander.borisov@nginx.com         rb_funcall(body, rb_intern("close"), 0);
1097584Salexander.borisov@nginx.com     }
1098584Salexander.borisov@nginx.com 
1099743Smax.romanov@nginx.com     return NXT_UNIT_OK;
1100743Smax.romanov@nginx.com }
1101743Smax.romanov@nginx.com 
1102743Smax.romanov@nginx.com 
1103743Smax.romanov@nginx.com typedef struct {
1104743Smax.romanov@nginx.com     int    fd;
1105743Smax.romanov@nginx.com     off_t  pos;
1106743Smax.romanov@nginx.com     off_t  rest;
1107743Smax.romanov@nginx.com } nxt_ruby_rack_file_t;
1108743Smax.romanov@nginx.com 
1109743Smax.romanov@nginx.com 
1110743Smax.romanov@nginx.com static ssize_t
nxt_ruby_rack_file_read(nxt_unit_read_info_t * read_info,void * dst,size_t size)1111743Smax.romanov@nginx.com nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size)
1112743Smax.romanov@nginx.com {
1113743Smax.romanov@nginx.com     ssize_t               res;
1114743Smax.romanov@nginx.com     nxt_ruby_rack_file_t  *file;
1115743Smax.romanov@nginx.com 
1116743Smax.romanov@nginx.com     file = read_info->data;
1117743Smax.romanov@nginx.com 
1118743Smax.romanov@nginx.com     size = nxt_min(size, (size_t) file->rest);
1119743Smax.romanov@nginx.com 
1120743Smax.romanov@nginx.com     res = pread(file->fd, dst, size, file->pos);
1121743Smax.romanov@nginx.com 
1122743Smax.romanov@nginx.com     if (res >= 0) {
1123743Smax.romanov@nginx.com         file->pos += res;
1124743Smax.romanov@nginx.com         file->rest -= res;
1125743Smax.romanov@nginx.com 
1126743Smax.romanov@nginx.com         if (size > (size_t) res) {
1127743Smax.romanov@nginx.com             file->rest = 0;
1128743Smax.romanov@nginx.com         }
1129743Smax.romanov@nginx.com     }
1130743Smax.romanov@nginx.com 
1131743Smax.romanov@nginx.com     read_info->eof = file->rest == 0;
1132743Smax.romanov@nginx.com 
1133743Smax.romanov@nginx.com     return res;
1134584Salexander.borisov@nginx.com }
1135584Salexander.borisov@nginx.com 
1136584Salexander.borisov@nginx.com 
11371687Smax.romanov@nginx.com typedef struct {
11381687Smax.romanov@nginx.com     nxt_unit_read_info_t     read_info;
11391687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
11401687Smax.romanov@nginx.com } nxt_ruby_read_info_t;
11411687Smax.romanov@nginx.com 
11421687Smax.romanov@nginx.com 
1143743Smax.romanov@nginx.com static int
nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t * req,VALUE filepath)11441687Smax.romanov@nginx.com nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req,
11451687Smax.romanov@nginx.com     VALUE filepath)
1146584Salexander.borisov@nginx.com {
1147743Smax.romanov@nginx.com     int                   fd, rc;
1148743Smax.romanov@nginx.com     struct stat           finfo;
1149743Smax.romanov@nginx.com     nxt_ruby_rack_file_t  ruby_file;
11501687Smax.romanov@nginx.com     nxt_ruby_read_info_t  ri;
1151584Salexander.borisov@nginx.com 
1152743Smax.romanov@nginx.com     fd = open(RSTRING_PTR(filepath), O_RDONLY, 0);
1153743Smax.romanov@nginx.com     if (nxt_slow_path(fd == -1)) {
11541687Smax.romanov@nginx.com         nxt_unit_req_error(req,
1155743Smax.romanov@nginx.com                            "Ruby: Failed to open content file \"%s\": %s (%d)",
1156743Smax.romanov@nginx.com                            RSTRING_PTR(filepath), strerror(errno), errno);
1157584Salexander.borisov@nginx.com 
1158743Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1159584Salexander.borisov@nginx.com     }
1160584Salexander.borisov@nginx.com 
1161743Smax.romanov@nginx.com     rc = fstat(fd, &finfo);
1162743Smax.romanov@nginx.com     if (nxt_slow_path(rc == -1)) {
11631687Smax.romanov@nginx.com         nxt_unit_req_error(req,
1164743Smax.romanov@nginx.com                            "Ruby: Content file fstat(\"%s\") failed: %s (%d)",
1165743Smax.romanov@nginx.com                            RSTRING_PTR(filepath), strerror(errno), errno);
1166584Salexander.borisov@nginx.com 
1167743Smax.romanov@nginx.com         close(fd);
1168584Salexander.borisov@nginx.com 
1169743Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
1170584Salexander.borisov@nginx.com     }
1171584Salexander.borisov@nginx.com 
1172743Smax.romanov@nginx.com     ruby_file.fd = fd;
1173743Smax.romanov@nginx.com     ruby_file.pos = 0;
1174743Smax.romanov@nginx.com     ruby_file.rest = finfo.st_size;
1175584Salexander.borisov@nginx.com 
11761687Smax.romanov@nginx.com     ri.read_info.read = nxt_ruby_rack_file_read;
11771687Smax.romanov@nginx.com     ri.read_info.eof = ruby_file.rest == 0;
11781687Smax.romanov@nginx.com     ri.read_info.buf_size = ruby_file.rest;
11791687Smax.romanov@nginx.com     ri.read_info.data = &ruby_file;
11801687Smax.romanov@nginx.com     ri.req = req;
1181584Salexander.borisov@nginx.com 
11821687Smax.romanov@nginx.com     rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb,
11831687Smax.romanov@nginx.com                                                &ri,
11841687Smax.romanov@nginx.com                                                nxt_ruby_ubf,
11851687Smax.romanov@nginx.com                                                req->ctx);
1186584Salexander.borisov@nginx.com 
1187743Smax.romanov@nginx.com     close(fd);
1188584Salexander.borisov@nginx.com 
1189743Smax.romanov@nginx.com     return rc;
1190584Salexander.borisov@nginx.com }
1191584Salexander.borisov@nginx.com 
1192584Salexander.borisov@nginx.com 
11931687Smax.romanov@nginx.com static void *
nxt_ruby_response_write_cb(void * data)11941687Smax.romanov@nginx.com nxt_ruby_response_write_cb(void *data)
11951687Smax.romanov@nginx.com {
11961687Smax.romanov@nginx.com     int                   rc;
11971687Smax.romanov@nginx.com     nxt_ruby_read_info_t  *ri;
11981687Smax.romanov@nginx.com 
11991687Smax.romanov@nginx.com     ri = data;
12001687Smax.romanov@nginx.com 
12011687Smax.romanov@nginx.com     rc = nxt_unit_response_write_cb(ri->req, &ri->read_info);
12021687Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
12031687Smax.romanov@nginx.com         nxt_unit_req_error(ri->req, "Ruby: Failed to write content file.");
12041687Smax.romanov@nginx.com     }
12051687Smax.romanov@nginx.com 
12061687Smax.romanov@nginx.com     return (void *) (intptr_t) rc;
12071687Smax.romanov@nginx.com }
12081687Smax.romanov@nginx.com 
12091687Smax.romanov@nginx.com 
12101687Smax.romanov@nginx.com typedef struct {
12111687Smax.romanov@nginx.com     VALUE                    body;
12121687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
12131687Smax.romanov@nginx.com } nxt_ruby_write_info_t;
12141687Smax.romanov@nginx.com 
12151687Smax.romanov@nginx.com 
1216584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_result_body_each(VALUE body,VALUE arg,int argc,const VALUE * argv,VALUE blockarg)12171337Smax.romanov@nginx.com nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc,
12181337Smax.romanov@nginx.com     const VALUE *argv, VALUE blockarg)
1219584Salexander.borisov@nginx.com {
12201687Smax.romanov@nginx.com     nxt_ruby_write_info_t  wi;
1221584Salexander.borisov@nginx.com 
1222584Salexander.borisov@nginx.com     if (TYPE(body) != T_STRING) {
1223584Salexander.borisov@nginx.com         return Qnil;
1224584Salexander.borisov@nginx.com     }
1225584Salexander.borisov@nginx.com 
12261687Smax.romanov@nginx.com     wi.body = body;
12271687Smax.romanov@nginx.com     wi.req = (void *) (uintptr_t) arg;
12281687Smax.romanov@nginx.com 
12291687Smax.romanov@nginx.com     (void) rb_thread_call_without_gvl(nxt_ruby_response_write,
12301687Smax.romanov@nginx.com                                       (void *) (uintptr_t) &wi,
12311687Smax.romanov@nginx.com                                       nxt_ruby_ubf, wi.req->ctx);
1232584Salexander.borisov@nginx.com 
1233584Salexander.borisov@nginx.com     return Qnil;
1234584Salexander.borisov@nginx.com }
1235584Salexander.borisov@nginx.com 
1236584Salexander.borisov@nginx.com 
12371687Smax.romanov@nginx.com static void *
nxt_ruby_response_write(void * data)12381687Smax.romanov@nginx.com nxt_ruby_response_write(void *data)
12391687Smax.romanov@nginx.com {
12401687Smax.romanov@nginx.com     int                    rc;
12411687Smax.romanov@nginx.com     nxt_ruby_write_info_t  *wi;
12421687Smax.romanov@nginx.com 
12431687Smax.romanov@nginx.com     wi = data;
12441687Smax.romanov@nginx.com 
12451687Smax.romanov@nginx.com     rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body),
12461687Smax.romanov@nginx.com                                  RSTRING_LEN(wi->body));
12471687Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
12481687Smax.romanov@nginx.com         nxt_unit_req_error(wi->req,
12491687Smax.romanov@nginx.com                            "Ruby: Failed to write 'body' from application");
12501687Smax.romanov@nginx.com     }
12511687Smax.romanov@nginx.com 
12521687Smax.romanov@nginx.com     return (void *) (intptr_t) rc;
12531687Smax.romanov@nginx.com }
12541687Smax.romanov@nginx.com 
12551687Smax.romanov@nginx.com 
1256584Salexander.borisov@nginx.com static void
nxt_ruby_exception_log(nxt_unit_request_info_t * req,uint32_t level,const char * desc)12571687Smax.romanov@nginx.com nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level,
12581687Smax.romanov@nginx.com     const char *desc)
1259584Salexander.borisov@nginx.com {
1260584Salexander.borisov@nginx.com     int    i;
1261584Salexander.borisov@nginx.com     VALUE  err, ary, eclass, msg;
1262584Salexander.borisov@nginx.com 
12631687Smax.romanov@nginx.com     nxt_unit_req_log(req, level, "Ruby: %s", desc);
1264584Salexander.borisov@nginx.com 
1265584Salexander.borisov@nginx.com     err = rb_errinfo();
1266743Smax.romanov@nginx.com     if (nxt_slow_path(err == Qnil)) {
1267743Smax.romanov@nginx.com         return;
1268743Smax.romanov@nginx.com     }
1269584Salexander.borisov@nginx.com 
12701905So.canty@f5.com     eclass = rb_class_name(rb_class_of(err));
12711905So.canty@f5.com 
12721905So.canty@f5.com     msg = rb_funcall(err, rb_intern("message"), 0);
1273743Smax.romanov@nginx.com     ary = rb_funcall(err, rb_intern("backtrace"), 0);
12741905So.canty@f5.com 
12751905So.canty@f5.com     if (RARRAY_LEN(ary) == 0) {
12761905So.canty@f5.com         nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg),
12771905So.canty@f5.com                          RSTRING_PTR(eclass));
12781905So.canty@f5.com 
1279584Salexander.borisov@nginx.com         return;
1280584Salexander.borisov@nginx.com     }
1281584Salexander.borisov@nginx.com 
12821687Smax.romanov@nginx.com     nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)",
1283743Smax.romanov@nginx.com                      RSTRING_PTR(RARRAY_PTR(ary)[0]),
1284743Smax.romanov@nginx.com                      RSTRING_PTR(msg), RSTRING_PTR(eclass));
1285584Salexander.borisov@nginx.com 
1286584Salexander.borisov@nginx.com     for (i = 1; i < RARRAY_LEN(ary); i++) {
12871687Smax.romanov@nginx.com         nxt_unit_req_log(req, level, "from %s",
12881687Smax.romanov@nginx.com                          RSTRING_PTR(RARRAY_PTR(ary)[i]));
12891687Smax.romanov@nginx.com     }
12901687Smax.romanov@nginx.com }
12911687Smax.romanov@nginx.com 
1292743Smax.romanov@nginx.com 
12931687Smax.romanov@nginx.com static void
nxt_ruby_ctx_done(nxt_ruby_ctx_t * rctx)12941687Smax.romanov@nginx.com nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx)
12951687Smax.romanov@nginx.com {
12961687Smax.romanov@nginx.com     if (rctx->io_input != Qnil) {
12971687Smax.romanov@nginx.com         rb_gc_unregister_address(&rctx->io_input);
12981687Smax.romanov@nginx.com     }
12991687Smax.romanov@nginx.com 
13001687Smax.romanov@nginx.com     if (rctx->io_error != Qnil) {
13011687Smax.romanov@nginx.com         rb_gc_unregister_address(&rctx->io_error);
13021687Smax.romanov@nginx.com     }
13031687Smax.romanov@nginx.com 
13041687Smax.romanov@nginx.com     if (rctx->env != Qnil) {
13051687Smax.romanov@nginx.com         rb_gc_unregister_address(&rctx->env);
1306584Salexander.borisov@nginx.com     }
1307584Salexander.borisov@nginx.com }
1308584Salexander.borisov@nginx.com 
1309584Salexander.borisov@nginx.com 
1310584Salexander.borisov@nginx.com static void
nxt_ruby_atexit(void)1311743Smax.romanov@nginx.com nxt_ruby_atexit(void)
1312584Salexander.borisov@nginx.com {
13131687Smax.romanov@nginx.com     if (nxt_ruby_rackup != Qnil) {
13141687Smax.romanov@nginx.com         rb_gc_unregister_address(&nxt_ruby_rackup);
13151687Smax.romanov@nginx.com     }
1316584Salexander.borisov@nginx.com 
13171687Smax.romanov@nginx.com     if (nxt_ruby_call != Qnil) {
13181687Smax.romanov@nginx.com         rb_gc_unregister_address(&nxt_ruby_call);
13191687Smax.romanov@nginx.com     }
1320584Salexander.borisov@nginx.com 
13211910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
13221910So.canty@f5.com         rb_gc_unregister_address(&nxt_ruby_hook_procs);
13231910So.canty@f5.com     }
13241910So.canty@f5.com 
13251686Smax.romanov@nginx.com     nxt_ruby_done_strings();
13261686Smax.romanov@nginx.com 
1327584Salexander.borisov@nginx.com     ruby_cleanup(0);
1328584Salexander.borisov@nginx.com }
13291687Smax.romanov@nginx.com 
13301687Smax.romanov@nginx.com 
13311687Smax.romanov@nginx.com static int
nxt_ruby_ready_handler(nxt_unit_ctx_t * ctx)13321687Smax.romanov@nginx.com nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx)
13331687Smax.romanov@nginx.com {
13341687Smax.romanov@nginx.com     VALUE                res;
13351687Smax.romanov@nginx.com     uint32_t             i;
13361687Smax.romanov@nginx.com     nxt_ruby_ctx_t       *rctx;
13371687Smax.romanov@nginx.com     nxt_ruby_app_conf_t  *c;
13381687Smax.romanov@nginx.com 
13391687Smax.romanov@nginx.com     c = ctx->unit->data;
13401687Smax.romanov@nginx.com 
13411687Smax.romanov@nginx.com     if (c->threads <= 1) {
13421687Smax.romanov@nginx.com         return NXT_UNIT_OK;
13431687Smax.romanov@nginx.com     }
13441687Smax.romanov@nginx.com 
13451687Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
13461687Smax.romanov@nginx.com         rctx = &nxt_ruby_ctxs[i];
13471687Smax.romanov@nginx.com 
13481687Smax.romanov@nginx.com         rctx->ctx = ctx;
13491687Smax.romanov@nginx.com 
13501738Smax.romanov@nginx.com         res = (VALUE) rb_thread_call_with_gvl(nxt_ruby_thread_create_gvl, rctx);
13511687Smax.romanov@nginx.com 
13521687Smax.romanov@nginx.com         if (nxt_fast_path(res != Qnil)) {
13531687Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
13541687Smax.romanov@nginx.com 
13551687Smax.romanov@nginx.com             rctx->thread = res;
13561687Smax.romanov@nginx.com 
13571687Smax.romanov@nginx.com         } else {
13581687Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d create failed", (int) (i + 1));
13591691Smax.romanov@nginx.com 
13601691Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
13611687Smax.romanov@nginx.com         }
13621687Smax.romanov@nginx.com     }
13631687Smax.romanov@nginx.com 
13641687Smax.romanov@nginx.com     return NXT_UNIT_OK;
13651687Smax.romanov@nginx.com }
13661687Smax.romanov@nginx.com 
13671687Smax.romanov@nginx.com 
13681738Smax.romanov@nginx.com static void *
nxt_ruby_thread_create_gvl(void * rctx)13691738Smax.romanov@nginx.com nxt_ruby_thread_create_gvl(void *rctx)
13701738Smax.romanov@nginx.com {
13711738Smax.romanov@nginx.com     VALUE  res;
13721738Smax.romanov@nginx.com 
13731738Smax.romanov@nginx.com     res = rb_thread_create(RUBY_METHOD_FUNC(nxt_ruby_thread_func), rctx);
13741738Smax.romanov@nginx.com 
13751738Smax.romanov@nginx.com     return (void *) (uintptr_t) res;
13761738Smax.romanov@nginx.com }
13771738Smax.romanov@nginx.com 
13781738Smax.romanov@nginx.com 
13791687Smax.romanov@nginx.com static VALUE
nxt_ruby_thread_func(VALUE arg)13801687Smax.romanov@nginx.com nxt_ruby_thread_func(VALUE arg)
13811687Smax.romanov@nginx.com {
13821910So.canty@f5.com     int             state;
13831687Smax.romanov@nginx.com     nxt_unit_ctx_t  *ctx;
13841687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
13851687Smax.romanov@nginx.com 
13861687Smax.romanov@nginx.com     rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg;
13871687Smax.romanov@nginx.com 
13881687Smax.romanov@nginx.com     nxt_unit_debug(rctx->ctx, "worker thread start");
13891687Smax.romanov@nginx.com 
13901687Smax.romanov@nginx.com     ctx = nxt_unit_ctx_alloc(rctx->ctx, rctx);
13911687Smax.romanov@nginx.com     if (nxt_slow_path(ctx == NULL)) {
13921687Smax.romanov@nginx.com         goto fail;
13931687Smax.romanov@nginx.com     }
13941687Smax.romanov@nginx.com 
13951910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
13961910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state);
13971910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
13981910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
13991910So.canty@f5.com                                    "Failed to call on_thread_boot()");
14001910So.canty@f5.com         }
14011910So.canty@f5.com     }
14021910So.canty@f5.com 
14031687Smax.romanov@nginx.com     (void) rb_thread_call_without_gvl(nxt_ruby_unit_run, ctx,
14041687Smax.romanov@nginx.com                                       nxt_ruby_ubf, ctx);
14051687Smax.romanov@nginx.com 
14061910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
14071910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state);
14081910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
14091910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
14101910So.canty@f5.com                                    "Failed to call on_thread_shutdown()");
14111910So.canty@f5.com         }
14121910So.canty@f5.com     }
14131910So.canty@f5.com 
14141687Smax.romanov@nginx.com     nxt_unit_done(ctx);
14151687Smax.romanov@nginx.com 
14161687Smax.romanov@nginx.com fail:
14171687Smax.romanov@nginx.com 
14181687Smax.romanov@nginx.com     nxt_unit_debug(NULL, "worker thread end");
14191687Smax.romanov@nginx.com 
14201687Smax.romanov@nginx.com     return Qnil;
14211687Smax.romanov@nginx.com }
14221687Smax.romanov@nginx.com 
14231687Smax.romanov@nginx.com 
14241687Smax.romanov@nginx.com static void *
nxt_ruby_unit_run(void * ctx)14251687Smax.romanov@nginx.com nxt_ruby_unit_run(void *ctx)
14261687Smax.romanov@nginx.com {
14271687Smax.romanov@nginx.com     return (void *) (intptr_t) nxt_unit_run(ctx);
14281687Smax.romanov@nginx.com }
14291687Smax.romanov@nginx.com 
14301687Smax.romanov@nginx.com 
14311687Smax.romanov@nginx.com static void
nxt_ruby_ubf(void * ctx)14321687Smax.romanov@nginx.com nxt_ruby_ubf(void *ctx)
14331687Smax.romanov@nginx.com {
14341687Smax.romanov@nginx.com     nxt_unit_warn(ctx, "Ruby: UBF");
14351687Smax.romanov@nginx.com }
14361687Smax.romanov@nginx.com 
14371687Smax.romanov@nginx.com 
14381687Smax.romanov@nginx.com static int
nxt_ruby_init_threads(nxt_ruby_app_conf_t * c)14392163Salx.manpages@gmail.com nxt_ruby_init_threads(nxt_ruby_app_conf_t *c)
14401687Smax.romanov@nginx.com {
14411687Smax.romanov@nginx.com     int             state;
14421687Smax.romanov@nginx.com     uint32_t        i;
14431687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
14441687Smax.romanov@nginx.com 
14451687Smax.romanov@nginx.com     if (c->threads <= 1) {
14461687Smax.romanov@nginx.com         return NXT_UNIT_OK;
14471687Smax.romanov@nginx.com     }
14481687Smax.romanov@nginx.com 
14491687Smax.romanov@nginx.com     nxt_ruby_ctxs = nxt_unit_malloc(NULL, sizeof(nxt_ruby_ctx_t)
14501687Smax.romanov@nginx.com                                           * (c->threads - 1));
14511687Smax.romanov@nginx.com     if (nxt_slow_path(nxt_ruby_ctxs == NULL)) {
14521687Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate run contexts array");
14531687Smax.romanov@nginx.com 
14541687Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
14551687Smax.romanov@nginx.com     }
14561687Smax.romanov@nginx.com 
14571687Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
14581687Smax.romanov@nginx.com         rctx = &nxt_ruby_ctxs[i];
14591687Smax.romanov@nginx.com 
14601687Smax.romanov@nginx.com         rctx->env = Qnil;
14611687Smax.romanov@nginx.com         rctx->io_input = Qnil;
14621687Smax.romanov@nginx.com         rctx->io_error = Qnil;
14631687Smax.romanov@nginx.com         rctx->thread = Qnil;
14641687Smax.romanov@nginx.com     }
14651687Smax.romanov@nginx.com 
14661687Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
14671687Smax.romanov@nginx.com         rctx = &nxt_ruby_ctxs[i];
14681687Smax.romanov@nginx.com 
14691687Smax.romanov@nginx.com         rctx->env = rb_protect(nxt_ruby_rack_env_create,
14701687Smax.romanov@nginx.com                                (VALUE) (uintptr_t) rctx, &state);
14711687Smax.romanov@nginx.com         if (nxt_slow_path(rctx->env == Qnil || state != 0)) {
14721687Smax.romanov@nginx.com             nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
14731687Smax.romanov@nginx.com                                    "Failed to create 'environ' variable");
14741687Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
14751687Smax.romanov@nginx.com         }
14761687Smax.romanov@nginx.com     }
14771687Smax.romanov@nginx.com 
14781687Smax.romanov@nginx.com     return NXT_UNIT_OK;
14791687Smax.romanov@nginx.com }
14801687Smax.romanov@nginx.com 
14811687Smax.romanov@nginx.com 
14821687Smax.romanov@nginx.com static void
nxt_ruby_join_threads(nxt_unit_ctx_t * ctx,nxt_ruby_app_conf_t * c)14831687Smax.romanov@nginx.com nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, nxt_ruby_app_conf_t *c)
14841687Smax.romanov@nginx.com {
14851687Smax.romanov@nginx.com     uint32_t        i;
14861687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
14871687Smax.romanov@nginx.com 
14881687Smax.romanov@nginx.com     if (nxt_ruby_ctxs == NULL) {
14891687Smax.romanov@nginx.com         return;
14901687Smax.romanov@nginx.com     }
14911687Smax.romanov@nginx.com 
14921691Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
14931687Smax.romanov@nginx.com         rctx = &nxt_ruby_ctxs[i];
14941687Smax.romanov@nginx.com 
14951687Smax.romanov@nginx.com         if (rctx->thread != Qnil) {
14961687Smax.romanov@nginx.com             rb_funcall(rctx->thread, rb_intern("join"), 0);
14971687Smax.romanov@nginx.com 
14981687Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
14991687Smax.romanov@nginx.com 
15001687Smax.romanov@nginx.com         } else {
15011687Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d not started", (int) (i + 1));
15021687Smax.romanov@nginx.com         }
15031691Smax.romanov@nginx.com     }
15041687Smax.romanov@nginx.com 
15051691Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
15061691Smax.romanov@nginx.com         nxt_ruby_ctx_done(&nxt_ruby_ctxs[i]);
15071687Smax.romanov@nginx.com     }
15081687Smax.romanov@nginx.com 
15091687Smax.romanov@nginx.com     nxt_unit_free(ctx, nxt_ruby_ctxs);
15101687Smax.romanov@nginx.com }
1511