1*584Salexander.borisov@nginx.com 
2*584Salexander.borisov@nginx.com /*
3*584Salexander.borisov@nginx.com  * Copyright (C) Alexander Borisov
4*584Salexander.borisov@nginx.com  * Copyright (C) NGINX, Inc.
5*584Salexander.borisov@nginx.com  */
6*584Salexander.borisov@nginx.com 
7*584Salexander.borisov@nginx.com #include <ruby/nxt_ruby.h>
8*584Salexander.borisov@nginx.com 
9*584Salexander.borisov@nginx.com 
10*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_new(VALUE class, VALUE wrap);
11*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self);
12*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_gets(VALUE obj, VALUE args);
13*584Salexander.borisov@nginx.com static size_t nxt_ruby_stream_io_read_line(nxt_app_rmsg_t *rmsg, VALUE str);
14*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_each(VALUE obj, VALUE args);
15*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_read(VALUE obj, VALUE args);
16*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_rewind(VALUE obj, VALUE args);
17*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_puts(VALUE obj, VALUE args);
18*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_write(VALUE obj, VALUE args);
19*584Salexander.borisov@nginx.com nxt_inline long nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx,
20*584Salexander.borisov@nginx.com     VALUE val);
21*584Salexander.borisov@nginx.com static VALUE nxt_ruby_stream_io_flush(VALUE obj, VALUE args);
22*584Salexander.borisov@nginx.com 
23*584Salexander.borisov@nginx.com 
24*584Salexander.borisov@nginx.com VALUE
25*584Salexander.borisov@nginx.com nxt_ruby_stream_io_input_init(void)
26*584Salexander.borisov@nginx.com {
27*584Salexander.borisov@nginx.com     VALUE  stream_io;
28*584Salexander.borisov@nginx.com 
29*584Salexander.borisov@nginx.com     stream_io = rb_define_class("NGINX_Unit_Stream_IO_Read", rb_cData);
30*584Salexander.borisov@nginx.com 
31*584Salexander.borisov@nginx.com     rb_gc_register_address(&stream_io);
32*584Salexander.borisov@nginx.com 
33*584Salexander.borisov@nginx.com     rb_define_singleton_method(stream_io, "new", nxt_ruby_stream_io_new, 1);
34*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "initialize", nxt_ruby_stream_io_initialize, -1);
35*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "gets", nxt_ruby_stream_io_gets, 0);
36*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "each", nxt_ruby_stream_io_each, 0);
37*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "read", nxt_ruby_stream_io_read, -2);
38*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "rewind", nxt_ruby_stream_io_rewind, 0);
39*584Salexander.borisov@nginx.com 
40*584Salexander.borisov@nginx.com     return stream_io;
41*584Salexander.borisov@nginx.com }
42*584Salexander.borisov@nginx.com 
43*584Salexander.borisov@nginx.com 
44*584Salexander.borisov@nginx.com VALUE
45*584Salexander.borisov@nginx.com nxt_ruby_stream_io_error_init(void)
46*584Salexander.borisov@nginx.com {
47*584Salexander.borisov@nginx.com     VALUE  stream_io;
48*584Salexander.borisov@nginx.com 
49*584Salexander.borisov@nginx.com     stream_io = rb_define_class("NGINX_Unit_Stream_IO_Error", rb_cData);
50*584Salexander.borisov@nginx.com 
51*584Salexander.borisov@nginx.com     rb_gc_register_address(&stream_io);
52*584Salexander.borisov@nginx.com 
53*584Salexander.borisov@nginx.com     rb_define_singleton_method(stream_io, "new", nxt_ruby_stream_io_new, 1);
54*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "initialize", nxt_ruby_stream_io_initialize, -1);
55*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "puts", nxt_ruby_stream_io_puts, -2);
56*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "write", nxt_ruby_stream_io_write, -2);
57*584Salexander.borisov@nginx.com     rb_define_method(stream_io, "flush", nxt_ruby_stream_io_flush, 0);
58*584Salexander.borisov@nginx.com 
59*584Salexander.borisov@nginx.com     return stream_io;
60*584Salexander.borisov@nginx.com }
61*584Salexander.borisov@nginx.com 
62*584Salexander.borisov@nginx.com 
63*584Salexander.borisov@nginx.com static VALUE
64*584Salexander.borisov@nginx.com nxt_ruby_stream_io_new(VALUE class, VALUE wrap)
65*584Salexander.borisov@nginx.com {
66*584Salexander.borisov@nginx.com     VALUE               self;
67*584Salexander.borisov@nginx.com     nxt_ruby_run_ctx_t  *run_ctx;
68*584Salexander.borisov@nginx.com 
69*584Salexander.borisov@nginx.com     Data_Get_Struct(wrap, nxt_ruby_run_ctx_t, run_ctx);
70*584Salexander.borisov@nginx.com     self = Data_Wrap_Struct(class, 0, 0, run_ctx);
71*584Salexander.borisov@nginx.com 
72*584Salexander.borisov@nginx.com     rb_obj_call_init(self, 0, NULL);
73*584Salexander.borisov@nginx.com 
74*584Salexander.borisov@nginx.com     return self;
75*584Salexander.borisov@nginx.com }
76*584Salexander.borisov@nginx.com 
77*584Salexander.borisov@nginx.com 
78*584Salexander.borisov@nginx.com static VALUE
79*584Salexander.borisov@nginx.com nxt_ruby_stream_io_initialize(int argc, VALUE *argv, VALUE self)
80*584Salexander.borisov@nginx.com {
81*584Salexander.borisov@nginx.com     return self;
82*584Salexander.borisov@nginx.com }
83*584Salexander.borisov@nginx.com 
84*584Salexander.borisov@nginx.com 
85*584Salexander.borisov@nginx.com static VALUE
86*584Salexander.borisov@nginx.com nxt_ruby_stream_io_gets(VALUE obj, VALUE args)
87*584Salexander.borisov@nginx.com {
88*584Salexander.borisov@nginx.com     VALUE               buf;
89*584Salexander.borisov@nginx.com     nxt_ruby_run_ctx_t  *run_ctx;
90*584Salexander.borisov@nginx.com 
91*584Salexander.borisov@nginx.com     Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
92*584Salexander.borisov@nginx.com 
93*584Salexander.borisov@nginx.com     if (run_ctx->body_preread_size == 0) {
94*584Salexander.borisov@nginx.com         return Qnil;
95*584Salexander.borisov@nginx.com     }
96*584Salexander.borisov@nginx.com 
97*584Salexander.borisov@nginx.com     buf = rb_str_buf_new(1);
98*584Salexander.borisov@nginx.com 
99*584Salexander.borisov@nginx.com     if (buf == Qnil) {
100*584Salexander.borisov@nginx.com         return Qnil;
101*584Salexander.borisov@nginx.com     }
102*584Salexander.borisov@nginx.com 
103*584Salexander.borisov@nginx.com     run_ctx->body_preread_size -= nxt_ruby_stream_io_read_line(run_ctx->rmsg,
104*584Salexander.borisov@nginx.com                                                                buf);
105*584Salexander.borisov@nginx.com 
106*584Salexander.borisov@nginx.com     return buf;
107*584Salexander.borisov@nginx.com }
108*584Salexander.borisov@nginx.com 
109*584Salexander.borisov@nginx.com 
110*584Salexander.borisov@nginx.com static size_t
111*584Salexander.borisov@nginx.com nxt_ruby_stream_io_read_line(nxt_app_rmsg_t *rmsg, VALUE str)
112*584Salexander.borisov@nginx.com {
113*584Salexander.borisov@nginx.com     size_t     len, size;
114*584Salexander.borisov@nginx.com     u_char     *p;
115*584Salexander.borisov@nginx.com     nxt_buf_t  *buf;
116*584Salexander.borisov@nginx.com 
117*584Salexander.borisov@nginx.com     len = 0;
118*584Salexander.borisov@nginx.com 
119*584Salexander.borisov@nginx.com     for (buf = rmsg->buf; buf != NULL; buf = buf->next) {
120*584Salexander.borisov@nginx.com 
121*584Salexander.borisov@nginx.com         size = nxt_buf_mem_used_size(&buf->mem);
122*584Salexander.borisov@nginx.com         p = memchr(buf->mem.pos, '\n', size);
123*584Salexander.borisov@nginx.com 
124*584Salexander.borisov@nginx.com         if (p != NULL) {
125*584Salexander.borisov@nginx.com             p++;
126*584Salexander.borisov@nginx.com             size = p - buf->mem.pos;
127*584Salexander.borisov@nginx.com 
128*584Salexander.borisov@nginx.com             rb_str_cat(str, (const char *) buf->mem.pos, size);
129*584Salexander.borisov@nginx.com 
130*584Salexander.borisov@nginx.com             len += size;
131*584Salexander.borisov@nginx.com             buf->mem.pos = p;
132*584Salexander.borisov@nginx.com 
133*584Salexander.borisov@nginx.com             break;
134*584Salexander.borisov@nginx.com         }
135*584Salexander.borisov@nginx.com 
136*584Salexander.borisov@nginx.com         rb_str_cat(str, (const char *) buf->mem.pos, size);
137*584Salexander.borisov@nginx.com 
138*584Salexander.borisov@nginx.com         len += size;
139*584Salexander.borisov@nginx.com         buf->mem.pos = buf->mem.free;
140*584Salexander.borisov@nginx.com     }
141*584Salexander.borisov@nginx.com 
142*584Salexander.borisov@nginx.com     rmsg->buf = buf;
143*584Salexander.borisov@nginx.com 
144*584Salexander.borisov@nginx.com     return len;
145*584Salexander.borisov@nginx.com }
146*584Salexander.borisov@nginx.com 
147*584Salexander.borisov@nginx.com 
148*584Salexander.borisov@nginx.com static VALUE
149*584Salexander.borisov@nginx.com nxt_ruby_stream_io_each(VALUE obj, VALUE args)
150*584Salexander.borisov@nginx.com {
151*584Salexander.borisov@nginx.com     VALUE  chunk;
152*584Salexander.borisov@nginx.com 
153*584Salexander.borisov@nginx.com     if (rb_block_given_p() == 0) {
154*584Salexander.borisov@nginx.com         rb_raise(rb_eArgError, "Expected block on rack.input 'each' method");
155*584Salexander.borisov@nginx.com     }
156*584Salexander.borisov@nginx.com 
157*584Salexander.borisov@nginx.com     for ( ;; ) {
158*584Salexander.borisov@nginx.com         chunk = nxt_ruby_stream_io_gets(obj, Qnil);
159*584Salexander.borisov@nginx.com 
160*584Salexander.borisov@nginx.com         if (chunk == Qnil) {
161*584Salexander.borisov@nginx.com             return Qnil;
162*584Salexander.borisov@nginx.com         }
163*584Salexander.borisov@nginx.com 
164*584Salexander.borisov@nginx.com         rb_yield(chunk);
165*584Salexander.borisov@nginx.com     }
166*584Salexander.borisov@nginx.com 
167*584Salexander.borisov@nginx.com     return Qnil;
168*584Salexander.borisov@nginx.com }
169*584Salexander.borisov@nginx.com 
170*584Salexander.borisov@nginx.com 
171*584Salexander.borisov@nginx.com static VALUE
172*584Salexander.borisov@nginx.com nxt_ruby_stream_io_read(VALUE obj, VALUE args)
173*584Salexander.borisov@nginx.com {
174*584Salexander.borisov@nginx.com     VALUE                buf;
175*584Salexander.borisov@nginx.com     long                 copy_size, u_size;
176*584Salexander.borisov@nginx.com     size_t               len;
177*584Salexander.borisov@nginx.com     nxt_ruby_run_ctx_t  *run_ctx;
178*584Salexander.borisov@nginx.com 
179*584Salexander.borisov@nginx.com     Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
180*584Salexander.borisov@nginx.com 
181*584Salexander.borisov@nginx.com     copy_size = run_ctx->body_preread_size;
182*584Salexander.borisov@nginx.com 
183*584Salexander.borisov@nginx.com     if (RARRAY_LEN(args) > 0 && TYPE(RARRAY_PTR(args)[0]) == T_FIXNUM) {
184*584Salexander.borisov@nginx.com         u_size = NUM2LONG(RARRAY_PTR(args)[0]);
185*584Salexander.borisov@nginx.com 
186*584Salexander.borisov@nginx.com         if (u_size < 0 || copy_size == 0) {
187*584Salexander.borisov@nginx.com             return Qnil;
188*584Salexander.borisov@nginx.com         }
189*584Salexander.borisov@nginx.com 
190*584Salexander.borisov@nginx.com         if (copy_size > u_size) {
191*584Salexander.borisov@nginx.com             copy_size = u_size;
192*584Salexander.borisov@nginx.com         }
193*584Salexander.borisov@nginx.com     }
194*584Salexander.borisov@nginx.com 
195*584Salexander.borisov@nginx.com     if (copy_size == 0) {
196*584Salexander.borisov@nginx.com         return rb_str_new_cstr("");
197*584Salexander.borisov@nginx.com     }
198*584Salexander.borisov@nginx.com 
199*584Salexander.borisov@nginx.com     buf = rb_str_buf_new(copy_size);
200*584Salexander.borisov@nginx.com 
201*584Salexander.borisov@nginx.com     if (nxt_slow_path(buf == Qnil)) {
202*584Salexander.borisov@nginx.com         return Qnil;
203*584Salexander.borisov@nginx.com     }
204*584Salexander.borisov@nginx.com 
205*584Salexander.borisov@nginx.com     len = nxt_app_msg_read_raw(run_ctx->task, run_ctx->rmsg,
206*584Salexander.borisov@nginx.com                                RSTRING_PTR(buf), (size_t) copy_size);
207*584Salexander.borisov@nginx.com 
208*584Salexander.borisov@nginx.com     if (RARRAY_LEN(args) > 1 && TYPE(RARRAY_PTR(args)[1]) == T_STRING) {
209*584Salexander.borisov@nginx.com 
210*584Salexander.borisov@nginx.com         rb_str_set_len(RARRAY_PTR(args)[1], 0);
211*584Salexander.borisov@nginx.com         rb_str_cat(RARRAY_PTR(args)[1], RSTRING_PTR(buf), copy_size);
212*584Salexander.borisov@nginx.com     }
213*584Salexander.borisov@nginx.com 
214*584Salexander.borisov@nginx.com     rb_str_set_len(buf, (long) len);
215*584Salexander.borisov@nginx.com 
216*584Salexander.borisov@nginx.com     run_ctx->body_preread_size -= len;
217*584Salexander.borisov@nginx.com 
218*584Salexander.borisov@nginx.com     return buf;
219*584Salexander.borisov@nginx.com }
220*584Salexander.borisov@nginx.com 
221*584Salexander.borisov@nginx.com 
222*584Salexander.borisov@nginx.com static VALUE
223*584Salexander.borisov@nginx.com nxt_ruby_stream_io_rewind(VALUE obj, VALUE args)
224*584Salexander.borisov@nginx.com {
225*584Salexander.borisov@nginx.com     return Qnil;
226*584Salexander.borisov@nginx.com }
227*584Salexander.borisov@nginx.com 
228*584Salexander.borisov@nginx.com 
229*584Salexander.borisov@nginx.com static VALUE
230*584Salexander.borisov@nginx.com nxt_ruby_stream_io_puts(VALUE obj, VALUE args)
231*584Salexander.borisov@nginx.com {
232*584Salexander.borisov@nginx.com     nxt_ruby_run_ctx_t  *run_ctx;
233*584Salexander.borisov@nginx.com 
234*584Salexander.borisov@nginx.com     if (RARRAY_LEN(args) != 1) {
235*584Salexander.borisov@nginx.com         return Qnil;
236*584Salexander.borisov@nginx.com     }
237*584Salexander.borisov@nginx.com 
238*584Salexander.borisov@nginx.com     Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
239*584Salexander.borisov@nginx.com 
240*584Salexander.borisov@nginx.com     nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]);
241*584Salexander.borisov@nginx.com 
242*584Salexander.borisov@nginx.com     return Qnil;
243*584Salexander.borisov@nginx.com }
244*584Salexander.borisov@nginx.com 
245*584Salexander.borisov@nginx.com 
246*584Salexander.borisov@nginx.com static VALUE
247*584Salexander.borisov@nginx.com nxt_ruby_stream_io_write(VALUE obj, VALUE args)
248*584Salexander.borisov@nginx.com {
249*584Salexander.borisov@nginx.com     long                len;
250*584Salexander.borisov@nginx.com     nxt_ruby_run_ctx_t  *run_ctx;
251*584Salexander.borisov@nginx.com 
252*584Salexander.borisov@nginx.com     if (RARRAY_LEN(args) != 1) {
253*584Salexander.borisov@nginx.com         return Qnil;
254*584Salexander.borisov@nginx.com     }
255*584Salexander.borisov@nginx.com 
256*584Salexander.borisov@nginx.com     Data_Get_Struct(obj, nxt_ruby_run_ctx_t, run_ctx);
257*584Salexander.borisov@nginx.com 
258*584Salexander.borisov@nginx.com     len = nxt_ruby_stream_io_s_write(run_ctx, RARRAY_PTR(args)[0]);
259*584Salexander.borisov@nginx.com 
260*584Salexander.borisov@nginx.com     return LONG2FIX(len);
261*584Salexander.borisov@nginx.com }
262*584Salexander.borisov@nginx.com 
263*584Salexander.borisov@nginx.com 
264*584Salexander.borisov@nginx.com nxt_inline long
265*584Salexander.borisov@nginx.com nxt_ruby_stream_io_s_write(nxt_ruby_run_ctx_t *run_ctx, VALUE val)
266*584Salexander.borisov@nginx.com {
267*584Salexander.borisov@nginx.com     if (nxt_slow_path(val == Qnil)) {
268*584Salexander.borisov@nginx.com         return 0;
269*584Salexander.borisov@nginx.com     }
270*584Salexander.borisov@nginx.com 
271*584Salexander.borisov@nginx.com     if (TYPE(val) != T_STRING) {
272*584Salexander.borisov@nginx.com         val = rb_funcall(val, rb_intern("to_s"), 0);
273*584Salexander.borisov@nginx.com 
274*584Salexander.borisov@nginx.com         if (TYPE(val) != T_STRING) {
275*584Salexander.borisov@nginx.com             return 0;
276*584Salexander.borisov@nginx.com         }
277*584Salexander.borisov@nginx.com     }
278*584Salexander.borisov@nginx.com 
279*584Salexander.borisov@nginx.com     nxt_log_error(NXT_LOG_ERR, run_ctx->task->log, "Ruby: %s",
280*584Salexander.borisov@nginx.com                   RSTRING_PTR(val));
281*584Salexander.borisov@nginx.com 
282*584Salexander.borisov@nginx.com     return RSTRING_LEN(val);
283*584Salexander.borisov@nginx.com }
284*584Salexander.borisov@nginx.com 
285*584Salexander.borisov@nginx.com 
286*584Salexander.borisov@nginx.com static VALUE
287*584Salexander.borisov@nginx.com nxt_ruby_stream_io_flush(VALUE obj, VALUE args)
288*584Salexander.borisov@nginx.com {
289*584Salexander.borisov@nginx.com     return Qnil;
290*584Salexander.borisov@nginx.com }
291