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