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