1/* 2 * Copyright (C) Alexander Borisov 3 * Copyright (C) NGINX, Inc. 4 */ 5 6#include <ruby/nxt_ruby.h> 7 8#include <nxt_unit.h> 9#include <nxt_unit_request.h> 10 11#include <ruby/thread.h> 12 13#include NXT_RUBY_MOUNTS_H 14 15#include <locale.h> 16 17 18#define NXT_RUBY_RACK_API_VERSION_MAJOR 1 19#define NXT_RUBY_RACK_API_VERSION_MINOR 3 20 21 22typedef struct { 23 nxt_task_t *task; 24 nxt_str_t *script; 25 nxt_ruby_ctx_t *rctx; 26} nxt_ruby_rack_init_t; 27 28 29static nxt_int_t nxt_ruby_start(nxt_task_t *task, 30 nxt_process_data_t *data); 31static VALUE nxt_ruby_init_basic(VALUE arg); 32 33static VALUE nxt_ruby_hook_procs_load(VALUE path); 34static VALUE nxt_ruby_hook_register(VALUE arg); 35static VALUE nxt_ruby_hook_call(VALUE name); 36 37static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 38 39static VALUE nxt_ruby_require_rubygems(VALUE arg); 40static VALUE nxt_ruby_bundler_setup(VALUE arg); 41static VALUE nxt_ruby_require_rack(VALUE arg); 42static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 43static VALUE nxt_ruby_rack_env_create(VALUE arg); 44static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx); 45static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); 46static void *nxt_ruby_request_handler_gvl(void *req); 47static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx); 48static void *nxt_ruby_thread_create_gvl(void *rctx); 49static VALUE nxt_ruby_thread_func(VALUE arg); 50static void *nxt_ruby_unit_run(void *ctx); 51static void nxt_ruby_ubf(void *ctx); 52static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c); 53static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, 54 nxt_ruby_app_conf_t *c); 55 56static VALUE nxt_ruby_rack_app_run(VALUE arg); 57static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env); 58nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 59 nxt_unit_sptr_t *sptr, uint32_t len); 60static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, 61 VALUE result); 62static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, 63 VALUE result, nxt_int_t status); 64static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); 65static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); 66static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, 67 VALUE result); 68static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 69 VALUE filepath); 70static void *nxt_ruby_response_write_cb(void *read_info); 71static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, 72 int argc, const VALUE *argv, VALUE blockarg); 73static void *nxt_ruby_response_write(void *body); 74 75static void nxt_ruby_exception_log(nxt_unit_request_info_t *req, 76 uint32_t level, const char *desc); 77 78static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx); 79static void nxt_ruby_atexit(void); 80 81 82static uint32_t compat[] = { 83 NXT_VERNUM, NXT_DEBUG, 84}; 85 86static VALUE nxt_ruby_hook_procs; 87static VALUE nxt_ruby_rackup; 88static VALUE nxt_ruby_call; 89 90static uint32_t nxt_ruby_threads; 91static nxt_ruby_ctx_t *nxt_ruby_ctxs; 92 93NXT_EXPORT nxt_app_module_t nxt_app_module = { 94 sizeof(compat), 95 compat, 96 nxt_string("ruby"), 97 ruby_version, 98 nxt_ruby_mounts, 99 nxt_nitems(nxt_ruby_mounts), 100 NULL, 101 nxt_ruby_start, 102}; 103 104typedef struct { 105 nxt_str_t string; 106 VALUE *v; 107} nxt_ruby_string_t; 108 109static VALUE nxt_rb_80_str; 110static VALUE nxt_rb_content_length_str; 111static VALUE nxt_rb_content_type_str; 112static VALUE nxt_rb_http_str; 113static VALUE nxt_rb_https_str; 114static VALUE nxt_rb_path_info_str; 115static VALUE nxt_rb_query_string_str; 116static VALUE nxt_rb_rack_url_scheme_str; 117static VALUE nxt_rb_remote_addr_str; 118static VALUE nxt_rb_request_method_str; 119static VALUE nxt_rb_request_uri_str; 120static VALUE nxt_rb_server_addr_str; 121static VALUE nxt_rb_server_name_str; 122static VALUE nxt_rb_server_port_str; 123static VALUE nxt_rb_server_protocol_str; 124static VALUE nxt_rb_on_worker_boot; 125static VALUE nxt_rb_on_worker_shutdown; 126static VALUE nxt_rb_on_thread_boot; 127static VALUE nxt_rb_on_thread_shutdown; 128 129static nxt_ruby_string_t nxt_rb_strings[] = { 130 { nxt_string("80"), &nxt_rb_80_str }, 131 { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str }, 132 { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str }, 133 { nxt_string("http"), &nxt_rb_http_str }, 134 { nxt_string("https"), &nxt_rb_https_str }, 135 { nxt_string("PATH_INFO"), &nxt_rb_path_info_str }, 136 { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str }, 137 { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str }, 138 { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str }, 139 { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str }, 140 { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str }, 141 { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str }, 142 { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, 143 { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, 144 { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, 145 { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot }, 146 { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown }, 147 { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot }, 148 { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown }, 149 { nxt_null_string, NULL }, 150}; 151 152 153static int 154nxt_ruby_init_strings(void) 155{ 156 VALUE v; 157 nxt_ruby_string_t *pstr; 158 159 pstr = nxt_rb_strings; 160 161 while (pstr->string.start != NULL) { 162 v = rb_str_new_static((char *) pstr->string.start, pstr->string.length); 163 164 if (nxt_slow_path(v == Qnil)) { 165 nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'", 166 (int) pstr->string.length, 167 (char *) pstr->string.start); 168 169 return NXT_UNIT_ERROR; 170 } 171 172 *pstr->v = v; 173 174 rb_gc_register_address(pstr->v); 175 176 pstr++; 177 } 178 179 return NXT_UNIT_OK; 180} 181 182 183static void 184nxt_ruby_done_strings(void) 185{ 186 nxt_ruby_string_t *pstr; 187 188 pstr = nxt_rb_strings; 189 190 while (pstr->string.start != NULL) { 191 rb_gc_unregister_address(pstr->v); 192 193 *pstr->v = Qnil; 194 195 pstr++; 196 } 197} 198 199 200static VALUE 201nxt_ruby_hook_procs_load(VALUE path) 202{ 203 VALUE module, file, file_obj; 204 205 module = rb_define_module("Unit"); 206 207 nxt_ruby_hook_procs = rb_hash_new(); 208 209 rb_gc_register_address(&nxt_ruby_hook_procs); 210 211 rb_define_module_function(module, "on_worker_boot", 212 &nxt_ruby_hook_register, 0); 213 rb_define_module_function(module, "on_worker_shutdown", 214 &nxt_ruby_hook_register, 0); 215 rb_define_module_function(module, "on_thread_boot", 216 &nxt_ruby_hook_register, 0); 217 rb_define_module_function(module, "on_thread_shutdown", 218 &nxt_ruby_hook_register, 0); 219 220 file = rb_const_get(rb_cObject, rb_intern("File")); 221 file_obj = rb_funcall(file, rb_intern("read"), 1, path); 222 223 return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path, 224 INT2NUM(1)); 225} 226 227 228static VALUE 229nxt_ruby_hook_register(VALUE arg) 230{ 231 VALUE kernel, callee, callee_str; 232 233 rb_need_block(); 234 235 kernel = rb_const_get(rb_cObject, rb_intern("Kernel")); 236 callee = rb_funcall(kernel, rb_intern("__callee__"), 0); 237 callee_str = rb_funcall(callee, rb_intern("to_s"), 0); 238 239 rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc()); 240 241 return Qnil; 242} 243 244 245static VALUE 246nxt_ruby_hook_call(VALUE name) 247{ 248 VALUE proc; 249 250 proc = rb_hash_lookup(nxt_ruby_hook_procs, name); 251 if (proc == Qnil) { 252 return Qnil; 253 } 254 255 return rb_funcall(proc, rb_intern("call"), 0); 256} 257 258 259static nxt_int_t 260nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) 261{ 262 int state, rc; 263 VALUE res, path; 264 nxt_ruby_ctx_t ruby_ctx; 265 nxt_unit_ctx_t *unit_ctx; 266 nxt_unit_init_t ruby_unit_init; 267 nxt_ruby_app_conf_t *c; 268 nxt_ruby_rack_init_t rack_init; 269 nxt_common_app_conf_t *conf; 270 271 static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; 272 273 conf = data->app; 274 c = &conf->u.ruby; 275 276 nxt_ruby_threads = c->threads; 277 278 setlocale(LC_CTYPE, ""); 279 280 RUBY_INIT_STACK 281 ruby_init(); 282 ruby_options(2, argv); 283 ruby_script("NGINX_Unit"); 284 285 ruby_ctx.env = Qnil; 286 ruby_ctx.io_input = Qnil; 287 ruby_ctx.io_error = Qnil; 288 ruby_ctx.thread = Qnil; 289 ruby_ctx.ctx = NULL; 290 ruby_ctx.req = NULL; 291 292 rack_init.task = task; 293 rack_init.script = &c->script; 294 rack_init.rctx = &ruby_ctx; 295 296 nxt_ruby_init_strings(); 297 298 res = rb_protect(nxt_ruby_init_basic, 299 (VALUE) (uintptr_t) &rack_init, &state); 300 if (nxt_slow_path(res == Qnil || state != 0)) { 301 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 302 "Failed to init basic variables"); 303 return NXT_ERROR; 304 } 305 306 nxt_ruby_call = Qnil; 307 nxt_ruby_hook_procs = Qnil; 308 309 if (c->hooks.start != NULL) { 310 path = rb_str_new((const char *) c->hooks.start, 311 (long) c->hooks.length); 312 313 rb_protect(nxt_ruby_hook_procs_load, path, &state); 314 rb_str_free(path); 315 if (nxt_slow_path(state != 0)) { 316 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 317 "Failed to setup hooks"); 318 return NXT_ERROR; 319 } 320 } 321 322 if (nxt_ruby_hook_procs != Qnil) { 323 rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state); 324 if (nxt_slow_path(state != 0)) { 325 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 326 "Failed to call on_worker_boot()"); 327 return NXT_ERROR; 328 } 329 } 330 331 nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); 332 if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { 333 return NXT_ERROR; 334 } 335 336 rb_gc_register_address(&nxt_ruby_rackup); 337 338 nxt_ruby_call = rb_intern("call"); 339 if (nxt_slow_path(nxt_ruby_call == Qnil)) { 340 nxt_alert(task, "Ruby: Unable to find rack entry point"); 341 342 goto fail; 343 } 344 345 rb_gc_register_address(&nxt_ruby_call); 346 347 ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create, 348 (VALUE) (uintptr_t) &ruby_ctx, &state); 349 if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) { 350 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 351 "Failed to create 'environ' variable"); 352 goto fail; 353 } 354 355 rc = nxt_ruby_init_threads(c); 356 if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 357 goto fail; 358 } 359
| 1/* 2 * Copyright (C) Alexander Borisov 3 * Copyright (C) NGINX, Inc. 4 */ 5 6#include <ruby/nxt_ruby.h> 7 8#include <nxt_unit.h> 9#include <nxt_unit_request.h> 10 11#include <ruby/thread.h> 12 13#include NXT_RUBY_MOUNTS_H 14 15#include <locale.h> 16 17 18#define NXT_RUBY_RACK_API_VERSION_MAJOR 1 19#define NXT_RUBY_RACK_API_VERSION_MINOR 3 20 21 22typedef struct { 23 nxt_task_t *task; 24 nxt_str_t *script; 25 nxt_ruby_ctx_t *rctx; 26} nxt_ruby_rack_init_t; 27 28 29static nxt_int_t nxt_ruby_start(nxt_task_t *task, 30 nxt_process_data_t *data); 31static VALUE nxt_ruby_init_basic(VALUE arg); 32 33static VALUE nxt_ruby_hook_procs_load(VALUE path); 34static VALUE nxt_ruby_hook_register(VALUE arg); 35static VALUE nxt_ruby_hook_call(VALUE name); 36 37static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 38 39static VALUE nxt_ruby_require_rubygems(VALUE arg); 40static VALUE nxt_ruby_bundler_setup(VALUE arg); 41static VALUE nxt_ruby_require_rack(VALUE arg); 42static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 43static VALUE nxt_ruby_rack_env_create(VALUE arg); 44static int nxt_ruby_init_io(nxt_ruby_ctx_t *rctx); 45static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); 46static void *nxt_ruby_request_handler_gvl(void *req); 47static int nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx); 48static void *nxt_ruby_thread_create_gvl(void *rctx); 49static VALUE nxt_ruby_thread_func(VALUE arg); 50static void *nxt_ruby_unit_run(void *ctx); 51static void nxt_ruby_ubf(void *ctx); 52static int nxt_ruby_init_threads(nxt_ruby_app_conf_t *c); 53static void nxt_ruby_join_threads(nxt_unit_ctx_t *ctx, 54 nxt_ruby_app_conf_t *c); 55 56static VALUE nxt_ruby_rack_app_run(VALUE arg); 57static int nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env); 58nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 59 nxt_unit_sptr_t *sptr, uint32_t len); 60static nxt_int_t nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, 61 VALUE result); 62static int nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, 63 VALUE result, nxt_int_t status); 64static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); 65static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); 66static int nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, 67 VALUE result); 68static int nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 69 VALUE filepath); 70static void *nxt_ruby_response_write_cb(void *read_info); 71static VALUE nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, 72 int argc, const VALUE *argv, VALUE blockarg); 73static void *nxt_ruby_response_write(void *body); 74 75static void nxt_ruby_exception_log(nxt_unit_request_info_t *req, 76 uint32_t level, const char *desc); 77 78static void nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx); 79static void nxt_ruby_atexit(void); 80 81 82static uint32_t compat[] = { 83 NXT_VERNUM, NXT_DEBUG, 84}; 85 86static VALUE nxt_ruby_hook_procs; 87static VALUE nxt_ruby_rackup; 88static VALUE nxt_ruby_call; 89 90static uint32_t nxt_ruby_threads; 91static nxt_ruby_ctx_t *nxt_ruby_ctxs; 92 93NXT_EXPORT nxt_app_module_t nxt_app_module = { 94 sizeof(compat), 95 compat, 96 nxt_string("ruby"), 97 ruby_version, 98 nxt_ruby_mounts, 99 nxt_nitems(nxt_ruby_mounts), 100 NULL, 101 nxt_ruby_start, 102}; 103 104typedef struct { 105 nxt_str_t string; 106 VALUE *v; 107} nxt_ruby_string_t; 108 109static VALUE nxt_rb_80_str; 110static VALUE nxt_rb_content_length_str; 111static VALUE nxt_rb_content_type_str; 112static VALUE nxt_rb_http_str; 113static VALUE nxt_rb_https_str; 114static VALUE nxt_rb_path_info_str; 115static VALUE nxt_rb_query_string_str; 116static VALUE nxt_rb_rack_url_scheme_str; 117static VALUE nxt_rb_remote_addr_str; 118static VALUE nxt_rb_request_method_str; 119static VALUE nxt_rb_request_uri_str; 120static VALUE nxt_rb_server_addr_str; 121static VALUE nxt_rb_server_name_str; 122static VALUE nxt_rb_server_port_str; 123static VALUE nxt_rb_server_protocol_str; 124static VALUE nxt_rb_on_worker_boot; 125static VALUE nxt_rb_on_worker_shutdown; 126static VALUE nxt_rb_on_thread_boot; 127static VALUE nxt_rb_on_thread_shutdown; 128 129static nxt_ruby_string_t nxt_rb_strings[] = { 130 { nxt_string("80"), &nxt_rb_80_str }, 131 { nxt_string("CONTENT_LENGTH"), &nxt_rb_content_length_str }, 132 { nxt_string("CONTENT_TYPE"), &nxt_rb_content_type_str }, 133 { nxt_string("http"), &nxt_rb_http_str }, 134 { nxt_string("https"), &nxt_rb_https_str }, 135 { nxt_string("PATH_INFO"), &nxt_rb_path_info_str }, 136 { nxt_string("QUERY_STRING"), &nxt_rb_query_string_str }, 137 { nxt_string("rack.url_scheme"), &nxt_rb_rack_url_scheme_str }, 138 { nxt_string("REMOTE_ADDR"), &nxt_rb_remote_addr_str }, 139 { nxt_string("REQUEST_METHOD"), &nxt_rb_request_method_str }, 140 { nxt_string("REQUEST_URI"), &nxt_rb_request_uri_str }, 141 { nxt_string("SERVER_ADDR"), &nxt_rb_server_addr_str }, 142 { nxt_string("SERVER_NAME"), &nxt_rb_server_name_str }, 143 { nxt_string("SERVER_PORT"), &nxt_rb_server_port_str }, 144 { nxt_string("SERVER_PROTOCOL"), &nxt_rb_server_protocol_str }, 145 { nxt_string("on_worker_boot"), &nxt_rb_on_worker_boot }, 146 { nxt_string("on_worker_shutdown"), &nxt_rb_on_worker_shutdown }, 147 { nxt_string("on_thread_boot"), &nxt_rb_on_thread_boot }, 148 { nxt_string("on_thread_shutdown"), &nxt_rb_on_thread_shutdown }, 149 { nxt_null_string, NULL }, 150}; 151 152 153static int 154nxt_ruby_init_strings(void) 155{ 156 VALUE v; 157 nxt_ruby_string_t *pstr; 158 159 pstr = nxt_rb_strings; 160 161 while (pstr->string.start != NULL) { 162 v = rb_str_new_static((char *) pstr->string.start, pstr->string.length); 163 164 if (nxt_slow_path(v == Qnil)) { 165 nxt_unit_alert(NULL, "Ruby: failed to create const string '%.*s'", 166 (int) pstr->string.length, 167 (char *) pstr->string.start); 168 169 return NXT_UNIT_ERROR; 170 } 171 172 *pstr->v = v; 173 174 rb_gc_register_address(pstr->v); 175 176 pstr++; 177 } 178 179 return NXT_UNIT_OK; 180} 181 182 183static void 184nxt_ruby_done_strings(void) 185{ 186 nxt_ruby_string_t *pstr; 187 188 pstr = nxt_rb_strings; 189 190 while (pstr->string.start != NULL) { 191 rb_gc_unregister_address(pstr->v); 192 193 *pstr->v = Qnil; 194 195 pstr++; 196 } 197} 198 199 200static VALUE 201nxt_ruby_hook_procs_load(VALUE path) 202{ 203 VALUE module, file, file_obj; 204 205 module = rb_define_module("Unit"); 206 207 nxt_ruby_hook_procs = rb_hash_new(); 208 209 rb_gc_register_address(&nxt_ruby_hook_procs); 210 211 rb_define_module_function(module, "on_worker_boot", 212 &nxt_ruby_hook_register, 0); 213 rb_define_module_function(module, "on_worker_shutdown", 214 &nxt_ruby_hook_register, 0); 215 rb_define_module_function(module, "on_thread_boot", 216 &nxt_ruby_hook_register, 0); 217 rb_define_module_function(module, "on_thread_shutdown", 218 &nxt_ruby_hook_register, 0); 219 220 file = rb_const_get(rb_cObject, rb_intern("File")); 221 file_obj = rb_funcall(file, rb_intern("read"), 1, path); 222 223 return rb_funcall(module, rb_intern("module_eval"), 3, file_obj, path, 224 INT2NUM(1)); 225} 226 227 228static VALUE 229nxt_ruby_hook_register(VALUE arg) 230{ 231 VALUE kernel, callee, callee_str; 232 233 rb_need_block(); 234 235 kernel = rb_const_get(rb_cObject, rb_intern("Kernel")); 236 callee = rb_funcall(kernel, rb_intern("__callee__"), 0); 237 callee_str = rb_funcall(callee, rb_intern("to_s"), 0); 238 239 rb_hash_aset(nxt_ruby_hook_procs, callee_str, rb_block_proc()); 240 241 return Qnil; 242} 243 244 245static VALUE 246nxt_ruby_hook_call(VALUE name) 247{ 248 VALUE proc; 249 250 proc = rb_hash_lookup(nxt_ruby_hook_procs, name); 251 if (proc == Qnil) { 252 return Qnil; 253 } 254 255 return rb_funcall(proc, rb_intern("call"), 0); 256} 257 258 259static nxt_int_t 260nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) 261{ 262 int state, rc; 263 VALUE res, path; 264 nxt_ruby_ctx_t ruby_ctx; 265 nxt_unit_ctx_t *unit_ctx; 266 nxt_unit_init_t ruby_unit_init; 267 nxt_ruby_app_conf_t *c; 268 nxt_ruby_rack_init_t rack_init; 269 nxt_common_app_conf_t *conf; 270 271 static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; 272 273 conf = data->app; 274 c = &conf->u.ruby; 275 276 nxt_ruby_threads = c->threads; 277 278 setlocale(LC_CTYPE, ""); 279 280 RUBY_INIT_STACK 281 ruby_init(); 282 ruby_options(2, argv); 283 ruby_script("NGINX_Unit"); 284 285 ruby_ctx.env = Qnil; 286 ruby_ctx.io_input = Qnil; 287 ruby_ctx.io_error = Qnil; 288 ruby_ctx.thread = Qnil; 289 ruby_ctx.ctx = NULL; 290 ruby_ctx.req = NULL; 291 292 rack_init.task = task; 293 rack_init.script = &c->script; 294 rack_init.rctx = &ruby_ctx; 295 296 nxt_ruby_init_strings(); 297 298 res = rb_protect(nxt_ruby_init_basic, 299 (VALUE) (uintptr_t) &rack_init, &state); 300 if (nxt_slow_path(res == Qnil || state != 0)) { 301 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 302 "Failed to init basic variables"); 303 return NXT_ERROR; 304 } 305 306 nxt_ruby_call = Qnil; 307 nxt_ruby_hook_procs = Qnil; 308 309 if (c->hooks.start != NULL) { 310 path = rb_str_new((const char *) c->hooks.start, 311 (long) c->hooks.length); 312 313 rb_protect(nxt_ruby_hook_procs_load, path, &state); 314 rb_str_free(path); 315 if (nxt_slow_path(state != 0)) { 316 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 317 "Failed to setup hooks"); 318 return NXT_ERROR; 319 } 320 } 321 322 if (nxt_ruby_hook_procs != Qnil) { 323 rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_boot, &state); 324 if (nxt_slow_path(state != 0)) { 325 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 326 "Failed to call on_worker_boot()"); 327 return NXT_ERROR; 328 } 329 } 330 331 nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); 332 if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { 333 return NXT_ERROR; 334 } 335 336 rb_gc_register_address(&nxt_ruby_rackup); 337 338 nxt_ruby_call = rb_intern("call"); 339 if (nxt_slow_path(nxt_ruby_call == Qnil)) { 340 nxt_alert(task, "Ruby: Unable to find rack entry point"); 341 342 goto fail; 343 } 344 345 rb_gc_register_address(&nxt_ruby_call); 346 347 ruby_ctx.env = rb_protect(nxt_ruby_rack_env_create, 348 (VALUE) (uintptr_t) &ruby_ctx, &state); 349 if (nxt_slow_path(ruby_ctx.env == Qnil || state != 0)) { 350 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 351 "Failed to create 'environ' variable"); 352 goto fail; 353 } 354 355 rc = nxt_ruby_init_threads(c); 356 if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 357 goto fail; 358 } 359
|
365 ruby_unit_init.data = c; 366 ruby_unit_init.ctx_data = &ruby_ctx; 367 368 unit_ctx = nxt_unit_init(&ruby_unit_init); 369 if (nxt_slow_path(unit_ctx == NULL)) { 370 goto fail; 371 } 372 373 if (nxt_ruby_hook_procs != Qnil) { 374 rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); 375 if (nxt_slow_path(state != 0)) { 376 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 377 "Failed to call on_thread_boot()"); 378 } 379 } 380 381 rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, 382 nxt_ruby_ubf, unit_ctx); 383 384 if (nxt_ruby_hook_procs != Qnil) { 385 rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); 386 if (nxt_slow_path(state != 0)) { 387 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 388 "Failed to call on_thread_shutdown()"); 389 } 390 } 391 392 nxt_ruby_join_threads(unit_ctx, c); 393 394 if (nxt_ruby_hook_procs != Qnil) { 395 rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state); 396 if (nxt_slow_path(state != 0)) { 397 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 398 "Failed to call on_worker_shutdown()"); 399 } 400 } 401 402 nxt_unit_done(unit_ctx); 403 404 nxt_ruby_ctx_done(&ruby_ctx); 405 406 nxt_ruby_atexit(); 407 408 exit(rc); 409 410 return NXT_OK; 411 412fail: 413 414 nxt_ruby_join_threads(NULL, c); 415 416 nxt_ruby_ctx_done(&ruby_ctx); 417 418 nxt_ruby_atexit(); 419 420 return NXT_ERROR; 421} 422 423 424static VALUE 425nxt_ruby_init_basic(VALUE arg) 426{ 427 int state; 428 nxt_ruby_rack_init_t *rack_init; 429 430 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; 431 432 state = rb_enc_find_index("encdb"); 433 if (nxt_slow_path(state == 0)) { 434 nxt_alert(rack_init->task, 435 "Ruby: Failed to find encoding index 'encdb'"); 436 437 return Qnil; 438 } 439 440 rb_funcall(rb_cObject, rb_intern("require"), 1, 441 rb_str_new2("enc/trans/transdb")); 442 443 return arg; 444} 445 446 447static VALUE 448nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) 449{ 450 int state; 451 VALUE rackup, err; 452 453 rb_protect(nxt_ruby_require_rubygems, Qnil, &state); 454 if (nxt_slow_path(state != 0)) { 455 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 456 "Failed to require 'rubygems' package"); 457 return Qnil; 458 } 459 460 rb_protect(nxt_ruby_bundler_setup, Qnil, &state); 461 if (state != 0) { 462 err = rb_errinfo(); 463 464 if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { 465 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 466 "Failed to require 'bundler/setup' package"); 467 return Qnil; 468 } 469 470 rb_set_errinfo(Qnil); 471 } 472 473 rb_protect(nxt_ruby_require_rack, Qnil, &state); 474 if (nxt_slow_path(state != 0)) { 475 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 476 "Failed to require 'rack' package"); 477 return Qnil; 478 } 479 480 rackup = rb_protect(nxt_ruby_rack_parse_script, 481 (VALUE) (uintptr_t) rack_init, &state); 482 if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { 483 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 484 "Failed to parse rack script"); 485 return Qnil; 486 } 487 488 if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { 489 nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); 490 return Qnil; 491 } 492 493 return RARRAY_PTR(rackup)[0]; 494} 495 496 497static VALUE 498nxt_ruby_require_rubygems(VALUE arg) 499{ 500 return rb_funcall(rb_cObject, rb_intern("require"), 1, 501 rb_str_new2("rubygems")); 502} 503 504 505static VALUE 506nxt_ruby_bundler_setup(VALUE arg) 507{ 508 return rb_funcall(rb_cObject, rb_intern("require"), 1, 509 rb_str_new2("bundler/setup")); 510} 511 512 513static VALUE 514nxt_ruby_require_rack(VALUE arg) 515{ 516 return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 517} 518 519 520static VALUE 521nxt_ruby_rack_parse_script(VALUE ctx) 522{ 523 VALUE script, res, rack, builder; 524 nxt_ruby_rack_init_t *rack_init; 525 526 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; 527 528 rack = rb_const_get(rb_cObject, rb_intern("Rack")); 529 builder = rb_const_get(rack, rb_intern("Builder")); 530 531 script = rb_str_new((const char *) rack_init->script->start, 532 (long) rack_init->script->length); 533 534 res = rb_funcall(builder, rb_intern("parse_file"), 1, script); 535 536 rb_str_free(script); 537 538 return res; 539} 540 541 542static VALUE 543nxt_ruby_rack_env_create(VALUE arg) 544{ 545 int rc; 546 VALUE hash_env, version; 547 nxt_ruby_ctx_t *rctx; 548 549 rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 550 551 rc = nxt_ruby_init_io(rctx); 552 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 553 return Qnil; 554 } 555 556 hash_env = rb_hash_new(); 557 558 rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 559 rb_str_new((const char *) nxt_server.start, 560 (long) nxt_server.length)); 561 562 version = rb_ary_new(); 563 564 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 565 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 566 567 rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 568 rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input); 569 rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error); 570 rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), 571 nxt_ruby_threads > 1 ? Qtrue : Qfalse); 572 rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 573 rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 574 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 575 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 576 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 577 578 rctx->env = hash_env; 579 580 rb_gc_register_address(&rctx->env); 581 582 return hash_env; 583} 584 585 586static int 587nxt_ruby_init_io(nxt_ruby_ctx_t *rctx) 588{ 589 VALUE io_input, io_error; 590 591 io_input = nxt_ruby_stream_io_input_init(); 592 593 rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1, 594 (VALUE) (uintptr_t) rctx); 595 if (nxt_slow_path(rctx->io_input == Qnil)) { 596 nxt_unit_alert(NULL, 597 "Ruby: Failed to create environment 'rack.input' var"); 598 599 return NXT_UNIT_ERROR; 600 } 601 602 rb_gc_register_address(&rctx->io_input); 603 604 io_error = nxt_ruby_stream_io_error_init(); 605 606 rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1, 607 (VALUE) (uintptr_t) rctx); 608 if (nxt_slow_path(rctx->io_error == Qnil)) { 609 nxt_unit_alert(NULL, 610 "Ruby: Failed to create environment 'rack.error' var"); 611 612 return NXT_UNIT_ERROR; 613 } 614 615 rb_gc_register_address(&rctx->io_error); 616 617 return NXT_UNIT_OK; 618} 619 620 621static void 622nxt_ruby_request_handler(nxt_unit_request_info_t *req) 623{ 624 (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req); 625} 626 627 628static void * 629nxt_ruby_request_handler_gvl(void *data) 630{ 631 int state; 632 VALUE res; 633 nxt_ruby_ctx_t *rctx; 634 nxt_unit_request_info_t *req; 635 636 req = data; 637 638 rctx = req->ctx->data; 639 rctx->req = req; 640 641 res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state); 642 if (nxt_slow_path(res == Qnil || state != 0)) { 643 nxt_ruby_exception_log(req, NXT_LOG_ERR, 644 "Failed to run ruby script"); 645 646 nxt_unit_request_done(req, NXT_UNIT_ERROR); 647 648 } else { 649 nxt_unit_request_done(req, NXT_UNIT_OK); 650 } 651 652 rctx->req = NULL; 653 654 return NULL; 655} 656 657 658static VALUE 659nxt_ruby_rack_app_run(VALUE arg) 660{ 661 int rc; 662 VALUE env, result; 663 nxt_int_t status; 664 nxt_ruby_ctx_t *rctx; 665 nxt_unit_request_info_t *req; 666 667 req = (nxt_unit_request_info_t *) arg; 668 669 rctx = req->ctx->data; 670 671 env = rb_hash_dup(rctx->env); 672 673 rc = nxt_ruby_read_request(req, env); 674 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 675 nxt_unit_req_alert(req, 676 "Ruby: Failed to process incoming request"); 677 678 goto fail; 679 } 680 681 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 682 if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 683 nxt_unit_req_error(req, 684 "Ruby: Invalid response format from application"); 685 686 goto fail; 687 } 688 689 if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 690 nxt_unit_req_error(req, 691 "Ruby: Invalid response format from application. " 692 "Need 3 entries [Status, Headers, Body]"); 693 694 goto fail; 695 } 696 697 status = nxt_ruby_rack_result_status(req, result); 698 if (nxt_slow_path(status < 0)) { 699 nxt_unit_req_error(req, 700 "Ruby: Invalid response status from application."); 701 702 goto fail; 703 } 704 705 rc = nxt_ruby_rack_result_headers(req, result, status); 706 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 707 goto fail; 708 } 709 710 rc = nxt_ruby_rack_result_body(req, result); 711 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 712 goto fail; 713 } 714 715 rb_hash_delete(env, rb_obj_id(env)); 716 717 return result; 718 719fail: 720 721 rb_hash_delete(env, rb_obj_id(env)); 722 723 return Qnil; 724} 725 726 727static int 728nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) 729{ 730 VALUE name; 731 uint32_t i; 732 nxt_unit_field_t *f; 733 nxt_unit_request_t *r; 734 735 r = req->request; 736 737 nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, 738 r->method_length); 739 nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target, 740 r->target_length); 741 nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length); 742 nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query, 743 r->query_length); 744 nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version, 745 r->version_length); 746 nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, 747 r->remote_length); 748 nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, 749 r->local_length); 750 nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, 751 r->server_name_length); 752 753 rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str); 754 755 rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str, 756 r->tls ? nxt_rb_https_str : nxt_rb_http_str); 757 758 for (i = 0; i < r->fields_count; i++) { 759 f = r->fields + i; 760 761 name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length); 762 763 nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length); 764 } 765 766 if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 767 f = r->fields + r->content_length_field; 768 769 nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str, 770 &f->value, f->value_length); 771 } 772 773 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 774 f = r->fields + r->content_type_field; 775 776 nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str, 777 &f->value, f->value_length); 778 } 779 780 return NXT_UNIT_OK; 781} 782 783 784nxt_inline void 785nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 786 nxt_unit_sptr_t *sptr, uint32_t len) 787{ 788 char *str; 789 790 str = nxt_unit_sptr_get(sptr); 791 792 rb_hash_aset(hash_env, name, rb_str_new(str, len)); 793} 794 795 796static nxt_int_t 797nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result) 798{ 799 VALUE status; 800 801 status = rb_ary_entry(result, 0); 802 803 if (TYPE(status) == T_FIXNUM) { 804 return FIX2INT(status); 805 } 806 807 if (TYPE(status) == T_STRING) { 808 return nxt_int_parse((u_char *) RSTRING_PTR(status), 809 RSTRING_LEN(status)); 810 } 811 812 nxt_unit_req_error(req, "Ruby: Invalid response 'status' " 813 "format from application"); 814 815 return -2; 816} 817 818 819typedef struct { 820 int rc; 821 uint32_t fields; 822 uint32_t size; 823 nxt_unit_request_info_t *req; 824} nxt_ruby_headers_info_t; 825 826 827static int 828nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result, 829 nxt_int_t status) 830{ 831 int rc; 832 VALUE headers; 833 nxt_ruby_headers_info_t headers_info; 834 835 headers = rb_ary_entry(result, 1); 836 if (nxt_slow_path(TYPE(headers) != T_HASH)) { 837 nxt_unit_req_error(req, 838 "Ruby: Invalid response 'headers' format from " 839 "application"); 840 841 return NXT_UNIT_ERROR; 842 } 843 844 rc = NXT_UNIT_OK; 845 846 headers_info.rc = NXT_UNIT_OK; 847 headers_info.fields = 0; 848 headers_info.size = 0; 849 headers_info.req = req; 850 851 rb_hash_foreach(headers, nxt_ruby_hash_info, 852 (VALUE) (uintptr_t) &headers_info); 853 if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { 854 return headers_info.rc; 855 } 856 857 rc = nxt_unit_response_init(req, status, 858 headers_info.fields, headers_info.size); 859 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 860 return rc; 861 } 862 863 rb_hash_foreach(headers, nxt_ruby_hash_add, 864 (VALUE) (uintptr_t) &headers_info); 865 866 return rc; 867} 868 869 870static int 871nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) 872{ 873 const char *value, *value_end, *pos; 874 nxt_ruby_headers_info_t *headers_info; 875 876 headers_info = (void *) (uintptr_t) arg; 877 878 if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 879 nxt_unit_req_error(headers_info->req, 880 "Ruby: Wrong header entry 'key' from application"); 881 882 goto fail; 883 } 884 885 if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 886 nxt_unit_req_error(headers_info->req, 887 "Ruby: Wrong header entry 'value' from application"); 888 889 goto fail; 890 } 891 892 value = RSTRING_PTR(r_value); 893 value_end = value + RSTRING_LEN(r_value); 894 895 pos = value; 896 897 for ( ;; ) { 898 pos = strchr(pos, '\n'); 899 900 if (pos == NULL) { 901 break; 902 } 903 904 headers_info->fields++; 905 headers_info->size += RSTRING_LEN(r_key) + (pos - value); 906 907 pos++; 908 value = pos; 909 } 910 911 if (value <= value_end) { 912 headers_info->fields++; 913 headers_info->size += RSTRING_LEN(r_key) + (value_end - value); 914 } 915 916 return ST_CONTINUE; 917 918fail: 919 920 headers_info->rc = NXT_UNIT_ERROR; 921 922 return ST_STOP; 923} 924 925 926static int 927nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) 928{ 929 int *rc; 930 uint32_t key_len; 931 const char *value, *value_end, *pos; 932 nxt_ruby_headers_info_t *headers_info; 933 934 headers_info = (void *) (uintptr_t) arg; 935 rc = &headers_info->rc; 936 937 value = RSTRING_PTR(r_value); 938 value_end = value + RSTRING_LEN(r_value); 939 940 key_len = RSTRING_LEN(r_key); 941 942 pos = value; 943 944 for ( ;; ) { 945 pos = strchr(pos, '\n'); 946 947 if (pos == NULL) { 948 break; 949 } 950 951 *rc = nxt_unit_response_add_field(headers_info->req, 952 RSTRING_PTR(r_key), key_len, 953 value, pos - value); 954 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 955 goto fail; 956 } 957 958 pos++; 959 value = pos; 960 } 961 962 if (value <= value_end) { 963 *rc = nxt_unit_response_add_field(headers_info->req, 964 RSTRING_PTR(r_key), key_len, 965 value, value_end - value); 966 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 967 goto fail; 968 } 969 } 970 971 return ST_CONTINUE; 972 973fail: 974 975 *rc = NXT_UNIT_ERROR; 976 977 return ST_STOP; 978} 979 980 981static int 982nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result) 983{ 984 int rc; 985 VALUE fn, body; 986 987 body = rb_ary_entry(result, 2); 988 989 if (rb_respond_to(body, rb_intern("to_path"))) { 990 991 fn = rb_funcall(body, rb_intern("to_path"), 0); 992 if (nxt_slow_path(TYPE(fn) != T_STRING)) { 993 nxt_unit_req_error(req, 994 "Ruby: Failed to get 'body' file path from " 995 "application"); 996 997 return NXT_UNIT_ERROR; 998 } 999 1000 rc = nxt_ruby_rack_result_body_file_write(req, fn); 1001 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1002 return rc; 1003 } 1004 1005 } else if (rb_respond_to(body, rb_intern("each"))) { 1006 rb_block_call(body, rb_intern("each"), 0, 0, 1007 nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req); 1008 1009 } else { 1010 nxt_unit_req_error(req, 1011 "Ruby: Invalid response 'body' format " 1012 "from application"); 1013 1014 return NXT_UNIT_ERROR; 1015 } 1016 1017 if (rb_respond_to(body, rb_intern("close"))) { 1018 rb_funcall(body, rb_intern("close"), 0); 1019 } 1020 1021 return NXT_UNIT_OK; 1022} 1023 1024 1025typedef struct { 1026 int fd; 1027 off_t pos; 1028 off_t rest; 1029} nxt_ruby_rack_file_t; 1030 1031 1032static ssize_t 1033nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) 1034{ 1035 ssize_t res; 1036 nxt_ruby_rack_file_t *file; 1037 1038 file = read_info->data; 1039 1040 size = nxt_min(size, (size_t) file->rest); 1041 1042 res = pread(file->fd, dst, size, file->pos); 1043 1044 if (res >= 0) { 1045 file->pos += res; 1046 file->rest -= res; 1047 1048 if (size > (size_t) res) { 1049 file->rest = 0; 1050 } 1051 } 1052 1053 read_info->eof = file->rest == 0; 1054 1055 return res; 1056} 1057 1058 1059typedef struct { 1060 nxt_unit_read_info_t read_info; 1061 nxt_unit_request_info_t *req; 1062} nxt_ruby_read_info_t; 1063 1064 1065static int 1066nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 1067 VALUE filepath) 1068{ 1069 int fd, rc; 1070 struct stat finfo; 1071 nxt_ruby_rack_file_t ruby_file; 1072 nxt_ruby_read_info_t ri; 1073 1074 fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); 1075 if (nxt_slow_path(fd == -1)) { 1076 nxt_unit_req_error(req, 1077 "Ruby: Failed to open content file \"%s\": %s (%d)", 1078 RSTRING_PTR(filepath), strerror(errno), errno); 1079 1080 return NXT_UNIT_ERROR; 1081 } 1082 1083 rc = fstat(fd, &finfo); 1084 if (nxt_slow_path(rc == -1)) { 1085 nxt_unit_req_error(req, 1086 "Ruby: Content file fstat(\"%s\") failed: %s (%d)", 1087 RSTRING_PTR(filepath), strerror(errno), errno); 1088 1089 close(fd); 1090 1091 return NXT_UNIT_ERROR; 1092 } 1093 1094 ruby_file.fd = fd; 1095 ruby_file.pos = 0; 1096 ruby_file.rest = finfo.st_size; 1097 1098 ri.read_info.read = nxt_ruby_rack_file_read; 1099 ri.read_info.eof = ruby_file.rest == 0; 1100 ri.read_info.buf_size = ruby_file.rest; 1101 ri.read_info.data = &ruby_file; 1102 ri.req = req; 1103 1104 rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb, 1105 &ri, 1106 nxt_ruby_ubf, 1107 req->ctx); 1108 1109 close(fd); 1110 1111 return rc; 1112} 1113 1114 1115static void * 1116nxt_ruby_response_write_cb(void *data) 1117{ 1118 int rc; 1119 nxt_ruby_read_info_t *ri; 1120 1121 ri = data; 1122 1123 rc = nxt_unit_response_write_cb(ri->req, &ri->read_info); 1124 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1125 nxt_unit_req_error(ri->req, "Ruby: Failed to write content file."); 1126 } 1127 1128 return (void *) (intptr_t) rc; 1129} 1130 1131 1132typedef struct { 1133 VALUE body; 1134 nxt_unit_request_info_t *req; 1135} nxt_ruby_write_info_t; 1136 1137 1138static VALUE 1139nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, 1140 const VALUE *argv, VALUE blockarg) 1141{ 1142 nxt_ruby_write_info_t wi; 1143 1144 if (TYPE(body) != T_STRING) { 1145 return Qnil; 1146 } 1147 1148 wi.body = body; 1149 wi.req = (void *) (uintptr_t) arg; 1150 1151 (void) rb_thread_call_without_gvl(nxt_ruby_response_write, 1152 (void *) (uintptr_t) &wi, 1153 nxt_ruby_ubf, wi.req->ctx); 1154 1155 return Qnil; 1156} 1157 1158 1159static void * 1160nxt_ruby_response_write(void *data) 1161{ 1162 int rc; 1163 nxt_ruby_write_info_t *wi; 1164 1165 wi = data; 1166 1167 rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body), 1168 RSTRING_LEN(wi->body)); 1169 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1170 nxt_unit_req_error(wi->req, 1171 "Ruby: Failed to write 'body' from application"); 1172 } 1173 1174 return (void *) (intptr_t) rc; 1175} 1176 1177 1178static void 1179nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, 1180 const char *desc) 1181{ 1182 int i; 1183 VALUE err, ary, eclass, msg; 1184 1185 nxt_unit_req_log(req, level, "Ruby: %s", desc); 1186 1187 err = rb_errinfo(); 1188 if (nxt_slow_path(err == Qnil)) { 1189 return; 1190 } 1191 1192 eclass = rb_class_name(rb_class_of(err)); 1193 1194 msg = rb_funcall(err, rb_intern("message"), 0); 1195 ary = rb_funcall(err, rb_intern("backtrace"), 0); 1196 1197 if (RARRAY_LEN(ary) == 0) { 1198 nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg), 1199 RSTRING_PTR(eclass)); 1200 1201 return; 1202 } 1203 1204 nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", 1205 RSTRING_PTR(RARRAY_PTR(ary)[0]), 1206 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 1207 1208 for (i = 1; i < RARRAY_LEN(ary); i++) { 1209 nxt_unit_req_log(req, level, "from %s", 1210 RSTRING_PTR(RARRAY_PTR(ary)[i])); 1211 } 1212} 1213 1214 1215static void 1216nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx) 1217{ 1218 if (rctx->io_input != Qnil) { 1219 rb_gc_unregister_address(&rctx->io_input); 1220 } 1221 1222 if (rctx->io_error != Qnil) { 1223 rb_gc_unregister_address(&rctx->io_error); 1224 } 1225 1226 if (rctx->env != Qnil) { 1227 rb_gc_unregister_address(&rctx->env); 1228 } 1229} 1230 1231 1232static void 1233nxt_ruby_atexit(void) 1234{ 1235 if (nxt_ruby_rackup != Qnil) { 1236 rb_gc_unregister_address(&nxt_ruby_rackup); 1237 } 1238 1239 if (nxt_ruby_call != Qnil) { 1240 rb_gc_unregister_address(&nxt_ruby_call); 1241 } 1242 1243 if (nxt_ruby_hook_procs != Qnil) { 1244 rb_gc_unregister_address(&nxt_ruby_hook_procs); 1245 } 1246 1247 nxt_ruby_done_strings(); 1248 1249 ruby_cleanup(0); 1250} 1251 1252 1253static int 1254nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) 1255{ 1256 VALUE res; 1257 uint32_t i; 1258 nxt_ruby_ctx_t *rctx; 1259 nxt_ruby_app_conf_t *c; 1260
| 364 ruby_unit_init.data = c; 365 ruby_unit_init.ctx_data = &ruby_ctx; 366 367 unit_ctx = nxt_unit_init(&ruby_unit_init); 368 if (nxt_slow_path(unit_ctx == NULL)) { 369 goto fail; 370 } 371 372 if (nxt_ruby_hook_procs != Qnil) { 373 rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_boot, &state); 374 if (nxt_slow_path(state != 0)) { 375 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 376 "Failed to call on_thread_boot()"); 377 } 378 } 379 380 rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_unit_run, unit_ctx, 381 nxt_ruby_ubf, unit_ctx); 382 383 if (nxt_ruby_hook_procs != Qnil) { 384 rb_protect(nxt_ruby_hook_call, nxt_rb_on_thread_shutdown, &state); 385 if (nxt_slow_path(state != 0)) { 386 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 387 "Failed to call on_thread_shutdown()"); 388 } 389 } 390 391 nxt_ruby_join_threads(unit_ctx, c); 392 393 if (nxt_ruby_hook_procs != Qnil) { 394 rb_protect(nxt_ruby_hook_call, nxt_rb_on_worker_shutdown, &state); 395 if (nxt_slow_path(state != 0)) { 396 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 397 "Failed to call on_worker_shutdown()"); 398 } 399 } 400 401 nxt_unit_done(unit_ctx); 402 403 nxt_ruby_ctx_done(&ruby_ctx); 404 405 nxt_ruby_atexit(); 406 407 exit(rc); 408 409 return NXT_OK; 410 411fail: 412 413 nxt_ruby_join_threads(NULL, c); 414 415 nxt_ruby_ctx_done(&ruby_ctx); 416 417 nxt_ruby_atexit(); 418 419 return NXT_ERROR; 420} 421 422 423static VALUE 424nxt_ruby_init_basic(VALUE arg) 425{ 426 int state; 427 nxt_ruby_rack_init_t *rack_init; 428 429 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; 430 431 state = rb_enc_find_index("encdb"); 432 if (nxt_slow_path(state == 0)) { 433 nxt_alert(rack_init->task, 434 "Ruby: Failed to find encoding index 'encdb'"); 435 436 return Qnil; 437 } 438 439 rb_funcall(rb_cObject, rb_intern("require"), 1, 440 rb_str_new2("enc/trans/transdb")); 441 442 return arg; 443} 444 445 446static VALUE 447nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) 448{ 449 int state; 450 VALUE rackup, err; 451 452 rb_protect(nxt_ruby_require_rubygems, Qnil, &state); 453 if (nxt_slow_path(state != 0)) { 454 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 455 "Failed to require 'rubygems' package"); 456 return Qnil; 457 } 458 459 rb_protect(nxt_ruby_bundler_setup, Qnil, &state); 460 if (state != 0) { 461 err = rb_errinfo(); 462 463 if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { 464 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 465 "Failed to require 'bundler/setup' package"); 466 return Qnil; 467 } 468 469 rb_set_errinfo(Qnil); 470 } 471 472 rb_protect(nxt_ruby_require_rack, Qnil, &state); 473 if (nxt_slow_path(state != 0)) { 474 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 475 "Failed to require 'rack' package"); 476 return Qnil; 477 } 478 479 rackup = rb_protect(nxt_ruby_rack_parse_script, 480 (VALUE) (uintptr_t) rack_init, &state); 481 if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { 482 nxt_ruby_exception_log(NULL, NXT_LOG_ALERT, 483 "Failed to parse rack script"); 484 return Qnil; 485 } 486 487 if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { 488 nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); 489 return Qnil; 490 } 491 492 return RARRAY_PTR(rackup)[0]; 493} 494 495 496static VALUE 497nxt_ruby_require_rubygems(VALUE arg) 498{ 499 return rb_funcall(rb_cObject, rb_intern("require"), 1, 500 rb_str_new2("rubygems")); 501} 502 503 504static VALUE 505nxt_ruby_bundler_setup(VALUE arg) 506{ 507 return rb_funcall(rb_cObject, rb_intern("require"), 1, 508 rb_str_new2("bundler/setup")); 509} 510 511 512static VALUE 513nxt_ruby_require_rack(VALUE arg) 514{ 515 return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 516} 517 518 519static VALUE 520nxt_ruby_rack_parse_script(VALUE ctx) 521{ 522 VALUE script, res, rack, builder; 523 nxt_ruby_rack_init_t *rack_init; 524 525 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; 526 527 rack = rb_const_get(rb_cObject, rb_intern("Rack")); 528 builder = rb_const_get(rack, rb_intern("Builder")); 529 530 script = rb_str_new((const char *) rack_init->script->start, 531 (long) rack_init->script->length); 532 533 res = rb_funcall(builder, rb_intern("parse_file"), 1, script); 534 535 rb_str_free(script); 536 537 return res; 538} 539 540 541static VALUE 542nxt_ruby_rack_env_create(VALUE arg) 543{ 544 int rc; 545 VALUE hash_env, version; 546 nxt_ruby_ctx_t *rctx; 547 548 rctx = (nxt_ruby_ctx_t *) (uintptr_t) arg; 549 550 rc = nxt_ruby_init_io(rctx); 551 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 552 return Qnil; 553 } 554 555 hash_env = rb_hash_new(); 556 557 rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 558 rb_str_new((const char *) nxt_server.start, 559 (long) nxt_server.length)); 560 561 version = rb_ary_new(); 562 563 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 564 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 565 566 rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 567 rb_hash_aset(hash_env, rb_str_new2("rack.input"), rctx->io_input); 568 rb_hash_aset(hash_env, rb_str_new2("rack.errors"), rctx->io_error); 569 rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), 570 nxt_ruby_threads > 1 ? Qtrue : Qfalse); 571 rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 572 rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 573 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 574 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 575 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 576 577 rctx->env = hash_env; 578 579 rb_gc_register_address(&rctx->env); 580 581 return hash_env; 582} 583 584 585static int 586nxt_ruby_init_io(nxt_ruby_ctx_t *rctx) 587{ 588 VALUE io_input, io_error; 589 590 io_input = nxt_ruby_stream_io_input_init(); 591 592 rctx->io_input = rb_funcall(io_input, rb_intern("new"), 1, 593 (VALUE) (uintptr_t) rctx); 594 if (nxt_slow_path(rctx->io_input == Qnil)) { 595 nxt_unit_alert(NULL, 596 "Ruby: Failed to create environment 'rack.input' var"); 597 598 return NXT_UNIT_ERROR; 599 } 600 601 rb_gc_register_address(&rctx->io_input); 602 603 io_error = nxt_ruby_stream_io_error_init(); 604 605 rctx->io_error = rb_funcall(io_error, rb_intern("new"), 1, 606 (VALUE) (uintptr_t) rctx); 607 if (nxt_slow_path(rctx->io_error == Qnil)) { 608 nxt_unit_alert(NULL, 609 "Ruby: Failed to create environment 'rack.error' var"); 610 611 return NXT_UNIT_ERROR; 612 } 613 614 rb_gc_register_address(&rctx->io_error); 615 616 return NXT_UNIT_OK; 617} 618 619 620static void 621nxt_ruby_request_handler(nxt_unit_request_info_t *req) 622{ 623 (void) rb_thread_call_with_gvl(nxt_ruby_request_handler_gvl, req); 624} 625 626 627static void * 628nxt_ruby_request_handler_gvl(void *data) 629{ 630 int state; 631 VALUE res; 632 nxt_ruby_ctx_t *rctx; 633 nxt_unit_request_info_t *req; 634 635 req = data; 636 637 rctx = req->ctx->data; 638 rctx->req = req; 639 640 res = rb_protect(nxt_ruby_rack_app_run, (VALUE) (uintptr_t) req, &state); 641 if (nxt_slow_path(res == Qnil || state != 0)) { 642 nxt_ruby_exception_log(req, NXT_LOG_ERR, 643 "Failed to run ruby script"); 644 645 nxt_unit_request_done(req, NXT_UNIT_ERROR); 646 647 } else { 648 nxt_unit_request_done(req, NXT_UNIT_OK); 649 } 650 651 rctx->req = NULL; 652 653 return NULL; 654} 655 656 657static VALUE 658nxt_ruby_rack_app_run(VALUE arg) 659{ 660 int rc; 661 VALUE env, result; 662 nxt_int_t status; 663 nxt_ruby_ctx_t *rctx; 664 nxt_unit_request_info_t *req; 665 666 req = (nxt_unit_request_info_t *) arg; 667 668 rctx = req->ctx->data; 669 670 env = rb_hash_dup(rctx->env); 671 672 rc = nxt_ruby_read_request(req, env); 673 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 674 nxt_unit_req_alert(req, 675 "Ruby: Failed to process incoming request"); 676 677 goto fail; 678 } 679 680 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 681 if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 682 nxt_unit_req_error(req, 683 "Ruby: Invalid response format from application"); 684 685 goto fail; 686 } 687 688 if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 689 nxt_unit_req_error(req, 690 "Ruby: Invalid response format from application. " 691 "Need 3 entries [Status, Headers, Body]"); 692 693 goto fail; 694 } 695 696 status = nxt_ruby_rack_result_status(req, result); 697 if (nxt_slow_path(status < 0)) { 698 nxt_unit_req_error(req, 699 "Ruby: Invalid response status from application."); 700 701 goto fail; 702 } 703 704 rc = nxt_ruby_rack_result_headers(req, result, status); 705 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 706 goto fail; 707 } 708 709 rc = nxt_ruby_rack_result_body(req, result); 710 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 711 goto fail; 712 } 713 714 rb_hash_delete(env, rb_obj_id(env)); 715 716 return result; 717 718fail: 719 720 rb_hash_delete(env, rb_obj_id(env)); 721 722 return Qnil; 723} 724 725 726static int 727nxt_ruby_read_request(nxt_unit_request_info_t *req, VALUE hash_env) 728{ 729 VALUE name; 730 uint32_t i; 731 nxt_unit_field_t *f; 732 nxt_unit_request_t *r; 733 734 r = req->request; 735 736 nxt_ruby_add_sptr(hash_env, nxt_rb_request_method_str, &r->method, 737 r->method_length); 738 nxt_ruby_add_sptr(hash_env, nxt_rb_request_uri_str, &r->target, 739 r->target_length); 740 nxt_ruby_add_sptr(hash_env, nxt_rb_path_info_str, &r->path, r->path_length); 741 nxt_ruby_add_sptr(hash_env, nxt_rb_query_string_str, &r->query, 742 r->query_length); 743 nxt_ruby_add_sptr(hash_env, nxt_rb_server_protocol_str, &r->version, 744 r->version_length); 745 nxt_ruby_add_sptr(hash_env, nxt_rb_remote_addr_str, &r->remote, 746 r->remote_length); 747 nxt_ruby_add_sptr(hash_env, nxt_rb_server_addr_str, &r->local, 748 r->local_length); 749 nxt_ruby_add_sptr(hash_env, nxt_rb_server_name_str, &r->server_name, 750 r->server_name_length); 751 752 rb_hash_aset(hash_env, nxt_rb_server_port_str, nxt_rb_80_str); 753 754 rb_hash_aset(hash_env, nxt_rb_rack_url_scheme_str, 755 r->tls ? nxt_rb_https_str : nxt_rb_http_str); 756 757 for (i = 0; i < r->fields_count; i++) { 758 f = r->fields + i; 759 760 name = rb_str_new(nxt_unit_sptr_get(&f->name), f->name_length); 761 762 nxt_ruby_add_sptr(hash_env, name, &f->value, f->value_length); 763 } 764 765 if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 766 f = r->fields + r->content_length_field; 767 768 nxt_ruby_add_sptr(hash_env, nxt_rb_content_length_str, 769 &f->value, f->value_length); 770 } 771 772 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 773 f = r->fields + r->content_type_field; 774 775 nxt_ruby_add_sptr(hash_env, nxt_rb_content_type_str, 776 &f->value, f->value_length); 777 } 778 779 return NXT_UNIT_OK; 780} 781 782 783nxt_inline void 784nxt_ruby_add_sptr(VALUE hash_env, VALUE name, 785 nxt_unit_sptr_t *sptr, uint32_t len) 786{ 787 char *str; 788 789 str = nxt_unit_sptr_get(sptr); 790 791 rb_hash_aset(hash_env, name, rb_str_new(str, len)); 792} 793 794 795static nxt_int_t 796nxt_ruby_rack_result_status(nxt_unit_request_info_t *req, VALUE result) 797{ 798 VALUE status; 799 800 status = rb_ary_entry(result, 0); 801 802 if (TYPE(status) == T_FIXNUM) { 803 return FIX2INT(status); 804 } 805 806 if (TYPE(status) == T_STRING) { 807 return nxt_int_parse((u_char *) RSTRING_PTR(status), 808 RSTRING_LEN(status)); 809 } 810 811 nxt_unit_req_error(req, "Ruby: Invalid response 'status' " 812 "format from application"); 813 814 return -2; 815} 816 817 818typedef struct { 819 int rc; 820 uint32_t fields; 821 uint32_t size; 822 nxt_unit_request_info_t *req; 823} nxt_ruby_headers_info_t; 824 825 826static int 827nxt_ruby_rack_result_headers(nxt_unit_request_info_t *req, VALUE result, 828 nxt_int_t status) 829{ 830 int rc; 831 VALUE headers; 832 nxt_ruby_headers_info_t headers_info; 833 834 headers = rb_ary_entry(result, 1); 835 if (nxt_slow_path(TYPE(headers) != T_HASH)) { 836 nxt_unit_req_error(req, 837 "Ruby: Invalid response 'headers' format from " 838 "application"); 839 840 return NXT_UNIT_ERROR; 841 } 842 843 rc = NXT_UNIT_OK; 844 845 headers_info.rc = NXT_UNIT_OK; 846 headers_info.fields = 0; 847 headers_info.size = 0; 848 headers_info.req = req; 849 850 rb_hash_foreach(headers, nxt_ruby_hash_info, 851 (VALUE) (uintptr_t) &headers_info); 852 if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { 853 return headers_info.rc; 854 } 855 856 rc = nxt_unit_response_init(req, status, 857 headers_info.fields, headers_info.size); 858 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 859 return rc; 860 } 861 862 rb_hash_foreach(headers, nxt_ruby_hash_add, 863 (VALUE) (uintptr_t) &headers_info); 864 865 return rc; 866} 867 868 869static int 870nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) 871{ 872 const char *value, *value_end, *pos; 873 nxt_ruby_headers_info_t *headers_info; 874 875 headers_info = (void *) (uintptr_t) arg; 876 877 if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 878 nxt_unit_req_error(headers_info->req, 879 "Ruby: Wrong header entry 'key' from application"); 880 881 goto fail; 882 } 883 884 if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 885 nxt_unit_req_error(headers_info->req, 886 "Ruby: Wrong header entry 'value' from application"); 887 888 goto fail; 889 } 890 891 value = RSTRING_PTR(r_value); 892 value_end = value + RSTRING_LEN(r_value); 893 894 pos = value; 895 896 for ( ;; ) { 897 pos = strchr(pos, '\n'); 898 899 if (pos == NULL) { 900 break; 901 } 902 903 headers_info->fields++; 904 headers_info->size += RSTRING_LEN(r_key) + (pos - value); 905 906 pos++; 907 value = pos; 908 } 909 910 if (value <= value_end) { 911 headers_info->fields++; 912 headers_info->size += RSTRING_LEN(r_key) + (value_end - value); 913 } 914 915 return ST_CONTINUE; 916 917fail: 918 919 headers_info->rc = NXT_UNIT_ERROR; 920 921 return ST_STOP; 922} 923 924 925static int 926nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) 927{ 928 int *rc; 929 uint32_t key_len; 930 const char *value, *value_end, *pos; 931 nxt_ruby_headers_info_t *headers_info; 932 933 headers_info = (void *) (uintptr_t) arg; 934 rc = &headers_info->rc; 935 936 value = RSTRING_PTR(r_value); 937 value_end = value + RSTRING_LEN(r_value); 938 939 key_len = RSTRING_LEN(r_key); 940 941 pos = value; 942 943 for ( ;; ) { 944 pos = strchr(pos, '\n'); 945 946 if (pos == NULL) { 947 break; 948 } 949 950 *rc = nxt_unit_response_add_field(headers_info->req, 951 RSTRING_PTR(r_key), key_len, 952 value, pos - value); 953 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 954 goto fail; 955 } 956 957 pos++; 958 value = pos; 959 } 960 961 if (value <= value_end) { 962 *rc = nxt_unit_response_add_field(headers_info->req, 963 RSTRING_PTR(r_key), key_len, 964 value, value_end - value); 965 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 966 goto fail; 967 } 968 } 969 970 return ST_CONTINUE; 971 972fail: 973 974 *rc = NXT_UNIT_ERROR; 975 976 return ST_STOP; 977} 978 979 980static int 981nxt_ruby_rack_result_body(nxt_unit_request_info_t *req, VALUE result) 982{ 983 int rc; 984 VALUE fn, body; 985 986 body = rb_ary_entry(result, 2); 987 988 if (rb_respond_to(body, rb_intern("to_path"))) { 989 990 fn = rb_funcall(body, rb_intern("to_path"), 0); 991 if (nxt_slow_path(TYPE(fn) != T_STRING)) { 992 nxt_unit_req_error(req, 993 "Ruby: Failed to get 'body' file path from " 994 "application"); 995 996 return NXT_UNIT_ERROR; 997 } 998 999 rc = nxt_ruby_rack_result_body_file_write(req, fn); 1000 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1001 return rc; 1002 } 1003 1004 } else if (rb_respond_to(body, rb_intern("each"))) { 1005 rb_block_call(body, rb_intern("each"), 0, 0, 1006 nxt_ruby_rack_result_body_each, (VALUE) (uintptr_t) req); 1007 1008 } else { 1009 nxt_unit_req_error(req, 1010 "Ruby: Invalid response 'body' format " 1011 "from application"); 1012 1013 return NXT_UNIT_ERROR; 1014 } 1015 1016 if (rb_respond_to(body, rb_intern("close"))) { 1017 rb_funcall(body, rb_intern("close"), 0); 1018 } 1019 1020 return NXT_UNIT_OK; 1021} 1022 1023 1024typedef struct { 1025 int fd; 1026 off_t pos; 1027 off_t rest; 1028} nxt_ruby_rack_file_t; 1029 1030 1031static ssize_t 1032nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) 1033{ 1034 ssize_t res; 1035 nxt_ruby_rack_file_t *file; 1036 1037 file = read_info->data; 1038 1039 size = nxt_min(size, (size_t) file->rest); 1040 1041 res = pread(file->fd, dst, size, file->pos); 1042 1043 if (res >= 0) { 1044 file->pos += res; 1045 file->rest -= res; 1046 1047 if (size > (size_t) res) { 1048 file->rest = 0; 1049 } 1050 } 1051 1052 read_info->eof = file->rest == 0; 1053 1054 return res; 1055} 1056 1057 1058typedef struct { 1059 nxt_unit_read_info_t read_info; 1060 nxt_unit_request_info_t *req; 1061} nxt_ruby_read_info_t; 1062 1063 1064static int 1065nxt_ruby_rack_result_body_file_write(nxt_unit_request_info_t *req, 1066 VALUE filepath) 1067{ 1068 int fd, rc; 1069 struct stat finfo; 1070 nxt_ruby_rack_file_t ruby_file; 1071 nxt_ruby_read_info_t ri; 1072 1073 fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); 1074 if (nxt_slow_path(fd == -1)) { 1075 nxt_unit_req_error(req, 1076 "Ruby: Failed to open content file \"%s\": %s (%d)", 1077 RSTRING_PTR(filepath), strerror(errno), errno); 1078 1079 return NXT_UNIT_ERROR; 1080 } 1081 1082 rc = fstat(fd, &finfo); 1083 if (nxt_slow_path(rc == -1)) { 1084 nxt_unit_req_error(req, 1085 "Ruby: Content file fstat(\"%s\") failed: %s (%d)", 1086 RSTRING_PTR(filepath), strerror(errno), errno); 1087 1088 close(fd); 1089 1090 return NXT_UNIT_ERROR; 1091 } 1092 1093 ruby_file.fd = fd; 1094 ruby_file.pos = 0; 1095 ruby_file.rest = finfo.st_size; 1096 1097 ri.read_info.read = nxt_ruby_rack_file_read; 1098 ri.read_info.eof = ruby_file.rest == 0; 1099 ri.read_info.buf_size = ruby_file.rest; 1100 ri.read_info.data = &ruby_file; 1101 ri.req = req; 1102 1103 rc = (intptr_t) rb_thread_call_without_gvl(nxt_ruby_response_write_cb, 1104 &ri, 1105 nxt_ruby_ubf, 1106 req->ctx); 1107 1108 close(fd); 1109 1110 return rc; 1111} 1112 1113 1114static void * 1115nxt_ruby_response_write_cb(void *data) 1116{ 1117 int rc; 1118 nxt_ruby_read_info_t *ri; 1119 1120 ri = data; 1121 1122 rc = nxt_unit_response_write_cb(ri->req, &ri->read_info); 1123 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1124 nxt_unit_req_error(ri->req, "Ruby: Failed to write content file."); 1125 } 1126 1127 return (void *) (intptr_t) rc; 1128} 1129 1130 1131typedef struct { 1132 VALUE body; 1133 nxt_unit_request_info_t *req; 1134} nxt_ruby_write_info_t; 1135 1136 1137static VALUE 1138nxt_ruby_rack_result_body_each(VALUE body, VALUE arg, int argc, 1139 const VALUE *argv, VALUE blockarg) 1140{ 1141 nxt_ruby_write_info_t wi; 1142 1143 if (TYPE(body) != T_STRING) { 1144 return Qnil; 1145 } 1146 1147 wi.body = body; 1148 wi.req = (void *) (uintptr_t) arg; 1149 1150 (void) rb_thread_call_without_gvl(nxt_ruby_response_write, 1151 (void *) (uintptr_t) &wi, 1152 nxt_ruby_ubf, wi.req->ctx); 1153 1154 return Qnil; 1155} 1156 1157 1158static void * 1159nxt_ruby_response_write(void *data) 1160{ 1161 int rc; 1162 nxt_ruby_write_info_t *wi; 1163 1164 wi = data; 1165 1166 rc = nxt_unit_response_write(wi->req, RSTRING_PTR(wi->body), 1167 RSTRING_LEN(wi->body)); 1168 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1169 nxt_unit_req_error(wi->req, 1170 "Ruby: Failed to write 'body' from application"); 1171 } 1172 1173 return (void *) (intptr_t) rc; 1174} 1175 1176 1177static void 1178nxt_ruby_exception_log(nxt_unit_request_info_t *req, uint32_t level, 1179 const char *desc) 1180{ 1181 int i; 1182 VALUE err, ary, eclass, msg; 1183 1184 nxt_unit_req_log(req, level, "Ruby: %s", desc); 1185 1186 err = rb_errinfo(); 1187 if (nxt_slow_path(err == Qnil)) { 1188 return; 1189 } 1190 1191 eclass = rb_class_name(rb_class_of(err)); 1192 1193 msg = rb_funcall(err, rb_intern("message"), 0); 1194 ary = rb_funcall(err, rb_intern("backtrace"), 0); 1195 1196 if (RARRAY_LEN(ary) == 0) { 1197 nxt_unit_req_log(req, level, "Ruby: %s (%s)", RSTRING_PTR(msg), 1198 RSTRING_PTR(eclass)); 1199 1200 return; 1201 } 1202 1203 nxt_unit_req_log(req, level, "Ruby: %s: %s (%s)", 1204 RSTRING_PTR(RARRAY_PTR(ary)[0]), 1205 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 1206 1207 for (i = 1; i < RARRAY_LEN(ary); i++) { 1208 nxt_unit_req_log(req, level, "from %s", 1209 RSTRING_PTR(RARRAY_PTR(ary)[i])); 1210 } 1211} 1212 1213 1214static void 1215nxt_ruby_ctx_done(nxt_ruby_ctx_t *rctx) 1216{ 1217 if (rctx->io_input != Qnil) { 1218 rb_gc_unregister_address(&rctx->io_input); 1219 } 1220 1221 if (rctx->io_error != Qnil) { 1222 rb_gc_unregister_address(&rctx->io_error); 1223 } 1224 1225 if (rctx->env != Qnil) { 1226 rb_gc_unregister_address(&rctx->env); 1227 } 1228} 1229 1230 1231static void 1232nxt_ruby_atexit(void) 1233{ 1234 if (nxt_ruby_rackup != Qnil) { 1235 rb_gc_unregister_address(&nxt_ruby_rackup); 1236 } 1237 1238 if (nxt_ruby_call != Qnil) { 1239 rb_gc_unregister_address(&nxt_ruby_call); 1240 } 1241 1242 if (nxt_ruby_hook_procs != Qnil) { 1243 rb_gc_unregister_address(&nxt_ruby_hook_procs); 1244 } 1245 1246 nxt_ruby_done_strings(); 1247 1248 ruby_cleanup(0); 1249} 1250 1251 1252static int 1253nxt_ruby_ready_handler(nxt_unit_ctx_t *ctx) 1254{ 1255 VALUE res; 1256 uint32_t i; 1257 nxt_ruby_ctx_t *rctx; 1258 nxt_ruby_app_conf_t *c; 1259
|