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 12 #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 13 #define NXT_RUBY_RACK_API_VERSION_MINOR 3 14 15 #define NXT_RUBY_STRINGIZE_HELPER(x) #x 16 #define NXT_RUBY_STRINGIZE(x) NXT_RUBY_STRINGIZE_HELPER(x) 17 18 #define NXT_RUBY_LIB_VERSION \ 19 NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MAJOR) \ 20 "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MINOR) \ 21 "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_TEENY) 22 23 24 typedef struct { 25 nxt_task_t *task; 26 nxt_str_t *script; 27 VALUE builder; 28 } nxt_ruby_rack_init_t; 29 30 31 static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf); 32 static VALUE nxt_ruby_init_basic(VALUE arg); 33 static nxt_int_t nxt_ruby_init_io(nxt_task_t *task); 34 static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 35 36 static VALUE nxt_ruby_require_rubygems(VALUE arg); 37 static VALUE nxt_ruby_bundler_setup(VALUE arg); 38 static VALUE nxt_ruby_require_rack(VALUE arg); 39 static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 40 static VALUE nxt_ruby_rack_env_create(VALUE arg); 41 static void nxt_ruby_request_handler(nxt_unit_request_info_t *req); 42 43 static VALUE nxt_ruby_rack_app_run(VALUE arg); 44 static int nxt_ruby_read_request(VALUE hash_env); 45 nxt_inline void nxt_ruby_add_sptr(VALUE hash_env, 46 const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len); 47 nxt_inline void nxt_ruby_add_str(VALUE hash_env, 48 const char *name, uint32_t name_len, const char *str, uint32_t len); 49 static nxt_int_t nxt_ruby_rack_result_status(VALUE result); 50 static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status); 51 static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg); 52 static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg); 53 static int nxt_ruby_rack_result_body(VALUE result); 54 static int nxt_ruby_rack_result_body_file_write(VALUE filepath); 55 static VALUE nxt_ruby_rack_result_body_each(VALUE body); 56 57 static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, 58 const char *desc); 59 60 static void nxt_ruby_atexit(void); 61 62 63 static uint32_t compat[] = { 64 NXT_VERNUM, NXT_DEBUG, 65 }; 66 67 static VALUE nxt_ruby_rackup; 68 static VALUE nxt_ruby_call; 69 static VALUE nxt_ruby_env; 70 static VALUE nxt_ruby_io_input; 71 static VALUE nxt_ruby_io_error; 72 static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; 73 74 NXT_EXPORT nxt_app_module_t nxt_app_module = { 75 sizeof(compat), 76 compat, 77 nxt_string("ruby"), 78 ruby_version, 79 NULL, 80 nxt_ruby_init, 81 }; 82 83 84 static nxt_int_t 85 nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf) 86 { 87 int state, rc; 88 VALUE res; 89 nxt_unit_ctx_t *unit_ctx; 90 nxt_unit_init_t ruby_unit_init; 91 nxt_ruby_rack_init_t rack_init; 92 93 static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; 94 95 RUBY_INIT_STACK 96 ruby_init(); 97 ruby_options(2, argv); 98 ruby_script("NGINX_Unit"); 99 100 rack_init.task = task; 101 rack_init.script = &conf->u.ruby.script; 102 103 res = rb_protect(nxt_ruby_init_basic, 104 (VALUE) (uintptr_t) &rack_init, &state); 105 if (nxt_slow_path(res == Qnil || state != 0)) { 106 nxt_ruby_exception_log(task, NXT_LOG_ALERT, 107 "Failed to init basic variables"); 108 return NXT_ERROR; 109 } 110 111 nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init); 112 if (nxt_slow_path(nxt_ruby_rackup == Qnil)) { 113 return NXT_ERROR; 114 } 115 116 nxt_ruby_call = rb_intern("call"); 117 if (nxt_slow_path(nxt_ruby_call == Qnil)) { 118 nxt_alert(task, "Ruby: Unable to find rack entry point"); 119 120 return NXT_ERROR; 121 } 122 123 nxt_ruby_env = rb_protect(nxt_ruby_rack_env_create, Qnil, &state); 124 if (nxt_slow_path(state != 0)) { 125 nxt_ruby_exception_log(task, NXT_LOG_ALERT, 126 "Failed to create 'environ' variable"); 127 return NXT_ERROR; 128 } 129 130 rb_gc_register_address(&nxt_ruby_rackup); 131 rb_gc_register_address(&nxt_ruby_call); 132 rb_gc_register_address(&nxt_ruby_env); 133 134 nxt_unit_default_init(task, &ruby_unit_init); 135 136 ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler; 137 138 unit_ctx = nxt_unit_init(&ruby_unit_init); 139 if (nxt_slow_path(unit_ctx == NULL)) { 140 return NXT_ERROR; 141 } 142 143 nxt_ruby_run_ctx.unit_ctx = unit_ctx; 144 145 rc = nxt_unit_run(unit_ctx); 146 147 nxt_ruby_atexit(); 148 149 nxt_ruby_run_ctx.unit_ctx = NULL; 150 151 nxt_unit_done(unit_ctx); 152 153 exit(rc); 154 155 return NXT_OK; 156 } 157 158 159 static VALUE 160 nxt_ruby_init_basic(VALUE arg) 161 { 162 int state; 163 nxt_int_t rc; 164 nxt_ruby_rack_init_t *rack_init; 165 166 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg; 167 168 state = rb_enc_find_index("encdb"); 169 if (nxt_slow_path(state == 0)) { 170 nxt_alert(rack_init->task, 171 "Ruby: Failed to find encoding index 'encdb'"); 172 173 return Qnil; 174 } 175 176 rb_funcall(rb_cObject, rb_intern("require"), 1, 177 rb_str_new2("enc/trans/transdb")); 178 179 rc = nxt_ruby_init_io(rack_init->task); 180 if (nxt_slow_path(rc != NXT_OK)) { 181 return Qnil; 182 } 183 184 return arg; 185 } 186 187 188 static nxt_int_t 189 nxt_ruby_init_io(nxt_task_t *task) 190 { 191 VALUE rb, io_input, io_error; 192 193 io_input = nxt_ruby_stream_io_input_init(); 194 rb = Data_Wrap_Struct(io_input, 0, 0, &nxt_ruby_run_ctx); 195 196 nxt_ruby_io_input = rb_funcall(io_input, rb_intern("new"), 1, rb); 197 if (nxt_slow_path(nxt_ruby_io_input == Qnil)) { 198 nxt_alert(task, "Ruby: Failed to create environment 'rack.input' var"); 199 200 return NXT_ERROR; 201 } 202 203 io_error = nxt_ruby_stream_io_error_init(); 204 rb = Data_Wrap_Struct(io_error, 0, 0, &nxt_ruby_run_ctx); 205 206 nxt_ruby_io_error = rb_funcall(io_error, rb_intern("new"), 1, rb); 207 if (nxt_slow_path(nxt_ruby_io_error == Qnil)) { 208 nxt_alert(task, "Ruby: Failed to create environment 'rack.error' var"); 209 210 return NXT_ERROR; 211 } 212 213 rb_gc_register_address(&nxt_ruby_io_input); 214 rb_gc_register_address(&nxt_ruby_io_error); 215 216 return NXT_OK; 217 } 218 219 220 static VALUE 221 nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init) 222 { 223 int state; 224 VALUE rack, rackup, err; 225 226 rb_protect(nxt_ruby_require_rubygems, Qnil, &state); 227 if (nxt_slow_path(state != 0)) { 228 nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, 229 "Failed to require 'rubygems' package"); 230 return Qnil; 231 } 232 233 rb_protect(nxt_ruby_bundler_setup, Qnil, &state); 234 if (state != 0) { 235 err = rb_errinfo(); 236 237 if (rb_obj_is_kind_of(err, rb_eLoadError) == Qfalse) { 238 nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, 239 "Failed to require 'bundler/setup' package"); 240 return Qnil; 241 } 242 243 rb_set_errinfo(Qnil); 244 } 245 246 rb_protect(nxt_ruby_require_rack, Qnil, &state); 247 if (nxt_slow_path(state != 0)) { 248 nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, 249 "Failed to require 'rack' package"); 250 return Qnil; 251 } 252 253 rack = rb_const_get(rb_cObject, rb_intern("Rack")); 254 rack_init->builder = rb_const_get(rack, rb_intern("Builder")); 255 256 rackup = rb_protect(nxt_ruby_rack_parse_script, 257 (VALUE) (uintptr_t) rack_init, &state); 258 if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) { 259 nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT, 260 "Failed to parse rack script"); 261 return Qnil; 262 } 263 264 if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) { 265 nxt_alert(rack_init->task, "Ruby: Invalid rack config file"); 266 return Qnil; 267 } 268 269 return RARRAY_PTR(rackup)[0]; 270 } 271 272 273 static VALUE 274 nxt_ruby_require_rubygems(VALUE arg) 275 { 276 return rb_funcall(rb_cObject, rb_intern("require"), 1, 277 rb_str_new2("rubygems")); 278 } 279 280 281 static VALUE 282 nxt_ruby_bundler_setup(VALUE arg) 283 { 284 return rb_funcall(rb_cObject, rb_intern("require"), 1, 285 rb_str_new2("bundler/setup")); 286 } 287 288 289 static VALUE 290 nxt_ruby_require_rack(VALUE arg) 291 { 292 return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 293 } 294 295 296 static VALUE 297 nxt_ruby_rack_parse_script(VALUE ctx) 298 { 299 VALUE script, res; 300 nxt_ruby_rack_init_t *rack_init; 301 302 rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx; 303 304 script = rb_str_new((const char *) rack_init->script->start, 305 (long) rack_init->script->length); 306 307 res = rb_funcall(rack_init->builder, rb_intern("parse_file"), 1, script); 308 309 rb_str_free(script); 310 311 return res; 312 } 313 314 315 static VALUE 316 nxt_ruby_rack_env_create(VALUE arg) 317 { 318 VALUE hash_env, version; 319 320 hash_env = rb_hash_new(); 321 322 rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 323 rb_str_new((const char *) nxt_server.start, 324 (long) nxt_server.length)); 325 326 version = rb_ary_new(); 327 328 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 329 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 330 331 rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 332 rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input); 333 rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error); 334 rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse); 335 rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 336 rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 337 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 338 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 339 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 340 341 return hash_env; 342 } 343 344 345 static void 346 nxt_ruby_request_handler(nxt_unit_request_info_t *req) 347 { 348 int state; 349 VALUE res; 350 351 nxt_ruby_run_ctx.req = req; 352 353 res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); 354 if (nxt_slow_path(res == Qnil || state != 0)) { 355 nxt_ruby_exception_log(NULL, NXT_LOG_ERR, 356 "Failed to run ruby script"); 357 } 358 } 359 360 361 static VALUE 362 nxt_ruby_rack_app_run(VALUE arg) 363 { 364 int rc; 365 VALUE env, result; 366 nxt_int_t status; 367 368 env = rb_hash_dup(nxt_ruby_env); 369 370 rc = nxt_ruby_read_request(env); 371 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 372 nxt_unit_req_alert(nxt_ruby_run_ctx.req, 373 "Ruby: Failed to process incoming request"); 374 375 goto fail; 376 } 377 378 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 379 if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 380 nxt_unit_req_error(nxt_ruby_run_ctx.req, 381 "Ruby: Invalid response format from application"); 382 383 goto fail; 384 } 385 386 if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 387 nxt_unit_req_error(nxt_ruby_run_ctx.req, 388 "Ruby: Invalid response format from application. " 389 "Need 3 entries [Status, Headers, Body]"); 390 391 goto fail; 392 } 393 394 status = nxt_ruby_rack_result_status(result); 395 if (nxt_slow_path(status < 0)) { 396 nxt_unit_req_error(nxt_ruby_run_ctx.req, 397 "Ruby: Invalid response status from application."); 398 399 goto fail; 400 } 401 402 rc = nxt_ruby_rack_result_headers(result, status); 403 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 404 goto fail; 405 } 406 407 rc = nxt_ruby_rack_result_body(result); 408 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 409 goto fail; 410 } 411 412 nxt_unit_request_done(nxt_ruby_run_ctx.req, rc); 413 nxt_ruby_run_ctx.req = NULL; 414 415 rb_hash_delete(env, rb_obj_id(env)); 416 417 return result; 418 419 fail: 420 421 nxt_unit_request_done(nxt_ruby_run_ctx.req, NXT_UNIT_ERROR); 422 nxt_ruby_run_ctx.req = NULL; 423 424 rb_hash_delete(env, rb_obj_id(env)); 425 426 return Qnil; 427 } 428 429 430 static int 431 nxt_ruby_read_request(VALUE hash_env) 432 { 433 uint32_t i; 434 nxt_unit_field_t *f; 435 nxt_unit_request_t *r; 436 437 r = nxt_ruby_run_ctx.req->request; 438 439 #define NL(S) (S), sizeof(S)-1 440 441 nxt_ruby_add_sptr(hash_env, NL("REQUEST_METHOD"), &r->method, 442 r->method_length); 443 nxt_ruby_add_sptr(hash_env, NL("REQUEST_URI"), &r->target, 444 r->target_length); 445 nxt_ruby_add_sptr(hash_env, NL("PATH_INFO"), &r->path, r->path_length); 446 nxt_ruby_add_sptr(hash_env, NL("QUERY_STRING"), &r->query, 447 r->query_length); 448 nxt_ruby_add_sptr(hash_env, NL("SERVER_PROTOCOL"), &r->version, 449 r->version_length); 450 nxt_ruby_add_sptr(hash_env, NL("REMOTE_ADDR"), &r->remote, 451 r->remote_length); 452 nxt_ruby_add_sptr(hash_env, NL("SERVER_ADDR"), &r->local, r->local_length); 453 454 nxt_ruby_add_sptr(hash_env, NL("SERVER_NAME"), &r->server_name, 455 r->server_name_length); 456 nxt_ruby_add_str(hash_env, NL("SERVER_PORT"), "80", 2); 457 458 rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), 459 r->tls ? rb_str_new2("https") : rb_str_new2("http")); 460 461 for (i = 0; i < r->fields_count; i++) { 462 f = r->fields + i; 463 464 nxt_ruby_add_sptr(hash_env, nxt_unit_sptr_get(&f->name), f->name_length, 465 &f->value, f->value_length); 466 } 467 468 if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 469 f = r->fields + r->content_length_field; 470 471 nxt_ruby_add_sptr(hash_env, NL("CONTENT_LENGTH"), 472 &f->value, f->value_length); 473 } 474 475 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 476 f = r->fields + r->content_type_field; 477 478 nxt_ruby_add_sptr(hash_env, NL("CONTENT_TYPE"), 479 &f->value, f->value_length); 480 } 481 482 #undef NL 483 484 return NXT_UNIT_OK; 485 } 486 487 488 nxt_inline void 489 nxt_ruby_add_sptr(VALUE hash_env, 490 const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len) 491 { 492 char *str; 493 494 str = nxt_unit_sptr_get(sptr); 495 496 rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); 497 } 498 499 500 nxt_inline void 501 nxt_ruby_add_str(VALUE hash_env, 502 const char *name, uint32_t name_len, const char *str, uint32_t len) 503 { 504 rb_hash_aset(hash_env, rb_str_new(name, name_len), rb_str_new(str, len)); 505 } 506 507 508 static nxt_int_t 509 nxt_ruby_rack_result_status(VALUE result) 510 { 511 VALUE status; 512 513 status = rb_ary_entry(result, 0); 514 515 if (TYPE(status) == T_FIXNUM) { 516 return FIX2INT(status); 517 } 518 519 if (TYPE(status) == T_STRING) { 520 return nxt_int_parse((u_char *) RSTRING_PTR(status), 521 RSTRING_LEN(status)); 522 } 523 524 nxt_unit_req_error(nxt_ruby_run_ctx.req, "Ruby: Invalid response 'status' " 525 "format from application"); 526 527 return -2; 528 } 529 530 531 typedef struct { 532 int rc; 533 uint32_t fields; 534 uint32_t size; 535 } nxt_ruby_headers_info_t; 536 537 538 static int 539 nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status) 540 { 541 int rc; 542 VALUE headers; 543 nxt_ruby_headers_info_t headers_info; 544 545 headers = rb_ary_entry(result, 1); 546 if (nxt_slow_path(TYPE(headers) != T_HASH)) { 547 nxt_unit_req_error(nxt_ruby_run_ctx.req, 548 "Ruby: Invalid response 'headers' format from " 549 "application"); 550 551 return NXT_UNIT_ERROR; 552 } 553 554 rc = NXT_UNIT_OK; 555 556 headers_info.rc = NXT_UNIT_OK; 557 headers_info.fields = 0; 558 headers_info.size = 0; 559 560 rb_hash_foreach(headers, nxt_ruby_hash_info, 561 (VALUE) (uintptr_t) &headers_info); 562 if (nxt_slow_path(headers_info.rc != NXT_UNIT_OK)) { 563 return headers_info.rc; 564 } 565 566 rc = nxt_unit_response_init(nxt_ruby_run_ctx.req, status, 567 headers_info.fields, headers_info.size); 568 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 569 return rc; 570 } 571 572 rb_hash_foreach(headers, nxt_ruby_hash_add, (VALUE) (uintptr_t) &rc); 573 574 return rc; 575 } 576 577 578 static int 579 nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) 580 { 581 const char *value, *value_end, *pos; 582 nxt_ruby_headers_info_t *headers_info; 583 584 headers_info = (void *) (uintptr_t) arg; 585 586 if (nxt_slow_path(TYPE(r_key) != T_STRING)) { 587 nxt_unit_req_error(nxt_ruby_run_ctx.req, 588 "Ruby: Wrong header entry 'key' from application"); 589 590 goto fail; 591 } 592 593 if (nxt_slow_path(TYPE(r_value) != T_STRING)) { 594 nxt_unit_req_error(nxt_ruby_run_ctx.req, 595 "Ruby: Wrong header entry 'value' from application"); 596 597 goto fail; 598 } 599 600 value = RSTRING_PTR(r_value); 601 value_end = value + RSTRING_LEN(r_value); 602 603 pos = value; 604 605 for ( ;; ) { 606 pos = strchr(pos, '\n'); 607 608 if (pos == NULL) { 609 break; 610 } 611 612 headers_info->fields++; 613 headers_info->size += RSTRING_LEN(r_key) + (pos - value); 614 615 pos++; 616 value = pos; 617 } 618 619 if (value <= value_end) { 620 headers_info->fields++; 621 headers_info->size += RSTRING_LEN(r_key) + (value_end - value); 622 } 623 624 return ST_CONTINUE; 625 626 fail: 627 628 headers_info->rc = NXT_UNIT_ERROR; 629 630 return ST_STOP; 631 } 632 633 634 static int 635 nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) 636 { 637 int *rc; 638 uint32_t key_len; 639 const char *value, *value_end, *pos; 640 641 rc = (int *) (uintptr_t) arg; 642 643 value = RSTRING_PTR(r_value); 644 value_end = value + RSTRING_LEN(r_value); 645 646 key_len = RSTRING_LEN(r_key); 647 648 pos = value; 649 650 for ( ;; ) { 651 pos = strchr(pos, '\n'); 652 653 if (pos == NULL) { 654 break; 655 } 656 657 *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, 658 RSTRING_PTR(r_key), key_len, 659 value, pos - value); 660 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 661 goto fail; 662 } 663 664 pos++; 665 value = pos; 666 } 667 668 if (value <= value_end) { 669 *rc = nxt_unit_response_add_field(nxt_ruby_run_ctx.req, 670 RSTRING_PTR(r_key), key_len, 671 value, value_end - value); 672 if (nxt_slow_path(*rc != NXT_UNIT_OK)) { 673 goto fail; 674 } 675 } 676 677 return ST_CONTINUE; 678 679 fail: 680 681 *rc = NXT_UNIT_ERROR; 682 683 return ST_STOP; 684 } 685 686 687 static int 688 nxt_ruby_rack_result_body(VALUE result) 689 { 690 int rc; 691 VALUE fn, body; 692 693 body = rb_ary_entry(result, 2); 694 695 if (rb_respond_to(body, rb_intern("to_path"))) { 696 697 fn = rb_funcall(body, rb_intern("to_path"), 0); 698 if (nxt_slow_path(TYPE(fn) != T_STRING)) { 699 nxt_unit_req_error(nxt_ruby_run_ctx.req, 700 "Ruby: Failed to get 'body' file path from " 701 "application"); 702 703 return NXT_UNIT_ERROR; 704 } 705 706 rc = nxt_ruby_rack_result_body_file_write(fn); 707 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 708 return rc; 709 } 710 711 } else if (rb_respond_to(body, rb_intern("each"))) { 712 rb_block_call(body, rb_intern("each"), 0, 0, 713 nxt_ruby_rack_result_body_each, 0); 714 715 } else { 716 nxt_unit_req_error(nxt_ruby_run_ctx.req, 717 "Ruby: Invalid response 'body' format " 718 "from application"); 719 720 return NXT_UNIT_ERROR; 721 } 722 723 if (rb_respond_to(body, rb_intern("close"))) { 724 rb_funcall(body, rb_intern("close"), 0); 725 } 726 727 return NXT_UNIT_OK; 728 } 729 730 731 typedef struct { 732 int fd; 733 off_t pos; 734 off_t rest; 735 } nxt_ruby_rack_file_t; 736 737 738 static ssize_t 739 nxt_ruby_rack_file_read(nxt_unit_read_info_t *read_info, void *dst, size_t size) 740 { 741 ssize_t res; 742 nxt_ruby_rack_file_t *file; 743 744 file = read_info->data; 745 746 size = nxt_min(size, (size_t) file->rest); 747 748 res = pread(file->fd, dst, size, file->pos); 749 750 if (res >= 0) { 751 file->pos += res; 752 file->rest -= res; 753 754 if (size > (size_t) res) { 755 file->rest = 0; 756 } 757 } 758 759 read_info->eof = file->rest == 0; 760 761 return res; 762 } 763 764 765 static int 766 nxt_ruby_rack_result_body_file_write(VALUE filepath) 767 { 768 int fd, rc; 769 struct stat finfo; 770 nxt_ruby_rack_file_t ruby_file; 771 nxt_unit_read_info_t read_info; 772 773 fd = open(RSTRING_PTR(filepath), O_RDONLY, 0); 774 if (nxt_slow_path(fd == -1)) { 775 nxt_unit_req_error(nxt_ruby_run_ctx.req, 776 "Ruby: Failed to open content file \"%s\": %s (%d)", 777 RSTRING_PTR(filepath), strerror(errno), errno); 778 779 return NXT_UNIT_ERROR; 780 } 781 782 rc = fstat(fd, &finfo); 783 if (nxt_slow_path(rc == -1)) { 784 nxt_unit_req_error(nxt_ruby_run_ctx.req, 785 "Ruby: Content file fstat(\"%s\") failed: %s (%d)", 786 RSTRING_PTR(filepath), strerror(errno), errno); 787 788 close(fd); 789 790 return NXT_UNIT_ERROR; 791 } 792 793 ruby_file.fd = fd; 794 ruby_file.pos = 0; 795 ruby_file.rest = finfo.st_size; 796 797 read_info.read = nxt_ruby_rack_file_read; 798 read_info.eof = ruby_file.rest == 0; 799 read_info.buf_size = ruby_file.rest; 800 read_info.data = &ruby_file; 801 802 rc = nxt_unit_response_write_cb(nxt_ruby_run_ctx.req, &read_info); 803 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 804 nxt_unit_req_error(nxt_ruby_run_ctx.req, 805 "Ruby: Failed to write content file."); 806 } 807 808 close(fd); 809 810 return rc; 811 } 812 813 814 static VALUE 815 nxt_ruby_rack_result_body_each(VALUE body) 816 { 817 int rc; 818 819 if (TYPE(body) != T_STRING) { 820 return Qnil; 821 } 822 823 rc = nxt_unit_response_write(nxt_ruby_run_ctx.req, RSTRING_PTR(body), 824 RSTRING_LEN(body)); 825 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 826 nxt_unit_req_error(nxt_ruby_run_ctx.req, 827 "Ruby: Failed to write 'body' from application"); 828 } 829 830 return Qnil; 831 } 832 833 834 static void 835 nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) 836 { 837 int i; 838 VALUE err, ary, eclass, msg; 839 840 if (task != NULL) { 841 nxt_log(task, level, "Ruby: %s", desc); 842 843 } else { 844 nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s", desc); 845 } 846 847 err = rb_errinfo(); 848 if (nxt_slow_path(err == Qnil)) { 849 return; 850 } 851 852 ary = rb_funcall(err, rb_intern("backtrace"), 0); 853 if (nxt_slow_path(RARRAY_LEN(ary) == 0)) { 854 return; 855 } 856 857 eclass = rb_class_name(rb_class_of(err)); 858 msg = rb_funcall(err, rb_intern("message"), 0); 859 860 if (task != NULL) { 861 nxt_log(task, level, "Ruby: %s: %s (%s)", 862 RSTRING_PTR(RARRAY_PTR(ary)[0]), 863 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 864 865 } else { 866 nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "Ruby: %s: %s (%s)", 867 RSTRING_PTR(RARRAY_PTR(ary)[0]), 868 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 869 } 870 871 for (i = 1; i < RARRAY_LEN(ary); i++) { 872 if (task != NULL) { 873 nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); 874 875 } else { 876 nxt_unit_log(nxt_ruby_run_ctx.unit_ctx, level, "from %s", 877 RSTRING_PTR(RARRAY_PTR(ary)[i])); 878 } 879 } 880 } 881 882 883 static void 884 nxt_ruby_atexit(void) 885 { 886 rb_gc_unregister_address(&nxt_ruby_io_input); 887 rb_gc_unregister_address(&nxt_ruby_io_error); 888 889 rb_gc_unregister_address(&nxt_ruby_rackup); 890 rb_gc_unregister_address(&nxt_ruby_call); 891 rb_gc_unregister_address(&nxt_ruby_env); 892 893 ruby_cleanup(0); 894 } 895