xref: /unit/src/ruby/nxt_ruby.c (revision 2087:ce43da300a31)
1584Salexander.borisov@nginx.com /*
2584Salexander.borisov@nginx.com  * Copyright (C) Alexander Borisov
3584Salexander.borisov@nginx.com  * Copyright (C) NGINX, Inc.
4584Salexander.borisov@nginx.com  */
5584Salexander.borisov@nginx.com 
6584Salexander.borisov@nginx.com #include <ruby/nxt_ruby.h>
7584Salexander.borisov@nginx.com 
8743Smax.romanov@nginx.com #include <nxt_unit.h>
9743Smax.romanov@nginx.com #include <nxt_unit_request.h>
101532St.nateldemoura@f5.com 
111687Smax.romanov@nginx.com #include <ruby/thread.h>
121687Smax.romanov@nginx.com 
131532St.nateldemoura@f5.com #include NXT_RUBY_MOUNTS_H
14743Smax.romanov@nginx.com 
151814Svbart@nginx.com #include <locale.h>
161814Svbart@nginx.com 
17584Salexander.borisov@nginx.com 
18584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MAJOR  1
19584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MINOR  3
20584Salexander.borisov@nginx.com 
21584Salexander.borisov@nginx.com 
22584Salexander.borisov@nginx.com typedef struct {
231687Smax.romanov@nginx.com     nxt_task_t      *task;
241687Smax.romanov@nginx.com     nxt_str_t       *script;
251687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
26584Salexander.borisov@nginx.com } nxt_ruby_rack_init_t;
27584Salexander.borisov@nginx.com 
28584Salexander.borisov@nginx.com 
291488St.nateldemoura@f5.com static nxt_int_t nxt_ruby_start(nxt_task_t *task,
301488St.nateldemoura@f5.com     nxt_process_data_t *data);
31584Salexander.borisov@nginx.com static VALUE nxt_ruby_init_basic(VALUE arg);
32*2087Sz.hong@f5.com static VALUE nxt_ruby_script_basename(nxt_str_t *script);
331910So.canty@f5.com 
341910So.canty@f5.com static VALUE nxt_ruby_hook_procs_load(VALUE path);
351910So.canty@f5.com static VALUE nxt_ruby_hook_register(VALUE arg);
361910So.canty@f5.com static VALUE nxt_ruby_hook_call(VALUE name);
371910So.canty@f5.com 
38584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
39584Salexander.borisov@nginx.com 
40584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rubygems(VALUE arg);
41717Salexander.borisov@nginx.com static VALUE nxt_ruby_bundler_setup(VALUE arg);
42584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rack(VALUE arg);
43584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
44584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_env_create(VALUE arg);
451687Smax.romanov@nginx.com static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx);
46743Smax.romanov@nginx.com static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
471687Smax.romanov@nginx.com static void *nxt_ruby_request_handler_gvl(void *req);
481687Smax.romanov@nginx.com static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx);
491738Smax.romanov@nginx.com static void *nxt_ruby_thread_create_gvl(void *rctx);
501687Smax.romanov@nginx.com static VALUE nxt_ruby_thread_func(VALUE arg);
511687Smax.romanov@nginx.com static void *nxt_ruby_unit_run(void *ctx);
521687Smax.romanov@nginx.com static void nxt_ruby_ubf(void *ctx);
53*2087Sz.hong@f5.com static int nxt_ruby_init_threads(VALUE script, nxt_ruby_app_conf_t *c);
541687Smax.romanov@nginx.com static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx,
551687Smax.romanov@nginx.com     nxt_ruby_app_conf_t *c);
56584Salexander.borisov@nginx.com 
57584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_app_run(VALUE arg);
581687Smax.romanov@nginx.com static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env);
591686Smax.romanov@nginx.com nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name,
601686Smax.romanov@nginx.com     nxt_unit_sptr_t *sptr, uint32_t len);
611687Smax.romanov@nginx.com static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req,
621687Smax.romanov@nginx.com     VALUE result);
631687Smax.romanov@nginx.com static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req,
641687Smax.romanov@nginx.com     VALUE result, nxt_int_t status);
65743Smax.romanov@nginx.com static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg);
66743Smax.romanov@nginx.com static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg);
671687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req,
681687Smax.romanov@nginx.com     VALUE result);
691687Smax.romanov@nginx.com static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req,
701687Smax.romanov@nginx.com     VALUE filepath);
711687Smax.romanov@nginx.com static void *nxt_ruby_response_write_cb(void *read_info);
721337Smax.romanov@nginx.com static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg,
731337Smax.romanov@nginx.com     int argc, const VALUE *argv, VALUE blockarg);
741687Smax.romanov@nginx.com static void *nxt_ruby_response_write(void *body);
75584Salexander.borisov@nginx.com 
761687Smax.romanov@nginx.com static void nxt_ruby_exception_log(nxt_unit_request_info_t *req,
771687Smax.romanov@nginx.com     uint32_t level, const char *desc);
78584Salexander.borisov@nginx.com 
791687Smax.romanov@nginx.com static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx);
80743Smax.romanov@nginx.com static void nxt_ruby_atexit(void);
81584Salexander.borisov@nginx.com 
82584Salexander.borisov@nginx.com 
83584Salexander.borisov@nginx.com static uint32_t  compat[] = {
84584Salexander.borisov@nginx.com     NXT_VERNUM, NXT_DEBUG,
85584Salexander.borisov@nginx.com };
86584Salexander.borisov@nginx.com 
871910So.canty@f5.com static VALUE  nxt_ruby_hook_procs;
881687Smax.romanov@nginx.com static VALUE  nxt_ruby_rackup;
891687Smax.romanov@nginx.com static VALUE  nxt_ruby_call;
901687Smax.romanov@nginx.com 
911687Smax.romanov@nginx.com static uint32_t        nxt_ruby_threads;
921687Smax.romanov@nginx.com static nxt_ruby_ctx_t  *nxt_ruby_ctxs;
93584Salexander.borisov@nginx.com 
94743Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t  nxt_app_module = {
95584Salexander.borisov@nginx.com     sizeof(compat),
96584Salexander.borisov@nginx.com     compat,
97584Salexander.borisov@nginx.com     nxt_string("ruby"),
98612Salexander.borisov@nginx.com     ruby_version,
991489St.nateldemoura@f5.com     nxt_ruby_mounts,
1001489St.nateldemoura@f5.com     nxt_nitems(nxt_ruby_mounts),
101977Smax.romanov@gmail.com     NULL,
1021488St.nateldemoura@f5.com     nxt_ruby_start,
103584Salexander.borisov@nginx.com };
104584Salexander.borisov@nginx.com 
1051686Smax.romanov@nginx.com typedef struct {
1061686Smax.romanov@nginx.com     nxt_str_t  string;
1071686Smax.romanov@nginx.com     VALUE      *v;
1081686Smax.romanov@nginx.com } nxt_ruby_string_t;
1091686Smax.romanov@nginx.com 
1101686Smax.romanov@nginx.com static VALUE  nxt_rb_80_str;
1111686Smax.romanov@nginx.com static VALUE  nxt_rb_content_length_str;
1121686Smax.romanov@nginx.com static VALUE  nxt_rb_content_type_str;
1131686Smax.romanov@nginx.com static VALUE  nxt_rb_http_str;
1141686Smax.romanov@nginx.com static VALUE  nxt_rb_https_str;
1151686Smax.romanov@nginx.com static VALUE  nxt_rb_path_info_str;
1161686Smax.romanov@nginx.com static VALUE  nxt_rb_query_string_str;
1171686Smax.romanov@nginx.com static VALUE  nxt_rb_rack_url_scheme_str;
1181686Smax.romanov@nginx.com static VALUE  nxt_rb_remote_addr_str;
1191686Smax.romanov@nginx.com static VALUE  nxt_rb_request_method_str;
1201686Smax.romanov@nginx.com static VALUE  nxt_rb_request_uri_str;
1211686Smax.romanov@nginx.com static VALUE  nxt_rb_server_addr_str;
1221686Smax.romanov@nginx.com static VALUE  nxt_rb_server_name_str;
1231686Smax.romanov@nginx.com static VALUE  nxt_rb_server_port_str;
1241686Smax.romanov@nginx.com static VALUE  nxt_rb_server_protocol_str;
1251910So.canty@f5.com static VALUE  nxt_rb_on_worker_boot;
1261910So.canty@f5.com static VALUE  nxt_rb_on_worker_shutdown;
1271910So.canty@f5.com static VALUE  nxt_rb_on_thread_boot;
1281910So.canty@f5.com static VALUE  nxt_rb_on_thread_shutdown;
1291686Smax.romanov@nginx.com 
1301686Smax.romanov@nginx.com static nxt_ruby_string_t nxt_rb_strings[] = {
1311686Smax.romanov@nginx.com     { nxt_string("80"), &nxt_rb_80_str },
1321686Smax.romanov@nginx.com     { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str },
1331686Smax.romanov@nginx.com     { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str },
1341686Smax.romanov@nginx.com     { nxt_string("http"), &nxt_rb_http_str },
1351686Smax.romanov@nginx.com     { nxt_string("https"), &nxt_rb_https_str },
1361686Smax.romanov@nginx.com     { nxt_string("PATH_INFO"), &nxt_rb_path_info_str },
1371686Smax.romanov@nginx.com     { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str },
1381686Smax.romanov@nginx.com     { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str },
1391686Smax.romanov@nginx.com     { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str },
1401686Smax.romanov@nginx.com     { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str },
1411686Smax.romanov@nginx.com     { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str },
1421686Smax.romanov@nginx.com     { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str },
1431686Smax.romanov@nginx.com     { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str },
1441686Smax.romanov@nginx.com     { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str },
1451686Smax.romanov@nginx.com     { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str },
1461910So.canty@f5.com     { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot },
1471910So.canty@f5.com     { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown },
1481910So.canty@f5.com     { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot },
1491910So.canty@f5.com     { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown },
1501686Smax.romanov@nginx.com     { nxt_null_string, NULL },
1511686Smax.romanov@nginx.com };
1521686Smax.romanov@nginx.com 
1531686Smax.romanov@nginx.com 
1541686Smax.romanov@nginx.com static int
nxt_ruby_init_strings(void)1551686Smax.romanov@nginx.com nxt_ruby_init_strings(void)
1561686Smax.romanov@nginx.com {
1571686Smax.romanov@nginx.com     VALUE              v;
1581686Smax.romanov@nginx.com     nxt_ruby_string_t  *pstr;
1591686Smax.romanov@nginx.com 
1601686Smax.romanov@nginx.com     pstr = nxt_rb_strings;
1611686Smax.romanov@nginx.com 
1621686Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
1631686Smax.romanov@nginx.com         v = rb_str_new_static((char *) pstr->string.start, pstr->string.length);
1641686Smax.romanov@nginx.com 
1651686Smax.romanov@nginx.com         if (nxt_slow_path(v == Qnil)) {
1661686Smax.romanov@nginx.com             nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'",
1671686Smax.romanov@nginx.com                            (int) pstr->string.length,
1681686Smax.romanov@nginx.com                            (char *) pstr->string.start);
1691686Smax.romanov@nginx.com 
1701686Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
1711686Smax.romanov@nginx.com         }
1721686Smax.romanov@nginx.com 
1731686Smax.romanov@nginx.com         *pstr->v = v;
1741686Smax.romanov@nginx.com 
1751686Smax.romanov@nginx.com         rb_gc_register_address(pstr->v);
1761686Smax.romanov@nginx.com 
1771686Smax.romanov@nginx.com         pstr++;
1781686Smax.romanov@nginx.com     }
1791686Smax.romanov@nginx.com 
1801686Smax.romanov@nginx.com     return NXT_UNIT_OK;
1811686Smax.romanov@nginx.com }
1821686Smax.romanov@nginx.com 
1831686Smax.romanov@nginx.com 
1841686Smax.romanov@nginx.com static void
nxt_ruby_done_strings(void)1851686Smax.romanov@nginx.com nxt_ruby_done_strings(void)
1861686Smax.romanov@nginx.com {
1871686Smax.romanov@nginx.com     nxt_ruby_string_t  *pstr;
1881686Smax.romanov@nginx.com 
1891686Smax.romanov@nginx.com     pstr = nxt_rb_strings;
1901686Smax.romanov@nginx.com 
1911686Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
1921686Smax.romanov@nginx.com         rb_gc_unregister_address(pstr->v);
1931686Smax.romanov@nginx.com 
1941686Smax.romanov@nginx.com         *pstr->v = Qnil;
1951686Smax.romanov@nginx.com 
1961686Smax.romanov@nginx.com         pstr++;
1971686Smax.romanov@nginx.com     }
1981686Smax.romanov@nginx.com }
1991686Smax.romanov@nginx.com 
200584Salexander.borisov@nginx.com 
2011910So.canty@f5.com static VALUE
nxt_ruby_hook_procs_load(VALUE path)2021910So.canty@f5.com nxt_ruby_hook_procs_load(VALUE path)
2031910So.canty@f5.com {
2041910So.canty@f5.com     VALUE  module, file, file_obj;
2051910So.canty@f5.com 
2061910So.canty@f5.com     module = rb_define_module("Unit");
2071910So.canty@f5.com 
2081910So.canty@f5.com     nxt_ruby_hook_procs = rb_hash_new();
2091910So.canty@f5.com 
2101910So.canty@f5.com     rb_gc_register_address(&nxt_ruby_hook_procs);
2111910So.canty@f5.com 
2121910So.canty@f5.com     rb_define_module_function(module, "on_worker_boot",
2131910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2141910So.canty@f5.com     rb_define_module_function(module, "on_worker_shutdown",
2151910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2161910So.canty@f5.com     rb_define_module_function(module, "on_thread_boot",
2171910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2181910So.canty@f5.com     rb_define_module_function(module, "on_thread_shutdown",
2191910So.canty@f5.com                               &nxt_ruby_hook_register, 0);
2201910So.canty@f5.com 
2211910So.canty@f5.com     file = rb_const_get(rb_cObject, rb_intern("File"));
2221910So.canty@f5.com     file_obj = rb_funcall(file, rb_intern("read"), 1, path);
2231910So.canty@f5.com 
2241910So.canty@f5.com     return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path,
2251910So.canty@f5.com                       INT2NUM(1));
2261910So.canty@f5.com }
2271910So.canty@f5.com 
2281910So.canty@f5.com 
2291910So.canty@f5.com static VALUE
nxt_ruby_hook_register(VALUE arg)2301910So.canty@f5.com nxt_ruby_hook_register(VALUE arg)
2311910So.canty@f5.com {
2321910So.canty@f5.com     VALUE  kernel, callee, callee_str;
2331910So.canty@f5.com 
2341910So.canty@f5.com     rb_need_block();
2351910So.canty@f5.com 
2361910So.canty@f5.com     kernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
2371910So.canty@f5.com     callee = rb_funcall(kernel, rb_intern("__callee__"), 0);
2381910So.canty@f5.com     callee_str = rb_funcall(callee, rb_intern("to_s"), 0);
2391910So.canty@f5.com 
2401910So.canty@f5.com     rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc());
2411910So.canty@f5.com 
2421910So.canty@f5.com     return Qnil;
2431910So.canty@f5.com }
2441910So.canty@f5.com 
2451910So.canty@f5.com 
2461910So.canty@f5.com static VALUE
nxt_ruby_hook_call(VALUE name)2471910So.canty@f5.com nxt_ruby_hook_call(VALUE name)
2481910So.canty@f5.com {
2491910So.canty@f5.com     VALUE  proc;
2501910So.canty@f5.com 
2511910So.canty@f5.com     proc = rb_hash_lookup(nxt_ruby_hook_procs, name);
2521910So.canty@f5.com     if (proc == Qnil) {
2531910So.canty@f5.com         return Qnil;
2541910So.canty@f5.com     }
2551910So.canty@f5.com 
2561910So.canty@f5.com     return rb_funcall(proc, rb_intern("call"), 0);
2571910So.canty@f5.com }
2581910So.canty@f5.com 
2591910So.canty@f5.com 
260584Salexander.borisov@nginx.com static nxt_int_t
nxt_ruby_start(nxt_task_t * task,nxt_process_data_t * data)2611488St.nateldemoura@f5.com nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data)
262584Salexander.borisov@nginx.com {
2631488St.nateldemoura@f5.com     int                    state, rc;
264*2087Sz.hong@f5.com     VALUE                  res, path, script;
2651687Smax.romanov@nginx.com     nxt_ruby_ctx_t         ruby_ctx;
2661488St.nateldemoura@f5.com     nxt_unit_ctx_t         *unit_ctx;
2671488St.nateldemoura@f5.com     nxt_unit_init_t        ruby_unit_init;
2681687Smax.romanov@nginx.com     nxt_ruby_app_conf_t    *c;
2691488St.nateldemoura@f5.com     nxt_ruby_rack_init_t   rack_init;
2701488St.nateldemoura@f5.com     nxt_common_app_conf_t  *conf;
271584Salexander.borisov@nginx.com 
2721258Smax.romanov@nginx.com     static char  *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" };
2731258Smax.romanov@nginx.com 
2741488St.nateldemoura@f5.com     conf = data->app;
2751687Smax.romanov@nginx.com     c = &conf->u.ruby;
2761687Smax.romanov@nginx.com 
2771687Smax.romanov@nginx.com     nxt_ruby_threads = c->threads;
2781488St.nateldemoura@f5.com 
2791814Svbart@nginx.com     setlocale(LC_CTYPE, "");
2801814Svbart@nginx.com 
2811258Smax.romanov@nginx.com     RUBY_INIT_STACK
282584Salexander.borisov@nginx.com     ruby_init();
2831258Smax.romanov@nginx.com     ruby_options(2, argv);
284584Salexander.borisov@nginx.com     ruby_script("NGINX_Unit");
285584Salexander.borisov@nginx.com 
286*2087Sz.hong@f5.com     script = nxt_ruby_script_basename(&c->script);
287*2087Sz.hong@f5.com 
2881687Smax.romanov@nginx.com     ruby_ctx.env = Qnil;
289*2087Sz.hong@f5.com     ruby_ctx.script = script;
2901687Smax.romanov@nginx.com     ruby_ctx.io_input = Qnil;
2911687Smax.romanov@nginx.com     ruby_ctx.io_error = Qnil;
2921687Smax.romanov@nginx.com     ruby_ctx.thread = Qnil;
2931687Smax.romanov@nginx.com     ruby_ctx.ctx = NULL;
2941687Smax.romanov@nginx.com     ruby_ctx.req = NULL;
2951687Smax.romanov@nginx.com 
296584Salexander.borisov@nginx.com     rack_init.task = task;
2971687Smax.romanov@nginx.com     rack_init.script = &c->script;
2981687Smax.romanov@nginx.com     rack_init.rctx = &ruby_ctx;
299584Salexander.borisov@nginx.com 
3001686Smax.romanov@nginx.com     nxt_ruby_init_strings();
3011686Smax.romanov@nginx.com 
302584Salexander.borisov@nginx.com     res = rb_protect(nxt_ruby_init_basic,
303584Salexander.borisov@nginx.com                      (VALUE) (uintptr_t) &rack_init, &state);
304584Salexander.borisov@nginx.com     if (nxt_slow_path(res == Qnil || state != 0)) {
3051687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
306584Salexander.borisov@nginx.com                                "Failed to init basic variables");
307584Salexander.borisov@nginx.com         return NXT_ERROR;
308584Salexander.borisov@nginx.com     }
309584Salexander.borisov@nginx.com 
3101687Smax.romanov@nginx.com     nxt_ruby_call = Qnil;
3111910So.canty@f5.com     nxt_ruby_hook_procs = Qnil;
3121910So.canty@f5.com 
3131910So.canty@f5.com     if (c->hooks.start != NULL) {
3141910So.canty@f5.com         path = rb_str_new((const char *) c->hooks.start,
3151910So.canty@f5.com                           (long) c->hooks.length);
3161910So.canty@f5.com 
3171910So.canty@f5.com         rb_protect(nxt_ruby_hook_procs_load, path, &state);
3181910So.canty@f5.com         rb_str_free(path);
3191910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3201910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
3211910So.canty@f5.com                                    "Failed to setup hooks");
3221910So.canty@f5.com             return NXT_ERROR;
3231910So.canty@f5.com         }
3241910So.canty@f5.com     }
3251910So.canty@f5.com 
3261910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3271910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state);
3281910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3291910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3301910So.canty@f5.com                                    "Failed to call on_worker_boot()");
3311910So.canty@f5.com             return NXT_ERROR;
3321910So.canty@f5.com         }
3331910So.canty@f5.com     }
3341687Smax.romanov@nginx.com 
335584Salexander.borisov@nginx.com     nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init);
336584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_rackup == Qnil)) {
337584Salexander.borisov@nginx.com         return NXT_ERROR;
338584Salexander.borisov@nginx.com     }
339584Salexander.borisov@nginx.com 
3401687Smax.romanov@nginx.com     rb_gc_register_address(&nxt_ruby_rackup);
3411687Smax.romanov@nginx.com 
342584Salexander.borisov@nginx.com     nxt_ruby_call = rb_intern("call");
343584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_call == Qnil)) {
344584Salexander.borisov@nginx.com         nxt_alert(task, "Ruby: Unable to find rack entry point");
345584Salexander.borisov@nginx.com 
3461687Smax.romanov@nginx.com         goto fail;
347584Salexander.borisov@nginx.com     }
348584Salexander.borisov@nginx.com 
3491687Smax.romanov@nginx.com     rb_gc_register_address(&nxt_ruby_call);
3501687Smax.romanov@nginx.com 
3511687Smax.romanov@nginx.com     ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create,
3521687Smax.romanov@nginx.com                               (VALUE) (uintptr_t) &ruby_ctx, &state);
3531687Smax.romanov@nginx.com     if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) {
3541687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
355584Salexander.borisov@nginx.com                                "Failed to create 'environ' variable");
3561687Smax.romanov@nginx.com         goto fail;
357584Salexander.borisov@nginx.com     }
358584Salexander.borisov@nginx.com 
359*2087Sz.hong@f5.com     rc = nxt_ruby_init_threads(script, c);
3601687Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
3611687Smax.romanov@nginx.com         goto fail;
3621687Smax.romanov@nginx.com     }
363584Salexander.borisov@nginx.com 
3641980Smax.romanov@nginx.com     nxt_unit_default_init(task, &ruby_unit_init, conf);
365743Smax.romanov@nginx.com 
366743Smax.romanov@nginx.com     ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler;
3671687Smax.romanov@nginx.com     ruby_unit_init.callbacks.ready_handler = nxt_ruby_ready_handler;
3681687Smax.romanov@nginx.com     ruby_unit_init.data = c;
3691687Smax.romanov@nginx.com     ruby_unit_init.ctx_data = &ruby_ctx;
370743Smax.romanov@nginx.com 
371743Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&ruby_unit_init);
372743Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
3731687Smax.romanov@nginx.com         goto fail;
374743Smax.romanov@nginx.com     }
375743Smax.romanov@nginx.com 
3761910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3771910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state);
3781910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3791910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3801910So.canty@f5.com                                    "Failed to call on_thread_boot()");
3811910So.canty@f5.com         }
3821910So.canty@f5.com     }
3831910So.canty@f5.com 
3841687Smax.romanov@nginx.com     rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx,
3851687Smax.romanov@nginx.com                                                nxt_ruby_ubf, unit_ctx);
386743Smax.romanov@nginx.com 
3871910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3881910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state);
3891910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
3901910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
3911910So.canty@f5.com                                    "Failed to call on_thread_shutdown()");
3921910So.canty@f5.com         }
3931910So.canty@f5.com     }
3941910So.canty@f5.com 
3951687Smax.romanov@nginx.com     nxt_ruby_join_threads(unit_ctx, c);
3961687Smax.romanov@nginx.com 
3971910So.canty@f5.com     if (nxt_ruby_hook_procs != Qnil) {
3981910So.canty@f5.com         rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state);
3991910So.canty@f5.com         if (nxt_slow_path(state != 0)) {
4001910So.canty@f5.com             nxt_ruby_exception_log(NULL, NXT_LOG_ERR,
4011910So.canty@f5.com                                    "Failed to call on_worker_shutdown()");
4021910So.canty@f5.com         }
4031910So.canty@f5.com     }
4041910So.canty@f5.com 
4051687Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
4061687Smax.romanov@nginx.com 
4071687Smax.romanov@nginx.com     nxt_ruby_ctx_done(&ruby_ctx);
408743Smax.romanov@nginx.com 
409743Smax.romanov@nginx.com     nxt_ruby_atexit();
410743Smax.romanov@nginx.com 
411743Smax.romanov@nginx.com     exit(rc);
412743Smax.romanov@nginx.com 
413584Salexander.borisov@nginx.com     return NXT_OK;
4141687Smax.romanov@nginx.com 
4151687Smax.romanov@nginx.com fail:
4161687Smax.romanov@nginx.com 
4171687Smax.romanov@nginx.com     nxt_ruby_join_threads(NULL, c);
4181687Smax.romanov@nginx.com 
4191687Smax.romanov@nginx.com     nxt_ruby_ctx_done(&ruby_ctx);
4201687Smax.romanov@nginx.com 
4211687Smax.romanov@nginx.com     nxt_ruby_atexit();
4221687Smax.romanov@nginx.com 
4231687Smax.romanov@nginx.com     return NXT_ERROR;
424584Salexander.borisov@nginx.com }
425584Salexander.borisov@nginx.com 
426584Salexander.borisov@nginx.com 
427584Salexander.borisov@nginx.com static VALUE
nxt_ruby_script_basename(nxt_str_t * script)428*2087Sz.hong@f5.com nxt_ruby_script_basename(nxt_str_t *script)
429*2087Sz.hong@f5.com {
430*2087Sz.hong@f5.com     size_t  len;
431*2087Sz.hong@f5.com     u_char  *p, *last;
432*2087Sz.hong@f5.com 
433*2087Sz.hong@f5.com     last = NULL;
434*2087Sz.hong@f5.com     p = script->start + script->length;
435*2087Sz.hong@f5.com 
436*2087Sz.hong@f5.com     while (p > script->start) {
437*2087Sz.hong@f5.com 
438*2087Sz.hong@f5.com         if (p[-1] == '/') {
439*2087Sz.hong@f5.com             last = p;
440*2087Sz.hong@f5.com             break;
441*2087Sz.hong@f5.com         }
442*2087Sz.hong@f5.com 
443*2087Sz.hong@f5.com         p--;
444*2087Sz.hong@f5.com     }
445*2087Sz.hong@f5.com 
446*2087Sz.hong@f5.com     if (last != NULL) {
447*2087Sz.hong@f5.com         len = script->length - (last - script->start);
448*2087Sz.hong@f5.com 
449*2087Sz.hong@f5.com     } else {
450*2087Sz.hong@f5.com         last = script->start;
451*2087Sz.hong@f5.com         len = script->length;
452*2087Sz.hong@f5.com     }
453*2087Sz.hong@f5.com 
454*2087Sz.hong@f5.com     return rb_str_new((const char *) last, len);
455*2087Sz.hong@f5.com }
456*2087Sz.hong@f5.com 
457*2087Sz.hong@f5.com 
458*2087Sz.hong@f5.com static VALUE
nxt_ruby_init_basic(VALUE arg)459584Salexander.borisov@nginx.com nxt_ruby_init_basic(VALUE arg)
460584Salexander.borisov@nginx.com {
461584Salexander.borisov@nginx.com     int                   state;
462584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
463584Salexander.borisov@nginx.com 
464584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg;
465584Salexander.borisov@nginx.com 
466584Salexander.borisov@nginx.com     state = rb_enc_find_index("encdb");
467584Salexander.borisov@nginx.com     if (nxt_slow_path(state == 0)) {
468584Salexander.borisov@nginx.com         nxt_alert(rack_init->task,
469584Salexander.borisov@nginx.com                   "Ruby: Failed to find encoding index 'encdb'");
470584Salexander.borisov@nginx.com 
471584Salexander.borisov@nginx.com         return Qnil;
472584Salexander.borisov@nginx.com     }
473584Salexander.borisov@nginx.com 
474609Salexander.borisov@nginx.com     rb_funcall(rb_cObject, rb_intern("require"), 1,
475609Salexander.borisov@nginx.com                rb_str_new2("enc/trans/transdb"));
476609Salexander.borisov@nginx.com 
477584Salexander.borisov@nginx.com     return arg;
478584Salexander.borisov@nginx.com }
479584Salexander.borisov@nginx.com 
480584Salexander.borisov@nginx.com 
481584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_init(nxt_ruby_rack_init_t * rack_init)482584Salexander.borisov@nginx.com nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
483584Salexander.borisov@nginx.com {
484584Salexander.borisov@nginx.com     int    state;
4851687Smax.romanov@nginx.com     VALUE  rackup, err;
486584Salexander.borisov@nginx.com 
487584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rubygems, Qnil, &state);
488584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
4891687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
490584Salexander.borisov@nginx.com                                "Failed to require 'rubygems' package");
491584Salexander.borisov@nginx.com         return Qnil;
492584Salexander.borisov@nginx.com     }
493584Salexander.borisov@nginx.com 
494717Salexander.borisov@nginx.com     rb_protect(nxt_ruby_bundler_setup, Qnil, &state);
495717Salexander.borisov@nginx.com     if (state != 0) {
496717Salexander.borisov@nginx.com         err = rb_errinfo();
497717Salexander.borisov@nginx.com 
498717Salexander.borisov@nginx.com         if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) {
4991687Smax.romanov@nginx.com             nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
500717Salexander.borisov@nginx.com                                    "Failed to require 'bundler/setup' package");
501717Salexander.borisov@nginx.com             return Qnil;
502717Salexander.borisov@nginx.com         }
503717Salexander.borisov@nginx.com 
504717Salexander.borisov@nginx.com         rb_set_errinfo(Qnil);
505717Salexander.borisov@nginx.com     }
506717Salexander.borisov@nginx.com 
507584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rack, Qnil, &state);
508584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
5091687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
510584Salexander.borisov@nginx.com                                "Failed to require 'rack' package");
511584Salexander.borisov@nginx.com         return Qnil;
512584Salexander.borisov@nginx.com     }
513584Salexander.borisov@nginx.com 
514584Salexander.borisov@nginx.com     rackup = rb_protect(nxt_ruby_rack_parse_script,
515584Salexander.borisov@nginx.com                         (VALUE) (uintptr_t) rack_init, &state);
516584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) {
5171687Smax.romanov@nginx.com         nxt_ruby_exception_log(NULL, NXT_LOG_ALERT,
518584Salexander.borisov@nginx.com                                "Failed to parse rack script");
519584Salexander.borisov@nginx.com         return Qnil;
520584Salexander.borisov@nginx.com     }
521584Salexander.borisov@nginx.com 
522584Salexander.borisov@nginx.com     if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) {
523584Salexander.borisov@nginx.com         nxt_alert(rack_init->task, "Ruby: Invalid rack config file");
524584Salexander.borisov@nginx.com         return Qnil;
525584Salexander.borisov@nginx.com     }
526584Salexander.borisov@nginx.com 
527584Salexander.borisov@nginx.com     return RARRAY_PTR(rackup)[0];
528584Salexander.borisov@nginx.com }
529584Salexander.borisov@nginx.com 
530584Salexander.borisov@nginx.com 
531584Salexander.borisov@nginx.com static VALUE
nxt_ruby_require_rubygems(VALUE arg)532584Salexander.borisov@nginx.com nxt_ruby_require_rubygems(VALUE arg)
533584Salexander.borisov@nginx.com {
534584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1,
535584Salexander.borisov@nginx.com                       rb_str_new2("rubygems"));
536584Salexander.borisov@nginx.com }
537584Salexander.borisov@nginx.com 
538584Salexander.borisov@nginx.com 
539584Salexander.borisov@nginx.com static VALUE
nxt_ruby_bundler_setup(VALUE arg)540717Salexander.borisov@nginx.com nxt_ruby_bundler_setup(VALUE arg)
541717Salexander.borisov@nginx.com {
542717Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1,
543717Salexander.borisov@nginx.com                       rb_str_new2("bundler/setup"));
544717Salexander.borisov@nginx.com }
545717Salexander.borisov@nginx.com 
546717Salexander.borisov@nginx.com 
547717Salexander.borisov@nginx.com static VALUE
nxt_ruby_require_rack(VALUE arg)548584Salexander.borisov@nginx.com nxt_ruby_require_rack(VALUE arg)
549584Salexander.borisov@nginx.com {
550584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack"));
551584Salexander.borisov@nginx.com }
552584Salexander.borisov@nginx.com 
553584Salexander.borisov@nginx.com 
554584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_parse_script(VALUE ctx)555584Salexander.borisov@nginx.com nxt_ruby_rack_parse_script(VALUE ctx)
556584Salexander.borisov@nginx.com {
5571687Smax.romanov@nginx.com     VALUE                 script, res, rack, builder;
558584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
559584Salexander.borisov@nginx.com 
560584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx;
561584Salexander.borisov@nginx.com 
5621687Smax.romanov@nginx.com     rack = rb_const_get(rb_cObject, rb_intern("Rack"));
5631687Smax.romanov@nginx.com     builder = rb_const_get(rack, rb_intern("Builder"));
5641687Smax.romanov@nginx.com 
565584Salexander.borisov@nginx.com     script = rb_str_new((const char *) rack_init->script->start,
566584Salexander.borisov@nginx.com                         (long) rack_init->script->length);
567584Salexander.borisov@nginx.com 
5681687Smax.romanov@nginx.com     res = rb_funcall(builder, rb_intern("parse_file"), 1, script);
569584Salexander.borisov@nginx.com 
570584Salexander.borisov@nginx.com     rb_str_free(script);
571584Salexander.borisov@nginx.com 
572584Salexander.borisov@nginx.com     return res;
573584Salexander.borisov@nginx.com }
574584Salexander.borisov@nginx.com 
575584Salexander.borisov@nginx.com 
576584Salexander.borisov@nginx.com static VALUE
nxt_ruby_rack_env_create(VALUE arg)577584Salexander.borisov@nginx.com nxt_ruby_rack_env_create(VALUE arg)
578584Salexander.borisov@nginx.com {
5791687Smax.romanov@nginx.com     int             rc;
5801687Smax.romanov@nginx.com     VALUE           hash_env, version;
5811687Smax.romanov@nginx.com     nxt_ruby_ctx_t  *rctx;
5821687Smax.romanov@nginx.com 
5831687Smax.romanov@nginx.com     rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg;
5841687Smax.romanov@nginx.com 
5851687Smax.romanov@nginx.com     rc = nxt_ruby_init_io(rctx);
5861687Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
5871687Smax.romanov@nginx.com         return Qnil;
5881687Smax.romanov@nginx.com     }
589584Salexander.borisov@nginx.com 
590584Salexander.borisov@nginx.com     hash_env = rb_hash_new();
591673Svbart@nginx.com 
592673Svbart@nginx.com     rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"),
593673Svbart@nginx.com                  rb_str_new((const char *) nxt_server.start,
594673Svbart@nginx.com                             (long) nxt_server.length));
595673Svbart@nginx.com 
596584Salexander.borisov@nginx.com     version = rb_ary_new();
597584Salexander.borisov@nginx.com 
598584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR));
599584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
600584Salexander.borisov@nginx.com 
601*2087Sz.hong@f5.com     rb_hash_aset(hash_env, rb_str_new2("SCRIPT_NAME"), rctx->script);
602584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.version"), version);
6031687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input);
6041687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error);
6051687Smax.romanov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multithread"),
6061687Smax.romanov@nginx.com                  nxt_ruby_threads > 1 ? Qtrue : Qfalse);
607584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue);
608584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse);
609584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse);
610584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil);
611584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil);
612584Salexander.borisov@nginx.com 
6131687Smax.romanov@nginx.com     rctx->env = hash_env;
6141687Smax.romanov@nginx.com 
6151687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->env);
6161687Smax.romanov@nginx.com 
617584Salexander.borisov@nginx.com     return hash_env;
618584Salexander.borisov@nginx.com }
619584Salexander.borisov@nginx.com 
620584Salexander.borisov@nginx.com 
6211687Smax.romanov@nginx.com static int
nxt_ruby_init_io(nxt_ruby_ctx_t * rctx)6221687Smax.romanov@nginx.com nxt_ruby_init_io(nxt_ruby_ctx_t *rctx)
6231687Smax.romanov@nginx.com {
6241687Smax.romanov@nginx.com     VALUE  io_input, io_error;
6251687Smax.romanov@nginx.com 
6261687Smax.romanov@nginx.com     io_input = nxt_ruby_stream_io_input_init();
6271687Smax.romanov@nginx.com 
6281687Smax.romanov@nginx.com     rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1,
6291687Smax.romanov@nginx.com                                    (VALUE) (uintptr_t) rctx);
6301687Smax.romanov@nginx.com     if (nxt_slow_path(rctx->io_input == Qnil)) {
6311687Smax.romanov@nginx.com         nxt_unit_alert(NULL,
6321687Smax.romanov@nginx.com                        "Ruby: Failed to create environment 'rack.input' var");
6331687Smax.romanov@nginx.com 
6341687Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
6351687Smax.romanov@nginx.com     }
6361687Smax.romanov@nginx.com 
6371687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->io_input);
6381687Smax.romanov@nginx.com 
6391687Smax.romanov@nginx.com     io_error = nxt_ruby_stream_io_error_init();
6401687Smax.romanov@nginx.com 
6411687Smax.romanov@nginx.com     rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1,
6421687Smax.romanov@nginx.com                                    (VALUE) (uintptr_t) rctx);
6431687Smax.romanov@nginx.com     if (nxt_slow_path(rctx->io_error == Qnil)) {
6441687Smax.romanov@nginx.com         nxt_unit_alert(NULL,
6451687Smax.romanov@nginx.com                        "Ruby: Failed to create environment 'rack.error' var");
6461687Smax.romanov@nginx.com 
6471687Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
6481687Smax.romanov@nginx.com     }
6491687Smax.romanov@nginx.com 
6501687Smax.romanov@nginx.com     rb_gc_register_address(&rctx->io_error);
6511687Smax.romanov@nginx.com 
6521687Smax.romanov@nginx.com     return NXT_UNIT_OK;
6531687Smax.romanov@nginx.com }
6541687Smax.romanov@nginx.com 
6551687Smax.romanov@nginx.com 
656743Smax.romanov@nginx.com static void
nxt_ruby_request_handler(nxt_unit_request_info_t * req)657743Smax.romanov@nginx.com nxt_ruby_request_handler(nxt_unit_request_info_t *req)
658584Salexander.borisov@nginx.com {
6591687Smax.romanov@nginx.com     (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req);
6601687Smax.romanov@nginx.com }
6611687Smax.romanov@nginx.com 
662584Salexander.borisov@nginx.com 
6631687Smax.romanov@nginx.com static void *
nxt_ruby_request_handler_gvl(void * data)6641687Smax.romanov@nginx.com nxt_ruby_request_handler_gvl(void *data)
6651687Smax.romanov@nginx.com {
6661687Smax.romanov@nginx.com     int                      state;
6671687Smax.romanov@nginx.com     VALUE                    res;
6681687Smax.romanov@nginx.com     nxt_ruby_ctx_t           *rctx;
6691687Smax.romanov@nginx.com     nxt_unit_request_info_t  *req;
6701687Smax.romanov@nginx.com 
671