blob: b6c0791469362240cfd77dff3a22e72df519960f [file] [log] [blame]
nnoble097ef9b2014-12-01 17:06:10 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
nnoble097ef9b2014-12-01 17:06:10 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
nnoble097ef9b2014-12-01 17:06:10 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
nnoble097ef9b2014-12-01 17:06:10 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
nnoble097ef9b2014-12-01 17:06:10 -080016 *
17 */
18
Nicolas "Pixel" Nobled51d1212016-01-31 11:33:19 +010019#include <ruby/ruby.h>
Nicolas "Pixel" Noble9fcdc872016-05-05 06:15:34 +020020
nnoble097ef9b2014-12-01 17:06:10 -080021#include "rb_call.h"
Craig Tiller5b1c5f22017-04-19 09:52:18 -070022#include "rb_grpc_imports.generated.h"
nnoble097ef9b2014-12-01 17:06:10 -080023
nnoble097ef9b2014-12-01 17:06:10 -080024#include <grpc/grpc.h>
Alexander Polcyncdff92f2016-07-12 12:30:30 -070025#include <grpc/impl/codegen/compression_types.h>
Craig Tiller5b1c5f22017-04-19 09:52:18 -070026#include <grpc/support/alloc.h>
Alexander Polcyn85cc1432017-07-14 16:36:51 -070027#include <grpc/support/log.h>
Tim Emiola1dae4ac2015-03-27 17:17:37 -070028
nnoble097ef9b2014-12-01 17:06:10 -080029#include "rb_byte_buffer.h"
murgatroid999946f2b2015-12-04 14:36:27 -080030#include "rb_call_credentials.h"
nnoble097ef9b2014-12-01 17:06:10 -080031#include "rb_completion_queue.h"
nnoble097ef9b2014-12-01 17:06:10 -080032#include "rb_grpc.h"
33
Yuki Yugui Sonoda3c88e5d2015-04-16 20:09:00 +090034/* grpc_rb_cCall is the Call class whose instances proxy grpc_call. */
35static VALUE grpc_rb_cCall;
36
37/* grpc_rb_eCallError is the ruby class of the exception thrown during call
38 operations; */
39VALUE grpc_rb_eCallError = Qnil;
40
41/* grpc_rb_eOutOfTime is the ruby class of the exception thrown to indicate
42 a timeout. */
43static VALUE grpc_rb_eOutOfTime = Qnil;
44
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +090045/* grpc_rb_sBatchResult is struct class used to hold the results of a batch
46 * call. */
47static VALUE grpc_rb_sBatchResult;
Tim Emiola1dae4ac2015-03-27 17:17:37 -070048
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +090049/* grpc_rb_cMdAry is the MetadataArray class whose instances proxy
Tim Emiola1dae4ac2015-03-27 17:17:37 -070050 * grpc_metadata_array. */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +090051static VALUE grpc_rb_cMdAry;
Tim Emiola1dae4ac2015-03-27 17:17:37 -070052
murgatroid998c9edc22016-03-14 15:51:56 -070053/* id_credentials is the name of the hidden ivar that preserves the value
54 * of the credentials added to the call */
55static ID id_credentials;
56
nnoble097ef9b2014-12-01 17:06:10 -080057/* id_metadata is name of the attribute used to access the metadata hash
58 * received by the call and subsequently saved on it. */
59static ID id_metadata;
60
murgatroid99e69f0882016-07-07 15:52:27 -070061/* id_trailing_metadata is the name of the attribute used to access the trailing
62 * metadata hash received by the call and subsequently saved on it. */
63static ID id_trailing_metadata;
64
nnoble097ef9b2014-12-01 17:06:10 -080065/* id_status is name of the attribute used to access the status object
66 * received by the call and subsequently saved on it. */
67static ID id_status;
68
Tim Emiola7840a552015-08-20 13:12:33 -070069/* id_write_flag is name of the attribute used to access the write_flag
70 * saved on the call. */
71static ID id_write_flag;
72
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +090073/* sym_* are the symbol for attributes of grpc_rb_sBatchResult. */
Tim Emiola1dae4ac2015-03-27 17:17:37 -070074static VALUE sym_send_message;
75static VALUE sym_send_metadata;
76static VALUE sym_send_close;
77static VALUE sym_send_status;
78static VALUE sym_message;
79static VALUE sym_status;
80static VALUE sym_cancelled;
81
murgatroid99ec1588b2016-06-06 15:37:45 -070082typedef struct grpc_rb_call {
Craig Tillerbaa14a92017-11-03 09:09:36 -070083 grpc_call* wrapped;
84 grpc_completion_queue* queue;
murgatroid99ec1588b2016-06-06 15:37:45 -070085} grpc_rb_call;
86
Craig Tillerbaa14a92017-11-03 09:09:36 -070087static void destroy_call(grpc_rb_call* call) {
murgatroid9900338652016-06-28 14:14:49 -070088 /* Ensure that we only try to destroy the call once */
89 if (call->wrapped != NULL) {
Craig Tillerdd36b152017-03-31 08:27:28 -070090 grpc_call_unref(call->wrapped);
murgatroid9900338652016-06-28 14:14:49 -070091 call->wrapped = NULL;
92 grpc_rb_completion_queue_destroy(call->queue);
93 call->queue = NULL;
94 }
murgatroid99ec1588b2016-06-06 15:37:45 -070095}
96
nnoble097ef9b2014-12-01 17:06:10 -080097/* Destroys a Call. */
Craig Tillerbaa14a92017-11-03 09:09:36 -070098static void grpc_rb_call_destroy(void* p) {
nnoble097ef9b2014-12-01 17:06:10 -080099 if (p == NULL) {
100 return;
nnoble097ef9b2014-12-01 17:06:10 -0800101 }
Craig Tillerbaa14a92017-11-03 09:09:36 -0700102 destroy_call((grpc_rb_call*)p);
Alexander Polcynd24e9662017-09-11 13:40:12 -0700103 xfree(p);
nnoble097ef9b2014-12-01 17:06:10 -0800104}
105
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900106static const rb_data_type_t grpc_rb_md_ary_data_type = {
107 "grpc_metadata_array",
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700108 {GRPC_RB_GC_NOT_MARKED,
109 GRPC_RB_GC_DONT_FREE,
everysickdfc95152018-03-25 21:21:15 +0900110 GRPC_RB_MEMSIZE_UNAVAILABLE,
murgatroid9987afb5d2015-07-16 16:01:02 -0700111 {NULL, NULL}},
Craig Tiller1a727fd2015-04-24 13:21:22 -0700112 NULL,
113 NULL,
Tim Emiola9161a822015-11-11 15:58:44 -0800114#ifdef RUBY_TYPED_FREE_IMMEDIATELY
115 /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because
116 * grpc_rb_call_destroy
117 * touches a hash object.
118 * TODO(yugui) Directly use st_table and call the free function earlier?
119 */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700120 0,
Tim Emiola9161a822015-11-11 15:58:44 -0800121#endif
122};
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900123
Yuki Yugui Sonoda76801d22015-04-11 14:20:27 +0900124/* Describes grpc_call struct for RTypedData */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700125static const rb_data_type_t grpc_call_data_type = {"grpc_call",
126 {GRPC_RB_GC_NOT_MARKED,
127 grpc_rb_call_destroy,
128 GRPC_RB_MEMSIZE_UNAVAILABLE,
129 {NULL, NULL}},
130 NULL,
131 NULL,
Tim Emiola9161a822015-11-11 15:58:44 -0800132#ifdef RUBY_TYPED_FREE_IMMEDIATELY
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700133 RUBY_TYPED_FREE_IMMEDIATELY
Tim Emiola9161a822015-11-11 15:58:44 -0800134#endif
135};
Yuki Yugui Sonoda76801d22015-04-11 14:20:27 +0900136
nnoble097ef9b2014-12-01 17:06:10 -0800137/* Error code details is a hash containing text strings describing errors */
138VALUE rb_error_code_details;
139
140/* Obtains the error detail string for given error code */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700141const char* grpc_call_error_detail_of(grpc_call_error err) {
nnoble097ef9b2014-12-01 17:06:10 -0800142 VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err));
Craig Tillerbaa14a92017-11-03 09:09:36 -0700143 const char* detail = "unknown error code!";
nnoble097ef9b2014-12-01 17:06:10 -0800144 if (detail_ref != Qnil) {
145 detail = StringValueCStr(detail_ref);
146 }
147 return detail;
148}
149
nnoble097ef9b2014-12-01 17:06:10 -0800150/* Called by clients to cancel an RPC on the server.
151 Can be called multiple times, from any thread. */
152static VALUE grpc_rb_call_cancel(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700153 grpc_rb_call* call = NULL;
nnoble097ef9b2014-12-01 17:06:10 -0800154 grpc_call_error err;
Ken Paysondce1ee62016-05-20 10:29:34 -0700155 if (RTYPEDDATA_DATA(self) == NULL) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700156 // This call has been closed
Ken Paysondce1ee62016-05-20 10:29:34 -0700157 return Qnil;
158 }
159
murgatroid99ec1588b2016-06-06 15:37:45 -0700160 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
161 err = grpc_call_cancel(call->wrapped, NULL);
nnoble097ef9b2014-12-01 17:06:10 -0800162 if (err != GRPC_CALL_OK) {
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900163 rb_raise(grpc_rb_eCallError, "cancel failed: %s (code=%d)",
nnoble097ef9b2014-12-01 17:06:10 -0800164 grpc_call_error_detail_of(err), err);
165 }
166
167 return Qnil;
168}
169
Alexander Polcync6627ca2017-04-13 10:47:16 -0700170/* TODO: expose this as part of the surface API if needed.
171 * This is meant for internal usage by the "write thread" of grpc-ruby
172 * client-side bidi calls. It provides a way for the background write-thread
173 * to propogate failures to the main read-thread and give the user an error
174 * message. */
175static VALUE grpc_rb_call_cancel_with_status(VALUE self, VALUE status_code,
176 VALUE details) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700177 grpc_rb_call* call = NULL;
Alexander Polcync6627ca2017-04-13 10:47:16 -0700178 grpc_call_error err;
179 if (RTYPEDDATA_DATA(self) == NULL) {
180 // This call has been closed
181 return Qnil;
182 }
183
184 if (TYPE(details) != T_STRING || TYPE(status_code) != T_FIXNUM) {
185 rb_raise(rb_eTypeError,
186 "Bad parameter type error for cancel with status. Want Fixnum, "
187 "String.");
188 return Qnil;
189 }
190
191 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
192 err = grpc_call_cancel_with_status(call->wrapped, NUM2LONG(status_code),
193 StringValueCStr(details), NULL);
194 if (err != GRPC_CALL_OK) {
195 rb_raise(grpc_rb_eCallError, "cancel with status failed: %s (code=%d)",
196 grpc_call_error_detail_of(err), err);
197 }
198
199 return Qnil;
200}
201
Ken Paysondce1ee62016-05-20 10:29:34 -0700202/* Releases the c-level resources associated with a call
203 Once a call has been closed, no further requests can be
204 processed.
205*/
206static VALUE grpc_rb_call_close(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700207 grpc_rb_call* call = NULL;
murgatroid99ec1588b2016-06-06 15:37:45 -0700208 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700209 if (call != NULL) {
murgatroid99ec1588b2016-06-06 15:37:45 -0700210 destroy_call(call);
Alexander Polcyn40d30622017-10-17 17:39:57 -0700211 xfree(RTYPEDDATA_DATA(self));
Ken Paysondce1ee62016-05-20 10:29:34 -0700212 RTYPEDDATA_DATA(self) = NULL;
213 }
214 return Qnil;
215}
216
Tim Emiola623a74d2015-08-11 09:24:20 -0700217/* Called to obtain the peer that this call is connected to. */
218static VALUE grpc_rb_call_get_peer(VALUE self) {
219 VALUE res = Qnil;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700220 grpc_rb_call* call = NULL;
221 char* peer = NULL;
Ken Paysondce1ee62016-05-20 10:29:34 -0700222 if (RTYPEDDATA_DATA(self) == NULL) {
223 rb_raise(grpc_rb_eCallError, "Cannot get peer value on closed call");
224 return Qnil;
225 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700226 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
227 peer = grpc_call_get_peer(call->wrapped);
Tim Emiola623a74d2015-08-11 09:24:20 -0700228 res = rb_str_new2(peer);
229 gpr_free(peer);
230
231 return res;
232}
233
Eric Richardson69d735a2016-04-20 13:23:32 -0400234/* Called to obtain the x509 cert of an authenticated peer. */
235static VALUE grpc_rb_call_get_peer_cert(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700236 grpc_rb_call* call = NULL;
Eric Richardson69d735a2016-04-20 13:23:32 -0400237 VALUE res = Qnil;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700238 grpc_auth_context* ctx = NULL;
Ken Paysondce1ee62016-05-20 10:29:34 -0700239 if (RTYPEDDATA_DATA(self) == NULL) {
240 rb_raise(grpc_rb_eCallError, "Cannot get peer cert on closed call");
241 return Qnil;
242 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700243 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Eric Richardson69d735a2016-04-20 13:23:32 -0400244
murgatroid99ec1588b2016-06-06 15:37:45 -0700245 ctx = grpc_call_auth_context(call->wrapped);
Eric Richardson69d735a2016-04-20 13:23:32 -0400246
Eric Richardsonbdfaf482016-04-20 13:56:55 -0400247 if (!ctx || !grpc_auth_context_peer_is_authenticated(ctx)) {
248 return Qnil;
249 }
250
Eric Richardson336b7442016-04-21 14:46:59 -0400251 {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700252 grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(
253 ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700254 const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
Eric Richardson336b7442016-04-21 14:46:59 -0400255 if (prop == NULL) {
256 return Qnil;
257 }
Eric Richardson69d735a2016-04-20 13:23:32 -0400258
Eric Richardson336b7442016-04-21 14:46:59 -0400259 res = rb_str_new2(prop->value);
260 }
Eric Richardson69d735a2016-04-20 13:23:32 -0400261
Eric Richardsonbdfaf482016-04-20 13:56:55 -0400262 grpc_auth_context_release(ctx);
263
Eric Richardson69d735a2016-04-20 13:23:32 -0400264 return res;
265}
266
nnoble097ef9b2014-12-01 17:06:10 -0800267/*
268 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700269 status = call.status
nnoble097ef9b2014-12-01 17:06:10 -0800270
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700271 Gets the status object saved the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800272static VALUE grpc_rb_call_get_status(VALUE self) {
273 return rb_ivar_get(self, id_status);
274}
275
276/*
277 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700278 call.status = status
nnoble097ef9b2014-12-01 17:06:10 -0800279
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700280 Saves a status object on the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800281static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900282 if (!NIL_P(status) && rb_obj_class(status) != grpc_rb_sStatus) {
temiola58327912014-12-15 17:51:16 -0800283 rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>",
nnoble097ef9b2014-12-01 17:06:10 -0800284 rb_obj_classname(status));
285 return Qnil;
286 }
287
288 return rb_ivar_set(self, id_status, status);
289}
290
291/*
292 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700293 metadata = call.metadata
nnoble097ef9b2014-12-01 17:06:10 -0800294
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700295 Gets the metadata object saved the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800296static VALUE grpc_rb_call_get_metadata(VALUE self) {
297 return rb_ivar_get(self, id_metadata);
298}
299
300/*
301 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700302 call.metadata = metadata
nnoble097ef9b2014-12-01 17:06:10 -0800303
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700304 Saves the metadata hash on the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800305static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
306 if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
307 rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
308 rb_obj_classname(metadata));
309 return Qnil;
310 }
311
312 return rb_ivar_set(self, id_metadata, metadata);
313}
314
Tim Emiola7840a552015-08-20 13:12:33 -0700315/*
316 call-seq:
murgatroid99e69f0882016-07-07 15:52:27 -0700317 trailing_metadata = call.trailing_metadata
318
319 Gets the trailing metadata object saved on the call */
320static VALUE grpc_rb_call_get_trailing_metadata(VALUE self) {
321 return rb_ivar_get(self, id_trailing_metadata);
322}
323
324/*
325 call-seq:
326 call.trailing_metadata = trailing_metadata
327
328 Saves the trailing metadata hash on the call. */
329static VALUE grpc_rb_call_set_trailing_metadata(VALUE self, VALUE metadata) {
330 if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
331 rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
332 rb_obj_classname(metadata));
333 return Qnil;
334 }
335
336 return rb_ivar_set(self, id_trailing_metadata, metadata);
337}
338
339/*
340 call-seq:
Tim Emiola7840a552015-08-20 13:12:33 -0700341 write_flag = call.write_flag
342
343 Gets the write_flag value saved the call. */
344static VALUE grpc_rb_call_get_write_flag(VALUE self) {
345 return rb_ivar_get(self, id_write_flag);
346}
347
348/*
349 call-seq:
350 call.write_flag = write_flag
351
352 Saves the write_flag on the call. */
353static VALUE grpc_rb_call_set_write_flag(VALUE self, VALUE write_flag) {
354 if (!NIL_P(write_flag) && TYPE(write_flag) != T_FIXNUM) {
355 rb_raise(rb_eTypeError, "bad write_flag: got:<%s> want: <Fixnum>",
356 rb_obj_classname(write_flag));
357 return Qnil;
358 }
359
360 return rb_ivar_set(self, id_write_flag, write_flag);
361}
362
murgatroid999946f2b2015-12-04 14:36:27 -0800363/*
364 call-seq:
365 call.set_credentials call_credentials
366
367 Sets credentials on a call */
368static VALUE grpc_rb_call_set_credentials(VALUE self, VALUE credentials) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700369 grpc_rb_call* call = NULL;
370 grpc_call_credentials* creds;
murgatroid999946f2b2015-12-04 14:36:27 -0800371 grpc_call_error err;
Ken Paysondce1ee62016-05-20 10:29:34 -0700372 if (RTYPEDDATA_DATA(self) == NULL) {
373 rb_raise(grpc_rb_eCallError, "Cannot set credentials of closed call");
374 return Qnil;
375 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700376 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
murgatroid999946f2b2015-12-04 14:36:27 -0800377 creds = grpc_rb_get_wrapped_call_credentials(credentials);
murgatroid99ec1588b2016-06-06 15:37:45 -0700378 err = grpc_call_set_credentials(call->wrapped, creds);
murgatroid999946f2b2015-12-04 14:36:27 -0800379 if (err != GRPC_CALL_OK) {
380 rb_raise(grpc_rb_eCallError,
381 "grpc_call_set_credentials failed with %s (code=%d)",
382 grpc_call_error_detail_of(err), err);
383 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700384 /* We need the credentials to be alive for as long as the call is alive,
385 but we don't care about destruction order. */
murgatroid998c9edc22016-03-14 15:51:56 -0700386 rb_ivar_set(self, id_credentials, credentials);
murgatroid999946f2b2015-12-04 14:36:27 -0800387 return Qnil;
388}
389
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700390/* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used
391 to fill grpc_metadata_array.
nnoble097ef9b2014-12-01 17:06:10 -0800392
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700393 it's capacity should have been computed via a prior call to
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700394 grpc_rb_md_ary_capacity_hash_cb
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700395*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900396static int grpc_rb_md_ary_fill_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700397 grpc_metadata_array* md_ary = NULL;
Marcin Wyszynskiac91edd2015-07-23 19:59:46 +0200398 long array_length;
Marcin Wyszynskia5fea602015-07-24 10:24:32 +0200399 long i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800400 grpc_slice key_slice;
401 grpc_slice value_slice;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700402 char* tmp_str = NULL;
murgatroid9956fada52016-01-06 14:40:38 -0800403
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800404 if (TYPE(key) == T_SYMBOL) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800405 key_slice = grpc_slice_from_static_string(rb_id2name(SYM2ID(key)));
406 } else if (TYPE(key) == T_STRING) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700407 key_slice =
408 grpc_slice_from_copied_buffer(RSTRING_PTR(key), RSTRING_LEN(key));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800409 } else {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700410 rb_raise(rb_eTypeError,
411 "grpc_rb_md_ary_fill_hash_cb: bad type for key parameter");
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700412 return ST_STOP;
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800413 }
414
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800415 if (!grpc_header_key_is_legal(key_slice)) {
416 tmp_str = grpc_slice_to_c_string(key_slice);
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800417 rb_raise(rb_eArgError,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700418 "'%s' is an invalid header key, must match [a-z0-9-_.]+", tmp_str);
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800419 return ST_STOP;
420 }
421
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700422 /* Construct a metadata object from key and value and add it */
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900423 TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
424 &grpc_rb_md_ary_data_type, md_ary);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700425
426 if (TYPE(val) == T_ARRAY) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700427 array_length = RARRAY_LEN(val);
murgatroid9956fada52016-01-06 14:40:38 -0800428 /* If the value is an array, add capacity for each value in the array */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700429 for (i = 0; i < array_length; i++) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700430 value_slice = grpc_slice_from_copied_buffer(
431 RSTRING_PTR(rb_ary_entry(val, i)), RSTRING_LEN(rb_ary_entry(val, i)));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800432 if (!grpc_is_binary_header(key_slice) &&
433 !grpc_header_nonbin_value_is_legal(value_slice)) {
murgatroid9956fada52016-01-06 14:40:38 -0800434 // The value has invalid characters
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800435 tmp_str = grpc_slice_to_c_string(value_slice);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700436 rb_raise(rb_eArgError, "Header value '%s' has invalid characters",
437 tmp_str);
murgatroid9956fada52016-01-06 14:40:38 -0800438 return ST_STOP;
439 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700440 GPR_ASSERT(md_ary->count < md_ary->capacity);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800441 md_ary->metadata[md_ary->count].key = key_slice;
442 md_ary->metadata[md_ary->count].value = value_slice;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700443 md_ary->count += 1;
444 }
Rafael Salesac491d82016-03-02 02:30:29 -0300445 } else if (TYPE(val) == T_STRING) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700446 value_slice =
447 grpc_slice_from_copied_buffer(RSTRING_PTR(val), RSTRING_LEN(val));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800448 if (!grpc_is_binary_header(key_slice) &&
449 !grpc_header_nonbin_value_is_legal(value_slice)) {
murgatroid9956fada52016-01-06 14:40:38 -0800450 // The value has invalid characters
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800451 tmp_str = grpc_slice_to_c_string(value_slice);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700452 rb_raise(rb_eArgError, "Header value '%s' has invalid characters",
453 tmp_str);
murgatroid9956fada52016-01-06 14:40:38 -0800454 return ST_STOP;
455 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700456 GPR_ASSERT(md_ary->count < md_ary->capacity);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800457 md_ary->metadata[md_ary->count].key = key_slice;
458 md_ary->metadata[md_ary->count].value = value_slice;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700459 md_ary->count += 1;
Rafael Salesac491d82016-03-02 02:30:29 -0300460 } else {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700461 rb_raise(rb_eArgError, "Header values must be of type string or array");
Rafael Salesac491d82016-03-02 02:30:29 -0300462 return ST_STOP;
nnoble097ef9b2014-12-01 17:06:10 -0800463 }
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700464 return ST_CONTINUE;
nnoble097ef9b2014-12-01 17:06:10 -0800465}
466
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700467/* grpc_rb_md_ary_capacity_hash_cb is the hash iteration callback used
468 to pre-compute the capacity a grpc_metadata_array.
469*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900470static int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val,
471 VALUE md_ary_obj) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700472 grpc_metadata_array* md_ary = NULL;
temiola58327912014-12-15 17:51:16 -0800473
murgatroid9987afb5d2015-07-16 16:01:02 -0700474 (void)key;
475
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700476 /* Construct a metadata object from key and value and add it */
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900477 TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
478 &grpc_rb_md_ary_data_type, md_ary);
temiola58327912014-12-15 17:51:16 -0800479
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700480 if (TYPE(val) == T_ARRAY) {
481 /* If the value is an array, add capacity for each value in the array */
482 md_ary->capacity += RARRAY_LEN(val);
483 } else {
484 md_ary->capacity += 1;
nnoble097ef9b2014-12-01 17:06:10 -0800485 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700486
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700487 return ST_CONTINUE;
nnoble097ef9b2014-12-01 17:06:10 -0800488}
489
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700490/* grpc_rb_md_ary_convert converts a ruby metadata hash into
491 a grpc_metadata_array.
492*/
Craig Tillerbaa14a92017-11-03 09:09:36 -0700493void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array* md_ary) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700494 VALUE md_ary_obj = Qnil;
495 if (md_ary_hash == Qnil) {
Craig Tiller1a727fd2015-04-24 13:21:22 -0700496 return; /* Do nothing if the expected has value is nil */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700497 }
498 if (TYPE(md_ary_hash) != T_HASH) {
499 rb_raise(rb_eTypeError, "md_ary_convert: got <%s>, want <Hash>",
500 rb_obj_classname(md_ary_hash));
501 return;
nnoble097ef9b2014-12-01 17:06:10 -0800502 }
503
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700504 /* Initialize the array, compute it's capacity, then fill it. */
505 grpc_metadata_array_init(md_ary);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700506 md_ary_obj =
507 TypedData_Wrap_Struct(grpc_rb_cMdAry, &grpc_rb_md_ary_data_type, md_ary);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700508 rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj);
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700509 md_ary->metadata = gpr_zalloc(md_ary->capacity * sizeof(grpc_metadata));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700510 rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj);
511}
512
513/* Converts a metadata array to a hash. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700514VALUE grpc_rb_md_ary_to_h(grpc_metadata_array* md_ary) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700515 VALUE key = Qnil;
516 VALUE new_ary = Qnil;
517 VALUE value = Qnil;
518 VALUE result = rb_hash_new();
519 size_t i;
520
521 for (i = 0; i < md_ary->count; i++) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800522 key = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].key);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700523 value = rb_hash_aref(result, key);
524 if (value == Qnil) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800525 value = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700526 rb_hash_aset(result, key, value);
527 } else if (TYPE(value) == T_ARRAY) {
528 /* Add the string to the returned array */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700529 rb_ary_push(value,
530 grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700531 } else {
532 /* Add the current value with this key and the new one to an array */
533 new_ary = rb_ary_new();
534 rb_ary_push(new_ary, value);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700535 rb_ary_push(new_ary,
536 grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700537 rb_hash_aset(result, key, new_ary);
538 }
539 }
540 return result;
541}
542
543/* grpc_rb_call_check_op_keys_hash_cb is a hash iteration func that checks
544 each key of an ops hash is valid.
545*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900546static int grpc_rb_call_check_op_keys_hash_cb(VALUE key, VALUE val,
547 VALUE ops_ary) {
murgatroid9987afb5d2015-07-16 16:01:02 -0700548 (void)val;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700549 /* Update the capacity; the value is an array, add capacity for each value in
550 * the array */
551 if (TYPE(key) != T_FIXNUM) {
552 rb_raise(rb_eTypeError, "invalid operation : got <%s>, want <Fixnum>",
553 rb_obj_classname(key));
554 return ST_STOP;
555 }
Craig Tiller1a727fd2015-04-24 13:21:22 -0700556 switch (NUM2INT(key)) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700557 case GRPC_OP_SEND_INITIAL_METADATA:
558 case GRPC_OP_SEND_MESSAGE:
559 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
560 case GRPC_OP_SEND_STATUS_FROM_SERVER:
561 case GRPC_OP_RECV_INITIAL_METADATA:
562 case GRPC_OP_RECV_MESSAGE:
563 case GRPC_OP_RECV_STATUS_ON_CLIENT:
564 case GRPC_OP_RECV_CLOSE_ON_SERVER:
565 rb_ary_push(ops_ary, key);
566 return ST_CONTINUE;
567 default:
Craig Tiller1a727fd2015-04-24 13:21:22 -0700568 rb_raise(rb_eTypeError, "invalid operation : bad value %d", NUM2INT(key));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700569 };
570 return ST_STOP;
571}
572
573/* grpc_rb_op_update_status_from_server adds the values in a ruby status
574 struct to the 'send_status_from_server' portion of an op.
575*/
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700576static void grpc_rb_op_update_status_from_server(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700577 grpc_op* op, grpc_metadata_array* md_ary, grpc_slice* send_status_details,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700578 VALUE status) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700579 VALUE code = rb_struct_aref(status, sym_code);
580 VALUE details = rb_struct_aref(status, sym_details);
581 VALUE metadata_hash = rb_struct_aref(status, sym_metadata);
582
583 /* TODO: add check to ensure status is the correct struct type */
584 if (TYPE(code) != T_FIXNUM) {
585 rb_raise(rb_eTypeError, "invalid code : got <%s>, want <Fixnum>",
586 rb_obj_classname(code));
587 return;
588 }
589 if (TYPE(details) != T_STRING) {
590 rb_raise(rb_eTypeError, "invalid details : got <%s>, want <String>",
591 rb_obj_classname(code));
592 return;
593 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800594
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700595 *send_status_details =
596 grpc_slice_from_copied_buffer(RSTRING_PTR(details), RSTRING_LEN(details));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800597
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700598 op->data.send_status_from_server.status = NUM2INT(code);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800599 op->data.send_status_from_server.status_details = send_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700600 grpc_rb_md_ary_convert(metadata_hash, md_ary);
601 op->data.send_status_from_server.trailing_metadata_count = md_ary->count;
602 op->data.send_status_from_server.trailing_metadata = md_ary->metadata;
603}
604
605/* run_batch_stack holds various values used by the
606 * grpc_rb_call_run_batch function */
607typedef struct run_batch_stack {
608 /* The batch ops */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700609 grpc_op ops[8]; /* 8 is the maximum number of operations */
610 size_t op_num; /* tracks the last added operation */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700611
612 /* Data being sent */
613 grpc_metadata_array send_metadata;
614 grpc_metadata_array send_trailing_metadata;
615
616 /* Data being received */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700617 grpc_byte_buffer* recv_message;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700618 grpc_metadata_array recv_metadata;
619 grpc_metadata_array recv_trailing_metadata;
620 int recv_cancelled;
621 grpc_status_code recv_status;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800622 grpc_slice recv_status_details;
Nicolas "Pixel" Noble7b92db62016-01-26 22:46:34 +0100623 unsigned write_flag;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800624 grpc_slice send_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700625} run_batch_stack;
626
627/* grpc_run_batch_stack_init ensures the run_batch_stack is properly
628 * initialized */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700629static void grpc_run_batch_stack_init(run_batch_stack* st,
David Garcia Quintasb4e51b52016-01-27 19:57:58 -0800630 unsigned write_flag) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700631 MEMZERO(st, run_batch_stack, 1);
632 grpc_metadata_array_init(&st->send_metadata);
633 grpc_metadata_array_init(&st->send_trailing_metadata);
634 grpc_metadata_array_init(&st->recv_metadata);
635 grpc_metadata_array_init(&st->recv_trailing_metadata);
636 st->op_num = 0;
Tim Emiola7840a552015-08-20 13:12:33 -0700637 st->write_flag = write_flag;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700638}
639
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700640void grpc_rb_metadata_array_destroy_including_entries(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700641 grpc_metadata_array* array) {
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700642 size_t i;
643 if (array->metadata) {
644 for (i = 0; i < array->count; i++) {
645 grpc_slice_unref(array->metadata[i].key);
646 grpc_slice_unref(array->metadata[i].value);
647 }
648 }
649 grpc_metadata_array_destroy(array);
650}
651
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700652/* grpc_run_batch_stack_cleanup ensures the run_batch_stack is properly
653 * cleaned up */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700654static void grpc_run_batch_stack_cleanup(run_batch_stack* st) {
Zhuochun97daf352016-03-13 16:19:56 +0800655 size_t i = 0;
656
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700657 grpc_rb_metadata_array_destroy_including_entries(&st->send_metadata);
658 grpc_rb_metadata_array_destroy_including_entries(&st->send_trailing_metadata);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700659 grpc_metadata_array_destroy(&st->recv_metadata);
660 grpc_metadata_array_destroy(&st->recv_trailing_metadata);
Zhuochun97daf352016-03-13 16:19:56 +0800661
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800662 if (GRPC_SLICE_START_PTR(st->send_status_details) != NULL) {
663 grpc_slice_unref(st->send_status_details);
664 }
665
666 if (GRPC_SLICE_START_PTR(st->recv_status_details) != NULL) {
667 grpc_slice_unref(st->recv_status_details);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700668 }
Zhuochun97daf352016-03-13 16:19:56 +0800669
670 if (st->recv_message != NULL) {
671 grpc_byte_buffer_destroy(st->recv_message);
672 }
673
674 for (i = 0; i < st->op_num; i++) {
675 if (st->ops[i].op == GRPC_OP_SEND_MESSAGE) {
Mark D. Roth448c1f02017-01-25 10:44:30 -0800676 grpc_byte_buffer_destroy(st->ops[i].data.send_message.send_message);
Zhuochun97daf352016-03-13 16:19:56 +0800677 }
678 }
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700679}
680
681/* grpc_run_batch_stack_fill_ops fills the run_batch_stack ops array from
682 * ops_hash */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700683static void grpc_run_batch_stack_fill_ops(run_batch_stack* st, VALUE ops_hash) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700684 VALUE this_op = Qnil;
685 VALUE this_value = Qnil;
686 VALUE ops_ary = rb_ary_new();
687 size_t i = 0;
688
689 /* Create a ruby array with just the operation keys */
690 rb_hash_foreach(ops_hash, grpc_rb_call_check_op_keys_hash_cb, ops_ary);
691
692 /* Fill the ops array */
693 for (i = 0; i < (size_t)RARRAY_LEN(ops_ary); i++) {
694 this_op = rb_ary_entry(ops_ary, i);
695 this_value = rb_hash_aref(ops_hash, this_op);
Tim Emiola7840a552015-08-20 13:12:33 -0700696 st->ops[st->op_num].flags = 0;
Craig Tiller1a727fd2015-04-24 13:21:22 -0700697 switch (NUM2INT(this_op)) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700698 case GRPC_OP_SEND_INITIAL_METADATA:
699 grpc_rb_md_ary_convert(this_value, &st->send_metadata);
700 st->ops[st->op_num].data.send_initial_metadata.count =
701 st->send_metadata.count;
702 st->ops[st->op_num].data.send_initial_metadata.metadata =
703 st->send_metadata.metadata;
704 break;
705 case GRPC_OP_SEND_MESSAGE:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800706 st->ops[st->op_num].data.send_message.send_message =
707 grpc_rb_s_to_byte_buffer(RSTRING_PTR(this_value),
708 RSTRING_LEN(this_value));
Tim Emiola7840a552015-08-20 13:12:33 -0700709 st->ops[st->op_num].flags = st->write_flag;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700710 break;
711 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
712 break;
713 case GRPC_OP_SEND_STATUS_FROM_SERVER:
Craig Tiller1a727fd2015-04-24 13:21:22 -0700714 grpc_rb_op_update_status_from_server(
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700715 &st->ops[st->op_num], &st->send_trailing_metadata,
716 &st->send_status_details, this_value);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700717 break;
718 case GRPC_OP_RECV_INITIAL_METADATA:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800719 st->ops[st->op_num].data.recv_initial_metadata.recv_initial_metadata =
720 &st->recv_metadata;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700721 break;
722 case GRPC_OP_RECV_MESSAGE:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800723 st->ops[st->op_num].data.recv_message.recv_message = &st->recv_message;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700724 break;
725 case GRPC_OP_RECV_STATUS_ON_CLIENT:
726 st->ops[st->op_num].data.recv_status_on_client.trailing_metadata =
727 &st->recv_trailing_metadata;
728 st->ops[st->op_num].data.recv_status_on_client.status =
729 &st->recv_status;
730 st->ops[st->op_num].data.recv_status_on_client.status_details =
731 &st->recv_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700732 break;
733 case GRPC_OP_RECV_CLOSE_ON_SERVER:
734 st->ops[st->op_num].data.recv_close_on_server.cancelled =
735 &st->recv_cancelled;
736 break;
737 default:
738 grpc_run_batch_stack_cleanup(st);
739 rb_raise(rb_eTypeError, "invalid operation : bad value %d",
740 NUM2INT(this_op));
741 };
742 st->ops[st->op_num].op = (grpc_op_type)NUM2INT(this_op);
Craig Tiller42758992015-08-18 10:34:32 -0700743 st->ops[st->op_num].reserved = NULL;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700744 st->op_num++;
745 }
746}
747
748/* grpc_run_batch_stack_build_result fills constructs a ruby BatchResult struct
749 after the results have run */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700750static VALUE grpc_run_batch_stack_build_result(run_batch_stack* st) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700751 size_t i = 0;
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900752 VALUE result = rb_struct_new(grpc_rb_sBatchResult, Qnil, Qnil, Qnil, Qnil,
753 Qnil, Qnil, Qnil, Qnil, NULL);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700754 for (i = 0; i < st->op_num; i++) {
Craig Tiller1a727fd2015-04-24 13:21:22 -0700755 switch (st->ops[i].op) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700756 case GRPC_OP_SEND_INITIAL_METADATA:
757 rb_struct_aset(result, sym_send_metadata, Qtrue);
758 break;
759 case GRPC_OP_SEND_MESSAGE:
760 rb_struct_aset(result, sym_send_message, Qtrue);
761 break;
762 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
763 rb_struct_aset(result, sym_send_close, Qtrue);
764 break;
765 case GRPC_OP_SEND_STATUS_FROM_SERVER:
766 rb_struct_aset(result, sym_send_status, Qtrue);
767 break;
768 case GRPC_OP_RECV_INITIAL_METADATA:
769 rb_struct_aset(result, sym_metadata,
770 grpc_rb_md_ary_to_h(&st->recv_metadata));
771 case GRPC_OP_RECV_MESSAGE:
772 rb_struct_aset(result, sym_message,
773 grpc_rb_byte_buffer_to_s(st->recv_message));
774 break;
775 case GRPC_OP_RECV_STATUS_ON_CLIENT:
776 rb_struct_aset(
Craig Tiller1a727fd2015-04-24 13:21:22 -0700777 result, sym_status,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700778 rb_struct_new(
779 grpc_rb_sStatus, UINT2NUM(st->recv_status),
780 (GRPC_SLICE_START_PTR(st->recv_status_details) == NULL
781 ? Qnil
782 : grpc_rb_slice_to_ruby_string(st->recv_status_details)),
783 grpc_rb_md_ary_to_h(&st->recv_trailing_metadata), NULL));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700784 break;
785 case GRPC_OP_RECV_CLOSE_ON_SERVER:
786 rb_struct_aset(result, sym_send_close, Qtrue);
787 break;
788 default:
789 break;
790 }
791 }
792 return result;
nnoble097ef9b2014-12-01 17:06:10 -0800793}
794
795/* call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700796 ops = {
797 GRPC::Core::CallOps::SEND_INITIAL_METADATA => <op_value>,
798 GRPC::Core::CallOps::SEND_MESSAGE => <op_value>,
799 ...
800 }
801 tag = Object.new
802 timeout = 10
murgatroid99ec1588b2016-06-06 15:37:45 -0700803 call.start_batch(tag, timeout, ops)
nnoble097ef9b2014-12-01 17:06:10 -0800804
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700805 Start a batch of operations defined in the array ops; when complete, post a
806 completion of type 'tag' to the completion queue bound to the call.
temiola71bb1372014-12-11 11:27:25 -0800807
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700808 Also waits for the batch to complete, until timeout is reached.
809 The order of ops specified in the batch has no significance.
810 Only one operation of each type can be active at once in any given
811 batch */
murgatroid99ec1588b2016-06-06 15:37:45 -0700812static VALUE grpc_rb_call_run_batch(VALUE self, VALUE ops_hash) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700813 run_batch_stack* st = NULL;
814 grpc_rb_call* call = NULL;
Craig Tillerc7df2b82015-05-11 11:12:18 -0700815 grpc_event ev;
nnoble097ef9b2014-12-01 17:06:10 -0800816 grpc_call_error err;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700817 VALUE result = Qnil;
Tim Emiola7840a552015-08-20 13:12:33 -0700818 VALUE rb_write_flag = rb_ivar_get(self, id_write_flag);
Nicolas "Pixel" Noble7b92db62016-01-26 22:46:34 +0100819 unsigned write_flag = 0;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700820 void* tag = (void*)&st;
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700821
Ken Paysondce1ee62016-05-20 10:29:34 -0700822 if (RTYPEDDATA_DATA(self) == NULL) {
823 rb_raise(grpc_rb_eCallError, "Cannot run batch on closed call");
824 return Qnil;
825 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700826 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700827
828 /* Validate the ops args, adding them to a ruby array */
829 if (TYPE(ops_hash) != T_HASH) {
830 rb_raise(rb_eTypeError, "call#run_batch: ops hash should be a hash");
831 return Qnil;
temiola71bb1372014-12-11 11:27:25 -0800832 }
Tim Emiola7840a552015-08-20 13:12:33 -0700833 if (rb_write_flag != Qnil) {
834 write_flag = NUM2UINT(rb_write_flag);
835 }
Alexander Polcyn8d8dce82017-03-24 10:32:15 -0700836 st = gpr_malloc(sizeof(run_batch_stack));
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700837 grpc_run_batch_stack_init(st, write_flag);
838 grpc_run_batch_stack_fill_ops(st, ops_hash);
temiola71bb1372014-12-11 11:27:25 -0800839
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700840 /* call grpc_call_start_batch, then wait for it to complete using
841 * pluck_event */
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700842 err = grpc_call_start_batch(call->wrapped, st->ops, st->op_num, tag, NULL);
temiola71bb1372014-12-11 11:27:25 -0800843 if (err != GRPC_CALL_OK) {
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700844 grpc_run_batch_stack_cleanup(st);
845 gpr_free(st);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900846 rb_raise(grpc_rb_eCallError,
847 "grpc_call_start_batch failed with %s (code=%d)",
nnoble097ef9b2014-12-01 17:06:10 -0800848 grpc_call_error_detail_of(err), err);
Tim Emiola932b13b2015-04-24 08:52:46 -0700849 return Qnil;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700850 }
murgatroid995ea4a992016-06-13 10:36:41 -0700851 ev = rb_completion_queue_pluck(call->queue, tag,
852 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
853 if (!ev.success) {
854 rb_raise(grpc_rb_eCallError, "call#run_batch failed somehow");
855 }
Tim Emiolacdb7ccc2015-08-13 21:45:04 -0700856 /* Build and return the BatchResult struct result,
857 if there is an error, it's reflected in the status */
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700858 result = grpc_run_batch_stack_build_result(st);
859 grpc_run_batch_stack_cleanup(st);
860 gpr_free(st);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700861 return result;
nnoble097ef9b2014-12-01 17:06:10 -0800862}
863
Tim Emiola7840a552015-08-20 13:12:33 -0700864static void Init_grpc_write_flags() {
865 /* Constants representing the write flags in grpc.h */
866 VALUE grpc_rb_mWriteFlags =
867 rb_define_module_under(grpc_rb_mGrpcCore, "WriteFlags");
868 rb_define_const(grpc_rb_mWriteFlags, "BUFFER_HINT",
869 UINT2NUM(GRPC_WRITE_BUFFER_HINT));
870 rb_define_const(grpc_rb_mWriteFlags, "NO_COMPRESS",
871 UINT2NUM(GRPC_WRITE_NO_COMPRESS));
872}
873
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900874static void Init_grpc_error_codes() {
nnoble097ef9b2014-12-01 17:06:10 -0800875 /* Constants representing the error codes of grpc_call_error in grpc.h */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900876 VALUE grpc_rb_mRpcErrors =
877 rb_define_module_under(grpc_rb_mGrpcCore, "RpcErrors");
878 rb_define_const(grpc_rb_mRpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
879 rb_define_const(grpc_rb_mRpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
880 rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_SERVER",
nnoble097ef9b2014-12-01 17:06:10 -0800881 UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900882 rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_CLIENT",
nnoble097ef9b2014-12-01 17:06:10 -0800883 UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900884 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_ACCEPTED",
temiola71bb1372014-12-11 11:27:25 -0800885 UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900886 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_INVOKED",
nnoble097ef9b2014-12-01 17:06:10 -0800887 UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900888 rb_define_const(grpc_rb_mRpcErrors, "NOT_INVOKED",
nnoble097ef9b2014-12-01 17:06:10 -0800889 UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900890 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_FINISHED",
nnoble097ef9b2014-12-01 17:06:10 -0800891 UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900892 rb_define_const(grpc_rb_mRpcErrors, "TOO_MANY_OPERATIONS",
nnoble097ef9b2014-12-01 17:06:10 -0800893 UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900894 rb_define_const(grpc_rb_mRpcErrors, "INVALID_FLAGS",
nnoble097ef9b2014-12-01 17:06:10 -0800895 UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS));
896
Andre Medeirosb0eae8a2017-08-21 15:50:36 -0400897 /* Hint the GC that this is a global and shouldn't be sweeped. */
898 rb_global_variable(&rb_error_code_details);
899
nnoble097ef9b2014-12-01 17:06:10 -0800900 /* Add the detail strings to a Hash */
901 rb_error_code_details = rb_hash_new();
Craig Tillerb5dcec52015-01-13 11:13:42 -0800902 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_OK),
903 rb_str_new2("ok"));
nnoble097ef9b2014-12-01 17:06:10 -0800904 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR),
905 rb_str_new2("unknown error"));
906 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER),
907 rb_str_new2("not available on a server"));
908 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT),
909 rb_str_new2("not available on a client"));
temiola71bb1372014-12-11 11:27:25 -0800910 rb_hash_aset(rb_error_code_details,
911 UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED),
912 rb_str_new2("call is already accepted"));
nnoble097ef9b2014-12-01 17:06:10 -0800913 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED),
914 rb_str_new2("call is already invoked"));
915 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED),
916 rb_str_new2("call is not yet invoked"));
917 rb_hash_aset(rb_error_code_details,
918 UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED),
919 rb_str_new2("call is already finished"));
920 rb_hash_aset(rb_error_code_details,
921 UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS),
922 rb_str_new2("outstanding read or write present"));
923 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS),
924 rb_str_new2("a bad flag was given"));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900925 rb_define_const(grpc_rb_mRpcErrors, "ErrorMessages", rb_error_code_details);
nnoble097ef9b2014-12-01 17:06:10 -0800926 rb_obj_freeze(rb_error_code_details);
927}
928
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900929static void Init_grpc_op_codes() {
Tim Emiola37b09f42015-03-27 13:39:16 -0700930 /* Constants representing operation type codes in grpc.h */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700931 VALUE grpc_rb_mCallOps = rb_define_module_under(grpc_rb_mGrpcCore, "CallOps");
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900932 rb_define_const(grpc_rb_mCallOps, "SEND_INITIAL_METADATA",
Tim Emiola37b09f42015-03-27 13:39:16 -0700933 UINT2NUM(GRPC_OP_SEND_INITIAL_METADATA));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900934 rb_define_const(grpc_rb_mCallOps, "SEND_MESSAGE",
935 UINT2NUM(GRPC_OP_SEND_MESSAGE));
936 rb_define_const(grpc_rb_mCallOps, "SEND_CLOSE_FROM_CLIENT",
Tim Emiola37b09f42015-03-27 13:39:16 -0700937 UINT2NUM(GRPC_OP_SEND_CLOSE_FROM_CLIENT));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900938 rb_define_const(grpc_rb_mCallOps, "SEND_STATUS_FROM_SERVER",
Tim Emiola37b09f42015-03-27 13:39:16 -0700939 UINT2NUM(GRPC_OP_SEND_STATUS_FROM_SERVER));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900940 rb_define_const(grpc_rb_mCallOps, "RECV_INITIAL_METADATA",
Tim Emiola37b09f42015-03-27 13:39:16 -0700941 UINT2NUM(GRPC_OP_RECV_INITIAL_METADATA));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900942 rb_define_const(grpc_rb_mCallOps, "RECV_MESSAGE",
Tim Emiola37b09f42015-03-27 13:39:16 -0700943 UINT2NUM(GRPC_OP_RECV_MESSAGE));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900944 rb_define_const(grpc_rb_mCallOps, "RECV_STATUS_ON_CLIENT",
Tim Emiola37b09f42015-03-27 13:39:16 -0700945 UINT2NUM(GRPC_OP_RECV_STATUS_ON_CLIENT));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900946 rb_define_const(grpc_rb_mCallOps, "RECV_CLOSE_ON_SERVER",
Tim Emiola37b09f42015-03-27 13:39:16 -0700947 UINT2NUM(GRPC_OP_RECV_CLOSE_ON_SERVER));
948}
949
Alexander Polcyncdff92f2016-07-12 12:30:30 -0700950static void Init_grpc_metadata_keys() {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700951 VALUE grpc_rb_mMetadataKeys =
952 rb_define_module_under(grpc_rb_mGrpcCore, "MetadataKeys");
Alexander Polcyncdff92f2016-07-12 12:30:30 -0700953 rb_define_const(grpc_rb_mMetadataKeys, "COMPRESSION_REQUEST_ALGORITHM",
954 rb_str_new2(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY));
955}
956
Tim Emiola409e6c82015-02-17 17:46:35 -0800957void Init_grpc_call() {
nnoble097ef9b2014-12-01 17:06:10 -0800958 /* CallError inherits from Exception to signal that it is non-recoverable */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900959 grpc_rb_eCallError =
960 rb_define_class_under(grpc_rb_mGrpcCore, "CallError", rb_eException);
961 grpc_rb_eOutOfTime =
962 rb_define_class_under(grpc_rb_mGrpcCore, "OutOfTime", rb_eException);
963 grpc_rb_cCall = rb_define_class_under(grpc_rb_mGrpcCore, "Call", rb_cObject);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700964 grpc_rb_cMdAry =
965 rb_define_class_under(grpc_rb_mGrpcCore, "MetadataArray", rb_cObject);
nnoble097ef9b2014-12-01 17:06:10 -0800966
967 /* Prevent allocation or inialization of the Call class */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900968 rb_define_alloc_func(grpc_rb_cCall, grpc_rb_cannot_alloc);
969 rb_define_method(grpc_rb_cCall, "initialize", grpc_rb_cannot_init, 0);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700970 rb_define_method(grpc_rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy,
971 1);
nnoble097ef9b2014-12-01 17:06:10 -0800972
973 /* Add ruby analogues of the Call methods. */
murgatroid99ec1588b2016-06-06 15:37:45 -0700974 rb_define_method(grpc_rb_cCall, "run_batch", grpc_rb_call_run_batch, 1);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900975 rb_define_method(grpc_rb_cCall, "cancel", grpc_rb_call_cancel, 0);
Alexander Polcync6627ca2017-04-13 10:47:16 -0700976 rb_define_method(grpc_rb_cCall, "cancel_with_status",
977 grpc_rb_call_cancel_with_status, 2);
Ken Paysondce1ee62016-05-20 10:29:34 -0700978 rb_define_method(grpc_rb_cCall, "close", grpc_rb_call_close, 0);
Tim Emiola623a74d2015-08-11 09:24:20 -0700979 rb_define_method(grpc_rb_cCall, "peer", grpc_rb_call_get_peer, 0);
Eric Richardson69d735a2016-04-20 13:23:32 -0400980 rb_define_method(grpc_rb_cCall, "peer_cert", grpc_rb_call_get_peer_cert, 0);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900981 rb_define_method(grpc_rb_cCall, "status", grpc_rb_call_get_status, 0);
982 rb_define_method(grpc_rb_cCall, "status=", grpc_rb_call_set_status, 1);
983 rb_define_method(grpc_rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
984 rb_define_method(grpc_rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
murgatroid99e69f0882016-07-07 15:52:27 -0700985 rb_define_method(grpc_rb_cCall, "trailing_metadata",
986 grpc_rb_call_get_trailing_metadata, 0);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700987 rb_define_method(grpc_rb_cCall,
988 "trailing_metadata=", grpc_rb_call_set_trailing_metadata, 1);
Tim Emiola7840a552015-08-20 13:12:33 -0700989 rb_define_method(grpc_rb_cCall, "write_flag", grpc_rb_call_get_write_flag, 0);
990 rb_define_method(grpc_rb_cCall, "write_flag=", grpc_rb_call_set_write_flag,
991 1);
murgatroid999946f2b2015-12-04 14:36:27 -0800992 rb_define_method(grpc_rb_cCall, "set_credentials!",
993 grpc_rb_call_set_credentials, 1);
nnoble097ef9b2014-12-01 17:06:10 -0800994
995 /* Ids used to support call attributes */
996 id_metadata = rb_intern("metadata");
murgatroid99e69f0882016-07-07 15:52:27 -0700997 id_trailing_metadata = rb_intern("trailing_metadata");
nnoble097ef9b2014-12-01 17:06:10 -0800998 id_status = rb_intern("status");
Tim Emiola7840a552015-08-20 13:12:33 -0700999 id_write_flag = rb_intern("write_flag");
nnoble097ef9b2014-12-01 17:06:10 -08001000
1001 /* Ids used by the c wrapping internals. */
murgatroid998c9edc22016-03-14 15:51:56 -07001002 id_credentials = rb_intern("__credentials");
nnoble097ef9b2014-12-01 17:06:10 -08001003
Tim Emiola1dae4ac2015-03-27 17:17:37 -07001004 /* Ids used in constructing the batch result. */
1005 sym_send_message = ID2SYM(rb_intern("send_message"));
1006 sym_send_metadata = ID2SYM(rb_intern("send_metadata"));
1007 sym_send_close = ID2SYM(rb_intern("send_close"));
1008 sym_send_status = ID2SYM(rb_intern("send_status"));
1009 sym_message = ID2SYM(rb_intern("message"));
1010 sym_status = ID2SYM(rb_intern("status"));
1011 sym_cancelled = ID2SYM(rb_intern("cancelled"));
1012
1013 /* The Struct used to return the run_batch result. */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +09001014 grpc_rb_sBatchResult = rb_struct_define(
Craig Tiller1a727fd2015-04-24 13:21:22 -07001015 "BatchResult", "send_message", "send_metadata", "send_close",
1016 "send_status", "message", "metadata", "status", "cancelled", NULL);
Tim Emiola1dae4ac2015-03-27 17:17:37 -07001017
Tim Emiola409e6c82015-02-17 17:46:35 -08001018 Init_grpc_error_codes();
Tim Emiola37b09f42015-03-27 13:39:16 -07001019 Init_grpc_op_codes();
Tim Emiola7840a552015-08-20 13:12:33 -07001020 Init_grpc_write_flags();
Alexander Polcyncdff92f2016-07-12 12:30:30 -07001021 Init_grpc_metadata_keys();
nnoble097ef9b2014-12-01 17:06:10 -08001022}
1023
1024/* Gets the call from the ruby object */
Craig Tillerbaa14a92017-11-03 09:09:36 -07001025grpc_call* grpc_rb_get_wrapped_call(VALUE v) {
1026 grpc_rb_call* call = NULL;
murgatroid99ec1588b2016-06-06 15:37:45 -07001027 TypedData_Get_Struct(v, grpc_rb_call, &grpc_call_data_type, call);
1028 return call->wrapped;
nnoble097ef9b2014-12-01 17:06:10 -08001029}
1030
1031/* Obtains the wrapped object for a given call */
Craig Tillerbaa14a92017-11-03 09:09:36 -07001032VALUE grpc_rb_wrap_call(grpc_call* c, grpc_completion_queue* q) {
1033 grpc_rb_call* wrapper;
murgatroid99ec1588b2016-06-06 15:37:45 -07001034 if (c == NULL || q == NULL) {
nnoble097ef9b2014-12-01 17:06:10 -08001035 return Qnil;
1036 }
murgatroid995ea4a992016-06-13 10:36:41 -07001037 wrapper = ALLOC(grpc_rb_call);
murgatroid99ec1588b2016-06-06 15:37:45 -07001038 wrapper->wrapped = c;
1039 wrapper->queue = q;
1040 return TypedData_Wrap_Struct(grpc_rb_cCall, &grpc_call_data_type, wrapper);
nnoble097ef9b2014-12-01 17:06:10 -08001041}