Deleted Added
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) \

--- 13 unchanged lines hidden (view full) ---

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_bundler_setup(VALUE arg);
35static VALUE nxt_ruby_require_rack(VALUE arg);
36static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
37static VALUE nxt_ruby_rack_env_create(VALUE arg);
38static nxt_int_t nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg,
39 nxt_app_wmsg_t *wmsg);
40
41static VALUE nxt_ruby_rack_app_run(VALUE arg);
42static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx,
43 VALUE hash_env);
44nxt_inline nxt_int_t nxt_ruby_read_add_env(nxt_task_t *task,
45 nxt_app_rmsg_t *rmsg, VALUE hash_env, const char *name, nxt_str_t *str);
46static nxt_int_t nxt_ruby_rack_result_status(VALUE result);
47nxt_inline nxt_int_t nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg,
48 const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last);
49static nxt_int_t nxt_ruby_rack_result_headers(VALUE result);
50static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg);
51static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size,
52 const char *value, size_t value_size);
53static nxt_int_t nxt_ruby_rack_result_body(VALUE result);
54static nxt_int_t nxt_ruby_rack_result_body_file_write(VALUE filepath);
55static VALUE nxt_ruby_rack_result_body_each(VALUE body);
56
57static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level,
58 const char *desc);
59
60static void nxt_ruby_atexit(nxt_task_t *task);
61
62
63static uint32_t compat[] = {
64 NXT_VERNUM, NXT_DEBUG,
65};
66
67static VALUE nxt_ruby_rackup;
68static VALUE nxt_ruby_call;
69static VALUE nxt_ruby_env;
70static VALUE nxt_ruby_io_input;
71static VALUE nxt_ruby_io_error;
72static nxt_ruby_run_ctx_t nxt_ruby_run_ctx;
73
74NXT_EXPORT nxt_application_module_t nxt_app_module = {
75 sizeof(compat),
76 compat,
77 nxt_string("ruby"),
78 ruby_version,
79 nxt_ruby_init,
80 nxt_ruby_run,
81 nxt_ruby_atexit,
82};
83
84
85static nxt_int_t
86nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
87{
88 int state;
89 VALUE dummy, res;
90 nxt_ruby_rack_init_t rack_init;
91
92 ruby_init();
93 Init_stack(&dummy);
94 ruby_init_loadpath();
95 ruby_script("NGINX_Unit");
96
97 rack_init.task = task;

--- 25 unchanged lines hidden (view full) ---

123 "Failed to create 'environ' variable");
124 return NXT_ERROR;
125 }
126
127 rb_gc_register_address(&nxt_ruby_rackup);
128 rb_gc_register_address(&nxt_ruby_call);
129 rb_gc_register_address(&nxt_ruby_env);
130
131 return NXT_OK;
132}
133
134
135static VALUE
136nxt_ruby_init_basic(VALUE arg)
137{
138 int state;

--- 175 unchanged lines hidden (view full) ---

314 rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse);
315 rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil);
316 rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil);
317
318 return hash_env;
319}
320
321
322static nxt_int_t
323nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg)
324{
325 int state;
326 VALUE res;
327
328 nxt_ruby_run_ctx.task = task;
329 nxt_ruby_run_ctx.rmsg = rmsg;
330 nxt_ruby_run_ctx.wmsg = wmsg;
331
332 res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state);
333 if (nxt_slow_path(state != 0)) {
334 nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
335 "Failed to run ruby script");
336 return NXT_ERROR;
337 }
338
339 if (nxt_slow_path(res == Qnil)) {
340 return NXT_ERROR;
341 }
342
343 return NXT_OK;
344}
345
346
347static VALUE
348nxt_ruby_rack_app_run(VALUE arg)
349{
350 VALUE env, result;
351 nxt_int_t rc;
352
353 env = rb_hash_dup(nxt_ruby_env);
354
355 rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env);
356 if (nxt_slow_path(rc != NXT_OK)) {
357 nxt_alert(nxt_ruby_run_ctx.task,
358 "Ruby: Failed to process incoming request");
359
360 goto fail;
361 }
362
363 result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env);
364 if (nxt_slow_path(TYPE(result) != T_ARRAY)) {
365 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
366 "Ruby: Invalid response format from application");
367
368 goto fail;
369 }
370
371 if (nxt_slow_path(RARRAY_LEN(result) != 3)) {
372 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
373 "Ruby: Invalid response format from application. "
374 "Need 3 entries [Status, Headers, Body]");
375
376 goto fail;
377 }
378
379 rc = nxt_ruby_rack_result_status(result);
380 if (nxt_slow_path(rc != NXT_OK)) {
381 goto fail;
382 }
383
384 rc = nxt_ruby_rack_result_headers(result);
385 if (nxt_slow_path(rc != NXT_OK)) {
386 goto fail;
387 }
388
389 rc = nxt_ruby_rack_result_body(result);
390 if (nxt_slow_path(rc != NXT_OK)) {
391 goto fail;
392 }
393
394 rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1);
395 if (nxt_slow_path(rc != NXT_OK)) {
396 goto fail;
397 }
398
399 rb_hash_delete(env, rb_obj_id(env));
400
401 return result;
402
403fail:
404
405 rb_hash_delete(env, rb_obj_id(env));
406
407 return Qnil;
408}
409
410
411static nxt_int_t
412nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env)
413{
414 u_char *colon;
415 size_t query_size;
416 nxt_int_t rc;
417 nxt_str_t str, value, path, target;
418 nxt_str_t host, server_name, server_port;
419 nxt_task_t *task;
420 nxt_app_rmsg_t *rmsg;
421
422 static nxt_str_t def_host = nxt_string("localhost");
423 static nxt_str_t def_port = nxt_string("80");
424
425 task = run_ctx->task;
426 rmsg = run_ctx->rmsg;
427
428 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str);
429 if (nxt_slow_path(rc != NXT_OK)) {
430 return NXT_ERROR;
431 }
432
433 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target);
434 if (nxt_slow_path(rc != NXT_OK)) {
435 return NXT_ERROR;
436 }
437
438 rc = nxt_app_msg_read_str(task, rmsg, &path);
439 if (nxt_slow_path(rc != NXT_OK)) {
440 return NXT_ERROR;
441 }
442
443 rc = nxt_app_msg_read_size(task, rmsg, &query_size);
444 if (nxt_slow_path(rc != NXT_OK)) {
445 return NXT_ERROR;
446 }
447
448 if (path.start == NULL || path.length == 0) {
449 path = target;
450 }
451
452 rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"),
453 rb_str_new((const char *) path.start, (long) path.length));
454
455 if (query_size > 0) {
456 query_size--;
457
458 if (nxt_slow_path(target.length < query_size)) {
459 return NXT_ERROR;
460 }
461
462 str.start = &target.start[query_size];
463 str.length = target.length - query_size;
464
465 rb_hash_aset(hash_env, rb_str_new2("QUERY_STRING"),
466 rb_str_new((const char *) str.start, (long) str.length));
467 }
468
469 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &str);
470 if (nxt_slow_path(rc != NXT_OK)) {
471 return NXT_ERROR;
472 }
473
474 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REMOTE_ADDR", &str);
475 if (nxt_slow_path(rc != NXT_OK)) {
476 return NXT_ERROR;
477 }
478
479 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_ADDR", &str);
480 if (nxt_slow_path(rc != NXT_OK)) {
481 return NXT_ERROR;
482 }
483
484 rc = nxt_app_msg_read_str(task, rmsg, &host);
485 if (nxt_slow_path(rc != NXT_OK)) {
486 return NXT_ERROR;
487 }
488
489 if (host.length == 0) {
490 host = def_host;
491 }
492
493 colon = nxt_memchr(host.start, ':', host.length);
494 server_name = host;
495
496 if (colon != NULL) {
497 server_name.length = colon - host.start;
498
499 server_port.start = colon + 1;
500 server_port.length = host.length - server_name.length - 1;
501
502 } else {
503 server_port = def_port;
504 }
505
506 rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"),
507 rb_str_new((const char *) server_name.start,
508 (long) server_name.length));
509
510 rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"),
511 rb_str_new((const char *) server_port.start,
512 (long) server_port.length));
513
514 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str);
515 if (nxt_slow_path(rc != NXT_OK)) {
516 return NXT_ERROR;
517 }
518
519 rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str);
520 if (nxt_slow_path(rc != NXT_OK)) {
521 return NXT_ERROR;
522 }
523
524 for ( ;; ) {
525 rc = nxt_app_msg_read_str(task, rmsg, &str);
526 if (nxt_slow_path(rc != NXT_OK)) {
527 return NXT_ERROR;
528 }
529
530 if (nxt_slow_path(str.length == 0)) {
531 break;
532 }
533
534 rc = nxt_app_msg_read_str(task, rmsg, &value);
535 if (nxt_slow_path(rc != NXT_OK)) {
536 return NXT_ERROR;
537 }
538
539 rb_hash_aset(hash_env,
540 rb_str_new((char *) str.start, (long) str.length),
541 rb_str_new((const char *) value.start,
542 (long) value.length));
543 }
544
545 rc = nxt_app_msg_read_size(task, rmsg, &run_ctx->body_preread_size);
546 if (nxt_slow_path(rc != NXT_OK)) {
547 return NXT_ERROR;
548 }
549
550 return NXT_OK;
551}
552
553
554nxt_inline nxt_int_t
555nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env,
556 const char *name, nxt_str_t *str)
557{
558 nxt_int_t rc;
559
560 rc = nxt_app_msg_read_str(task, rmsg, str);
561 if (nxt_slow_path(rc != NXT_OK)) {
562 return rc;
563 }
564
565 if (str->start == NULL) {
566 rb_hash_aset(hash_env, rb_str_new2(name), Qnil);
567 return NXT_OK;
568 }
569
570 rb_hash_aset(hash_env, rb_str_new2(name),
571 rb_str_new((const char *) str->start, (long) str->length));
572
573 return NXT_OK;
574}
575
576
577static nxt_int_t
578nxt_ruby_rack_result_status(VALUE result)
579{
580 VALUE status;
581 u_char *p;
582 size_t len;
583 nxt_int_t rc;
584 u_char buf[3];
585
586 status = rb_ary_entry(result, 0);
587
588 if (TYPE(status) == T_FIXNUM) {
589 nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status));
590
591 p = buf;
592 len = 3;
593
594 } else if (TYPE(status) == T_STRING) {
595 p = (u_char *) RSTRING_PTR(status);
596 len = RSTRING_LEN(status);
597
598 } else {
599 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
600 "Ruby: Invalid response 'status' format from application");
601
602 return NXT_ERROR;
603 }
604
605 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
606 (u_char *) "Status: ", nxt_length("Status: "), 0, 0);
607 if (nxt_slow_path(rc != NXT_OK)) {
608 return NXT_ERROR;
609 }
610
611 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
612 p, len, 0, 0);
613 if (nxt_slow_path(rc != NXT_OK)) {
614 return NXT_ERROR;
615 }
616
617 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
618 (u_char *) "\r\n", nxt_length("\r\n"), 0, 0);
619 if (nxt_slow_path(rc != NXT_OK)) {
620 return NXT_ERROR;
621 }
622
623 return NXT_OK;
624}
625
626
627nxt_inline nxt_int_t
628nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg,
629 const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last)
630{
631 nxt_int_t rc;
632
633 rc = nxt_app_msg_write_raw(task, wmsg, data, len);
634 if (nxt_slow_path(rc != NXT_OK)) {
635 return rc;
636 }
637
638 if (flush || last) {
639 rc = nxt_app_msg_flush(task, wmsg, last);
640 }
641
642 return rc;
643}
644
645
646static nxt_int_t
647nxt_ruby_rack_result_headers(VALUE result)
648{
649 VALUE headers;
650 nxt_int_t rc;
651
652 headers = rb_ary_entry(result, 1);
653 if (nxt_slow_path(TYPE(headers) != T_HASH)) {
654 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
655 "Ruby: Invalid response 'headers' format from application");
656
657 return NXT_ERROR;
658 }
659
660 rc = NXT_OK;
661
662 rb_hash_foreach(headers, nxt_ruby_hash_foreach, (VALUE) (uintptr_t) &rc);
663 if (nxt_slow_path(rc != NXT_OK)) {
664 return NXT_ERROR;
665 }
666
667 rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
668 (u_char *) "\r\n", nxt_length("\r\n"), 0, 0);
669 if (nxt_slow_path(rc != NXT_OK)) {
670 return NXT_ERROR;
671 }
672
673 return NXT_OK;
674}
675
676
677static int
678nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg)
679{
680 nxt_int_t rc, *rc_p;
681 const char *value, *value_end, *pos;
682
683 rc_p = (nxt_int_t *) (uintptr_t) arg;
684
685 if (nxt_slow_path(TYPE(r_key) != T_STRING)) {
686 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
687 "Ruby: Wrong header entry 'key' from application");
688
689 goto fail;
690 }
691
692 if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
693 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
694 "Ruby: Wrong header entry 'value' from application");
695
696 goto fail;
697 }
698
699 value = RSTRING_PTR(r_value);
700 value_end = value + RSTRING_LEN(r_value);
701
702 pos = value;
703
704 for ( ;; ) {
705 pos = strchr(pos, '\n');
706
707 if (pos == NULL) {
708 break;
709 }
710
711 rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key),
712 value, pos - value);
713 if (nxt_slow_path(rc != NXT_OK)) {
714 goto fail;
715 }
716
717 pos++;
718 value = pos;
719 }
720
721 if (value <= value_end) {
722 rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key),
723 value, value_end - value);
724 if (nxt_slow_path(rc != NXT_OK)) {
725 goto fail;
726 }
727 }
728
729 *rc_p = NXT_OK;
730
731 return ST_CONTINUE;
732
733fail:
734
735 *rc_p = NXT_ERROR;
736
737 return ST_STOP;
738}
739
740
741static nxt_int_t
742nxt_ruby_head_send_part(const char *key, size_t key_size,
743 const char *value, size_t value_size)
744{
745 nxt_int_t rc;
746
747 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
748 (u_char *) key, key_size);
749 if (nxt_slow_path(rc != NXT_OK)) {
750 return rc;
751 }
752
753 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
754 (u_char *) ": ", nxt_length(": "));
755 if (nxt_slow_path(rc != NXT_OK)) {
756 return rc;
757 }
758
759 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
760 (u_char *) value, value_size);
761 if (nxt_slow_path(rc != NXT_OK)) {
762 return rc;
763 }
764
765 return nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
766 (u_char *) "\r\n", nxt_length("\r\n"));
767}
768
769
770static nxt_int_t
771nxt_ruby_rack_result_body(VALUE result)
772{
773 VALUE fn, body;
774 nxt_int_t rc;
775
776 body = rb_ary_entry(result, 2);
777
778 if (rb_respond_to(body, rb_intern("to_path"))) {
779
780 fn = rb_funcall(body, rb_intern("to_path"), 0);
781 if (nxt_slow_path(TYPE(fn) != T_STRING)) {
782 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
783 "Ruby: Failed to get 'body' file path from application");
784
785 return NXT_ERROR;
786 }
787
788 rc = nxt_ruby_rack_result_body_file_write(fn);
789 if (nxt_slow_path(rc != NXT_OK)) {
790 return NXT_ERROR;
791 }
792
793 } else if (rb_respond_to(body, rb_intern("each"))) {
794 rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0);
795
796 } else {
797 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
798 "Ruby: Invalid response 'body' format from application");
799
800 return NXT_ERROR;
801 }
802
803 if (rb_respond_to(body, rb_intern("close"))) {
804 rb_funcall(body, rb_intern("close"), 0);
805 }
806
807 return NXT_OK;
808}
809
810
811static nxt_int_t
812nxt_ruby_rack_result_body_file_write(VALUE filepath)
813{
814 size_t len;
815 ssize_t n;
816 nxt_off_t rest;
817 nxt_int_t rc;
818 nxt_file_t file;
819 nxt_file_info_t finfo;
820 u_char buf[8192];
821
822 nxt_memzero(&file, sizeof(nxt_file_t));
823
824 file.name = (nxt_file_name_t *) RSTRING_PTR(filepath);
825
826 rc = nxt_file_open(nxt_ruby_run_ctx.task, &file, NXT_FILE_RDONLY,
827 NXT_FILE_OPEN, 0);
828 if (nxt_slow_path(rc != NXT_OK)) {
829 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
830 "Ruby: Failed to open 'body' file: %s",
831 (const char *) file.name);
832
833 return NXT_ERROR;
834 }
835
836 rc = nxt_file_info(&file, &finfo);
837 if (nxt_slow_path(rc != NXT_OK)) {
838 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
839 "Ruby: Failed to get 'body' file information: %s",
840 (const char *) file.name);
841
842 goto fail;
843 }
844
845 rest = nxt_file_size(&finfo);
846
847 while (rest != 0) {
848 len = nxt_min(rest, (nxt_off_t) sizeof(buf));
849
850 n = nxt_file_read(&file, buf, len, nxt_file_size(&finfo) - rest);
851 if (nxt_slow_path(n != (ssize_t) len)) {
852 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
853 "Ruby: Failed to read 'body' file");
854
855 goto fail;
856 }
857
858 rest -= len;
859
860 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
861 buf, len);
862 if (nxt_slow_path(rc != NXT_OK)) {
863 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
864 "Ruby: Failed to write 'body' from application");
865
866 goto fail;
867 }
868 }
869
870 nxt_file_close(nxt_ruby_run_ctx.task, &file);
871
872 return NXT_OK;
873
874fail:
875
876 nxt_file_close(nxt_ruby_run_ctx.task, &file);
877
878 return NXT_ERROR;
879}
880
881
882static VALUE
883nxt_ruby_rack_result_body_each(VALUE body)
884{
885 nxt_int_t rc;
886
887 if (TYPE(body) != T_STRING) {
888 return Qnil;
889 }
890
891 rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
892 (u_char *) RSTRING_PTR(body), RSTRING_LEN(body));
893 if (nxt_slow_path(rc != NXT_OK)) {
894 nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
895 "Ruby: Failed to write 'body' from application");
896 }
897
898 return Qnil;
899}
900
901
902static void
903nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc)
904{
905 int i;
906 VALUE err, ary, eclass, msg;
907
908 nxt_log(task, level, "Ruby: %s", desc);
909
910 err = rb_errinfo();
911 ary = rb_funcall(err, rb_intern("backtrace"), 0);
912
913 if (RARRAY_LEN(ary) == 0) {
914 return;
915 }
916
917 eclass = rb_class_name(rb_class_of(err));
918 msg = rb_funcall(err, rb_intern("message"), 0);
919
920 nxt_log(task, level, "Ruby: %s: %s (%s)",
921 RSTRING_PTR(RARRAY_PTR(ary)[0]),
922 RSTRING_PTR(msg), RSTRING_PTR(eclass));
923
924 for (i = 1; i < RARRAY_LEN(ary); i++) {
925 nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i]));
926 }
927}
928
929
930static void
931nxt_ruby_atexit(nxt_task_t *task)
932{
933 rb_gc_unregister_address(&nxt_ruby_io_input);
934 rb_gc_unregister_address(&nxt_ruby_io_error);
935
936 rb_gc_unregister_address(&nxt_ruby_rackup);
937 rb_gc_unregister_address(&nxt_ruby_call);
938 rb_gc_unregister_address(&nxt_ruby_env);
939
940 ruby_cleanup(0);
941}