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 21typedef struct { 22 nxt_task_t *task; 23 nxt_str_t *script; 24 VALUE builder; 25} nxt_ruby_rack_init_t; 26 27 28static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf); 29static VALUE nxt_ruby_init_basic(VALUE arg); 30static nxt_int_t nxt_ruby_init_io(nxt_task_t *task); 31static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 32 33static VALUE nxt_ruby_require_rubygems(VALUE arg); 34static VALUE nxt_ruby_require_rack(VALUE arg); 35static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 36static VALUE nxt_ruby_rack_env_create(VALUE arg); 37static nxt_int_t nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, 38 nxt_app_wmsg_t *wmsg); 39 40static VALUE nxt_ruby_rack_app_run(VALUE arg); 41static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, 42 VALUE hash_env); 43nxt_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); 45static nxt_int_t nxt_ruby_rack_result_status(VALUE result); 46nxt_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); 48static nxt_int_t nxt_ruby_rack_result_headers(VALUE result); 49static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg); 50static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size, 51 const char *value, size_t value_size); 52static nxt_int_t nxt_ruby_rack_result_body(VALUE result); 53static nxt_int_t nxt_ruby_rack_result_body_file_write(VALUE filepath); 54static VALUE nxt_ruby_rack_result_body_each(VALUE body); 55 56static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, 57 const char *desc); 58 59static void nxt_ruby_atexit(nxt_task_t *task); 60 61 62static uint32_t compat[] = { 63 NXT_VERNUM, NXT_DEBUG, 64}; 65 66static VALUE nxt_ruby_rackup; 67static VALUE nxt_ruby_call; 68static VALUE nxt_ruby_env; 69static VALUE nxt_ruby_io_input; 70static VALUE nxt_ruby_io_error; 71static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; 72 73NXT_EXPORT nxt_application_module_t nxt_app_module = { 74 sizeof(compat), 75 compat, 76 nxt_string("ruby"), 77 ruby_version, 78 nxt_ruby_init, 79 nxt_ruby_run, 80 nxt_ruby_atexit, 81}; 82 83 84static nxt_int_t 85nxt_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 134static VALUE 135nxt_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 163static nxt_int_t 164nxt_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 195static VALUE 196nxt_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 235static VALUE 236nxt_ruby_require_rubygems(VALUE arg) 237{ 238 return rb_funcall(rb_cObject, rb_intern("require"), 1, 239 rb_str_new2("rubygems")); 240} 241 242 243static VALUE 244nxt_ruby_require_rack(VALUE arg) 245{ 246 return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 247} 248 249 250static VALUE 251nxt_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 269static VALUE 270nxt_ruby_rack_env_create(VALUE arg) 271{ 272 VALUE hash_env, version; 273 274 hash_env = rb_hash_new(); 275 276 rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 277 rb_str_new((const char *) nxt_server.start, 278 (long) nxt_server.length)); 279 280 version = rb_ary_new(); 281 282 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 283 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 284 285 rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 286 rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), rb_str_new2("http")); 287 rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input); 288 rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error); 289 rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse); 290 rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 291 rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 292 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 293 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 294 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 295 296 return hash_env; 297} 298 299 300static nxt_int_t 301nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) 302{ 303 int state; 304 VALUE res; 305 306 nxt_ruby_run_ctx.task = task; 307 nxt_ruby_run_ctx.rmsg = rmsg; 308 nxt_ruby_run_ctx.wmsg = wmsg; 309 310 res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); 311 if (nxt_slow_path(state != 0)) { 312 nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 313 "Failed to run ruby script"); 314 return NXT_ERROR; 315 } 316 317 if (nxt_slow_path(res == Qnil)) { 318 return NXT_ERROR; 319 } 320 321 return NXT_OK; 322} 323 324 325static VALUE 326nxt_ruby_rack_app_run(VALUE arg) 327{ 328 VALUE env, result; 329 nxt_int_t rc; 330 331 env = rb_hash_dup(nxt_ruby_env); 332 333 rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env); 334 if (nxt_slow_path(rc != NXT_OK)) { 335 nxt_alert(nxt_ruby_run_ctx.task, 336 "Ruby: Failed to process incoming request"); 337 338 goto fail; 339 } 340 341 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 342 if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 343 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 344 "Ruby: Invalid response format from application"); 345 346 goto fail; 347 } 348 349 if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 350 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 351 "Ruby: Invalid response format from application. " 352 "Need 3 entries [Status, Headers, Body]"); 353 354 goto fail; 355 } 356 357 rc = nxt_ruby_rack_result_status(result); 358 if (nxt_slow_path(rc != NXT_OK)) { 359 goto fail; 360 } 361 362 rc = nxt_ruby_rack_result_headers(result); 363 if (nxt_slow_path(rc != NXT_OK)) { 364 goto fail; 365 } 366 367 rc = nxt_ruby_rack_result_body(result); 368 if (nxt_slow_path(rc != NXT_OK)) { 369 goto fail; 370 } 371 372 rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1); 373 if (nxt_slow_path(rc != NXT_OK)) { 374 goto fail; 375 } 376 377 rb_hash_delete(env, rb_obj_id(env)); 378 379 return result; 380 381fail: 382 383 rb_hash_delete(env, rb_obj_id(env)); 384 385 return Qnil; 386} 387 388 389static nxt_int_t 390nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env) 391{ 392 u_char *colon; 393 size_t query_size; 394 nxt_int_t rc; 395 nxt_str_t str, value, path, target; 396 nxt_str_t host, server_name, server_port; 397 nxt_task_t *task; 398 nxt_app_rmsg_t *rmsg; 399 400 static nxt_str_t def_host = nxt_string("localhost"); 401 static nxt_str_t def_port = nxt_string("80"); 402 403 task = run_ctx->task; 404 rmsg = run_ctx->rmsg; 405 406 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str); 407 if (nxt_slow_path(rc != NXT_OK)) { 408 return NXT_ERROR; 409 } 410 411 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target); 412 if (nxt_slow_path(rc != NXT_OK)) { 413 return NXT_ERROR; 414 } 415 416 rc = nxt_app_msg_read_str(task, rmsg, &path); 417 if (nxt_slow_path(rc != NXT_OK)) { 418 return NXT_ERROR; 419 } 420 421 rc = nxt_app_msg_read_size(task, rmsg, &query_size); 422 if (nxt_slow_path(rc != NXT_OK)) { 423 return NXT_ERROR; 424 } 425 426 if (path.start == NULL || path.length == 0) { 427 path = target; 428 } 429 430 rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"), 431 rb_str_new((const char *) path.start, (long) path.length)); 432 433 if (query_size > 0) { 434 query_size--; 435 436 if (nxt_slow_path(target.length < query_size)) { 437 return NXT_ERROR; 438 } 439 440 str.start = &target.start[query_size]; 441 str.length = target.length - query_size; 442 443 rb_hash_aset(hash_env, rb_str_new2("QUERY_STRING"), 444 rb_str_new((const char *) str.start, (long) str.length)); 445 } 446 447 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &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, "REMOTE_ADDR", &str); 453 if (nxt_slow_path(rc != NXT_OK)) { 454 return NXT_ERROR; 455 } 456 457 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_ADDR", &str); 458 if (nxt_slow_path(rc != NXT_OK)) { 459 return NXT_ERROR; 460 } 461 462 rc = nxt_app_msg_read_str(task, rmsg, &host); 463 if (nxt_slow_path(rc != NXT_OK)) { 464 return NXT_ERROR; 465 } 466 467 if (host.length == 0) { 468 host = def_host; 469 } 470 471 colon = nxt_memchr(host.start, ':', host.length); 472 server_name = host; 473 474 if (colon != NULL) { 475 server_name.length = colon - host.start; 476 477 server_port.start = colon + 1; 478 server_port.length = host.length - server_name.length - 1; 479 480 } else { 481 server_port = def_port; 482 } 483 484 rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"), 485 rb_str_new((const char *) server_name.start, 486 (long) server_name.length)); 487 488 rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"), 489 rb_str_new((const char *) server_port.start, 490 (long) server_port.length)); 491 492 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str); 493 if (nxt_slow_path(rc != NXT_OK)) { 494 return NXT_ERROR; 495 } 496 497 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str); 498 if (nxt_slow_path(rc != NXT_OK)) { 499 return NXT_ERROR; 500 } 501 502 for ( ;; ) { 503 rc = nxt_app_msg_read_str(task, rmsg, &str); 504 if (nxt_slow_path(rc != NXT_OK)) { 505 return NXT_ERROR; 506 } 507 508 if (nxt_slow_path(str.length == 0)) { 509 break; 510 } 511 512 rc = nxt_app_msg_read_str(task, rmsg, &value); 513 if (nxt_slow_path(rc != NXT_OK)) { 514 return NXT_ERROR; 515 } 516 517 rb_hash_aset(hash_env, 518 rb_str_new((char *) str.start, (long) str.length), 519 rb_str_new((const char *) value.start, 520 (long) value.length)); 521 } 522 523 rc = nxt_app_msg_read_size(task, rmsg, &run_ctx->body_preread_size); 524 if (nxt_slow_path(rc != NXT_OK)) { 525 return NXT_ERROR; 526 } 527 528 return NXT_OK; 529} 530 531 532nxt_inline nxt_int_t 533nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env, 534 const char *name, nxt_str_t *str) 535{ 536 nxt_int_t rc; 537 538 rc = nxt_app_msg_read_str(task, rmsg, str); 539 if (nxt_slow_path(rc != NXT_OK)) { 540 return rc; 541 } 542 543 if (str->start == NULL) { 544 rb_hash_aset(hash_env, rb_str_new2(name), Qnil); 545 return NXT_OK; 546 } 547 548 rb_hash_aset(hash_env, rb_str_new2(name), 549 rb_str_new((const char *) str->start, (long) str->length)); 550 551 return NXT_OK; 552} 553 554 555static nxt_int_t 556nxt_ruby_rack_result_status(VALUE result) 557{ 558 VALUE status; 559 u_char *p; 560 size_t len; 561 nxt_int_t rc; 562 u_char buf[3]; 563 564 status = rb_ary_entry(result, 0); 565 566 if (TYPE(status) == T_FIXNUM) { 567 nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status)); 568 569 p = buf; 570 len = 3; 571 572 } else if (TYPE(status) == T_STRING) { 573 p = (u_char *) RSTRING_PTR(status); 574 len = RSTRING_LEN(status); 575 576 } else { 577 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 578 "Ruby: Invalid response 'status' format from application"); 579 580 return NXT_ERROR; 581 } 582 583 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
| 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 21typedef struct { 22 nxt_task_t *task; 23 nxt_str_t *script; 24 VALUE builder; 25} nxt_ruby_rack_init_t; 26 27 28static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf); 29static VALUE nxt_ruby_init_basic(VALUE arg); 30static nxt_int_t nxt_ruby_init_io(nxt_task_t *task); 31static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); 32 33static VALUE nxt_ruby_require_rubygems(VALUE arg); 34static VALUE nxt_ruby_require_rack(VALUE arg); 35static VALUE nxt_ruby_rack_parse_script(VALUE ctx); 36static VALUE nxt_ruby_rack_env_create(VALUE arg); 37static nxt_int_t nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, 38 nxt_app_wmsg_t *wmsg); 39 40static VALUE nxt_ruby_rack_app_run(VALUE arg); 41static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, 42 VALUE hash_env); 43nxt_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); 45static nxt_int_t nxt_ruby_rack_result_status(VALUE result); 46nxt_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); 48static nxt_int_t nxt_ruby_rack_result_headers(VALUE result); 49static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg); 50static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size, 51 const char *value, size_t value_size); 52static nxt_int_t nxt_ruby_rack_result_body(VALUE result); 53static nxt_int_t nxt_ruby_rack_result_body_file_write(VALUE filepath); 54static VALUE nxt_ruby_rack_result_body_each(VALUE body); 55 56static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, 57 const char *desc); 58 59static void nxt_ruby_atexit(nxt_task_t *task); 60 61 62static uint32_t compat[] = { 63 NXT_VERNUM, NXT_DEBUG, 64}; 65 66static VALUE nxt_ruby_rackup; 67static VALUE nxt_ruby_call; 68static VALUE nxt_ruby_env; 69static VALUE nxt_ruby_io_input; 70static VALUE nxt_ruby_io_error; 71static nxt_ruby_run_ctx_t nxt_ruby_run_ctx; 72 73NXT_EXPORT nxt_application_module_t nxt_app_module = { 74 sizeof(compat), 75 compat, 76 nxt_string("ruby"), 77 ruby_version, 78 nxt_ruby_init, 79 nxt_ruby_run, 80 nxt_ruby_atexit, 81}; 82 83 84static nxt_int_t 85nxt_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 134static VALUE 135nxt_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 163static nxt_int_t 164nxt_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 195static VALUE 196nxt_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 235static VALUE 236nxt_ruby_require_rubygems(VALUE arg) 237{ 238 return rb_funcall(rb_cObject, rb_intern("require"), 1, 239 rb_str_new2("rubygems")); 240} 241 242 243static VALUE 244nxt_ruby_require_rack(VALUE arg) 245{ 246 return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack")); 247} 248 249 250static VALUE 251nxt_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 269static VALUE 270nxt_ruby_rack_env_create(VALUE arg) 271{ 272 VALUE hash_env, version; 273 274 hash_env = rb_hash_new(); 275 276 rb_hash_aset(hash_env, rb_str_new2("SERVER_SOFTWARE"), 277 rb_str_new((const char *) nxt_server.start, 278 (long) nxt_server.length)); 279 280 version = rb_ary_new(); 281 282 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR)); 283 rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR)); 284 285 rb_hash_aset(hash_env, rb_str_new2("rack.version"), version); 286 rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), rb_str_new2("http")); 287 rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input); 288 rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error); 289 rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse); 290 rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue); 291 rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse); 292 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse); 293 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil); 294 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil); 295 296 return hash_env; 297} 298 299 300static nxt_int_t 301nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) 302{ 303 int state; 304 VALUE res; 305 306 nxt_ruby_run_ctx.task = task; 307 nxt_ruby_run_ctx.rmsg = rmsg; 308 nxt_ruby_run_ctx.wmsg = wmsg; 309 310 res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state); 311 if (nxt_slow_path(state != 0)) { 312 nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 313 "Failed to run ruby script"); 314 return NXT_ERROR; 315 } 316 317 if (nxt_slow_path(res == Qnil)) { 318 return NXT_ERROR; 319 } 320 321 return NXT_OK; 322} 323 324 325static VALUE 326nxt_ruby_rack_app_run(VALUE arg) 327{ 328 VALUE env, result; 329 nxt_int_t rc; 330 331 env = rb_hash_dup(nxt_ruby_env); 332 333 rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env); 334 if (nxt_slow_path(rc != NXT_OK)) { 335 nxt_alert(nxt_ruby_run_ctx.task, 336 "Ruby: Failed to process incoming request"); 337 338 goto fail; 339 } 340 341 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env); 342 if (nxt_slow_path(TYPE(result) != T_ARRAY)) { 343 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 344 "Ruby: Invalid response format from application"); 345 346 goto fail; 347 } 348 349 if (nxt_slow_path(RARRAY_LEN(result) != 3)) { 350 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 351 "Ruby: Invalid response format from application. " 352 "Need 3 entries [Status, Headers, Body]"); 353 354 goto fail; 355 } 356 357 rc = nxt_ruby_rack_result_status(result); 358 if (nxt_slow_path(rc != NXT_OK)) { 359 goto fail; 360 } 361 362 rc = nxt_ruby_rack_result_headers(result); 363 if (nxt_slow_path(rc != NXT_OK)) { 364 goto fail; 365 } 366 367 rc = nxt_ruby_rack_result_body(result); 368 if (nxt_slow_path(rc != NXT_OK)) { 369 goto fail; 370 } 371 372 rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1); 373 if (nxt_slow_path(rc != NXT_OK)) { 374 goto fail; 375 } 376 377 rb_hash_delete(env, rb_obj_id(env)); 378 379 return result; 380 381fail: 382 383 rb_hash_delete(env, rb_obj_id(env)); 384 385 return Qnil; 386} 387 388 389static nxt_int_t 390nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env) 391{ 392 u_char *colon; 393 size_t query_size; 394 nxt_int_t rc; 395 nxt_str_t str, value, path, target; 396 nxt_str_t host, server_name, server_port; 397 nxt_task_t *task; 398 nxt_app_rmsg_t *rmsg; 399 400 static nxt_str_t def_host = nxt_string("localhost"); 401 static nxt_str_t def_port = nxt_string("80"); 402 403 task = run_ctx->task; 404 rmsg = run_ctx->rmsg; 405 406 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str); 407 if (nxt_slow_path(rc != NXT_OK)) { 408 return NXT_ERROR; 409 } 410 411 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target); 412 if (nxt_slow_path(rc != NXT_OK)) { 413 return NXT_ERROR; 414 } 415 416 rc = nxt_app_msg_read_str(task, rmsg, &path); 417 if (nxt_slow_path(rc != NXT_OK)) { 418 return NXT_ERROR; 419 } 420 421 rc = nxt_app_msg_read_size(task, rmsg, &query_size); 422 if (nxt_slow_path(rc != NXT_OK)) { 423 return NXT_ERROR; 424 } 425 426 if (path.start == NULL || path.length == 0) { 427 path = target; 428 } 429 430 rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"), 431 rb_str_new((const char *) path.start, (long) path.length)); 432 433 if (query_size > 0) { 434 query_size--; 435 436 if (nxt_slow_path(target.length < query_size)) { 437 return NXT_ERROR; 438 } 439 440 str.start = &target.start[query_size]; 441 str.length = target.length - query_size; 442 443 rb_hash_aset(hash_env, rb_str_new2("QUERY_STRING"), 444 rb_str_new((const char *) str.start, (long) str.length)); 445 } 446 447 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &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, "REMOTE_ADDR", &str); 453 if (nxt_slow_path(rc != NXT_OK)) { 454 return NXT_ERROR; 455 } 456 457 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_ADDR", &str); 458 if (nxt_slow_path(rc != NXT_OK)) { 459 return NXT_ERROR; 460 } 461 462 rc = nxt_app_msg_read_str(task, rmsg, &host); 463 if (nxt_slow_path(rc != NXT_OK)) { 464 return NXT_ERROR; 465 } 466 467 if (host.length == 0) { 468 host = def_host; 469 } 470 471 colon = nxt_memchr(host.start, ':', host.length); 472 server_name = host; 473 474 if (colon != NULL) { 475 server_name.length = colon - host.start; 476 477 server_port.start = colon + 1; 478 server_port.length = host.length - server_name.length - 1; 479 480 } else { 481 server_port = def_port; 482 } 483 484 rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"), 485 rb_str_new((const char *) server_name.start, 486 (long) server_name.length)); 487 488 rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"), 489 rb_str_new((const char *) server_port.start, 490 (long) server_port.length)); 491 492 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str); 493 if (nxt_slow_path(rc != NXT_OK)) { 494 return NXT_ERROR; 495 } 496 497 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str); 498 if (nxt_slow_path(rc != NXT_OK)) { 499 return NXT_ERROR; 500 } 501 502 for ( ;; ) { 503 rc = nxt_app_msg_read_str(task, rmsg, &str); 504 if (nxt_slow_path(rc != NXT_OK)) { 505 return NXT_ERROR; 506 } 507 508 if (nxt_slow_path(str.length == 0)) { 509 break; 510 } 511 512 rc = nxt_app_msg_read_str(task, rmsg, &value); 513 if (nxt_slow_path(rc != NXT_OK)) { 514 return NXT_ERROR; 515 } 516 517 rb_hash_aset(hash_env, 518 rb_str_new((char *) str.start, (long) str.length), 519 rb_str_new((const char *) value.start, 520 (long) value.length)); 521 } 522 523 rc = nxt_app_msg_read_size(task, rmsg, &run_ctx->body_preread_size); 524 if (nxt_slow_path(rc != NXT_OK)) { 525 return NXT_ERROR; 526 } 527 528 return NXT_OK; 529} 530 531 532nxt_inline nxt_int_t 533nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env, 534 const char *name, nxt_str_t *str) 535{ 536 nxt_int_t rc; 537 538 rc = nxt_app_msg_read_str(task, rmsg, str); 539 if (nxt_slow_path(rc != NXT_OK)) { 540 return rc; 541 } 542 543 if (str->start == NULL) { 544 rb_hash_aset(hash_env, rb_str_new2(name), Qnil); 545 return NXT_OK; 546 } 547 548 rb_hash_aset(hash_env, rb_str_new2(name), 549 rb_str_new((const char *) str->start, (long) str->length)); 550 551 return NXT_OK; 552} 553 554 555static nxt_int_t 556nxt_ruby_rack_result_status(VALUE result) 557{ 558 VALUE status; 559 u_char *p; 560 size_t len; 561 nxt_int_t rc; 562 u_char buf[3]; 563 564 status = rb_ary_entry(result, 0); 565 566 if (TYPE(status) == T_FIXNUM) { 567 nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status)); 568 569 p = buf; 570 len = 3; 571 572 } else if (TYPE(status) == T_STRING) { 573 p = (u_char *) RSTRING_PTR(status); 574 len = RSTRING_LEN(status); 575 576 } else { 577 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 578 "Ruby: Invalid response 'status' format from application"); 579 580 return NXT_ERROR; 581 } 582 583 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
|
745} 746 747 748static nxt_int_t 749nxt_ruby_rack_result_body(VALUE result) 750{ 751 VALUE fn, body; 752 nxt_int_t rc; 753 754 body = rb_ary_entry(result, 2); 755 756 if (rb_respond_to(body, rb_intern("to_path"))) { 757 758 fn = rb_funcall(body, rb_intern("to_path"), 0); 759 if (nxt_slow_path(TYPE(fn) != T_STRING)) { 760 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 761 "Ruby: Failed to get 'body' file path from application"); 762 763 return NXT_ERROR; 764 } 765 766 rc = nxt_ruby_rack_result_body_file_write(fn); 767 if (nxt_slow_path(rc != NXT_OK)) { 768 return NXT_ERROR; 769 } 770 771 } else if (rb_respond_to(body, rb_intern("each"))) { 772 rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0); 773 774 } else { 775 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 776 "Ruby: Invalid response 'body' format from application"); 777 778 return NXT_ERROR; 779 } 780 781 if (rb_respond_to(body, rb_intern("close"))) { 782 rb_funcall(body, rb_intern("close"), 0); 783 } 784 785 return NXT_OK; 786} 787 788 789static nxt_int_t 790nxt_ruby_rack_result_body_file_write(VALUE filepath) 791{ 792 size_t len; 793 ssize_t n; 794 nxt_off_t rest; 795 nxt_int_t rc; 796 nxt_file_t file; 797 nxt_file_info_t finfo; 798 u_char buf[8192]; 799 800 nxt_memzero(&file, sizeof(nxt_file_t)); 801 802 file.name = (nxt_file_name_t *) RSTRING_PTR(filepath); 803 804 rc = nxt_file_open(nxt_ruby_run_ctx.task, &file, NXT_FILE_RDONLY, 805 NXT_FILE_OPEN, 0); 806 if (nxt_slow_path(rc != NXT_OK)) { 807 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 808 "Ruby: Failed to open 'body' file: %s", 809 (const char *) file.name); 810 811 return NXT_ERROR; 812 } 813 814 rc = nxt_file_info(&file, &finfo); 815 if (nxt_slow_path(rc != NXT_OK)) { 816 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 817 "Ruby: Failed to get 'body' file information: %s", 818 (const char *) file.name); 819 820 goto fail; 821 } 822 823 rest = nxt_file_size(&finfo); 824 825 while (rest != 0) { 826 len = nxt_min(rest, (nxt_off_t) sizeof(buf)); 827 828 n = nxt_file_read(&file, buf, len, nxt_file_size(&finfo) - rest); 829 if (nxt_slow_path(n != (ssize_t) len)) { 830 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 831 "Ruby: Failed to read 'body' file"); 832 833 goto fail; 834 } 835 836 rest -= len; 837 838 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 839 buf, len); 840 if (nxt_slow_path(rc != NXT_OK)) { 841 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 842 "Ruby: Failed to write 'body' from application"); 843 844 goto fail; 845 } 846 } 847 848 nxt_file_close(nxt_ruby_run_ctx.task, &file); 849 850 return NXT_OK; 851 852fail: 853 854 nxt_file_close(nxt_ruby_run_ctx.task, &file); 855 856 return NXT_ERROR; 857} 858 859 860static VALUE 861nxt_ruby_rack_result_body_each(VALUE body) 862{ 863 nxt_int_t rc; 864 865 if (TYPE(body) != T_STRING) { 866 return Qnil; 867 } 868 869 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 870 (u_char *) RSTRING_PTR(body), RSTRING_LEN(body)); 871 if (nxt_slow_path(rc != NXT_OK)) { 872 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 873 "Ruby: Failed to write 'body' from application"); 874 } 875 876 return Qnil; 877} 878 879 880static void 881nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) 882{ 883 int i; 884 VALUE err, ary, eclass, msg; 885 886 nxt_log(task, level, "Ruby: %s", desc); 887 888 err = rb_errinfo(); 889 ary = rb_funcall(err, rb_intern("backtrace"), 0); 890 891 if (RARRAY_LEN(ary) == 0) { 892 return; 893 } 894 895 eclass = rb_class_name(rb_class_of(err)); 896 msg = rb_funcall(err, rb_intern("message"), 0); 897 898 nxt_log(task, level, "Ruby: %s: %s (%s)", 899 RSTRING_PTR(RARRAY_PTR(ary)[0]), 900 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 901 902 for (i = 1; i < RARRAY_LEN(ary); i++) { 903 nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); 904 } 905} 906 907 908static void 909nxt_ruby_atexit(nxt_task_t *task) 910{ 911 rb_gc_unregister_address(&nxt_ruby_io_input); 912 rb_gc_unregister_address(&nxt_ruby_io_error); 913 914 rb_gc_unregister_address(&nxt_ruby_rackup); 915 rb_gc_unregister_address(&nxt_ruby_call); 916 rb_gc_unregister_address(&nxt_ruby_env); 917 918 ruby_cleanup(0); 919}
| 745} 746 747 748static nxt_int_t 749nxt_ruby_rack_result_body(VALUE result) 750{ 751 VALUE fn, body; 752 nxt_int_t rc; 753 754 body = rb_ary_entry(result, 2); 755 756 if (rb_respond_to(body, rb_intern("to_path"))) { 757 758 fn = rb_funcall(body, rb_intern("to_path"), 0); 759 if (nxt_slow_path(TYPE(fn) != T_STRING)) { 760 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 761 "Ruby: Failed to get 'body' file path from application"); 762 763 return NXT_ERROR; 764 } 765 766 rc = nxt_ruby_rack_result_body_file_write(fn); 767 if (nxt_slow_path(rc != NXT_OK)) { 768 return NXT_ERROR; 769 } 770 771 } else if (rb_respond_to(body, rb_intern("each"))) { 772 rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0); 773 774 } else { 775 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 776 "Ruby: Invalid response 'body' format from application"); 777 778 return NXT_ERROR; 779 } 780 781 if (rb_respond_to(body, rb_intern("close"))) { 782 rb_funcall(body, rb_intern("close"), 0); 783 } 784 785 return NXT_OK; 786} 787 788 789static nxt_int_t 790nxt_ruby_rack_result_body_file_write(VALUE filepath) 791{ 792 size_t len; 793 ssize_t n; 794 nxt_off_t rest; 795 nxt_int_t rc; 796 nxt_file_t file; 797 nxt_file_info_t finfo; 798 u_char buf[8192]; 799 800 nxt_memzero(&file, sizeof(nxt_file_t)); 801 802 file.name = (nxt_file_name_t *) RSTRING_PTR(filepath); 803 804 rc = nxt_file_open(nxt_ruby_run_ctx.task, &file, NXT_FILE_RDONLY, 805 NXT_FILE_OPEN, 0); 806 if (nxt_slow_path(rc != NXT_OK)) { 807 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 808 "Ruby: Failed to open 'body' file: %s", 809 (const char *) file.name); 810 811 return NXT_ERROR; 812 } 813 814 rc = nxt_file_info(&file, &finfo); 815 if (nxt_slow_path(rc != NXT_OK)) { 816 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 817 "Ruby: Failed to get 'body' file information: %s", 818 (const char *) file.name); 819 820 goto fail; 821 } 822 823 rest = nxt_file_size(&finfo); 824 825 while (rest != 0) { 826 len = nxt_min(rest, (nxt_off_t) sizeof(buf)); 827 828 n = nxt_file_read(&file, buf, len, nxt_file_size(&finfo) - rest); 829 if (nxt_slow_path(n != (ssize_t) len)) { 830 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 831 "Ruby: Failed to read 'body' file"); 832 833 goto fail; 834 } 835 836 rest -= len; 837 838 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 839 buf, len); 840 if (nxt_slow_path(rc != NXT_OK)) { 841 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 842 "Ruby: Failed to write 'body' from application"); 843 844 goto fail; 845 } 846 } 847 848 nxt_file_close(nxt_ruby_run_ctx.task, &file); 849 850 return NXT_OK; 851 852fail: 853 854 nxt_file_close(nxt_ruby_run_ctx.task, &file); 855 856 return NXT_ERROR; 857} 858 859 860static VALUE 861nxt_ruby_rack_result_body_each(VALUE body) 862{ 863 nxt_int_t rc; 864 865 if (TYPE(body) != T_STRING) { 866 return Qnil; 867 } 868 869 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 870 (u_char *) RSTRING_PTR(body), RSTRING_LEN(body)); 871 if (nxt_slow_path(rc != NXT_OK)) { 872 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR, 873 "Ruby: Failed to write 'body' from application"); 874 } 875 876 return Qnil; 877} 878 879 880static void 881nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc) 882{ 883 int i; 884 VALUE err, ary, eclass, msg; 885 886 nxt_log(task, level, "Ruby: %s", desc); 887 888 err = rb_errinfo(); 889 ary = rb_funcall(err, rb_intern("backtrace"), 0); 890 891 if (RARRAY_LEN(ary) == 0) { 892 return; 893 } 894 895 eclass = rb_class_name(rb_class_of(err)); 896 msg = rb_funcall(err, rb_intern("message"), 0); 897 898 nxt_log(task, level, "Ruby: %s: %s (%s)", 899 RSTRING_PTR(RARRAY_PTR(ary)[0]), 900 RSTRING_PTR(msg), RSTRING_PTR(eclass)); 901 902 for (i = 1; i < RARRAY_LEN(ary); i++) { 903 nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i])); 904 } 905} 906 907 908static void 909nxt_ruby_atexit(nxt_task_t *task) 910{ 911 rb_gc_unregister_address(&nxt_ruby_io_input); 912 rb_gc_unregister_address(&nxt_ruby_io_error); 913 914 rb_gc_unregister_address(&nxt_ruby_rackup); 915 rb_gc_unregister_address(&nxt_ruby_call); 916 rb_gc_unregister_address(&nxt_ruby_env); 917 918 ruby_cleanup(0); 919}
|