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