blob: b4b7044452dbae51745aab638f8240ef10f14cec [file] [log] [blame]
nnoble097ef9b2014-12-01 17:06:10 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
nnoble097ef9b2014-12-01 17:06:10 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "rb_grpc.h"
35
36#include <math.h>
Yuki Yugui Sonoda22887912015-04-16 20:57:17 +090037#include <ruby/ruby.h>
38#include <ruby/vm.h>
nnoble097ef9b2014-12-01 17:06:10 -080039#include <sys/time.h>
40
41#include <grpc/grpc.h>
42#include <grpc/support/time.h>
nnoble097ef9b2014-12-01 17:06:10 -080043#include "rb_call.h"
murgatroid999946f2b2015-12-04 14:36:27 -080044#include "rb_call_credentials.h"
nnoble097ef9b2014-12-01 17:06:10 -080045#include "rb_channel.h"
Tim Emiola9332ea62015-10-27 23:48:29 -070046#include "rb_channel_credentials.h"
nnoble097ef9b2014-12-01 17:06:10 -080047#include "rb_completion_queue.h"
nnoble097ef9b2014-12-01 17:06:10 -080048#include "rb_server.h"
nnoble0c475f02014-12-05 15:37:39 -080049#include "rb_server_credentials.h"
nnoble097ef9b2014-12-01 17:06:10 -080050
Yuki Yugui Sonoda3c88e5d2015-04-16 20:09:00 +090051static VALUE grpc_rb_cTimeVal = Qnil;
nnoble097ef9b2014-12-01 17:06:10 -080052
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +090053static rb_data_type_t grpc_rb_timespec_data_type = {
54 "gpr_timespec",
murgatroid9987afb5d2015-07-16 16:01:02 -070055 {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE,
56 {NULL, NULL}},
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +090057 NULL,
58 NULL,
Tim Emiola9161a822015-11-11 15:58:44 -080059#ifdef RUBY_TYPED_FREE_IMMEDIATELY
60 RUBY_TYPED_FREE_IMMEDIATELY
61#endif
62};
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +090063
nnoble097ef9b2014-12-01 17:06:10 -080064/* Alloc func that blocks allocation of a given object by raising an
65 * exception. */
66VALUE grpc_rb_cannot_alloc(VALUE cls) {
67 rb_raise(rb_eTypeError,
68 "allocation of %s only allowed from the gRPC native layer",
69 rb_class2name(cls));
70 return Qnil;
71}
72
73/* Init func that fails by raising an exception. */
74VALUE grpc_rb_cannot_init(VALUE self) {
75 rb_raise(rb_eTypeError,
76 "initialization of %s only allowed from the gRPC native layer",
77 rb_obj_classname(self));
78 return Qnil;
79}
80
81/* Init/Clone func that fails by raising an exception. */
82VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
murgatroid9987afb5d2015-07-16 16:01:02 -070083 (void)self;
nnoble097ef9b2014-12-01 17:06:10 -080084 rb_raise(rb_eTypeError,
85 "initialization of %s only allowed from the gRPC native layer",
86 rb_obj_classname(copy));
87 return Qnil;
88}
89
90/* id_tv_{,u}sec are accessor methods on Ruby Time instances. */
91static ID id_tv_sec;
92static ID id_tv_nsec;
93
94/**
95 * grpc_rb_time_timeval creates a time_eval from a ruby time object.
96 *
97 * This func is copied from ruby source, MRI/source/time.c, which is published
98 * under the same license as the ruby.h, on which the entire extensions is
99 * based.
100 */
101gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
102 gpr_timespec t;
103 gpr_timespec *time_const;
104 const char *tstr = interval ? "time interval" : "time";
105 const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
106
Craig Tiller5a1e7fd2015-07-14 07:14:47 -0700107 t.clock_type = GPR_CLOCK_REALTIME;
nnoble097ef9b2014-12-01 17:06:10 -0800108 switch (TYPE(time)) {
nnoble097ef9b2014-12-01 17:06:10 -0800109 case T_DATA:
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900110 if (CLASS_OF(time) == grpc_rb_cTimeVal) {
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +0900111 TypedData_Get_Struct(time, gpr_timespec, &grpc_rb_timespec_data_type,
112 time_const);
nnoble097ef9b2014-12-01 17:06:10 -0800113 t = *time_const;
114 } else if (CLASS_OF(time) == rb_cTime) {
Craig Tillerb5dcec52015-01-13 11:13:42 -0800115 t.tv_sec = NUM2INT(rb_funcall(time, id_tv_sec, 0));
nnoble097ef9b2014-12-01 17:06:10 -0800116 t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0));
117 } else {
Craig Tillerb5dcec52015-01-13 11:13:42 -0800118 rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
119 rb_obj_classname(time), want);
nnoble097ef9b2014-12-01 17:06:10 -0800120 }
121 break;
122
123 case T_FIXNUM:
124 t.tv_sec = FIX2LONG(time);
125 if (interval && t.tv_sec < 0)
126 rb_raise(rb_eArgError, "%s must be positive", tstr);
127 t.tv_nsec = 0;
128 break;
129
130 case T_FLOAT:
mattnb9e15632015-02-28 23:45:58 +0900131 if (interval && RFLOAT_VALUE(time) < 0.0)
nnoble097ef9b2014-12-01 17:06:10 -0800132 rb_raise(rb_eArgError, "%s must be positive", tstr);
133 else {
134 double f, d;
135
mattnb9e15632015-02-28 23:45:58 +0900136 d = modf(RFLOAT_VALUE(time), &f);
nnoble097ef9b2014-12-01 17:06:10 -0800137 if (d < 0) {
138 d += 1;
139 f -= 1;
140 }
141 t.tv_sec = (time_t)f;
142 if (f != t.tv_sec) {
143 rb_raise(rb_eRangeError, "%f out of Time range",
mattnb9e15632015-02-28 23:45:58 +0900144 RFLOAT_VALUE(time));
nnoble097ef9b2014-12-01 17:06:10 -0800145 }
Marcin Wyszynski1a2ac332015-07-23 20:12:33 +0200146 t.tv_nsec = (int)(d * 1e9 + 0.5);
nnoble097ef9b2014-12-01 17:06:10 -0800147 }
148 break;
149
150 case T_BIGNUM:
151 t.tv_sec = NUM2LONG(time);
152 if (interval && t.tv_sec < 0)
153 rb_raise(rb_eArgError, "%s must be positive", tstr);
154 t.tv_nsec = 0;
155 break;
156
157 default:
Craig Tillerb5dcec52015-01-13 11:13:42 -0800158 rb_raise(rb_eTypeError, "bad input: (%s)->c_timeval, got <%s>,%s", tstr,
159 rb_obj_classname(time), want);
nnoble097ef9b2014-12-01 17:06:10 -0800160 break;
161 }
162 return t;
163}
164
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900165static void Init_grpc_status_codes() {
temiola58327912014-12-15 17:51:16 -0800166 /* Constants representing the status codes or grpc_status_code in status.h */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900167 VALUE grpc_rb_mStatusCodes =
168 rb_define_module_under(grpc_rb_mGrpcCore, "StatusCodes");
169 rb_define_const(grpc_rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
170 rb_define_const(grpc_rb_mStatusCodes, "CANCELLED",
171 INT2NUM(GRPC_STATUS_CANCELLED));
172 rb_define_const(grpc_rb_mStatusCodes, "UNKNOWN",
173 INT2NUM(GRPC_STATUS_UNKNOWN));
174 rb_define_const(grpc_rb_mStatusCodes, "INVALID_ARGUMENT",
temiola58327912014-12-15 17:51:16 -0800175 INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900176 rb_define_const(grpc_rb_mStatusCodes, "DEADLINE_EXCEEDED",
temiola58327912014-12-15 17:51:16 -0800177 INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900178 rb_define_const(grpc_rb_mStatusCodes, "NOT_FOUND",
179 INT2NUM(GRPC_STATUS_NOT_FOUND));
180 rb_define_const(grpc_rb_mStatusCodes, "ALREADY_EXISTS",
temiola58327912014-12-15 17:51:16 -0800181 INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900182 rb_define_const(grpc_rb_mStatusCodes, "PERMISSION_DENIED",
temiola58327912014-12-15 17:51:16 -0800183 INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900184 rb_define_const(grpc_rb_mStatusCodes, "UNAUTHENTICATED",
temiola58327912014-12-15 17:51:16 -0800185 INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900186 rb_define_const(grpc_rb_mStatusCodes, "RESOURCE_EXHAUSTED",
temiola58327912014-12-15 17:51:16 -0800187 INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900188 rb_define_const(grpc_rb_mStatusCodes, "FAILED_PRECONDITION",
temiola58327912014-12-15 17:51:16 -0800189 INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900190 rb_define_const(grpc_rb_mStatusCodes, "ABORTED",
191 INT2NUM(GRPC_STATUS_ABORTED));
192 rb_define_const(grpc_rb_mStatusCodes, "OUT_OF_RANGE",
temiola58327912014-12-15 17:51:16 -0800193 INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900194 rb_define_const(grpc_rb_mStatusCodes, "UNIMPLEMENTED",
temiola58327912014-12-15 17:51:16 -0800195 INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900196 rb_define_const(grpc_rb_mStatusCodes, "INTERNAL",
197 INT2NUM(GRPC_STATUS_INTERNAL));
198 rb_define_const(grpc_rb_mStatusCodes, "UNAVAILABLE",
temiola58327912014-12-15 17:51:16 -0800199 INT2NUM(GRPC_STATUS_UNAVAILABLE));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900200 rb_define_const(grpc_rb_mStatusCodes, "DATA_LOSS",
201 INT2NUM(GRPC_STATUS_DATA_LOSS));
temiola58327912014-12-15 17:51:16 -0800202}
203
nnoble097ef9b2014-12-01 17:06:10 -0800204/* id_at is the constructor method of the ruby standard Time class. */
205static ID id_at;
206
207/* id_inspect is the inspect method found on various ruby objects. */
208static ID id_inspect;
209
210/* id_to_s is the to_s method found on various ruby objects. */
211static ID id_to_s;
212
Tim Emiola98a32d32015-03-28 01:48:44 -0700213/* Converts a wrapped time constant to a standard time. */
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900214static VALUE grpc_rb_time_val_to_time(VALUE self) {
nnoble097ef9b2014-12-01 17:06:10 -0800215 gpr_timespec *time_const = NULL;
Craig Tiller94329d02015-07-23 09:52:11 -0700216 gpr_timespec real_time;
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +0900217 TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type,
218 time_const);
Craig Tiller94329d02015-07-23 09:52:11 -0700219 real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME);
220 return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec),
221 INT2NUM(real_time.tv_nsec));
nnoble097ef9b2014-12-01 17:06:10 -0800222}
223
224/* Invokes inspect on the ctime version of the time val. */
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900225static VALUE grpc_rb_time_val_inspect(VALUE self) {
nnoble097ef9b2014-12-01 17:06:10 -0800226 return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0);
227}
228
229/* Invokes to_s on the ctime version of the time val. */
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900230static VALUE grpc_rb_time_val_to_s(VALUE self) {
nnoble097ef9b2014-12-01 17:06:10 -0800231 return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
232}
233
Craig Tiller354398f2015-07-13 09:16:03 -0700234static gpr_timespec zero_realtime;
235static gpr_timespec inf_future_realtime;
236static gpr_timespec inf_past_realtime;
237
nnoble097ef9b2014-12-01 17:06:10 -0800238/* Adds a module with constants that map to gpr's static timeval structs. */
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900239static void Init_grpc_time_consts() {
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900240 VALUE grpc_rb_mTimeConsts =
241 rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
242 grpc_rb_cTimeVal =
243 rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
Craig Tiller354398f2015-07-13 09:16:03 -0700244 zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME);
245 inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME);
246 inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME);
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +0900247 rb_define_const(
248 grpc_rb_mTimeConsts, "ZERO",
249 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
Craig Tiller354398f2015-07-13 09:16:03 -0700250 (void *)&zero_realtime));
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +0900251 rb_define_const(
252 grpc_rb_mTimeConsts, "INFINITE_FUTURE",
253 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
Craig Tiller354398f2015-07-13 09:16:03 -0700254 (void *)&inf_future_realtime));
Yuki Yugui Sonodad441c2e2015-04-11 15:33:58 +0900255 rb_define_const(
256 grpc_rb_mTimeConsts, "INFINITE_PAST",
257 TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
Craig Tiller354398f2015-07-13 09:16:03 -0700258 (void *)&inf_past_realtime));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900259 rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
260 rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
261 rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
nnoble097ef9b2014-12-01 17:06:10 -0800262 id_at = rb_intern("at");
263 id_inspect = rb_intern("inspect");
264 id_to_s = rb_intern("to_s");
265 id_tv_sec = rb_intern("tv_sec");
266 id_tv_nsec = rb_intern("tv_nsec");
267}
268
Tim Emiola9161a822015-11-11 15:58:44 -0800269/*
270 TODO: find an alternative to ruby_vm_at_exit that is ok in Ruby 2.0 where
271 RUBY_TYPED_FREE_IMMEDIATELY is not defined.
272
273 At the moment, registering a function using ruby_vm_at_exit segfaults in Ruby
274 2.0. This is not an issue with the gRPC handler. More likely, this was an
275 in issue with 2.0 that got resolved in 2.1 and has not been backported.
276*/
277#ifdef RUBY_TYPED_FREE_IMMEDIATELY
murgatroid9987afb5d2015-07-16 16:01:02 -0700278static void grpc_rb_shutdown(ruby_vm_t *vm) {
279 (void)vm;
280 grpc_shutdown();
281}
Tim Emiola9161a822015-11-11 15:58:44 -0800282#endif
nnoble097ef9b2014-12-01 17:06:10 -0800283
Tim Emiola409e6c82015-02-17 17:46:35 -0800284/* Initialize the GRPC module structs */
temiola21bb60c2014-12-18 10:58:22 -0800285
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900286/* grpc_rb_sNewServerRpc is the struct that holds new server rpc details. */
287VALUE grpc_rb_sNewServerRpc = Qnil;
288/* grpc_rb_sStatus is the struct that holds status details. */
289VALUE grpc_rb_sStatus = Qnil;
temiola21bb60c2014-12-18 10:58:22 -0800290
Tim Emiola409e6c82015-02-17 17:46:35 -0800291/* Initialize the GRPC module. */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900292VALUE grpc_rb_mGRPC = Qnil;
293VALUE grpc_rb_mGrpcCore = Qnil;
temiola21bb60c2014-12-18 10:58:22 -0800294
Yuki Yugui Sonoda99eb9f92015-04-16 20:09:55 +0900295/* cached Symbols for members in Status struct */
296VALUE sym_code = Qundef;
297VALUE sym_details = Qundef;
298VALUE sym_metadata = Qundef;
299
nnoble097ef9b2014-12-01 17:06:10 -0800300void Init_grpc() {
301 grpc_init();
Tim Emiola9161a822015-11-11 15:58:44 -0800302
303/* TODO: find alternative to ruby_vm_at_exit that is ok in Ruby 2.0 */
304#ifdef RUBY_TYPED_FREE_IMMEDIATELY
nnoble097ef9b2014-12-01 17:06:10 -0800305 ruby_vm_at_exit(grpc_rb_shutdown);
Tim Emiola9161a822015-11-11 15:58:44 -0800306#endif
307
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900308 grpc_rb_mGRPC = rb_define_module("GRPC");
309 grpc_rb_mGrpcCore = rb_define_module_under(grpc_rb_mGRPC, "Core");
310 grpc_rb_sNewServerRpc =
311 rb_struct_define("NewServerRpc", "method", "host",
312 "deadline", "metadata", "call", NULL);
313 grpc_rb_sStatus =
314 rb_struct_define("Status", "code", "details", "metadata", NULL);
Tim Emiola98a32d32015-03-28 01:48:44 -0700315 sym_code = ID2SYM(rb_intern("code"));
316 sym_details = ID2SYM(rb_intern("details"));
317 sym_metadata = ID2SYM(rb_intern("metadata"));
nnoble097ef9b2014-12-01 17:06:10 -0800318
Tim Emiola409e6c82015-02-17 17:46:35 -0800319 Init_grpc_channel();
320 Init_grpc_completion_queue();
321 Init_grpc_call();
murgatroid999946f2b2015-12-04 14:36:27 -0800322 Init_grpc_call_credentials();
Tim Emiola9332ea62015-10-27 23:48:29 -0700323 Init_grpc_channel_credentials();
Tim Emiola409e6c82015-02-17 17:46:35 -0800324 Init_grpc_server();
325 Init_grpc_server_credentials();
326 Init_grpc_status_codes();
327 Init_grpc_time_consts();
nnoble097ef9b2014-12-01 17:06:10 -0800328}