nxt_ruby.c (717:f8e279782e5e) nxt_ruby.c (743:e0f0cd7d244a)
1/*
2 * Copyright (C) Alexander Borisov
3 * Copyright (C) NGINX, Inc.
4 */
5
6#include <ruby/nxt_ruby.h>
7
1/*
2 * Copyright (C) Alexander Borisov
3 * Copyright (C) NGINX, Inc.
4 */
5
6#include <ruby/nxt_ruby.h>
7
8#include <nxt_unit.h>
9#include <nxt_unit_request.h>
8
10
11
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);
12#define NXT_RUBY_RACK_API_VERSION_MAJOR 1
13#define NXT_RUBY_RACK_API_VERSION_MINOR 3
14
15#define NXT_RUBY_STRINGIZE_HELPER(x) #x
16#define NXT_RUBY_STRINGIZE(x) NXT_RUBY_STRINGIZE_HELPER(x)
17
18#define NXT_RUBY_LIB_VERSION \
19 NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MAJOR) \

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

33static nxt_int_t nxt_ruby_init_io(nxt_task_t *task);
34static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
35
36static VALUE nxt_ruby_require_rubygems(VALUE arg);
37static VALUE nxt_ruby_bundler_setup(VALUE arg);
38static VALUE nxt_ruby_require_rack(VALUE arg);
39static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
40static 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);
41static void nxt_ruby_request_handler(nxt_unit_request_info_t *req);
40
41static VALUE nxt_ruby_rack_app_run(VALUE arg);
42
43static 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);
44static int nxt_ruby_read_request(VALUE hash_env);
45nxt_inline void nxt_ruby_add_sptr(VALUE hash_env,
46 const char *name, uint32_t name_len, nxt_unit_sptr_t *sptr, uint32_t len);
47nxt_inline void nxt_ruby_add_str(VALUE hash_env,
48 const char *name, uint32_t name_len, char *str, uint32_t len);
46static nxt_int_t nxt_ruby_rack_result_status(VALUE result);
49static 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);
50static int nxt_ruby_rack_result_headers(VALUE result, nxt_int_t status);
51static int nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg);
52static int nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg);
53static int nxt_ruby_rack_result_body(VALUE result);
54static int 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
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);
60static void nxt_ruby_atexit(void);
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
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 = {
74NXT_EXPORT nxt_app_module_t nxt_app_module = {
75 sizeof(compat),
76 compat,
77 nxt_string("ruby"),
78 ruby_version,
79 nxt_ruby_init,
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{
80};
81
82
83static nxt_int_t
84nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
85{
88 int state;
86 int state, rc;
89 VALUE dummy, res;
87 VALUE dummy, res;
88 nxt_unit_ctx_t *unit_ctx;
89 nxt_unit_init_t ruby_unit_init;
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
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 nxt_unit_default_init(task, &ruby_unit_init);
132
133 ruby_unit_init.callbacks.request_handler = nxt_ruby_request_handler;
134
135 unit_ctx = nxt_unit_init(&ruby_unit_init);
136 if (nxt_slow_path(unit_ctx == NULL)) {
137 return NXT_ERROR;
138 }
139
140 nxt_ruby_run_ctx.unit_ctx = unit_ctx;
141
142 rc = nxt_unit_run(unit_ctx);
143
144 nxt_ruby_atexit();
145
146 nxt_ruby_run_ctx.unit_ctx = NULL;
147
148 nxt_unit_done(unit_ctx);
149
150 exit(rc);
151
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
152 return NXT_OK;
153}
154
155
156static VALUE
157nxt_ruby_init_basic(VALUE arg)
158{
159 int state;

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

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