xref: /unit/src/ruby/nxt_ruby.c (revision 609)
1584Salexander.borisov@nginx.com /*
2584Salexander.borisov@nginx.com  * Copyright (C) Alexander Borisov
3584Salexander.borisov@nginx.com  * Copyright (C) NGINX, Inc.
4584Salexander.borisov@nginx.com  */
5584Salexander.borisov@nginx.com 
6584Salexander.borisov@nginx.com #include <ruby/nxt_ruby.h>
7584Salexander.borisov@nginx.com 
8584Salexander.borisov@nginx.com 
9584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MAJOR  1
10584Salexander.borisov@nginx.com #define NXT_RUBY_RACK_API_VERSION_MINOR  3
11584Salexander.borisov@nginx.com 
12584Salexander.borisov@nginx.com #define NXT_RUBY_STRINGIZE_HELPER(x)     #x
13584Salexander.borisov@nginx.com #define NXT_RUBY_STRINGIZE(x)            NXT_RUBY_STRINGIZE_HELPER(x)
14584Salexander.borisov@nginx.com 
15584Salexander.borisov@nginx.com #define NXT_RUBY_LIB_VERSION                                                   \
16584Salexander.borisov@nginx.com     NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MAJOR)                                 \
17584Salexander.borisov@nginx.com     "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_MINOR)                             \
18584Salexander.borisov@nginx.com     "." NXT_RUBY_STRINGIZE(RUBY_API_VERSION_TEENY)
19584Salexander.borisov@nginx.com 
20584Salexander.borisov@nginx.com 
21584Salexander.borisov@nginx.com typedef struct {
22584Salexander.borisov@nginx.com     nxt_task_t  *task;
23584Salexander.borisov@nginx.com     nxt_str_t   *script;
24584Salexander.borisov@nginx.com     VALUE       builder;
25584Salexander.borisov@nginx.com } nxt_ruby_rack_init_t;
26584Salexander.borisov@nginx.com 
27584Salexander.borisov@nginx.com 
28584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
29584Salexander.borisov@nginx.com static VALUE nxt_ruby_init_basic(VALUE arg);
30584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_init_io(nxt_task_t *task);
31584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init);
32584Salexander.borisov@nginx.com 
33584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rubygems(VALUE arg);
34584Salexander.borisov@nginx.com static VALUE nxt_ruby_require_rack(VALUE arg);
35584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_parse_script(VALUE ctx);
36584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_env_create(VALUE arg);
37584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg,
38584Salexander.borisov@nginx.com     nxt_app_wmsg_t *wmsg);
39584Salexander.borisov@nginx.com 
40584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_app_run(VALUE arg);
41584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx,
42584Salexander.borisov@nginx.com     VALUE hash_env);
43584Salexander.borisov@nginx.com nxt_inline nxt_int_t nxt_ruby_read_add_env(nxt_task_t *task,
44584Salexander.borisov@nginx.com     nxt_app_rmsg_t *rmsg, VALUE hash_env, const char *name, nxt_str_t *str);
45584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_rack_result_status(VALUE result);
46584Salexander.borisov@nginx.com nxt_inline nxt_int_t nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg,
47584Salexander.borisov@nginx.com     const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last);
48584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_rack_result_headers(VALUE result);
49584Salexander.borisov@nginx.com static int nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg);
50584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_head_send_part(const char *key, size_t key_size,
51584Salexander.borisov@nginx.com     const char *value, size_t value_size);
52584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_rack_result_body(VALUE result);
53584Salexander.borisov@nginx.com static nxt_int_t nxt_ruby_rack_result_body_file_write(VALUE filepath);
54584Salexander.borisov@nginx.com static VALUE nxt_ruby_rack_result_body_each(VALUE body);
55584Salexander.borisov@nginx.com 
56584Salexander.borisov@nginx.com static void nxt_ruby_exception_log(nxt_task_t *task, uint32_t level,
57584Salexander.borisov@nginx.com     const char *desc);
58584Salexander.borisov@nginx.com 
59584Salexander.borisov@nginx.com static void nxt_ruby_atexit(nxt_task_t *task);
60584Salexander.borisov@nginx.com 
61584Salexander.borisov@nginx.com 
62584Salexander.borisov@nginx.com static uint32_t  compat[] = {
63584Salexander.borisov@nginx.com     NXT_VERNUM, NXT_DEBUG,
64584Salexander.borisov@nginx.com };
65584Salexander.borisov@nginx.com 
66584Salexander.borisov@nginx.com static VALUE               nxt_ruby_rackup;
67584Salexander.borisov@nginx.com static VALUE               nxt_ruby_call;
68584Salexander.borisov@nginx.com static VALUE               nxt_ruby_env;
69584Salexander.borisov@nginx.com static VALUE               nxt_ruby_io_input;
70584Salexander.borisov@nginx.com static VALUE               nxt_ruby_io_error;
71584Salexander.borisov@nginx.com static nxt_ruby_run_ctx_t  nxt_ruby_run_ctx;
72584Salexander.borisov@nginx.com 
73584Salexander.borisov@nginx.com NXT_EXPORT nxt_application_module_t  nxt_app_module = {
74584Salexander.borisov@nginx.com     sizeof(compat),
75584Salexander.borisov@nginx.com     compat,
76584Salexander.borisov@nginx.com     nxt_string("ruby"),
77584Salexander.borisov@nginx.com     nxt_string(NXT_RUBY_LIB_VERSION),
78584Salexander.borisov@nginx.com     nxt_ruby_init,
79584Salexander.borisov@nginx.com     nxt_ruby_run,
80584Salexander.borisov@nginx.com     nxt_ruby_atexit,
81584Salexander.borisov@nginx.com };
82584Salexander.borisov@nginx.com 
83584Salexander.borisov@nginx.com 
84584Salexander.borisov@nginx.com static nxt_int_t
85584Salexander.borisov@nginx.com nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
86584Salexander.borisov@nginx.com {
87584Salexander.borisov@nginx.com     int                   state;
88584Salexander.borisov@nginx.com     VALUE                 dummy, res;
89584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  rack_init;
90584Salexander.borisov@nginx.com 
91584Salexander.borisov@nginx.com     ruby_init();
92584Salexander.borisov@nginx.com     Init_stack(&dummy);
93584Salexander.borisov@nginx.com     ruby_init_loadpath();
94584Salexander.borisov@nginx.com     ruby_script("NGINX_Unit");
95584Salexander.borisov@nginx.com 
96584Salexander.borisov@nginx.com     rack_init.task = task;
97584Salexander.borisov@nginx.com     rack_init.script = &conf->u.ruby.script;
98584Salexander.borisov@nginx.com 
99584Salexander.borisov@nginx.com     res = rb_protect(nxt_ruby_init_basic,
100584Salexander.borisov@nginx.com                      (VALUE) (uintptr_t) &rack_init, &state);
101584Salexander.borisov@nginx.com     if (nxt_slow_path(res == Qnil || state != 0)) {
102584Salexander.borisov@nginx.com         nxt_ruby_exception_log(task, NXT_LOG_ALERT,
103584Salexander.borisov@nginx.com                                "Failed to init basic variables");
104584Salexander.borisov@nginx.com         return NXT_ERROR;
105584Salexander.borisov@nginx.com     }
106584Salexander.borisov@nginx.com 
107584Salexander.borisov@nginx.com     nxt_ruby_rackup = nxt_ruby_rack_init(&rack_init);
108584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_rackup == Qnil)) {
109584Salexander.borisov@nginx.com         return NXT_ERROR;
110584Salexander.borisov@nginx.com     }
111584Salexander.borisov@nginx.com 
112584Salexander.borisov@nginx.com     nxt_ruby_call = rb_intern("call");
113584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_call == Qnil)) {
114584Salexander.borisov@nginx.com         nxt_alert(task, "Ruby: Unable to find rack entry point");
115584Salexander.borisov@nginx.com 
116584Salexander.borisov@nginx.com         return NXT_ERROR;
117584Salexander.borisov@nginx.com     }
118584Salexander.borisov@nginx.com 
119584Salexander.borisov@nginx.com     nxt_ruby_env = rb_protect(nxt_ruby_rack_env_create, Qnil, &state);
120584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
121584Salexander.borisov@nginx.com         nxt_ruby_exception_log(task, NXT_LOG_ALERT,
122584Salexander.borisov@nginx.com                                "Failed to create 'environ' variable");
123584Salexander.borisov@nginx.com         return NXT_ERROR;
124584Salexander.borisov@nginx.com     }
125584Salexander.borisov@nginx.com 
126584Salexander.borisov@nginx.com     rb_gc_register_address(&nxt_ruby_rackup);
127584Salexander.borisov@nginx.com     rb_gc_register_address(&nxt_ruby_call);
128584Salexander.borisov@nginx.com     rb_gc_register_address(&nxt_ruby_env);
129584Salexander.borisov@nginx.com 
130584Salexander.borisov@nginx.com     return NXT_OK;
131584Salexander.borisov@nginx.com }
132584Salexander.borisov@nginx.com 
133584Salexander.borisov@nginx.com 
134584Salexander.borisov@nginx.com static VALUE
135584Salexander.borisov@nginx.com nxt_ruby_init_basic(VALUE arg)
136584Salexander.borisov@nginx.com {
137584Salexander.borisov@nginx.com     int                   state;
138584Salexander.borisov@nginx.com     nxt_int_t             rc;
139584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
140584Salexander.borisov@nginx.com 
141584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) arg;
142584Salexander.borisov@nginx.com 
143584Salexander.borisov@nginx.com     state = rb_enc_find_index("encdb");
144584Salexander.borisov@nginx.com     if (nxt_slow_path(state == 0)) {
145584Salexander.borisov@nginx.com         nxt_alert(rack_init->task,
146584Salexander.borisov@nginx.com                   "Ruby: Failed to find encoding index 'encdb'");
147584Salexander.borisov@nginx.com 
148584Salexander.borisov@nginx.com         return Qnil;
149584Salexander.borisov@nginx.com     }
150584Salexander.borisov@nginx.com 
151*609Salexander.borisov@nginx.com     rb_funcall(rb_cObject, rb_intern("require"), 1,
152*609Salexander.borisov@nginx.com                rb_str_new2("enc/trans/transdb"));
153*609Salexander.borisov@nginx.com 
154584Salexander.borisov@nginx.com     rc = nxt_ruby_init_io(rack_init->task);
155584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
156584Salexander.borisov@nginx.com         return Qnil;
157584Salexander.borisov@nginx.com     }
158584Salexander.borisov@nginx.com 
159584Salexander.borisov@nginx.com     return arg;
160584Salexander.borisov@nginx.com }
161584Salexander.borisov@nginx.com 
162584Salexander.borisov@nginx.com 
163584Salexander.borisov@nginx.com static nxt_int_t
164584Salexander.borisov@nginx.com nxt_ruby_init_io(nxt_task_t *task)
165584Salexander.borisov@nginx.com {
166584Salexander.borisov@nginx.com     VALUE  rb, io_input, io_error;
167584Salexander.borisov@nginx.com 
168584Salexander.borisov@nginx.com     io_input = nxt_ruby_stream_io_input_init();
169584Salexander.borisov@nginx.com     rb = Data_Wrap_Struct(io_input, 0, 0, &nxt_ruby_run_ctx);
170584Salexander.borisov@nginx.com 
171584Salexander.borisov@nginx.com     nxt_ruby_io_input = rb_funcall(io_input, rb_intern("new"), 1, rb);
172584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_io_input == Qnil)) {
173584Salexander.borisov@nginx.com         nxt_alert(task, "Ruby: Failed to create environment 'rack.input' var");
174584Salexander.borisov@nginx.com 
175584Salexander.borisov@nginx.com         return NXT_ERROR;
176584Salexander.borisov@nginx.com     }
177584Salexander.borisov@nginx.com 
178584Salexander.borisov@nginx.com     io_error = nxt_ruby_stream_io_error_init();
179584Salexander.borisov@nginx.com     rb = Data_Wrap_Struct(io_error, 0, 0, &nxt_ruby_run_ctx);
180584Salexander.borisov@nginx.com 
181584Salexander.borisov@nginx.com     nxt_ruby_io_error = rb_funcall(io_error, rb_intern("new"), 1, rb);
182584Salexander.borisov@nginx.com     if (nxt_slow_path(nxt_ruby_io_error == Qnil)) {
183584Salexander.borisov@nginx.com         nxt_alert(task, "Ruby: Failed to create environment 'rack.error' var");
184584Salexander.borisov@nginx.com 
185584Salexander.borisov@nginx.com         return NXT_ERROR;
186584Salexander.borisov@nginx.com     }
187584Salexander.borisov@nginx.com 
188584Salexander.borisov@nginx.com     rb_gc_register_address(&nxt_ruby_io_input);
189584Salexander.borisov@nginx.com     rb_gc_register_address(&nxt_ruby_io_error);
190584Salexander.borisov@nginx.com 
191584Salexander.borisov@nginx.com     return NXT_OK;
192584Salexander.borisov@nginx.com }
193584Salexander.borisov@nginx.com 
194584Salexander.borisov@nginx.com 
195584Salexander.borisov@nginx.com static VALUE
196584Salexander.borisov@nginx.com nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init)
197584Salexander.borisov@nginx.com {
198584Salexander.borisov@nginx.com     int    state;
199584Salexander.borisov@nginx.com     VALUE  rack, rackup;
200584Salexander.borisov@nginx.com 
201584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rubygems, Qnil, &state);
202584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
203584Salexander.borisov@nginx.com         nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
204584Salexander.borisov@nginx.com                                "Failed to require 'rubygems' package");
205584Salexander.borisov@nginx.com         return Qnil;
206584Salexander.borisov@nginx.com     }
207584Salexander.borisov@nginx.com 
208584Salexander.borisov@nginx.com     rb_protect(nxt_ruby_require_rack, Qnil, &state);
209584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
210584Salexander.borisov@nginx.com         nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
211584Salexander.borisov@nginx.com                                "Failed to require 'rack' package");
212584Salexander.borisov@nginx.com         return Qnil;
213584Salexander.borisov@nginx.com     }
214584Salexander.borisov@nginx.com 
215584Salexander.borisov@nginx.com     rack = rb_const_get(rb_cObject, rb_intern("Rack"));
216584Salexander.borisov@nginx.com     rack_init->builder = rb_const_get(rack, rb_intern("Builder"));
217584Salexander.borisov@nginx.com 
218584Salexander.borisov@nginx.com     rackup = rb_protect(nxt_ruby_rack_parse_script,
219584Salexander.borisov@nginx.com                         (VALUE) (uintptr_t) rack_init, &state);
220584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(rackup) != T_ARRAY || state != 0)) {
221584Salexander.borisov@nginx.com         nxt_ruby_exception_log(rack_init->task, NXT_LOG_ALERT,
222584Salexander.borisov@nginx.com                                "Failed to parse rack script");
223584Salexander.borisov@nginx.com         return Qnil;
224584Salexander.borisov@nginx.com     }
225584Salexander.borisov@nginx.com 
226584Salexander.borisov@nginx.com     if (nxt_slow_path(RARRAY_LEN(rackup) < 1)) {
227584Salexander.borisov@nginx.com         nxt_alert(rack_init->task, "Ruby: Invalid rack config file");
228584Salexander.borisov@nginx.com         return Qnil;
229584Salexander.borisov@nginx.com     }
230584Salexander.borisov@nginx.com 
231584Salexander.borisov@nginx.com     return RARRAY_PTR(rackup)[0];
232584Salexander.borisov@nginx.com }
233584Salexander.borisov@nginx.com 
234584Salexander.borisov@nginx.com 
235584Salexander.borisov@nginx.com static VALUE
236584Salexander.borisov@nginx.com nxt_ruby_require_rubygems(VALUE arg)
237584Salexander.borisov@nginx.com {
238584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1,
239584Salexander.borisov@nginx.com                       rb_str_new2("rubygems"));
240584Salexander.borisov@nginx.com }
241584Salexander.borisov@nginx.com 
242584Salexander.borisov@nginx.com 
243584Salexander.borisov@nginx.com static VALUE
244584Salexander.borisov@nginx.com nxt_ruby_require_rack(VALUE arg)
245584Salexander.borisov@nginx.com {
246584Salexander.borisov@nginx.com     return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack"));
247584Salexander.borisov@nginx.com }
248584Salexander.borisov@nginx.com 
249584Salexander.borisov@nginx.com 
250584Salexander.borisov@nginx.com static VALUE
251584Salexander.borisov@nginx.com nxt_ruby_rack_parse_script(VALUE ctx)
252584Salexander.borisov@nginx.com {
253584Salexander.borisov@nginx.com     VALUE                 script, res;
254584Salexander.borisov@nginx.com     nxt_ruby_rack_init_t  *rack_init;
255584Salexander.borisov@nginx.com 
256584Salexander.borisov@nginx.com     rack_init = (nxt_ruby_rack_init_t *) (uintptr_t) ctx;
257584Salexander.borisov@nginx.com 
258584Salexander.borisov@nginx.com     script = rb_str_new((const char *) rack_init->script->start,
259584Salexander.borisov@nginx.com                         (long) rack_init->script->length);
260584Salexander.borisov@nginx.com 
261584Salexander.borisov@nginx.com     res = rb_funcall(rack_init->builder, rb_intern("parse_file"), 1, script);
262584Salexander.borisov@nginx.com 
263584Salexander.borisov@nginx.com     rb_str_free(script);
264584Salexander.borisov@nginx.com 
265584Salexander.borisov@nginx.com     return res;
266584Salexander.borisov@nginx.com }
267584Salexander.borisov@nginx.com 
268584Salexander.borisov@nginx.com 
269584Salexander.borisov@nginx.com static VALUE
270584Salexander.borisov@nginx.com nxt_ruby_rack_env_create(VALUE arg)
271584Salexander.borisov@nginx.com {
272584Salexander.borisov@nginx.com     VALUE  hash_env, version;
273584Salexander.borisov@nginx.com 
274584Salexander.borisov@nginx.com     hash_env = rb_hash_new();
275584Salexander.borisov@nginx.com     version = rb_ary_new();
276584Salexander.borisov@nginx.com 
277584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MAJOR));
278584Salexander.borisov@nginx.com     rb_ary_push(version, UINT2NUM(NXT_RUBY_RACK_API_VERSION_MINOR));
279584Salexander.borisov@nginx.com 
280584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.version"), version);
281584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.url_scheme"), rb_str_new2("http"));
282584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.input"), nxt_ruby_io_input);
283584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.errors"), nxt_ruby_io_error);
284584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multithread"), Qfalse);
285584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.multiprocess"), Qtrue);
286584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.run_once"), Qfalse);
287584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack?"), Qfalse);
288584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack"), Qnil);
289584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("rack.hijack_io"), Qnil);
290584Salexander.borisov@nginx.com 
291584Salexander.borisov@nginx.com     return hash_env;
292584Salexander.borisov@nginx.com }
293584Salexander.borisov@nginx.com 
294584Salexander.borisov@nginx.com 
295584Salexander.borisov@nginx.com static nxt_int_t
296584Salexander.borisov@nginx.com nxt_ruby_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg)
297584Salexander.borisov@nginx.com {
298584Salexander.borisov@nginx.com     int    state;
299584Salexander.borisov@nginx.com     VALUE  res;
300584Salexander.borisov@nginx.com 
301584Salexander.borisov@nginx.com     nxt_ruby_run_ctx.task = task;
302584Salexander.borisov@nginx.com     nxt_ruby_run_ctx.rmsg = rmsg;
303584Salexander.borisov@nginx.com     nxt_ruby_run_ctx.wmsg = wmsg;
304584Salexander.borisov@nginx.com 
305584Salexander.borisov@nginx.com     res = rb_protect(nxt_ruby_rack_app_run, Qnil, &state);
306584Salexander.borisov@nginx.com     if (nxt_slow_path(state != 0)) {
307584Salexander.borisov@nginx.com         nxt_ruby_exception_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
308584Salexander.borisov@nginx.com                                "Failed to run ruby script");
309584Salexander.borisov@nginx.com         return NXT_ERROR;
310584Salexander.borisov@nginx.com     }
311584Salexander.borisov@nginx.com 
312584Salexander.borisov@nginx.com     if (nxt_slow_path(res == Qnil)) {
313584Salexander.borisov@nginx.com         return NXT_ERROR;
314584Salexander.borisov@nginx.com     }
315584Salexander.borisov@nginx.com 
316584Salexander.borisov@nginx.com     return NXT_OK;
317584Salexander.borisov@nginx.com }
318584Salexander.borisov@nginx.com 
319584Salexander.borisov@nginx.com 
320584Salexander.borisov@nginx.com static VALUE
321584Salexander.borisov@nginx.com nxt_ruby_rack_app_run(VALUE arg)
322584Salexander.borisov@nginx.com {
323584Salexander.borisov@nginx.com     VALUE      env, result;
324584Salexander.borisov@nginx.com     nxt_int_t  rc;
325584Salexander.borisov@nginx.com 
326584Salexander.borisov@nginx.com     env = rb_hash_dup(nxt_ruby_env);
327584Salexander.borisov@nginx.com 
328584Salexander.borisov@nginx.com     rc = nxt_ruby_read_request(&nxt_ruby_run_ctx, env);
329584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
330584Salexander.borisov@nginx.com         nxt_alert(nxt_ruby_run_ctx.task,
331584Salexander.borisov@nginx.com                   "Ruby: Failed to process incoming request");
332584Salexander.borisov@nginx.com 
333584Salexander.borisov@nginx.com         goto fail;
334584Salexander.borisov@nginx.com     }
335584Salexander.borisov@nginx.com 
336584Salexander.borisov@nginx.com     result = rb_funcall(nxt_ruby_rackup, nxt_ruby_call, 1, env);
337584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(result) != T_ARRAY)) {
338584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
339584Salexander.borisov@nginx.com                 "Ruby: Invalid response format from application");
340584Salexander.borisov@nginx.com 
341584Salexander.borisov@nginx.com         goto fail;
342584Salexander.borisov@nginx.com     }
343584Salexander.borisov@nginx.com 
344584Salexander.borisov@nginx.com     if (nxt_slow_path(RARRAY_LEN(result) != 3)) {
345584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
346584Salexander.borisov@nginx.com                 "Ruby: Invalid response format from application. "
347584Salexander.borisov@nginx.com                 "Need 3 entries [Status, Headers, Body]");
348584Salexander.borisov@nginx.com 
349584Salexander.borisov@nginx.com         goto fail;
350584Salexander.borisov@nginx.com     }
351584Salexander.borisov@nginx.com 
352584Salexander.borisov@nginx.com     rc = nxt_ruby_rack_result_status(result);
353584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
354584Salexander.borisov@nginx.com         goto fail;
355584Salexander.borisov@nginx.com     }
356584Salexander.borisov@nginx.com 
357584Salexander.borisov@nginx.com     rc = nxt_ruby_rack_result_headers(result);
358584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
359584Salexander.borisov@nginx.com         goto fail;
360584Salexander.borisov@nginx.com     }
361584Salexander.borisov@nginx.com 
362584Salexander.borisov@nginx.com     rc = nxt_ruby_rack_result_body(result);
363584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
364584Salexander.borisov@nginx.com         goto fail;
365584Salexander.borisov@nginx.com     }
366584Salexander.borisov@nginx.com 
367584Salexander.borisov@nginx.com     rc = nxt_app_msg_flush(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg, 1);
368584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
369584Salexander.borisov@nginx.com         goto fail;
370584Salexander.borisov@nginx.com     }
371584Salexander.borisov@nginx.com 
372584Salexander.borisov@nginx.com     rb_hash_delete(env, rb_obj_id(env));
373584Salexander.borisov@nginx.com 
374584Salexander.borisov@nginx.com     return result;
375584Salexander.borisov@nginx.com 
376584Salexander.borisov@nginx.com fail:
377584Salexander.borisov@nginx.com 
378584Salexander.borisov@nginx.com     rb_hash_delete(env, rb_obj_id(env));
379584Salexander.borisov@nginx.com 
380584Salexander.borisov@nginx.com     return Qnil;
381584Salexander.borisov@nginx.com }
382584Salexander.borisov@nginx.com 
383584Salexander.borisov@nginx.com 
384584Salexander.borisov@nginx.com static nxt_int_t
385584Salexander.borisov@nginx.com nxt_ruby_read_request(nxt_ruby_run_ctx_t *run_ctx, VALUE hash_env)
386584Salexander.borisov@nginx.com {
387584Salexander.borisov@nginx.com     u_char          *colon;
388584Salexander.borisov@nginx.com     size_t          query_size;
389584Salexander.borisov@nginx.com     nxt_int_t       rc;
390584Salexander.borisov@nginx.com     nxt_str_t       str, value, path, target;
391584Salexander.borisov@nginx.com     nxt_str_t       host, server_name, server_port;
392584Salexander.borisov@nginx.com     nxt_task_t      *task;
393584Salexander.borisov@nginx.com     nxt_app_rmsg_t  *rmsg;
394584Salexander.borisov@nginx.com 
395584Salexander.borisov@nginx.com     static nxt_str_t  def_host = nxt_string("localhost");
396584Salexander.borisov@nginx.com     static nxt_str_t  def_port = nxt_string("80");
397584Salexander.borisov@nginx.com 
398584Salexander.borisov@nginx.com     task = run_ctx->task;
399584Salexander.borisov@nginx.com     rmsg = run_ctx->rmsg;
400584Salexander.borisov@nginx.com 
401584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_METHOD", &str);
402584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
403584Salexander.borisov@nginx.com         return NXT_ERROR;
404584Salexander.borisov@nginx.com     }
405584Salexander.borisov@nginx.com 
406584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REQUEST_URI", &target);
407584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
408584Salexander.borisov@nginx.com         return NXT_ERROR;
409584Salexander.borisov@nginx.com     }
410584Salexander.borisov@nginx.com 
411584Salexander.borisov@nginx.com     rc = nxt_app_msg_read_str(task, rmsg, &path);
412584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
413584Salexander.borisov@nginx.com         return NXT_ERROR;
414584Salexander.borisov@nginx.com     }
415584Salexander.borisov@nginx.com 
416584Salexander.borisov@nginx.com     rc = nxt_app_msg_read_size(task, rmsg, &query_size);
417584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
418584Salexander.borisov@nginx.com         return NXT_ERROR;
419584Salexander.borisov@nginx.com     }
420584Salexander.borisov@nginx.com 
421584Salexander.borisov@nginx.com     if (path.start == NULL || path.length == 0) {
422584Salexander.borisov@nginx.com         path = target;
423584Salexander.borisov@nginx.com     }
424584Salexander.borisov@nginx.com 
425584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("PATH_INFO"),
426584Salexander.borisov@nginx.com                  rb_str_new((const char *) path.start, (long) path.length));
427584Salexander.borisov@nginx.com 
428584Salexander.borisov@nginx.com     if (query_size > 0) {
429584Salexander.borisov@nginx.com         query_size--;
430584Salexander.borisov@nginx.com 
431584Salexander.borisov@nginx.com         if (nxt_slow_path(target.length < query_size)) {
432584Salexander.borisov@nginx.com             return NXT_ERROR;
433584Salexander.borisov@nginx.com         }
434584Salexander.borisov@nginx.com 
435584Salexander.borisov@nginx.com         str.start  = &target.start[query_size];
436584Salexander.borisov@nginx.com         str.length = target.length - query_size;
437584Salexander.borisov@nginx.com 
438584Salexander.borisov@nginx.com         rb_hash_aset(hash_env, rb_str_new2("QUERY_STRING"),
439584Salexander.borisov@nginx.com                      rb_str_new((const char *) str.start, (long) str.length));
440584Salexander.borisov@nginx.com     }
441584Salexander.borisov@nginx.com 
442584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_PROTOCOL", &str);
443584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
444584Salexander.borisov@nginx.com         return NXT_ERROR;
445584Salexander.borisov@nginx.com     }
446584Salexander.borisov@nginx.com 
447584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "REMOTE_ADDR", &str);
448584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
449584Salexander.borisov@nginx.com         return NXT_ERROR;
450584Salexander.borisov@nginx.com     }
451584Salexander.borisov@nginx.com 
452584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "SERVER_ADDR", &str);
453584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
454584Salexander.borisov@nginx.com         return NXT_ERROR;
455584Salexander.borisov@nginx.com     }
456584Salexander.borisov@nginx.com 
457584Salexander.borisov@nginx.com     rc = nxt_app_msg_read_str(task, rmsg, &host);
458584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
459584Salexander.borisov@nginx.com         return NXT_ERROR;
460584Salexander.borisov@nginx.com     }
461584Salexander.borisov@nginx.com 
462584Salexander.borisov@nginx.com     if (host.length == 0) {
463584Salexander.borisov@nginx.com         host = def_host;
464584Salexander.borisov@nginx.com     }
465584Salexander.borisov@nginx.com 
466584Salexander.borisov@nginx.com     colon = nxt_memchr(host.start, ':', host.length);
467584Salexander.borisov@nginx.com     server_name = host;
468584Salexander.borisov@nginx.com 
469584Salexander.borisov@nginx.com     if (colon != NULL) {
470584Salexander.borisov@nginx.com         server_name.length = colon - host.start;
471584Salexander.borisov@nginx.com 
472584Salexander.borisov@nginx.com         server_port.start = colon + 1;
473584Salexander.borisov@nginx.com         server_port.length = host.length - server_name.length - 1;
474584Salexander.borisov@nginx.com 
475584Salexander.borisov@nginx.com     } else {
476584Salexander.borisov@nginx.com         server_port = def_port;
477584Salexander.borisov@nginx.com     }
478584Salexander.borisov@nginx.com 
479584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("SERVER_NAME"),
480584Salexander.borisov@nginx.com                  rb_str_new((const char *) server_name.start,
481584Salexander.borisov@nginx.com                             (long) server_name.length));
482584Salexander.borisov@nginx.com 
483584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2("SERVER_PORT"),
484584Salexander.borisov@nginx.com                  rb_str_new((const char *) server_port.start,
485584Salexander.borisov@nginx.com                             (long) server_port.length));
486584Salexander.borisov@nginx.com 
487584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_TYPE", &str);
488584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
489584Salexander.borisov@nginx.com         return NXT_ERROR;
490584Salexander.borisov@nginx.com     }
491584Salexander.borisov@nginx.com 
492584Salexander.borisov@nginx.com     rc = nxt_ruby_read_add_env(task, rmsg, hash_env, "CONTENT_LENGTH", &str);
493584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
494584Salexander.borisov@nginx.com         return NXT_ERROR;
495584Salexander.borisov@nginx.com     }
496584Salexander.borisov@nginx.com 
497584Salexander.borisov@nginx.com     for ( ;; ) {
498584Salexander.borisov@nginx.com         rc = nxt_app_msg_read_str(task, rmsg, &str);
499584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
500584Salexander.borisov@nginx.com             return NXT_ERROR;
501584Salexander.borisov@nginx.com         }
502584Salexander.borisov@nginx.com 
503584Salexander.borisov@nginx.com         if (nxt_slow_path(str.length == 0)) {
504584Salexander.borisov@nginx.com             break;
505584Salexander.borisov@nginx.com         }
506584Salexander.borisov@nginx.com 
507584Salexander.borisov@nginx.com         rc = nxt_app_msg_read_str(task, rmsg, &value);
508584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
509584Salexander.borisov@nginx.com             return NXT_ERROR;
510584Salexander.borisov@nginx.com         }
511584Salexander.borisov@nginx.com 
512584Salexander.borisov@nginx.com         rb_hash_aset(hash_env,
513584Salexander.borisov@nginx.com                      rb_str_new((char *) str.start, (long) str.length),
514584Salexander.borisov@nginx.com                      rb_str_new((const char *) value.start,
515584Salexander.borisov@nginx.com                                 (long) value.length));
516584Salexander.borisov@nginx.com     }
517584Salexander.borisov@nginx.com 
518584Salexander.borisov@nginx.com     rc = nxt_app_msg_read_size(task, rmsg, &run_ctx->body_preread_size);
519584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
520584Salexander.borisov@nginx.com         return NXT_ERROR;
521584Salexander.borisov@nginx.com     }
522584Salexander.borisov@nginx.com 
523584Salexander.borisov@nginx.com     return NXT_OK;
524584Salexander.borisov@nginx.com }
525584Salexander.borisov@nginx.com 
526584Salexander.borisov@nginx.com 
527584Salexander.borisov@nginx.com nxt_inline nxt_int_t
528584Salexander.borisov@nginx.com nxt_ruby_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, VALUE hash_env,
529584Salexander.borisov@nginx.com     const char *name, nxt_str_t *str)
530584Salexander.borisov@nginx.com {
531584Salexander.borisov@nginx.com     nxt_int_t  rc;
532584Salexander.borisov@nginx.com 
533584Salexander.borisov@nginx.com     rc = nxt_app_msg_read_str(task, rmsg, str);
534584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
535584Salexander.borisov@nginx.com         return rc;
536584Salexander.borisov@nginx.com     }
537584Salexander.borisov@nginx.com 
538584Salexander.borisov@nginx.com     if (str->start == NULL) {
539584Salexander.borisov@nginx.com         rb_hash_aset(hash_env, rb_str_new2(name), Qnil);
540584Salexander.borisov@nginx.com         return NXT_OK;
541584Salexander.borisov@nginx.com     }
542584Salexander.borisov@nginx.com 
543584Salexander.borisov@nginx.com     rb_hash_aset(hash_env, rb_str_new2(name),
544584Salexander.borisov@nginx.com                  rb_str_new((const char *) str->start, (long) str->length));
545584Salexander.borisov@nginx.com 
546584Salexander.borisov@nginx.com     return NXT_OK;
547584Salexander.borisov@nginx.com }
548584Salexander.borisov@nginx.com 
549584Salexander.borisov@nginx.com 
550584Salexander.borisov@nginx.com static nxt_int_t
551584Salexander.borisov@nginx.com nxt_ruby_rack_result_status(VALUE result)
552584Salexander.borisov@nginx.com {
553584Salexander.borisov@nginx.com     VALUE      status;
554584Salexander.borisov@nginx.com     u_char     *p;
555584Salexander.borisov@nginx.com     size_t     len;
556584Salexander.borisov@nginx.com     nxt_int_t  rc;
557584Salexander.borisov@nginx.com     u_char     buf[3];
558584Salexander.borisov@nginx.com 
559584Salexander.borisov@nginx.com     status = rb_ary_entry(result, 0);
560584Salexander.borisov@nginx.com 
561584Salexander.borisov@nginx.com     if (TYPE(status) == T_FIXNUM) {
562584Salexander.borisov@nginx.com         nxt_sprintf(buf, buf + 3, "%03d", FIX2INT(status));
563584Salexander.borisov@nginx.com 
564584Salexander.borisov@nginx.com         p = buf;
565584Salexander.borisov@nginx.com         len = 3;
566584Salexander.borisov@nginx.com 
567584Salexander.borisov@nginx.com     } else if (TYPE(status) == T_STRING) {
568584Salexander.borisov@nginx.com         p = (u_char *) RSTRING_PTR(status);
569584Salexander.borisov@nginx.com         len = RSTRING_LEN(status);
570584Salexander.borisov@nginx.com 
571584Salexander.borisov@nginx.com     } else {
572584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
573584Salexander.borisov@nginx.com                 "Ruby: Invalid response 'status' format from application");
574584Salexander.borisov@nginx.com 
575584Salexander.borisov@nginx.com         return NXT_ERROR;
576584Salexander.borisov@nginx.com     }
577584Salexander.borisov@nginx.com 
578584Salexander.borisov@nginx.com     rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
579584Salexander.borisov@nginx.com                         (u_char *) "Status: ", (sizeof("Status: ") - 1), 0, 0);
580584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
581584Salexander.borisov@nginx.com         return NXT_ERROR;
582584Salexander.borisov@nginx.com     }
583584Salexander.borisov@nginx.com 
584584Salexander.borisov@nginx.com     rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
585584Salexander.borisov@nginx.com                         p, len, 0, 0);
586584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
587584Salexander.borisov@nginx.com         return NXT_ERROR;
588584Salexander.borisov@nginx.com     }
589584Salexander.borisov@nginx.com 
590584Salexander.borisov@nginx.com     rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
591584Salexander.borisov@nginx.com                         (u_char *) "\r\n", (sizeof("\r\n") - 1), 0, 0);
592584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
593584Salexander.borisov@nginx.com         return NXT_ERROR;
594584Salexander.borisov@nginx.com     }
595584Salexander.borisov@nginx.com 
596584Salexander.borisov@nginx.com     return NXT_OK;
597584Salexander.borisov@nginx.com }
598584Salexander.borisov@nginx.com 
599584Salexander.borisov@nginx.com 
600584Salexander.borisov@nginx.com nxt_inline nxt_int_t
601584Salexander.borisov@nginx.com nxt_ruby_write(nxt_task_t *task, nxt_app_wmsg_t *wmsg,
602584Salexander.borisov@nginx.com     const u_char *data, size_t len, nxt_bool_t flush, nxt_bool_t last)
603584Salexander.borisov@nginx.com {
604584Salexander.borisov@nginx.com     nxt_int_t  rc;
605584Salexander.borisov@nginx.com 
606584Salexander.borisov@nginx.com     rc = nxt_app_msg_write_raw(task, wmsg, data, len);
607584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
608584Salexander.borisov@nginx.com         return rc;
609584Salexander.borisov@nginx.com     }
610584Salexander.borisov@nginx.com 
611584Salexander.borisov@nginx.com     if (flush || last) {
612584Salexander.borisov@nginx.com         rc = nxt_app_msg_flush(task, wmsg, last);
613584Salexander.borisov@nginx.com     }
614584Salexander.borisov@nginx.com 
615584Salexander.borisov@nginx.com     return rc;
616584Salexander.borisov@nginx.com }
617584Salexander.borisov@nginx.com 
618584Salexander.borisov@nginx.com 
619584Salexander.borisov@nginx.com static nxt_int_t
620584Salexander.borisov@nginx.com nxt_ruby_rack_result_headers(VALUE result)
621584Salexander.borisov@nginx.com {
622584Salexander.borisov@nginx.com     VALUE      headers;
623584Salexander.borisov@nginx.com     nxt_int_t  rc;
624584Salexander.borisov@nginx.com 
625584Salexander.borisov@nginx.com     headers = rb_ary_entry(result, 1);
626584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(headers) != T_HASH)) {
627584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
628584Salexander.borisov@nginx.com                 "Ruby: Invalid response 'headers' format from application");
629584Salexander.borisov@nginx.com 
630584Salexander.borisov@nginx.com         return NXT_ERROR;
631584Salexander.borisov@nginx.com     }
632584Salexander.borisov@nginx.com 
633584Salexander.borisov@nginx.com     rc = NXT_OK;
634584Salexander.borisov@nginx.com 
635584Salexander.borisov@nginx.com     rb_hash_foreach(headers, nxt_ruby_hash_foreach, (VALUE) (uintptr_t) &rc);
636584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
637584Salexander.borisov@nginx.com         return NXT_ERROR;
638584Salexander.borisov@nginx.com     }
639584Salexander.borisov@nginx.com 
640584Salexander.borisov@nginx.com     rc = nxt_ruby_write(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
641584Salexander.borisov@nginx.com                         (u_char *) "\r\n", (sizeof("\r\n") - 1), 0, 0);
642584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
643584Salexander.borisov@nginx.com         return NXT_ERROR;
644584Salexander.borisov@nginx.com     }
645584Salexander.borisov@nginx.com 
646584Salexander.borisov@nginx.com     return NXT_OK;
647584Salexander.borisov@nginx.com }
648584Salexander.borisov@nginx.com 
649584Salexander.borisov@nginx.com 
650584Salexander.borisov@nginx.com static int
651584Salexander.borisov@nginx.com nxt_ruby_hash_foreach(VALUE r_key, VALUE r_value, VALUE arg)
652584Salexander.borisov@nginx.com {
653584Salexander.borisov@nginx.com     nxt_int_t   rc, *rc_p;
654584Salexander.borisov@nginx.com     const char  *value, *value_end, *pos;
655584Salexander.borisov@nginx.com 
656584Salexander.borisov@nginx.com     rc_p = (nxt_int_t *) (uintptr_t) arg;
657584Salexander.borisov@nginx.com 
658584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(r_key) != T_STRING)) {
659584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
660584Salexander.borisov@nginx.com                 "Ruby: Wrong header entry 'key' from application");
661584Salexander.borisov@nginx.com 
662584Salexander.borisov@nginx.com         goto fail;
663584Salexander.borisov@nginx.com     }
664584Salexander.borisov@nginx.com 
665584Salexander.borisov@nginx.com     if (nxt_slow_path(TYPE(r_value) != T_STRING)) {
666584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
667584Salexander.borisov@nginx.com                 "Ruby: Wrong header entry 'value' from application");
668584Salexander.borisov@nginx.com 
669584Salexander.borisov@nginx.com         goto fail;
670584Salexander.borisov@nginx.com     }
671584Salexander.borisov@nginx.com 
672584Salexander.borisov@nginx.com     value = RSTRING_PTR(r_value);
673584Salexander.borisov@nginx.com     value_end = value + RSTRING_LEN(r_value);
674584Salexander.borisov@nginx.com 
675584Salexander.borisov@nginx.com     pos = value;
676584Salexander.borisov@nginx.com 
677584Salexander.borisov@nginx.com     for ( ;; ) {
678584Salexander.borisov@nginx.com         pos = strchr(pos, '\n');
679584Salexander.borisov@nginx.com 
680584Salexander.borisov@nginx.com         if (pos == NULL) {
681584Salexander.borisov@nginx.com             break;
682584Salexander.borisov@nginx.com         }
683584Salexander.borisov@nginx.com 
684584Salexander.borisov@nginx.com         rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key),
685584Salexander.borisov@nginx.com                                      value, pos - value);
686584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
687584Salexander.borisov@nginx.com             goto fail;
688584Salexander.borisov@nginx.com         }
689584Salexander.borisov@nginx.com 
690584Salexander.borisov@nginx.com         pos++;
691584Salexander.borisov@nginx.com         value = pos;
692584Salexander.borisov@nginx.com     }
693584Salexander.borisov@nginx.com 
694584Salexander.borisov@nginx.com     if (value <= value_end) {
695584Salexander.borisov@nginx.com         rc = nxt_ruby_head_send_part(RSTRING_PTR(r_key), RSTRING_LEN(r_key),
696584Salexander.borisov@nginx.com                                      value, value_end - value);
697584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
698584Salexander.borisov@nginx.com             goto fail;
699584Salexander.borisov@nginx.com         }
700584Salexander.borisov@nginx.com     }
701584Salexander.borisov@nginx.com 
702584Salexander.borisov@nginx.com     *rc_p = NXT_OK;
703584Salexander.borisov@nginx.com 
704584Salexander.borisov@nginx.com     return ST_CONTINUE;
705584Salexander.borisov@nginx.com 
706584Salexander.borisov@nginx.com fail:
707584Salexander.borisov@nginx.com 
708584Salexander.borisov@nginx.com     *rc_p = NXT_ERROR;
709584Salexander.borisov@nginx.com 
710584Salexander.borisov@nginx.com     return ST_STOP;
711584Salexander.borisov@nginx.com }
712584Salexander.borisov@nginx.com 
713584Salexander.borisov@nginx.com 
714584Salexander.borisov@nginx.com static nxt_int_t
715584Salexander.borisov@nginx.com nxt_ruby_head_send_part(const char *key, size_t key_size,
716584Salexander.borisov@nginx.com     const char *value, size_t value_size)
717584Salexander.borisov@nginx.com {
718584Salexander.borisov@nginx.com     nxt_int_t  rc;
719584Salexander.borisov@nginx.com 
720584Salexander.borisov@nginx.com     rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
721584Salexander.borisov@nginx.com                                (u_char *) key, key_size);
722584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
723584Salexander.borisov@nginx.com         return rc;
724584Salexander.borisov@nginx.com     }
725584Salexander.borisov@nginx.com 
726584Salexander.borisov@nginx.com     rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
727584Salexander.borisov@nginx.com                                (u_char *) ": ", (sizeof(": ") - 1));
728584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
729584Salexander.borisov@nginx.com         return rc;
730584Salexander.borisov@nginx.com     }
731584Salexander.borisov@nginx.com 
732584Salexander.borisov@nginx.com     rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
733584Salexander.borisov@nginx.com                                (u_char *) value, value_size);
734584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
735584Salexander.borisov@nginx.com         return rc;
736584Salexander.borisov@nginx.com     }
737584Salexander.borisov@nginx.com 
738584Salexander.borisov@nginx.com     return nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
739584Salexander.borisov@nginx.com                                  (u_char *) "\r\n", (sizeof("\r\n") - 1));
740584Salexander.borisov@nginx.com }
741584Salexander.borisov@nginx.com 
742584Salexander.borisov@nginx.com 
743584Salexander.borisov@nginx.com static nxt_int_t
744584Salexander.borisov@nginx.com nxt_ruby_rack_result_body(VALUE result)
745584Salexander.borisov@nginx.com {
746584Salexander.borisov@nginx.com     VALUE      fn, body;
747584Salexander.borisov@nginx.com     nxt_int_t  rc;
748584Salexander.borisov@nginx.com 
749584Salexander.borisov@nginx.com     body = rb_ary_entry(result, 2);
750584Salexander.borisov@nginx.com 
751584Salexander.borisov@nginx.com     if (rb_respond_to(body, rb_intern("to_path"))) {
752584Salexander.borisov@nginx.com 
753584Salexander.borisov@nginx.com         fn = rb_funcall(body, rb_intern("to_path"), 0);
754584Salexander.borisov@nginx.com         if (nxt_slow_path(TYPE(fn) != T_STRING)) {
755584Salexander.borisov@nginx.com             nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
756584Salexander.borisov@nginx.com                     "Ruby: Failed to get 'body' file path from application");
757584Salexander.borisov@nginx.com 
758584Salexander.borisov@nginx.com             return NXT_ERROR;
759584Salexander.borisov@nginx.com         }
760584Salexander.borisov@nginx.com 
761584Salexander.borisov@nginx.com         rc = nxt_ruby_rack_result_body_file_write(fn);
762584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
763584Salexander.borisov@nginx.com             return NXT_ERROR;
764584Salexander.borisov@nginx.com         }
765584Salexander.borisov@nginx.com 
766584Salexander.borisov@nginx.com     } else if (rb_respond_to(body, rb_intern("each"))) {
767584Salexander.borisov@nginx.com         rb_iterate(rb_each, body, nxt_ruby_rack_result_body_each, 0);
768584Salexander.borisov@nginx.com 
769584Salexander.borisov@nginx.com     } else {
770584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
771584Salexander.borisov@nginx.com                 "Ruby: Invalid response 'body' format from application");
772584Salexander.borisov@nginx.com 
773584Salexander.borisov@nginx.com         return NXT_ERROR;
774584Salexander.borisov@nginx.com     }
775584Salexander.borisov@nginx.com 
776584Salexander.borisov@nginx.com     if (rb_respond_to(body, rb_intern("close"))) {
777584Salexander.borisov@nginx.com         rb_funcall(body, rb_intern("close"), 0);
778584Salexander.borisov@nginx.com     }
779584Salexander.borisov@nginx.com 
780584Salexander.borisov@nginx.com     return NXT_OK;
781584Salexander.borisov@nginx.com }
782584Salexander.borisov@nginx.com 
783584Salexander.borisov@nginx.com 
784584Salexander.borisov@nginx.com static nxt_int_t
785584Salexander.borisov@nginx.com nxt_ruby_rack_result_body_file_write(VALUE filepath)
786584Salexander.borisov@nginx.com {
787584Salexander.borisov@nginx.com     size_t           len;
788584Salexander.borisov@nginx.com     ssize_t          n;
789584Salexander.borisov@nginx.com     nxt_off_t        rest;
790584Salexander.borisov@nginx.com     nxt_int_t        rc;
791584Salexander.borisov@nginx.com     nxt_file_t       file;
792584Salexander.borisov@nginx.com     nxt_file_info_t  finfo;
793584Salexander.borisov@nginx.com     u_char           buf[8192];
794584Salexander.borisov@nginx.com 
795584Salexander.borisov@nginx.com     nxt_memzero(&file, sizeof(nxt_file_t));
796584Salexander.borisov@nginx.com 
797584Salexander.borisov@nginx.com     file.name = (nxt_file_name_t *) RSTRING_PTR(filepath);
798584Salexander.borisov@nginx.com 
799584Salexander.borisov@nginx.com     rc = nxt_file_open(nxt_ruby_run_ctx.task, &file, NXT_FILE_RDONLY,
800584Salexander.borisov@nginx.com                        NXT_FILE_OPEN, 0);
801584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
802584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
803584Salexander.borisov@nginx.com                 "Ruby: Failed to open 'body' file: %s",
804584Salexander.borisov@nginx.com                 (const char *) file.name);
805584Salexander.borisov@nginx.com 
806584Salexander.borisov@nginx.com         return NXT_ERROR;
807584Salexander.borisov@nginx.com     }
808584Salexander.borisov@nginx.com 
809584Salexander.borisov@nginx.com     rc = nxt_file_info(&file, &finfo);
810584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
811584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
812584Salexander.borisov@nginx.com                 "Ruby: Failed to get 'body' file information: %s",
813584Salexander.borisov@nginx.com                 (const char *) file.name);
814584Salexander.borisov@nginx.com 
815584Salexander.borisov@nginx.com         goto fail;
816584Salexander.borisov@nginx.com     }
817584Salexander.borisov@nginx.com 
818584Salexander.borisov@nginx.com     rest = nxt_file_size(&finfo);
819584Salexander.borisov@nginx.com 
820584Salexander.borisov@nginx.com     while (rest != 0) {
821584Salexander.borisov@nginx.com         len = nxt_min(rest, (nxt_off_t) sizeof(buf));
822584Salexander.borisov@nginx.com 
823584Salexander.borisov@nginx.com         n = nxt_file_read(&file, buf, len, nxt_file_size(&finfo) - rest);
824584Salexander.borisov@nginx.com         if (nxt_slow_path(n != (ssize_t) len)) {
825584Salexander.borisov@nginx.com             nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
826584Salexander.borisov@nginx.com                     "Ruby: Failed to read 'body' file");
827584Salexander.borisov@nginx.com 
828584Salexander.borisov@nginx.com             goto fail;
829584Salexander.borisov@nginx.com         }
830584Salexander.borisov@nginx.com 
831584Salexander.borisov@nginx.com         rest -= len;
832584Salexander.borisov@nginx.com 
833584Salexander.borisov@nginx.com         rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
834584Salexander.borisov@nginx.com                                    buf, len);
835584Salexander.borisov@nginx.com         if (nxt_slow_path(rc != NXT_OK)) {
836584Salexander.borisov@nginx.com             nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
837584Salexander.borisov@nginx.com                     "Ruby: Failed to write 'body' from application");
838584Salexander.borisov@nginx.com 
839584Salexander.borisov@nginx.com             goto fail;
840584Salexander.borisov@nginx.com         }
841584Salexander.borisov@nginx.com     }
842584Salexander.borisov@nginx.com 
843584Salexander.borisov@nginx.com     nxt_file_close(nxt_ruby_run_ctx.task, &file);
844584Salexander.borisov@nginx.com 
845584Salexander.borisov@nginx.com     return NXT_OK;
846584Salexander.borisov@nginx.com 
847584Salexander.borisov@nginx.com fail:
848584Salexander.borisov@nginx.com 
849584Salexander.borisov@nginx.com     nxt_file_close(nxt_ruby_run_ctx.task, &file);
850584Salexander.borisov@nginx.com 
851584Salexander.borisov@nginx.com     return NXT_ERROR;
852584Salexander.borisov@nginx.com }
853584Salexander.borisov@nginx.com 
854584Salexander.borisov@nginx.com 
855584Salexander.borisov@nginx.com static VALUE
856584Salexander.borisov@nginx.com nxt_ruby_rack_result_body_each(VALUE body)
857584Salexander.borisov@nginx.com {
858584Salexander.borisov@nginx.com     nxt_int_t  rc;
859584Salexander.borisov@nginx.com 
860584Salexander.borisov@nginx.com     if (TYPE(body) != T_STRING) {
861584Salexander.borisov@nginx.com         return Qnil;
862584Salexander.borisov@nginx.com     }
863584Salexander.borisov@nginx.com 
864584Salexander.borisov@nginx.com     rc = nxt_app_msg_write_raw(nxt_ruby_run_ctx.task, nxt_ruby_run_ctx.wmsg,
865584Salexander.borisov@nginx.com                                (u_char *) RSTRING_PTR(body), RSTRING_LEN(body));
866584Salexander.borisov@nginx.com     if (nxt_slow_path(rc != NXT_OK)) {
867584Salexander.borisov@nginx.com         nxt_log(nxt_ruby_run_ctx.task, NXT_LOG_ERR,
868584Salexander.borisov@nginx.com                 "Ruby: Failed to write 'body' from application");
869584Salexander.borisov@nginx.com     }
870584Salexander.borisov@nginx.com 
871584Salexander.borisov@nginx.com     return Qnil;
872584Salexander.borisov@nginx.com }
873584Salexander.borisov@nginx.com 
874584Salexander.borisov@nginx.com 
875584Salexander.borisov@nginx.com static void
876584Salexander.borisov@nginx.com nxt_ruby_exception_log(nxt_task_t *task, uint32_t level, const char *desc)
877584Salexander.borisov@nginx.com {
878584Salexander.borisov@nginx.com     int    i;
879584Salexander.borisov@nginx.com     VALUE  err, ary, eclass, msg;
880584Salexander.borisov@nginx.com 
881584Salexander.borisov@nginx.com     nxt_log(task, level, "Ruby: %s", desc);
882584Salexander.borisov@nginx.com 
883584Salexander.borisov@nginx.com     err = rb_errinfo();
884584Salexander.borisov@nginx.com     ary = rb_funcall(err, rb_intern("backtrace"), 0);
885584Salexander.borisov@nginx.com 
886584Salexander.borisov@nginx.com     if (RARRAY_LEN(ary) == 0) {
887584Salexander.borisov@nginx.com         return;
888584Salexander.borisov@nginx.com     }
889584Salexander.borisov@nginx.com 
890584Salexander.borisov@nginx.com     eclass = rb_class_name(rb_class_of(err));
891584Salexander.borisov@nginx.com     msg = rb_funcall(err, rb_intern("message"), 0);
892584Salexander.borisov@nginx.com 
893584Salexander.borisov@nginx.com     nxt_log(task, level, "Ruby: %s: %s (%s)",
894584Salexander.borisov@nginx.com             RSTRING_PTR(RARRAY_PTR(ary)[0]),
895584Salexander.borisov@nginx.com             RSTRING_PTR(msg), RSTRING_PTR(eclass));
896584Salexander.borisov@nginx.com 
897584Salexander.borisov@nginx.com     for (i = 1; i < RARRAY_LEN(ary); i++) {
898584Salexander.borisov@nginx.com         nxt_log(task, level, "from %s", RSTRING_PTR(RARRAY_PTR(ary)[i]));
899584Salexander.borisov@nginx.com     }
900584Salexander.borisov@nginx.com }
901584Salexander.borisov@nginx.com 
902584Salexander.borisov@nginx.com 
903584Salexander.borisov@nginx.com static void
904584Salexander.borisov@nginx.com nxt_ruby_atexit(nxt_task_t *task)
905584Salexander.borisov@nginx.com {
906584Salexander.borisov@nginx.com     rb_gc_unregister_address(&nxt_ruby_io_input);
907584Salexander.borisov@nginx.com     rb_gc_unregister_address(&nxt_ruby_io_error);
908584Salexander.borisov@nginx.com 
909584Salexander.borisov@nginx.com     rb_gc_unregister_address(&nxt_ruby_rackup);
910584Salexander.borisov@nginx.com     rb_gc_unregister_address(&nxt_ruby_call);
911584Salexander.borisov@nginx.com     rb_gc_unregister_address(&nxt_ruby_env);
912584Salexander.borisov@nginx.com 
913584Salexander.borisov@nginx.com     ruby_cleanup(0);
914584Salexander.borisov@nginx.com }
915