blob: 7f3ca2a8e70eb24e9c9191703d2e0ff6bb994f33 [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
Craig Tillerbaa14a92017-11-03 09:09:36 -0700106static size_t md_ary_datasize(const void* p) {
107 const grpc_metadata_array* const ary = (grpc_metadata_array*)p;
Craig Tiller1a727fd2015-04-24 13:21:22 -0700108 size_t i, datasize = sizeof(grpc_metadata_array);
109 for (i = 0; i < ary->count; ++i) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700110 const grpc_metadata* const md = &ary->metadata[i];
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800111 datasize += GRPC_SLICE_LENGTH(md->key);
112 datasize += GRPC_SLICE_LENGTH(md->value);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700113 }
114 datasize += ary->capacity * sizeof(grpc_metadata);
115 return datasize;
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900116}
117
118static const rb_data_type_t grpc_rb_md_ary_data_type = {
119 "grpc_metadata_array",
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700120 {GRPC_RB_GC_NOT_MARKED,
121 GRPC_RB_GC_DONT_FREE,
122 md_ary_datasize,
murgatroid9987afb5d2015-07-16 16:01:02 -0700123 {NULL, NULL}},
Craig Tiller1a727fd2015-04-24 13:21:22 -0700124 NULL,
125 NULL,
Tim Emiola9161a822015-11-11 15:58:44 -0800126#ifdef RUBY_TYPED_FREE_IMMEDIATELY
127 /* it is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because
128 * grpc_rb_call_destroy
129 * touches a hash object.
130 * TODO(yugui) Directly use st_table and call the free function earlier?
131 */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700132 0,
Tim Emiola9161a822015-11-11 15:58:44 -0800133#endif
134};
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900135
Yuki Yugui Sonoda76801d22015-04-11 14:20:27 +0900136/* Describes grpc_call struct for RTypedData */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700137static const rb_data_type_t grpc_call_data_type = {"grpc_call",
138 {GRPC_RB_GC_NOT_MARKED,
139 grpc_rb_call_destroy,
140 GRPC_RB_MEMSIZE_UNAVAILABLE,
141 {NULL, NULL}},
142 NULL,
143 NULL,
Tim Emiola9161a822015-11-11 15:58:44 -0800144#ifdef RUBY_TYPED_FREE_IMMEDIATELY
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700145 RUBY_TYPED_FREE_IMMEDIATELY
Tim Emiola9161a822015-11-11 15:58:44 -0800146#endif
147};
Yuki Yugui Sonoda76801d22015-04-11 14:20:27 +0900148
nnoble097ef9b2014-12-01 17:06:10 -0800149/* Error code details is a hash containing text strings describing errors */
150VALUE rb_error_code_details;
151
152/* Obtains the error detail string for given error code */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700153const char* grpc_call_error_detail_of(grpc_call_error err) {
nnoble097ef9b2014-12-01 17:06:10 -0800154 VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err));
Craig Tillerbaa14a92017-11-03 09:09:36 -0700155 const char* detail = "unknown error code!";
nnoble097ef9b2014-12-01 17:06:10 -0800156 if (detail_ref != Qnil) {
157 detail = StringValueCStr(detail_ref);
158 }
159 return detail;
160}
161
nnoble097ef9b2014-12-01 17:06:10 -0800162/* Called by clients to cancel an RPC on the server.
163 Can be called multiple times, from any thread. */
164static VALUE grpc_rb_call_cancel(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700165 grpc_rb_call* call = NULL;
nnoble097ef9b2014-12-01 17:06:10 -0800166 grpc_call_error err;
Ken Paysondce1ee62016-05-20 10:29:34 -0700167 if (RTYPEDDATA_DATA(self) == NULL) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700168 // This call has been closed
Ken Paysondce1ee62016-05-20 10:29:34 -0700169 return Qnil;
170 }
171
murgatroid99ec1588b2016-06-06 15:37:45 -0700172 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
173 err = grpc_call_cancel(call->wrapped, NULL);
nnoble097ef9b2014-12-01 17:06:10 -0800174 if (err != GRPC_CALL_OK) {
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900175 rb_raise(grpc_rb_eCallError, "cancel failed: %s (code=%d)",
nnoble097ef9b2014-12-01 17:06:10 -0800176 grpc_call_error_detail_of(err), err);
177 }
178
179 return Qnil;
180}
181
Alexander Polcync6627ca2017-04-13 10:47:16 -0700182/* TODO: expose this as part of the surface API if needed.
183 * This is meant for internal usage by the "write thread" of grpc-ruby
184 * client-side bidi calls. It provides a way for the background write-thread
185 * to propogate failures to the main read-thread and give the user an error
186 * message. */
187static VALUE grpc_rb_call_cancel_with_status(VALUE self, VALUE status_code,
188 VALUE details) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700189 grpc_rb_call* call = NULL;
Alexander Polcync6627ca2017-04-13 10:47:16 -0700190 grpc_call_error err;
191 if (RTYPEDDATA_DATA(self) == NULL) {
192 // This call has been closed
193 return Qnil;
194 }
195
196 if (TYPE(details) != T_STRING || TYPE(status_code) != T_FIXNUM) {
197 rb_raise(rb_eTypeError,
198 "Bad parameter type error for cancel with status. Want Fixnum, "
199 "String.");
200 return Qnil;
201 }
202
203 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
204 err = grpc_call_cancel_with_status(call->wrapped, NUM2LONG(status_code),
205 StringValueCStr(details), NULL);
206 if (err != GRPC_CALL_OK) {
207 rb_raise(grpc_rb_eCallError, "cancel with status failed: %s (code=%d)",
208 grpc_call_error_detail_of(err), err);
209 }
210
211 return Qnil;
212}
213
Ken Paysondce1ee62016-05-20 10:29:34 -0700214/* Releases the c-level resources associated with a call
215 Once a call has been closed, no further requests can be
216 processed.
217*/
218static VALUE grpc_rb_call_close(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700219 grpc_rb_call* call = NULL;
murgatroid99ec1588b2016-06-06 15:37:45 -0700220 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700221 if (call != NULL) {
murgatroid99ec1588b2016-06-06 15:37:45 -0700222 destroy_call(call);
Alexander Polcyn40d30622017-10-17 17:39:57 -0700223 xfree(RTYPEDDATA_DATA(self));
Ken Paysondce1ee62016-05-20 10:29:34 -0700224 RTYPEDDATA_DATA(self) = NULL;
225 }
226 return Qnil;
227}
228
Tim Emiola623a74d2015-08-11 09:24:20 -0700229/* Called to obtain the peer that this call is connected to. */
230static VALUE grpc_rb_call_get_peer(VALUE self) {
231 VALUE res = Qnil;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700232 grpc_rb_call* call = NULL;
233 char* peer = NULL;
Ken Paysondce1ee62016-05-20 10:29:34 -0700234 if (RTYPEDDATA_DATA(self) == NULL) {
235 rb_raise(grpc_rb_eCallError, "Cannot get peer value on closed call");
236 return Qnil;
237 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700238 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
239 peer = grpc_call_get_peer(call->wrapped);
Tim Emiola623a74d2015-08-11 09:24:20 -0700240 res = rb_str_new2(peer);
241 gpr_free(peer);
242
243 return res;
244}
245
Eric Richardson69d735a2016-04-20 13:23:32 -0400246/* Called to obtain the x509 cert of an authenticated peer. */
247static VALUE grpc_rb_call_get_peer_cert(VALUE self) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700248 grpc_rb_call* call = NULL;
Eric Richardson69d735a2016-04-20 13:23:32 -0400249 VALUE res = Qnil;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700250 grpc_auth_context* ctx = NULL;
Ken Paysondce1ee62016-05-20 10:29:34 -0700251 if (RTYPEDDATA_DATA(self) == NULL) {
252 rb_raise(grpc_rb_eCallError, "Cannot get peer cert on closed call");
253 return Qnil;
254 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700255 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Eric Richardson69d735a2016-04-20 13:23:32 -0400256
murgatroid99ec1588b2016-06-06 15:37:45 -0700257 ctx = grpc_call_auth_context(call->wrapped);
Eric Richardson69d735a2016-04-20 13:23:32 -0400258
Eric Richardsonbdfaf482016-04-20 13:56:55 -0400259 if (!ctx || !grpc_auth_context_peer_is_authenticated(ctx)) {
260 return Qnil;
261 }
262
Eric Richardson336b7442016-04-21 14:46:59 -0400263 {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700264 grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(
265 ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700266 const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
Eric Richardson336b7442016-04-21 14:46:59 -0400267 if (prop == NULL) {
268 return Qnil;
269 }
Eric Richardson69d735a2016-04-20 13:23:32 -0400270
Eric Richardson336b7442016-04-21 14:46:59 -0400271 res = rb_str_new2(prop->value);
272 }
Eric Richardson69d735a2016-04-20 13:23:32 -0400273
Eric Richardsonbdfaf482016-04-20 13:56:55 -0400274 grpc_auth_context_release(ctx);
275
Eric Richardson69d735a2016-04-20 13:23:32 -0400276 return res;
277}
278
nnoble097ef9b2014-12-01 17:06:10 -0800279/*
280 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700281 status = call.status
nnoble097ef9b2014-12-01 17:06:10 -0800282
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700283 Gets the status object saved the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800284static VALUE grpc_rb_call_get_status(VALUE self) {
285 return rb_ivar_get(self, id_status);
286}
287
288/*
289 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700290 call.status = status
nnoble097ef9b2014-12-01 17:06:10 -0800291
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700292 Saves a status object on the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800293static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900294 if (!NIL_P(status) && rb_obj_class(status) != grpc_rb_sStatus) {
temiola58327912014-12-15 17:51:16 -0800295 rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>",
nnoble097ef9b2014-12-01 17:06:10 -0800296 rb_obj_classname(status));
297 return Qnil;
298 }
299
300 return rb_ivar_set(self, id_status, status);
301}
302
303/*
304 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700305 metadata = call.metadata
nnoble097ef9b2014-12-01 17:06:10 -0800306
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700307 Gets the metadata object saved the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800308static VALUE grpc_rb_call_get_metadata(VALUE self) {
309 return rb_ivar_get(self, id_metadata);
310}
311
312/*
313 call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700314 call.metadata = metadata
nnoble097ef9b2014-12-01 17:06:10 -0800315
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700316 Saves the metadata hash on the call. */
nnoble097ef9b2014-12-01 17:06:10 -0800317static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
318 if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
319 rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
320 rb_obj_classname(metadata));
321 return Qnil;
322 }
323
324 return rb_ivar_set(self, id_metadata, metadata);
325}
326
Tim Emiola7840a552015-08-20 13:12:33 -0700327/*
328 call-seq:
murgatroid99e69f0882016-07-07 15:52:27 -0700329 trailing_metadata = call.trailing_metadata
330
331 Gets the trailing metadata object saved on the call */
332static VALUE grpc_rb_call_get_trailing_metadata(VALUE self) {
333 return rb_ivar_get(self, id_trailing_metadata);
334}
335
336/*
337 call-seq:
338 call.trailing_metadata = trailing_metadata
339
340 Saves the trailing metadata hash on the call. */
341static VALUE grpc_rb_call_set_trailing_metadata(VALUE self, VALUE metadata) {
342 if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
343 rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
344 rb_obj_classname(metadata));
345 return Qnil;
346 }
347
348 return rb_ivar_set(self, id_trailing_metadata, metadata);
349}
350
351/*
352 call-seq:
Tim Emiola7840a552015-08-20 13:12:33 -0700353 write_flag = call.write_flag
354
355 Gets the write_flag value saved the call. */
356static VALUE grpc_rb_call_get_write_flag(VALUE self) {
357 return rb_ivar_get(self, id_write_flag);
358}
359
360/*
361 call-seq:
362 call.write_flag = write_flag
363
364 Saves the write_flag on the call. */
365static VALUE grpc_rb_call_set_write_flag(VALUE self, VALUE write_flag) {
366 if (!NIL_P(write_flag) && TYPE(write_flag) != T_FIXNUM) {
367 rb_raise(rb_eTypeError, "bad write_flag: got:<%s> want: <Fixnum>",
368 rb_obj_classname(write_flag));
369 return Qnil;
370 }
371
372 return rb_ivar_set(self, id_write_flag, write_flag);
373}
374
murgatroid999946f2b2015-12-04 14:36:27 -0800375/*
376 call-seq:
377 call.set_credentials call_credentials
378
379 Sets credentials on a call */
380static VALUE grpc_rb_call_set_credentials(VALUE self, VALUE credentials) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700381 grpc_rb_call* call = NULL;
382 grpc_call_credentials* creds;
murgatroid999946f2b2015-12-04 14:36:27 -0800383 grpc_call_error err;
Ken Paysondce1ee62016-05-20 10:29:34 -0700384 if (RTYPEDDATA_DATA(self) == NULL) {
385 rb_raise(grpc_rb_eCallError, "Cannot set credentials of closed call");
386 return Qnil;
387 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700388 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
murgatroid999946f2b2015-12-04 14:36:27 -0800389 creds = grpc_rb_get_wrapped_call_credentials(credentials);
murgatroid99ec1588b2016-06-06 15:37:45 -0700390 err = grpc_call_set_credentials(call->wrapped, creds);
murgatroid999946f2b2015-12-04 14:36:27 -0800391 if (err != GRPC_CALL_OK) {
392 rb_raise(grpc_rb_eCallError,
393 "grpc_call_set_credentials failed with %s (code=%d)",
394 grpc_call_error_detail_of(err), err);
395 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700396 /* We need the credentials to be alive for as long as the call is alive,
397 but we don't care about destruction order. */
murgatroid998c9edc22016-03-14 15:51:56 -0700398 rb_ivar_set(self, id_credentials, credentials);
murgatroid999946f2b2015-12-04 14:36:27 -0800399 return Qnil;
400}
401
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700402/* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used
403 to fill grpc_metadata_array.
nnoble097ef9b2014-12-01 17:06:10 -0800404
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700405 it's capacity should have been computed via a prior call to
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700406 grpc_rb_md_ary_capacity_hash_cb
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700407*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900408static int grpc_rb_md_ary_fill_hash_cb(VALUE key, VALUE val, VALUE md_ary_obj) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700409 grpc_metadata_array* md_ary = NULL;
Marcin Wyszynskiac91edd2015-07-23 19:59:46 +0200410 long array_length;
Marcin Wyszynskia5fea602015-07-24 10:24:32 +0200411 long i;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800412 grpc_slice key_slice;
413 grpc_slice value_slice;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700414 char* tmp_str = NULL;
murgatroid9956fada52016-01-06 14:40:38 -0800415
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800416 if (TYPE(key) == T_SYMBOL) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800417 key_slice = grpc_slice_from_static_string(rb_id2name(SYM2ID(key)));
418 } else if (TYPE(key) == T_STRING) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700419 key_slice =
420 grpc_slice_from_copied_buffer(RSTRING_PTR(key), RSTRING_LEN(key));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800421 } else {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700422 rb_raise(rb_eTypeError,
423 "grpc_rb_md_ary_fill_hash_cb: bad type for key parameter");
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700424 return ST_STOP;
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800425 }
426
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800427 if (!grpc_header_key_is_legal(key_slice)) {
428 tmp_str = grpc_slice_to_c_string(key_slice);
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800429 rb_raise(rb_eArgError,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700430 "'%s' is an invalid header key, must match [a-z0-9-_.]+", tmp_str);
murgatroid99c2ea3ae2016-01-06 11:48:15 -0800431 return ST_STOP;
432 }
433
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700434 /* Construct a metadata object from key and value and add it */
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900435 TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
436 &grpc_rb_md_ary_data_type, md_ary);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700437
438 if (TYPE(val) == T_ARRAY) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700439 array_length = RARRAY_LEN(val);
murgatroid9956fada52016-01-06 14:40:38 -0800440 /* If the value is an array, add capacity for each value in the array */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700441 for (i = 0; i < array_length; i++) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700442 value_slice = grpc_slice_from_copied_buffer(
443 RSTRING_PTR(rb_ary_entry(val, i)), RSTRING_LEN(rb_ary_entry(val, i)));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800444 if (!grpc_is_binary_header(key_slice) &&
445 !grpc_header_nonbin_value_is_legal(value_slice)) {
murgatroid9956fada52016-01-06 14:40:38 -0800446 // The value has invalid characters
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800447 tmp_str = grpc_slice_to_c_string(value_slice);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700448 rb_raise(rb_eArgError, "Header value '%s' has invalid characters",
449 tmp_str);
murgatroid9956fada52016-01-06 14:40:38 -0800450 return ST_STOP;
451 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700452 GPR_ASSERT(md_ary->count < md_ary->capacity);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800453 md_ary->metadata[md_ary->count].key = key_slice;
454 md_ary->metadata[md_ary->count].value = value_slice;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700455 md_ary->count += 1;
456 }
Rafael Salesac491d82016-03-02 02:30:29 -0300457 } else if (TYPE(val) == T_STRING) {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700458 value_slice =
459 grpc_slice_from_copied_buffer(RSTRING_PTR(val), RSTRING_LEN(val));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800460 if (!grpc_is_binary_header(key_slice) &&
461 !grpc_header_nonbin_value_is_legal(value_slice)) {
murgatroid9956fada52016-01-06 14:40:38 -0800462 // The value has invalid characters
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800463 tmp_str = grpc_slice_to_c_string(value_slice);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700464 rb_raise(rb_eArgError, "Header value '%s' has invalid characters",
465 tmp_str);
murgatroid9956fada52016-01-06 14:40:38 -0800466 return ST_STOP;
467 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700468 GPR_ASSERT(md_ary->count < md_ary->capacity);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800469 md_ary->metadata[md_ary->count].key = key_slice;
470 md_ary->metadata[md_ary->count].value = value_slice;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700471 md_ary->count += 1;
Rafael Salesac491d82016-03-02 02:30:29 -0300472 } else {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700473 rb_raise(rb_eArgError, "Header values must be of type string or array");
Rafael Salesac491d82016-03-02 02:30:29 -0300474 return ST_STOP;
nnoble097ef9b2014-12-01 17:06:10 -0800475 }
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700476 return ST_CONTINUE;
nnoble097ef9b2014-12-01 17:06:10 -0800477}
478
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700479/* grpc_rb_md_ary_capacity_hash_cb is the hash iteration callback used
480 to pre-compute the capacity a grpc_metadata_array.
481*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900482static int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val,
483 VALUE md_ary_obj) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700484 grpc_metadata_array* md_ary = NULL;
temiola58327912014-12-15 17:51:16 -0800485
murgatroid9987afb5d2015-07-16 16:01:02 -0700486 (void)key;
487
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700488 /* Construct a metadata object from key and value and add it */
Yuki Yugui Sonoda961f0bc2015-04-11 14:39:07 +0900489 TypedData_Get_Struct(md_ary_obj, grpc_metadata_array,
490 &grpc_rb_md_ary_data_type, md_ary);
temiola58327912014-12-15 17:51:16 -0800491
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700492 if (TYPE(val) == T_ARRAY) {
493 /* If the value is an array, add capacity for each value in the array */
494 md_ary->capacity += RARRAY_LEN(val);
495 } else {
496 md_ary->capacity += 1;
nnoble097ef9b2014-12-01 17:06:10 -0800497 }
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700498
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700499 return ST_CONTINUE;
nnoble097ef9b2014-12-01 17:06:10 -0800500}
501
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700502/* grpc_rb_md_ary_convert converts a ruby metadata hash into
503 a grpc_metadata_array.
504*/
Craig Tillerbaa14a92017-11-03 09:09:36 -0700505void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array* md_ary) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700506 VALUE md_ary_obj = Qnil;
507 if (md_ary_hash == Qnil) {
Craig Tiller1a727fd2015-04-24 13:21:22 -0700508 return; /* Do nothing if the expected has value is nil */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700509 }
510 if (TYPE(md_ary_hash) != T_HASH) {
511 rb_raise(rb_eTypeError, "md_ary_convert: got <%s>, want <Hash>",
512 rb_obj_classname(md_ary_hash));
513 return;
nnoble097ef9b2014-12-01 17:06:10 -0800514 }
515
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700516 /* Initialize the array, compute it's capacity, then fill it. */
517 grpc_metadata_array_init(md_ary);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700518 md_ary_obj =
519 TypedData_Wrap_Struct(grpc_rb_cMdAry, &grpc_rb_md_ary_data_type, md_ary);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700520 rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_capacity_hash_cb, md_ary_obj);
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700521 md_ary->metadata = gpr_zalloc(md_ary->capacity * sizeof(grpc_metadata));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700522 rb_hash_foreach(md_ary_hash, grpc_rb_md_ary_fill_hash_cb, md_ary_obj);
523}
524
525/* Converts a metadata array to a hash. */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700526VALUE grpc_rb_md_ary_to_h(grpc_metadata_array* md_ary) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700527 VALUE key = Qnil;
528 VALUE new_ary = Qnil;
529 VALUE value = Qnil;
530 VALUE result = rb_hash_new();
531 size_t i;
532
533 for (i = 0; i < md_ary->count; i++) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800534 key = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].key);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700535 value = rb_hash_aref(result, key);
536 if (value == Qnil) {
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800537 value = grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700538 rb_hash_aset(result, key, value);
539 } else if (TYPE(value) == T_ARRAY) {
540 /* Add the string to the returned array */
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700541 rb_ary_push(value,
542 grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700543 } else {
544 /* Add the current value with this key and the new one to an array */
545 new_ary = rb_ary_new();
546 rb_ary_push(new_ary, value);
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700547 rb_ary_push(new_ary,
548 grpc_rb_slice_to_ruby_string(md_ary->metadata[i].value));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700549 rb_hash_aset(result, key, new_ary);
550 }
551 }
552 return result;
553}
554
555/* grpc_rb_call_check_op_keys_hash_cb is a hash iteration func that checks
556 each key of an ops hash is valid.
557*/
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900558static int grpc_rb_call_check_op_keys_hash_cb(VALUE key, VALUE val,
559 VALUE ops_ary) {
murgatroid9987afb5d2015-07-16 16:01:02 -0700560 (void)val;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700561 /* Update the capacity; the value is an array, add capacity for each value in
562 * the array */
563 if (TYPE(key) != T_FIXNUM) {
564 rb_raise(rb_eTypeError, "invalid operation : got <%s>, want <Fixnum>",
565 rb_obj_classname(key));
566 return ST_STOP;
567 }
Craig Tiller1a727fd2015-04-24 13:21:22 -0700568 switch (NUM2INT(key)) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700569 case GRPC_OP_SEND_INITIAL_METADATA:
570 case GRPC_OP_SEND_MESSAGE:
571 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
572 case GRPC_OP_SEND_STATUS_FROM_SERVER:
573 case GRPC_OP_RECV_INITIAL_METADATA:
574 case GRPC_OP_RECV_MESSAGE:
575 case GRPC_OP_RECV_STATUS_ON_CLIENT:
576 case GRPC_OP_RECV_CLOSE_ON_SERVER:
577 rb_ary_push(ops_ary, key);
578 return ST_CONTINUE;
579 default:
Craig Tiller1a727fd2015-04-24 13:21:22 -0700580 rb_raise(rb_eTypeError, "invalid operation : bad value %d", NUM2INT(key));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700581 };
582 return ST_STOP;
583}
584
585/* grpc_rb_op_update_status_from_server adds the values in a ruby status
586 struct to the 'send_status_from_server' portion of an op.
587*/
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700588static void grpc_rb_op_update_status_from_server(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700589 grpc_op* op, grpc_metadata_array* md_ary, grpc_slice* send_status_details,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700590 VALUE status) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700591 VALUE code = rb_struct_aref(status, sym_code);
592 VALUE details = rb_struct_aref(status, sym_details);
593 VALUE metadata_hash = rb_struct_aref(status, sym_metadata);
594
595 /* TODO: add check to ensure status is the correct struct type */
596 if (TYPE(code) != T_FIXNUM) {
597 rb_raise(rb_eTypeError, "invalid code : got <%s>, want <Fixnum>",
598 rb_obj_classname(code));
599 return;
600 }
601 if (TYPE(details) != T_STRING) {
602 rb_raise(rb_eTypeError, "invalid details : got <%s>, want <String>",
603 rb_obj_classname(code));
604 return;
605 }
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800606
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700607 *send_status_details =
608 grpc_slice_from_copied_buffer(RSTRING_PTR(details), RSTRING_LEN(details));
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800609
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700610 op->data.send_status_from_server.status = NUM2INT(code);
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800611 op->data.send_status_from_server.status_details = send_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700612 grpc_rb_md_ary_convert(metadata_hash, md_ary);
613 op->data.send_status_from_server.trailing_metadata_count = md_ary->count;
614 op->data.send_status_from_server.trailing_metadata = md_ary->metadata;
615}
616
617/* run_batch_stack holds various values used by the
618 * grpc_rb_call_run_batch function */
619typedef struct run_batch_stack {
620 /* The batch ops */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700621 grpc_op ops[8]; /* 8 is the maximum number of operations */
622 size_t op_num; /* tracks the last added operation */
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700623
624 /* Data being sent */
625 grpc_metadata_array send_metadata;
626 grpc_metadata_array send_trailing_metadata;
627
628 /* Data being received */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700629 grpc_byte_buffer* recv_message;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700630 grpc_metadata_array recv_metadata;
631 grpc_metadata_array recv_trailing_metadata;
632 int recv_cancelled;
633 grpc_status_code recv_status;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800634 grpc_slice recv_status_details;
Nicolas "Pixel" Noble7b92db62016-01-26 22:46:34 +0100635 unsigned write_flag;
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800636 grpc_slice send_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700637} run_batch_stack;
638
639/* grpc_run_batch_stack_init ensures the run_batch_stack is properly
640 * initialized */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700641static void grpc_run_batch_stack_init(run_batch_stack* st,
David Garcia Quintasb4e51b52016-01-27 19:57:58 -0800642 unsigned write_flag) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700643 MEMZERO(st, run_batch_stack, 1);
644 grpc_metadata_array_init(&st->send_metadata);
645 grpc_metadata_array_init(&st->send_trailing_metadata);
646 grpc_metadata_array_init(&st->recv_metadata);
647 grpc_metadata_array_init(&st->recv_trailing_metadata);
648 st->op_num = 0;
Tim Emiola7840a552015-08-20 13:12:33 -0700649 st->write_flag = write_flag;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700650}
651
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700652void grpc_rb_metadata_array_destroy_including_entries(
Craig Tillerbaa14a92017-11-03 09:09:36 -0700653 grpc_metadata_array* array) {
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700654 size_t i;
655 if (array->metadata) {
656 for (i = 0; i < array->count; i++) {
657 grpc_slice_unref(array->metadata[i].key);
658 grpc_slice_unref(array->metadata[i].value);
659 }
660 }
661 grpc_metadata_array_destroy(array);
662}
663
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700664/* grpc_run_batch_stack_cleanup ensures the run_batch_stack is properly
665 * cleaned up */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700666static void grpc_run_batch_stack_cleanup(run_batch_stack* st) {
Zhuochun97daf352016-03-13 16:19:56 +0800667 size_t i = 0;
668
Alexander Polcyn85cc1432017-07-14 16:36:51 -0700669 grpc_rb_metadata_array_destroy_including_entries(&st->send_metadata);
670 grpc_rb_metadata_array_destroy_including_entries(&st->send_trailing_metadata);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700671 grpc_metadata_array_destroy(&st->recv_metadata);
672 grpc_metadata_array_destroy(&st->recv_trailing_metadata);
Zhuochun97daf352016-03-13 16:19:56 +0800673
Craig Tiller7c70b6c2017-01-23 07:48:42 -0800674 if (GRPC_SLICE_START_PTR(st->send_status_details) != NULL) {
675 grpc_slice_unref(st->send_status_details);
676 }
677
678 if (GRPC_SLICE_START_PTR(st->recv_status_details) != NULL) {
679 grpc_slice_unref(st->recv_status_details);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700680 }
Zhuochun97daf352016-03-13 16:19:56 +0800681
682 if (st->recv_message != NULL) {
683 grpc_byte_buffer_destroy(st->recv_message);
684 }
685
686 for (i = 0; i < st->op_num; i++) {
687 if (st->ops[i].op == GRPC_OP_SEND_MESSAGE) {
Mark D. Roth448c1f02017-01-25 10:44:30 -0800688 grpc_byte_buffer_destroy(st->ops[i].data.send_message.send_message);
Zhuochun97daf352016-03-13 16:19:56 +0800689 }
690 }
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700691}
692
693/* grpc_run_batch_stack_fill_ops fills the run_batch_stack ops array from
694 * ops_hash */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700695static void grpc_run_batch_stack_fill_ops(run_batch_stack* st, VALUE ops_hash) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700696 VALUE this_op = Qnil;
697 VALUE this_value = Qnil;
698 VALUE ops_ary = rb_ary_new();
699 size_t i = 0;
700
701 /* Create a ruby array with just the operation keys */
702 rb_hash_foreach(ops_hash, grpc_rb_call_check_op_keys_hash_cb, ops_ary);
703
704 /* Fill the ops array */
705 for (i = 0; i < (size_t)RARRAY_LEN(ops_ary); i++) {
706 this_op = rb_ary_entry(ops_ary, i);
707 this_value = rb_hash_aref(ops_hash, this_op);
Tim Emiola7840a552015-08-20 13:12:33 -0700708 st->ops[st->op_num].flags = 0;
Craig Tiller1a727fd2015-04-24 13:21:22 -0700709 switch (NUM2INT(this_op)) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700710 case GRPC_OP_SEND_INITIAL_METADATA:
711 grpc_rb_md_ary_convert(this_value, &st->send_metadata);
712 st->ops[st->op_num].data.send_initial_metadata.count =
713 st->send_metadata.count;
714 st->ops[st->op_num].data.send_initial_metadata.metadata =
715 st->send_metadata.metadata;
716 break;
717 case GRPC_OP_SEND_MESSAGE:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800718 st->ops[st->op_num].data.send_message.send_message =
719 grpc_rb_s_to_byte_buffer(RSTRING_PTR(this_value),
720 RSTRING_LEN(this_value));
Tim Emiola7840a552015-08-20 13:12:33 -0700721 st->ops[st->op_num].flags = st->write_flag;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700722 break;
723 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
724 break;
725 case GRPC_OP_SEND_STATUS_FROM_SERVER:
Craig Tiller1a727fd2015-04-24 13:21:22 -0700726 grpc_rb_op_update_status_from_server(
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700727 &st->ops[st->op_num], &st->send_trailing_metadata,
728 &st->send_status_details, this_value);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700729 break;
730 case GRPC_OP_RECV_INITIAL_METADATA:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800731 st->ops[st->op_num].data.recv_initial_metadata.recv_initial_metadata =
732 &st->recv_metadata;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700733 break;
734 case GRPC_OP_RECV_MESSAGE:
Mark D. Roth448c1f02017-01-25 10:44:30 -0800735 st->ops[st->op_num].data.recv_message.recv_message = &st->recv_message;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700736 break;
737 case GRPC_OP_RECV_STATUS_ON_CLIENT:
738 st->ops[st->op_num].data.recv_status_on_client.trailing_metadata =
739 &st->recv_trailing_metadata;
740 st->ops[st->op_num].data.recv_status_on_client.status =
741 &st->recv_status;
742 st->ops[st->op_num].data.recv_status_on_client.status_details =
743 &st->recv_status_details;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700744 break;
745 case GRPC_OP_RECV_CLOSE_ON_SERVER:
746 st->ops[st->op_num].data.recv_close_on_server.cancelled =
747 &st->recv_cancelled;
748 break;
749 default:
750 grpc_run_batch_stack_cleanup(st);
751 rb_raise(rb_eTypeError, "invalid operation : bad value %d",
752 NUM2INT(this_op));
753 };
754 st->ops[st->op_num].op = (grpc_op_type)NUM2INT(this_op);
Craig Tiller42758992015-08-18 10:34:32 -0700755 st->ops[st->op_num].reserved = NULL;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700756 st->op_num++;
757 }
758}
759
760/* grpc_run_batch_stack_build_result fills constructs a ruby BatchResult struct
761 after the results have run */
Craig Tillerbaa14a92017-11-03 09:09:36 -0700762static VALUE grpc_run_batch_stack_build_result(run_batch_stack* st) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700763 size_t i = 0;
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900764 VALUE result = rb_struct_new(grpc_rb_sBatchResult, Qnil, Qnil, Qnil, Qnil,
765 Qnil, Qnil, Qnil, Qnil, NULL);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700766 for (i = 0; i < st->op_num; i++) {
Craig Tiller1a727fd2015-04-24 13:21:22 -0700767 switch (st->ops[i].op) {
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700768 case GRPC_OP_SEND_INITIAL_METADATA:
769 rb_struct_aset(result, sym_send_metadata, Qtrue);
770 break;
771 case GRPC_OP_SEND_MESSAGE:
772 rb_struct_aset(result, sym_send_message, Qtrue);
773 break;
774 case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
775 rb_struct_aset(result, sym_send_close, Qtrue);
776 break;
777 case GRPC_OP_SEND_STATUS_FROM_SERVER:
778 rb_struct_aset(result, sym_send_status, Qtrue);
779 break;
780 case GRPC_OP_RECV_INITIAL_METADATA:
781 rb_struct_aset(result, sym_metadata,
782 grpc_rb_md_ary_to_h(&st->recv_metadata));
783 case GRPC_OP_RECV_MESSAGE:
784 rb_struct_aset(result, sym_message,
785 grpc_rb_byte_buffer_to_s(st->recv_message));
786 break;
787 case GRPC_OP_RECV_STATUS_ON_CLIENT:
788 rb_struct_aset(
Craig Tiller1a727fd2015-04-24 13:21:22 -0700789 result, sym_status,
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700790 rb_struct_new(
791 grpc_rb_sStatus, UINT2NUM(st->recv_status),
792 (GRPC_SLICE_START_PTR(st->recv_status_details) == NULL
793 ? Qnil
794 : grpc_rb_slice_to_ruby_string(st->recv_status_details)),
795 grpc_rb_md_ary_to_h(&st->recv_trailing_metadata), NULL));
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700796 break;
797 case GRPC_OP_RECV_CLOSE_ON_SERVER:
798 rb_struct_aset(result, sym_send_close, Qtrue);
799 break;
800 default:
801 break;
802 }
803 }
804 return result;
nnoble097ef9b2014-12-01 17:06:10 -0800805}
806
807/* call-seq:
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700808 ops = {
809 GRPC::Core::CallOps::SEND_INITIAL_METADATA => <op_value>,
810 GRPC::Core::CallOps::SEND_MESSAGE => <op_value>,
811 ...
812 }
813 tag = Object.new
814 timeout = 10
murgatroid99ec1588b2016-06-06 15:37:45 -0700815 call.start_batch(tag, timeout, ops)
nnoble097ef9b2014-12-01 17:06:10 -0800816
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700817 Start a batch of operations defined in the array ops; when complete, post a
818 completion of type 'tag' to the completion queue bound to the call.
temiola71bb1372014-12-11 11:27:25 -0800819
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700820 Also waits for the batch to complete, until timeout is reached.
821 The order of ops specified in the batch has no significance.
822 Only one operation of each type can be active at once in any given
823 batch */
murgatroid99ec1588b2016-06-06 15:37:45 -0700824static VALUE grpc_rb_call_run_batch(VALUE self, VALUE ops_hash) {
Craig Tillerbaa14a92017-11-03 09:09:36 -0700825 run_batch_stack* st = NULL;
826 grpc_rb_call* call = NULL;
Craig Tillerc7df2b82015-05-11 11:12:18 -0700827 grpc_event ev;
nnoble097ef9b2014-12-01 17:06:10 -0800828 grpc_call_error err;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700829 VALUE result = Qnil;
Tim Emiola7840a552015-08-20 13:12:33 -0700830 VALUE rb_write_flag = rb_ivar_get(self, id_write_flag);
Nicolas "Pixel" Noble7b92db62016-01-26 22:46:34 +0100831 unsigned write_flag = 0;
Craig Tillerbaa14a92017-11-03 09:09:36 -0700832 void* tag = (void*)&st;
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700833
Ken Paysondce1ee62016-05-20 10:29:34 -0700834 if (RTYPEDDATA_DATA(self) == NULL) {
835 rb_raise(grpc_rb_eCallError, "Cannot run batch on closed call");
836 return Qnil;
837 }
murgatroid99ec1588b2016-06-06 15:37:45 -0700838 TypedData_Get_Struct(self, grpc_rb_call, &grpc_call_data_type, call);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700839
840 /* Validate the ops args, adding them to a ruby array */
841 if (TYPE(ops_hash) != T_HASH) {
842 rb_raise(rb_eTypeError, "call#run_batch: ops hash should be a hash");
843 return Qnil;
temiola71bb1372014-12-11 11:27:25 -0800844 }
Tim Emiola7840a552015-08-20 13:12:33 -0700845 if (rb_write_flag != Qnil) {
846 write_flag = NUM2UINT(rb_write_flag);
847 }
Alexander Polcyn8d8dce82017-03-24 10:32:15 -0700848 st = gpr_malloc(sizeof(run_batch_stack));
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700849 grpc_run_batch_stack_init(st, write_flag);
850 grpc_run_batch_stack_fill_ops(st, ops_hash);
temiola71bb1372014-12-11 11:27:25 -0800851
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700852 /* call grpc_call_start_batch, then wait for it to complete using
853 * pluck_event */
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700854 err = grpc_call_start_batch(call->wrapped, st->ops, st->op_num, tag, NULL);
temiola71bb1372014-12-11 11:27:25 -0800855 if (err != GRPC_CALL_OK) {
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700856 grpc_run_batch_stack_cleanup(st);
857 gpr_free(st);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900858 rb_raise(grpc_rb_eCallError,
859 "grpc_call_start_batch failed with %s (code=%d)",
nnoble097ef9b2014-12-01 17:06:10 -0800860 grpc_call_error_detail_of(err), err);
Tim Emiola932b13b2015-04-24 08:52:46 -0700861 return Qnil;
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700862 }
murgatroid995ea4a992016-06-13 10:36:41 -0700863 ev = rb_completion_queue_pluck(call->queue, tag,
864 gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
865 if (!ev.success) {
866 rb_raise(grpc_rb_eCallError, "call#run_batch failed somehow");
867 }
Tim Emiolacdb7ccc2015-08-13 21:45:04 -0700868 /* Build and return the BatchResult struct result,
869 if there is an error, it's reflected in the status */
Alexander Polcyn34bb6df2017-03-23 22:18:02 -0700870 result = grpc_run_batch_stack_build_result(st);
871 grpc_run_batch_stack_cleanup(st);
872 gpr_free(st);
Tim Emiola1dae4ac2015-03-27 17:17:37 -0700873 return result;
nnoble097ef9b2014-12-01 17:06:10 -0800874}
875
Tim Emiola7840a552015-08-20 13:12:33 -0700876static void Init_grpc_write_flags() {
877 /* Constants representing the write flags in grpc.h */
878 VALUE grpc_rb_mWriteFlags =
879 rb_define_module_under(grpc_rb_mGrpcCore, "WriteFlags");
880 rb_define_const(grpc_rb_mWriteFlags, "BUFFER_HINT",
881 UINT2NUM(GRPC_WRITE_BUFFER_HINT));
882 rb_define_const(grpc_rb_mWriteFlags, "NO_COMPRESS",
883 UINT2NUM(GRPC_WRITE_NO_COMPRESS));
884}
885
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900886static void Init_grpc_error_codes() {
nnoble097ef9b2014-12-01 17:06:10 -0800887 /* Constants representing the error codes of grpc_call_error in grpc.h */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900888 VALUE grpc_rb_mRpcErrors =
889 rb_define_module_under(grpc_rb_mGrpcCore, "RpcErrors");
890 rb_define_const(grpc_rb_mRpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
891 rb_define_const(grpc_rb_mRpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
892 rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_SERVER",
nnoble097ef9b2014-12-01 17:06:10 -0800893 UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900894 rb_define_const(grpc_rb_mRpcErrors, "NOT_ON_CLIENT",
nnoble097ef9b2014-12-01 17:06:10 -0800895 UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900896 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_ACCEPTED",
temiola71bb1372014-12-11 11:27:25 -0800897 UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900898 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_INVOKED",
nnoble097ef9b2014-12-01 17:06:10 -0800899 UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900900 rb_define_const(grpc_rb_mRpcErrors, "NOT_INVOKED",
nnoble097ef9b2014-12-01 17:06:10 -0800901 UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900902 rb_define_const(grpc_rb_mRpcErrors, "ALREADY_FINISHED",
nnoble097ef9b2014-12-01 17:06:10 -0800903 UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900904 rb_define_const(grpc_rb_mRpcErrors, "TOO_MANY_OPERATIONS",
nnoble097ef9b2014-12-01 17:06:10 -0800905 UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900906 rb_define_const(grpc_rb_mRpcErrors, "INVALID_FLAGS",
nnoble097ef9b2014-12-01 17:06:10 -0800907 UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS));
908
Andre Medeirosb0eae8a2017-08-21 15:50:36 -0400909 /* Hint the GC that this is a global and shouldn't be sweeped. */
910 rb_global_variable(&rb_error_code_details);
911
nnoble097ef9b2014-12-01 17:06:10 -0800912 /* Add the detail strings to a Hash */
913 rb_error_code_details = rb_hash_new();
Craig Tillerb5dcec52015-01-13 11:13:42 -0800914 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_OK),
915 rb_str_new2("ok"));
nnoble097ef9b2014-12-01 17:06:10 -0800916 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR),
917 rb_str_new2("unknown error"));
918 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER),
919 rb_str_new2("not available on a server"));
920 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT),
921 rb_str_new2("not available on a client"));
temiola71bb1372014-12-11 11:27:25 -0800922 rb_hash_aset(rb_error_code_details,
923 UINT2NUM(GRPC_CALL_ERROR_ALREADY_ACCEPTED),
924 rb_str_new2("call is already accepted"));
nnoble097ef9b2014-12-01 17:06:10 -0800925 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED),
926 rb_str_new2("call is already invoked"));
927 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED),
928 rb_str_new2("call is not yet invoked"));
929 rb_hash_aset(rb_error_code_details,
930 UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED),
931 rb_str_new2("call is already finished"));
932 rb_hash_aset(rb_error_code_details,
933 UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS),
934 rb_str_new2("outstanding read or write present"));
935 rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS),
936 rb_str_new2("a bad flag was given"));
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900937 rb_define_const(grpc_rb_mRpcErrors, "ErrorMessages", rb_error_code_details);
nnoble097ef9b2014-12-01 17:06:10 -0800938 rb_obj_freeze(rb_error_code_details);
939}
940
Yuki Yugui Sonodaf0eee5f2015-04-16 20:25:28 +0900941static void Init_grpc_op_codes() {
Tim Emiola37b09f42015-03-27 13:39:16 -0700942 /* Constants representing operation type codes in grpc.h */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700943 VALUE grpc_rb_mCallOps = rb_define_module_under(grpc_rb_mGrpcCore, "CallOps");
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900944 rb_define_const(grpc_rb_mCallOps, "SEND_INITIAL_METADATA",
Tim Emiola37b09f42015-03-27 13:39:16 -0700945 UINT2NUM(GRPC_OP_SEND_INITIAL_METADATA));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900946 rb_define_const(grpc_rb_mCallOps, "SEND_MESSAGE",
947 UINT2NUM(GRPC_OP_SEND_MESSAGE));
948 rb_define_const(grpc_rb_mCallOps, "SEND_CLOSE_FROM_CLIENT",
Tim Emiola37b09f42015-03-27 13:39:16 -0700949 UINT2NUM(GRPC_OP_SEND_CLOSE_FROM_CLIENT));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900950 rb_define_const(grpc_rb_mCallOps, "SEND_STATUS_FROM_SERVER",
Tim Emiola37b09f42015-03-27 13:39:16 -0700951 UINT2NUM(GRPC_OP_SEND_STATUS_FROM_SERVER));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900952 rb_define_const(grpc_rb_mCallOps, "RECV_INITIAL_METADATA",
Tim Emiola37b09f42015-03-27 13:39:16 -0700953 UINT2NUM(GRPC_OP_RECV_INITIAL_METADATA));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900954 rb_define_const(grpc_rb_mCallOps, "RECV_MESSAGE",
Tim Emiola37b09f42015-03-27 13:39:16 -0700955 UINT2NUM(GRPC_OP_RECV_MESSAGE));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900956 rb_define_const(grpc_rb_mCallOps, "RECV_STATUS_ON_CLIENT",
Tim Emiola37b09f42015-03-27 13:39:16 -0700957 UINT2NUM(GRPC_OP_RECV_STATUS_ON_CLIENT));
Yuki Yugui Sonoda2e50a7a2015-04-11 12:01:58 +0900958 rb_define_const(grpc_rb_mCallOps, "RECV_CLOSE_ON_SERVER",
Tim Emiola37b09f42015-03-27 13:39:16 -0700959 UINT2NUM(GRPC_OP_RECV_CLOSE_ON_SERVER));
960}
961
Alexander Polcyncdff92f2016-07-12 12:30:30 -0700962static void Init_grpc_metadata_keys() {
Craig Tiller5b1c5f22017-04-19 09:52:18 -0700963 VALUE grpc_rb_mMetadataKeys =
964 rb_define_module_under(grpc_rb_mGrpcCore, "MetadataKeys");
Alexander Polcyncdff92f2016-07-12 12:30:30 -0700965 rb_define_const(grpc_rb_mMetadataKeys, "COMPRESSION_REQUEST_ALGORITHM",
966 rb_str_new2(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY));
967}
968
Tim Emiola409e6c82015-02-17 17:46:35 -0800969void Init_grpc_call() {
nnoble097ef9b2014-12-01 17:06:10 -0800970 /* CallError inherits from Exception to signal that it is non-recoverable */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900971 grpc_rb_eCallError =
972 rb_define_class_under(grpc_rb_mGrpcCore, "CallError", rb_eException);
973 grpc_rb_eOutOfTime =
974 rb_define_class_under(grpc_rb_mGrpcCore, "OutOfTime", rb_eException);
975 grpc_rb_cCall = rb_define_class_under(grpc_rb_mGrpcCore, "Call", rb_cObject);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700976 grpc_rb_cMdAry =
977 rb_define_class_under(grpc_rb_mGrpcCore, "MetadataArray", rb_cObject);
nnoble097ef9b2014-12-01 17:06:10 -0800978
979 /* Prevent allocation or inialization of the Call class */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900980 rb_define_alloc_func(grpc_rb_cCall, grpc_rb_cannot_alloc);
981 rb_define_method(grpc_rb_cCall, "initialize", grpc_rb_cannot_init, 0);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700982 rb_define_method(grpc_rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy,
983 1);
nnoble097ef9b2014-12-01 17:06:10 -0800984
985 /* Add ruby analogues of the Call methods. */
murgatroid99ec1588b2016-06-06 15:37:45 -0700986 rb_define_method(grpc_rb_cCall, "run_batch", grpc_rb_call_run_batch, 1);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900987 rb_define_method(grpc_rb_cCall, "cancel", grpc_rb_call_cancel, 0);
Alexander Polcync6627ca2017-04-13 10:47:16 -0700988 rb_define_method(grpc_rb_cCall, "cancel_with_status",
989 grpc_rb_call_cancel_with_status, 2);
Ken Paysondce1ee62016-05-20 10:29:34 -0700990 rb_define_method(grpc_rb_cCall, "close", grpc_rb_call_close, 0);
Tim Emiola623a74d2015-08-11 09:24:20 -0700991 rb_define_method(grpc_rb_cCall, "peer", grpc_rb_call_get_peer, 0);
Eric Richardson69d735a2016-04-20 13:23:32 -0400992 rb_define_method(grpc_rb_cCall, "peer_cert", grpc_rb_call_get_peer_cert, 0);
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +0900993 rb_define_method(grpc_rb_cCall, "status", grpc_rb_call_get_status, 0);
994 rb_define_method(grpc_rb_cCall, "status=", grpc_rb_call_set_status, 1);
995 rb_define_method(grpc_rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
996 rb_define_method(grpc_rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
murgatroid99e69f0882016-07-07 15:52:27 -0700997 rb_define_method(grpc_rb_cCall, "trailing_metadata",
998 grpc_rb_call_get_trailing_metadata, 0);
Craig Tillerbaa14a92017-11-03 09:09:36 -0700999 rb_define_method(grpc_rb_cCall,
1000 "trailing_metadata=", grpc_rb_call_set_trailing_metadata, 1);
Tim Emiola7840a552015-08-20 13:12:33 -07001001 rb_define_method(grpc_rb_cCall, "write_flag", grpc_rb_call_get_write_flag, 0);
1002 rb_define_method(grpc_rb_cCall, "write_flag=", grpc_rb_call_set_write_flag,
1003 1);
murgatroid999946f2b2015-12-04 14:36:27 -08001004 rb_define_method(grpc_rb_cCall, "set_credentials!",
1005 grpc_rb_call_set_credentials, 1);
nnoble097ef9b2014-12-01 17:06:10 -08001006
1007 /* Ids used to support call attributes */
1008 id_metadata = rb_intern("metadata");
murgatroid99e69f0882016-07-07 15:52:27 -07001009 id_trailing_metadata = rb_intern("trailing_metadata");
nnoble097ef9b2014-12-01 17:06:10 -08001010 id_status = rb_intern("status");
Tim Emiola7840a552015-08-20 13:12:33 -07001011 id_write_flag = rb_intern("write_flag");
nnoble097ef9b2014-12-01 17:06:10 -08001012
1013 /* Ids used by the c wrapping internals. */
murgatroid998c9edc22016-03-14 15:51:56 -07001014 id_credentials = rb_intern("__credentials");
nnoble097ef9b2014-12-01 17:06:10 -08001015
Tim Emiola1dae4ac2015-03-27 17:17:37 -07001016 /* Ids used in constructing the batch result. */
1017 sym_send_message = ID2SYM(rb_intern("send_message"));
1018 sym_send_metadata = ID2SYM(rb_intern("send_metadata"));
1019 sym_send_close = ID2SYM(rb_intern("send_close"));
1020 sym_send_status = ID2SYM(rb_intern("send_status"));
1021 sym_message = ID2SYM(rb_intern("message"));
1022 sym_status = ID2SYM(rb_intern("status"));
1023 sym_cancelled = ID2SYM(rb_intern("cancelled"));
1024
1025 /* The Struct used to return the run_batch result. */
Yuki Yugui Sonodaa7d369e2015-04-11 11:48:36 +09001026 grpc_rb_sBatchResult = rb_struct_define(
Craig Tiller1a727fd2015-04-24 13:21:22 -07001027 "BatchResult", "send_message", "send_metadata", "send_close",
1028 "send_status", "message", "metadata", "status", "cancelled", NULL);
Tim Emiola1dae4ac2015-03-27 17:17:37 -07001029
Tim Emiola409e6c82015-02-17 17:46:35 -08001030 Init_grpc_error_codes();
Tim Emiola37b09f42015-03-27 13:39:16 -07001031 Init_grpc_op_codes();
Tim Emiola7840a552015-08-20 13:12:33 -07001032 Init_grpc_write_flags();
Alexander Polcyncdff92f2016-07-12 12:30:30 -07001033 Init_grpc_metadata_keys();
nnoble097ef9b2014-12-01 17:06:10 -08001034}
1035
1036/* Gets the call from the ruby object */
Craig Tillerbaa14a92017-11-03 09:09:36 -07001037grpc_call* grpc_rb_get_wrapped_call(VALUE v) {
1038 grpc_rb_call* call = NULL;
murgatroid99ec1588b2016-06-06 15:37:45 -07001039 TypedData_Get_Struct(v, grpc_rb_call, &grpc_call_data_type, call);
1040 return call->wrapped;
nnoble097ef9b2014-12-01 17:06:10 -08001041}
1042
1043/* Obtains the wrapped object for a given call */
Craig Tillerbaa14a92017-11-03 09:09:36 -07001044VALUE grpc_rb_wrap_call(grpc_call* c, grpc_completion_queue* q) {
1045 grpc_rb_call* wrapper;
murgatroid99ec1588b2016-06-06 15:37:45 -07001046 if (c == NULL || q == NULL) {
nnoble097ef9b2014-12-01 17:06:10 -08001047 return Qnil;
1048 }
murgatroid995ea4a992016-06-13 10:36:41 -07001049 wrapper = ALLOC(grpc_rb_call);
murgatroid99ec1588b2016-06-06 15:37:45 -07001050 wrapper->wrapped = c;
1051 wrapper->queue = q;
1052 return TypedData_Wrap_Struct(grpc_rb_cCall, &grpc_call_data_type, wrapper);
nnoble097ef9b2014-12-01 17:06:10 -08001053}