grpc 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grpc might be problematic. Click here for more details.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -2
  3. data/bin/{interop/test/cpp/interop/test.rb → grpc_ruby_interop_client} +4 -14
  4. data/bin/{interop/test/cpp/interop/empty.rb → grpc_ruby_interop_server} +4 -15
  5. data/bin/interop/interop_client.rb +9 -335
  6. data/bin/interop/interop_server.rb +9 -153
  7. data/bin/math_client.rb +3 -3
  8. data/bin/math_server.rb +18 -1
  9. data/bin/noproto_server.rb +2 -1
  10. data/ext/grpc/rb_call.c +82 -15
  11. data/ext/grpc/rb_channel.c +141 -11
  12. data/ext/grpc/rb_channel_args.c +2 -1
  13. data/ext/grpc/rb_completion_queue.c +8 -7
  14. data/ext/grpc/rb_credentials.c +7 -6
  15. data/ext/grpc/rb_grpc.c +23 -8
  16. data/ext/grpc/rb_server.c +31 -45
  17. data/ext/grpc/rb_server_credentials.c +91 -34
  18. data/lib/grpc/generic/active_call.rb +7 -7
  19. data/lib/grpc/generic/bidi_call.rb +17 -12
  20. data/lib/grpc/generic/client_stub.rb +88 -22
  21. data/lib/grpc/generic/rpc_server.rb +19 -18
  22. data/lib/grpc/generic/service.rb +8 -10
  23. data/lib/grpc/grpc.so +0 -0
  24. data/lib/grpc/logconfig.rb +26 -10
  25. data/lib/grpc/version.rb +1 -1
  26. data/spec/call_spec.rb +9 -1
  27. data/spec/channel_spec.rb +2 -2
  28. data/spec/client_server_spec.rb +28 -11
  29. data/spec/credentials_spec.rb +7 -7
  30. data/spec/generic/active_call_spec.rb +43 -18
  31. data/spec/generic/client_stub_spec.rb +21 -1
  32. data/spec/generic/rpc_server_spec.rb +20 -9
  33. data/spec/pb/health/checker_spec.rb +232 -0
  34. data/spec/server_credentials_spec.rb +32 -7
  35. data/spec/server_spec.rb +8 -4
  36. data/spec/spec_helper.rb +13 -1
  37. metadata +31 -51
  38. data/.gitignore +0 -15
  39. data/.rspec +0 -4
  40. data/.rubocop.yml +0 -10
  41. data/.rubocop_todo.yml +0 -44
  42. data/CHANGELOG.md +0 -11
  43. data/Gemfile +0 -4
  44. data/README.md +0 -84
  45. data/bin/interop/README.md +0 -8
  46. data/bin/interop/test/cpp/interop/messages.rb +0 -89
  47. data/bin/interop/test/cpp/interop/test_services.rb +0 -60
  48. data/grpc.gemspec +0 -40
@@ -41,7 +41,8 @@
41
41
 
42
42
  static rb_data_type_t grpc_rb_channel_args_data_type = {
43
43
  "grpc_channel_args",
44
- {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE},
44
+ {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE,
45
+ {NULL, NULL}},
45
46
  NULL, NULL,
46
47
  RUBY_TYPED_FREE_IMMEDIATELY
47
48
  };
@@ -56,7 +56,7 @@ typedef struct next_call_stack {
56
56
  static void *grpc_rb_completion_queue_next_no_gil(void *param) {
57
57
  next_call_stack *const next_call = (next_call_stack*)param;
58
58
  next_call->event =
59
- grpc_completion_queue_next(next_call->cq, next_call->timeout);
59
+ grpc_completion_queue_next(next_call->cq, next_call->timeout, NULL);
60
60
  return NULL;
61
61
  }
62
62
 
@@ -64,7 +64,7 @@ static void *grpc_rb_completion_queue_next_no_gil(void *param) {
64
64
  static void *grpc_rb_completion_queue_pluck_no_gil(void *param) {
65
65
  next_call_stack *const next_call = (next_call_stack*)param;
66
66
  next_call->event = grpc_completion_queue_pluck(next_call->cq, next_call->tag,
67
- next_call->timeout);
67
+ next_call->timeout, NULL);
68
68
  return NULL;
69
69
  }
70
70
 
@@ -82,7 +82,7 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) {
82
82
  next_call.cq = cq;
83
83
  next_call.event.type = GRPC_QUEUE_TIMEOUT;
84
84
  /* TODO: the timeout should be a module level constant that defaults
85
- * to gpr_inf_future.
85
+ * to gpr_inf_future(GPR_CLOCK_REALTIME).
86
86
  *
87
87
  * - at the moment this does not work, it stalls. Using a small timeout like
88
88
  * this one works, and leads to fast test run times; a longer timeout was
@@ -91,7 +91,8 @@ static void grpc_rb_completion_queue_shutdown_drain(grpc_completion_queue *cq) {
91
91
  * - investigate further, this is probably another example of C-level cleanup
92
92
  * not working consistently in all cases.
93
93
  */
94
- next_call.timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(5e3));
94
+ next_call.timeout = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
95
+ gpr_time_from_micros(5e3, GPR_TIMESPAN));
95
96
  do {
96
97
  rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil,
97
98
  (void *)&next_call, NULL, NULL);
@@ -118,7 +119,7 @@ static void grpc_rb_completion_queue_destroy(void *p) {
118
119
  static rb_data_type_t grpc_rb_completion_queue_data_type = {
119
120
  "grpc_completion_queue",
120
121
  {GRPC_RB_GC_NOT_MARKED, grpc_rb_completion_queue_destroy,
121
- GRPC_RB_MEMSIZE_UNAVAILABLE},
122
+ GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}},
122
123
  NULL, NULL,
123
124
  /* cannot immediately free because grpc_rb_completion_queue_shutdown_drain
124
125
  * calls rb_thread_call_without_gvl. */
@@ -127,7 +128,7 @@ static rb_data_type_t grpc_rb_completion_queue_data_type = {
127
128
 
128
129
  /* Allocates a completion queue. */
129
130
  static VALUE grpc_rb_completion_queue_alloc(VALUE cls) {
130
- grpc_completion_queue *cq = grpc_completion_queue_create();
131
+ grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
131
132
  if (cq == NULL) {
132
133
  rb_raise(rb_eArgError, "could not create a completion queue: not sure why");
133
134
  }
@@ -143,7 +144,7 @@ grpc_event grpc_rb_completion_queue_pluck_event(VALUE self, VALUE tag,
143
144
  TypedData_Get_Struct(self, grpc_completion_queue,
144
145
  &grpc_rb_completion_queue_data_type, next_call.cq);
145
146
  if (TYPE(timeout) == T_NIL) {
146
- next_call.timeout = gpr_inf_future;
147
+ next_call.timeout = gpr_inf_future(GPR_CLOCK_REALTIME);
147
148
  } else {
148
149
  next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
149
150
  }
@@ -89,7 +89,7 @@ static void grpc_rb_credentials_mark(void *p) {
89
89
  static rb_data_type_t grpc_rb_credentials_data_type = {
90
90
  "grpc_credentials",
91
91
  {grpc_rb_credentials_mark, grpc_rb_credentials_free,
92
- GRPC_RB_MEMSIZE_UNAVAILABLE},
92
+ GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}},
93
93
  NULL,
94
94
  NULL,
95
95
  RUBY_TYPED_FREE_IMMEDIATELY};
@@ -154,7 +154,7 @@ static VALUE grpc_rb_default_credentials_create(VALUE cls) {
154
154
  Creates the default credential instances. */
155
155
  static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) {
156
156
  grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
157
- wrapper->wrapped = grpc_compute_engine_credentials_create();
157
+ wrapper->wrapped = grpc_google_compute_engine_credentials_create(NULL);
158
158
  if (wrapper->wrapped == NULL) {
159
159
  rb_raise(rb_eRuntimeError,
160
160
  "could not create composite engine credentials, not sure why");
@@ -181,8 +181,8 @@ static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) {
181
181
  TypedData_Get_Struct(other, grpc_rb_credentials,
182
182
  &grpc_rb_credentials_data_type, other_wrapper);
183
183
  wrapper = ALLOC(grpc_rb_credentials);
184
- wrapper->wrapped = grpc_composite_credentials_create(self_wrapper->wrapped,
185
- other_wrapper->wrapped);
184
+ wrapper->wrapped = grpc_composite_credentials_create(
185
+ self_wrapper->wrapped, other_wrapper->wrapped, NULL);
186
186
  if (wrapper->wrapped == NULL) {
187
187
  rb_raise(rb_eRuntimeError,
188
188
  "could not create composite credentials, not sure why");
@@ -234,12 +234,13 @@ static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
234
234
  return Qnil;
235
235
  }
236
236
  if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
237
- creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL);
237
+ creds =
238
+ grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL, NULL);
238
239
  } else {
239
240
  key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
240
241
  key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
241
242
  creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs),
242
- &key_cert_pair);
243
+ &key_cert_pair, NULL);
243
244
  }
244
245
  if (creds == NULL) {
245
246
  rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
@@ -51,7 +51,8 @@ static VALUE grpc_rb_cTimeVal = Qnil;
51
51
 
52
52
  static rb_data_type_t grpc_rb_timespec_data_type = {
53
53
  "gpr_timespec",
54
- {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE},
54
+ {GRPC_RB_GC_NOT_MARKED, GRPC_RB_GC_DONT_FREE, GRPC_RB_MEMSIZE_UNAVAILABLE,
55
+ {NULL, NULL}},
55
56
  NULL,
56
57
  NULL,
57
58
  RUBY_TYPED_FREE_IMMEDIATELY};
@@ -75,6 +76,7 @@ VALUE grpc_rb_cannot_init(VALUE self) {
75
76
 
76
77
  /* Init/Clone func that fails by raising an exception. */
77
78
  VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
79
+ (void)self;
78
80
  rb_raise(rb_eTypeError,
79
81
  "initialization of %s only allowed from the gRPC native layer",
80
82
  rb_obj_classname(copy));
@@ -98,6 +100,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
98
100
  const char *tstr = interval ? "time interval" : "time";
99
101
  const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
100
102
 
103
+ t.clock_type = GPR_CLOCK_REALTIME;
101
104
  switch (TYPE(time)) {
102
105
  case T_DATA:
103
106
  if (CLASS_OF(time) == grpc_rb_cTimeVal) {
@@ -136,7 +139,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
136
139
  rb_raise(rb_eRangeError, "%f out of Time range",
137
140
  RFLOAT_VALUE(time));
138
141
  }
139
- t.tv_nsec = (time_t)(d * 1e9 + 0.5);
142
+ t.tv_nsec = (int)(d * 1e9 + 0.5);
140
143
  }
141
144
  break;
142
145
 
@@ -206,10 +209,12 @@ static ID id_to_s;
206
209
  /* Converts a wrapped time constant to a standard time. */
207
210
  static VALUE grpc_rb_time_val_to_time(VALUE self) {
208
211
  gpr_timespec *time_const = NULL;
212
+ gpr_timespec real_time;
209
213
  TypedData_Get_Struct(self, gpr_timespec, &grpc_rb_timespec_data_type,
210
214
  time_const);
211
- return rb_funcall(rb_cTime, id_at, 2, INT2NUM(time_const->tv_sec),
212
- INT2NUM(time_const->tv_nsec));
215
+ real_time = gpr_convert_clock_type(*time_const, GPR_CLOCK_REALTIME);
216
+ return rb_funcall(rb_cTime, id_at, 2, INT2NUM(real_time.tv_sec),
217
+ INT2NUM(real_time.tv_nsec));
213
218
  }
214
219
 
215
220
  /* Invokes inspect on the ctime version of the time val. */
@@ -222,24 +227,31 @@ static VALUE grpc_rb_time_val_to_s(VALUE self) {
222
227
  return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
223
228
  }
224
229
 
230
+ static gpr_timespec zero_realtime;
231
+ static gpr_timespec inf_future_realtime;
232
+ static gpr_timespec inf_past_realtime;
233
+
225
234
  /* Adds a module with constants that map to gpr's static timeval structs. */
226
235
  static void Init_grpc_time_consts() {
227
236
  VALUE grpc_rb_mTimeConsts =
228
237
  rb_define_module_under(grpc_rb_mGrpcCore, "TimeConsts");
229
238
  grpc_rb_cTimeVal =
230
239
  rb_define_class_under(grpc_rb_mGrpcCore, "TimeSpec", rb_cObject);
240
+ zero_realtime = gpr_time_0(GPR_CLOCK_REALTIME);
241
+ inf_future_realtime = gpr_inf_future(GPR_CLOCK_REALTIME);
242
+ inf_past_realtime = gpr_inf_past(GPR_CLOCK_REALTIME);
231
243
  rb_define_const(
232
244
  grpc_rb_mTimeConsts, "ZERO",
233
245
  TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
234
- (void *)&gpr_time_0));
246
+ (void *)&zero_realtime));
235
247
  rb_define_const(
236
248
  grpc_rb_mTimeConsts, "INFINITE_FUTURE",
237
249
  TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
238
- (void *)&gpr_inf_future));
250
+ (void *)&inf_future_realtime));
239
251
  rb_define_const(
240
252
  grpc_rb_mTimeConsts, "INFINITE_PAST",
241
253
  TypedData_Wrap_Struct(grpc_rb_cTimeVal, &grpc_rb_timespec_data_type,
242
- (void *)&gpr_inf_past));
254
+ (void *)&inf_past_realtime));
243
255
  rb_define_method(grpc_rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
244
256
  rb_define_method(grpc_rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
245
257
  rb_define_method(grpc_rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
@@ -250,7 +262,10 @@ static void Init_grpc_time_consts() {
250
262
  id_tv_nsec = rb_intern("tv_nsec");
251
263
  }
252
264
 
253
- static void grpc_rb_shutdown(ruby_vm_t *vm) { grpc_shutdown(); }
265
+ static void grpc_rb_shutdown(ruby_vm_t *vm) {
266
+ (void)vm;
267
+ grpc_shutdown();
268
+ }
254
269
 
255
270
  /* Initialize the GRPC module structs */
256
271
 
@@ -49,6 +49,9 @@ static VALUE grpc_rb_cServer = Qnil;
49
49
  /* id_at is the constructor method of the ruby standard Time class. */
50
50
  static ID id_at;
51
51
 
52
+ /* id_insecure_server is used to indicate that a server is insecure */
53
+ static VALUE id_insecure_server;
54
+
52
55
  /* grpc_rb_server wraps a grpc_server. It provides a peer ruby object,
53
56
  'mark' to minimize copying when a server is created from ruby. */
54
57
  typedef struct grpc_rb_server {
@@ -94,7 +97,8 @@ static void grpc_rb_server_mark(void *p) {
94
97
 
95
98
  static const rb_data_type_t grpc_rb_server_data_type = {
96
99
  "grpc_server",
97
- {grpc_rb_server_mark, grpc_rb_server_free, GRPC_RB_MEMSIZE_UNAVAILABLE},
100
+ {grpc_rb_server_mark, grpc_rb_server_free, GRPC_RB_MEMSIZE_UNAVAILABLE,
101
+ {NULL, NULL}},
98
102
  NULL,
99
103
  NULL,
100
104
  /* It is unsafe to specify RUBY_TYPED_FREE_IMMEDIATELY because the free function would block
@@ -127,7 +131,7 @@ static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
127
131
  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type,
128
132
  wrapper);
129
133
  grpc_rb_hash_convert_to_channel_args(channel_args, &args);
130
- srv = grpc_server_create(&args);
134
+ srv = grpc_server_create(&args, NULL);
131
135
 
132
136
  if (args.args != NULL) {
133
137
  xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
@@ -135,7 +139,7 @@ static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
135
139
  if (srv == NULL) {
136
140
  rb_raise(rb_eRuntimeError, "could not create a gRPC server, not sure why");
137
141
  }
138
- grpc_server_register_completion_queue(srv, cq);
142
+ grpc_server_register_completion_queue(srv, cq, NULL);
139
143
  wrapper->wrapped = srv;
140
144
 
141
145
  /* Add the cq as the server's mark object. This ensures the ruby cq can't be
@@ -212,6 +216,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
212
216
  grpc_call_error err;
213
217
  request_call_stack st;
214
218
  VALUE result;
219
+ gpr_timespec deadline;
215
220
  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
216
221
  if (s->wrapped == NULL) {
217
222
  rb_raise(rb_eRuntimeError, "destroyed!");
@@ -232,6 +237,7 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
232
237
  grpc_call_error_detail_of(err), err);
233
238
  return Qnil;
234
239
  }
240
+
235
241
  ev = grpc_rb_completion_queue_pluck_event(cqueue, tag_new, timeout);
236
242
  if (ev.type == GRPC_QUEUE_TIMEOUT) {
237
243
  grpc_request_call_stack_cleanup(&st);
@@ -244,15 +250,13 @@ static VALUE grpc_rb_server_request_call(VALUE self, VALUE cqueue,
244
250
  }
245
251
 
246
252
  /* build the NewServerRpc struct result */
253
+ deadline = gpr_convert_clock_type(st.details.deadline, GPR_CLOCK_REALTIME);
247
254
  result = rb_struct_new(
248
- grpc_rb_sNewServerRpc,
249
- rb_str_new2(st.details.method),
255
+ grpc_rb_sNewServerRpc, rb_str_new2(st.details.method),
250
256
  rb_str_new2(st.details.host),
251
- rb_funcall(rb_cTime, id_at, 2, INT2NUM(st.details.deadline.tv_sec),
252
- INT2NUM(st.details.deadline.tv_nsec)),
253
- grpc_rb_md_ary_to_h(&st.md_ary),
254
- grpc_rb_wrap_call(call),
255
- NULL);
257
+ rb_funcall(rb_cTime, id_at, 2, INT2NUM(deadline.tv_sec),
258
+ INT2NUM(deadline.tv_nsec)),
259
+ grpc_rb_md_ary_to_h(&st.md_ary), grpc_rb_wrap_call(call), NULL);
256
260
  grpc_request_call_stack_cleanup(&st);
257
261
  return result;
258
262
  }
@@ -298,43 +302,22 @@ static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
298
302
  if (s->wrapped != NULL) {
299
303
  grpc_server_shutdown_and_notify(s->wrapped, cq, NULL);
300
304
  ev = grpc_rb_completion_queue_pluck_event(cqueue, Qnil, timeout);
301
-
302
305
  if (!ev.success) {
303
- rb_warn("server shutdown failed, there will be a LEAKED object warning");
304
- return Qnil;
305
- /*
306
- TODO: renable the rb_raise below.
307
-
308
- At the moment if the timeout is INFINITE_FUTURE as recommended, the
309
- pluck blocks forever, even though
310
-
311
- the outstanding server_request_calls correctly fail on the other
312
- thread that they are running on.
313
-
314
- it's almost as if calls that fail on the other thread do not get
315
- cleaned up by shutdown request, even though it caused htem to
316
- terminate.
317
-
318
- rb_raise(rb_eRuntimeError, "grpc server shutdown did not succeed");
319
- return Qnil;
320
-
321
- The workaround is just to use a timeout and return without really
322
- shutting down the server, and rely on the grpc core garbage collection
323
- it down as a 'LEAKED OBJECT'.
324
-
325
- */
306
+ rb_warn("server shutdown failed, cancelling the calls, objects may leak");
307
+ grpc_server_cancel_all_calls(s->wrapped);
308
+ return Qfalse;
326
309
  }
327
310
  grpc_server_destroy(s->wrapped);
328
311
  s->wrapped = NULL;
329
312
  }
330
- return Qnil;
313
+ return Qtrue;
331
314
  }
332
315
 
333
316
  /*
334
317
  call-seq:
335
318
  // insecure port
336
319
  insecure_server = Server.new(cq, {'arg1': 'value1'})
337
- insecure_server.add_http2_port('mydomain:50051')
320
+ insecure_server.add_http2_port('mydomain:50051', :this_port_is_insecure)
338
321
 
339
322
  // secure port
340
323
  server_creds = ...
@@ -342,22 +325,24 @@ static VALUE grpc_rb_server_destroy(int argc, VALUE *argv, VALUE self) {
342
325
  secure_server.add_http_port('mydomain:50051', server_creds)
343
326
 
344
327
  Adds a http2 port to server */
345
- static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
346
- VALUE port = Qnil;
347
- VALUE rb_creds = Qnil;
328
+ static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port,
329
+ VALUE rb_creds) {
348
330
  grpc_rb_server *s = NULL;
349
331
  grpc_server_credentials *creds = NULL;
350
332
  int recvd_port = 0;
351
333
 
352
- /* "11" == 1 mandatory args, 1 (rb_creds) is optional */
353
- rb_scan_args(argc, argv, "11", &port, &rb_creds);
354
-
355
334
  TypedData_Get_Struct(self, grpc_rb_server, &grpc_rb_server_data_type, s);
356
335
  if (s->wrapped == NULL) {
357
336
  rb_raise(rb_eRuntimeError, "destroyed!");
358
337
  return Qnil;
359
- } else if (rb_creds == Qnil) {
360
- recvd_port = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
338
+ } else if (TYPE(rb_creds) == T_SYMBOL) {
339
+ if (id_insecure_server != SYM2ID(rb_creds)) {
340
+ rb_raise(rb_eTypeError,
341
+ "bad creds symbol, want :this_port_is_insecure");
342
+ return Qnil;
343
+ }
344
+ recvd_port =
345
+ grpc_server_add_insecure_http2_port(s->wrapped, StringValueCStr(port));
361
346
  if (recvd_port == 0) {
362
347
  rb_raise(rb_eRuntimeError,
363
348
  "could not add port %s to server, not sure why",
@@ -397,8 +382,9 @@ void Init_grpc_server() {
397
382
  rb_define_alias(grpc_rb_cServer, "close", "destroy");
398
383
  rb_define_method(grpc_rb_cServer, "add_http2_port",
399
384
  grpc_rb_server_add_http2_port,
400
- -1);
385
+ 2);
401
386
  id_at = rb_intern("at");
387
+ id_insecure_server = rb_intern("this_port_is_insecure");
402
388
  }
403
389
 
404
390
  /* Gets the wrapped server from the ruby wrapper */
@@ -89,7 +89,7 @@ static void grpc_rb_server_credentials_mark(void *p) {
89
89
  static const rb_data_type_t grpc_rb_server_credentials_data_type = {
90
90
  "grpc_server_credentials",
91
91
  {grpc_rb_server_credentials_mark, grpc_rb_server_credentials_free,
92
- GRPC_RB_MEMSIZE_UNAVAILABLE},
92
+ GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}},
93
93
  NULL, NULL,
94
94
  RUBY_TYPED_FREE_IMMEDIATELY
95
95
  };
@@ -135,61 +135,117 @@ static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
135
135
  return copy;
136
136
  }
137
137
 
138
- /* The attribute used on the mark object to hold the pem_root_certs. */
138
+ /* The attribute used on the mark object to preserve the pem_root_certs. */
139
139
  static ID id_pem_root_certs;
140
140
 
141
- /* The attribute used on the mark object to hold the pem_private_key. */
142
- static ID id_pem_private_key;
141
+ /* The attribute used on the mark object to preserve the pem_key_certs */
142
+ static ID id_pem_key_certs;
143
143
 
144
- /* The attribute used on the mark object to hold the pem_private_key. */
145
- static ID id_pem_cert_chain;
144
+ /* The key used to access the pem cert in a key_cert pair hash */
145
+ static VALUE sym_cert_chain;
146
+
147
+ /* The key used to access the pem private key in a key_cert pair hash */
148
+ static VALUE sym_private_key;
146
149
 
147
150
  /*
148
151
  call-seq:
149
- creds = ServerCredentials.new(pem_root_certs, pem_private_key,
150
- pem_cert_chain)
151
- creds = ServerCredentials.new(nil, pem_private_key,
152
- pem_cert_chain)
153
-
154
- pem_root_certs: (required) PEM encoding of the server root certificate
155
- pem_private_key: (optional) PEM encoding of the server's private key
156
- pem_cert_chain: (optional) PEM encoding of the server's cert chain
152
+ creds = ServerCredentials.new(nil,
153
+ [{private_key: <pem_private_key1>,
154
+ {cert_chain: <pem_cert_chain1>}],
155
+ force_client_auth)
156
+ creds = ServerCredentials.new(pem_root_certs,
157
+ [{private_key: <pem_private_key1>,
158
+ {cert_chain: <pem_cert_chain1>}],
159
+ force_client_auth)
160
+
161
+ pem_root_certs: (optional) PEM encoding of the server root certificate
162
+ pem_private_key: (required) PEM encoding of the server's private keys
163
+ force_client_auth: indicatees
157
164
 
158
165
  Initializes ServerCredential instances. */
159
166
  static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
160
- VALUE pem_private_key,
161
- VALUE pem_cert_chain) {
162
- /* TODO support multiple key cert pairs in the ruby API. */
167
+ VALUE pem_key_certs,
168
+ VALUE force_client_auth) {
163
169
  grpc_rb_server_credentials *wrapper = NULL;
164
170
  grpc_server_credentials *creds = NULL;
165
- grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
166
- TypedData_Get_Struct(self, grpc_rb_server_credentials,
167
- &grpc_rb_server_credentials_data_type, wrapper);
168
- if (pem_cert_chain == Qnil) {
169
- rb_raise(rb_eRuntimeError,
170
- "could not create a server credential: nil pem_cert_chain");
171
+ grpc_ssl_pem_key_cert_pair *key_cert_pairs = NULL;
172
+ VALUE cert = Qnil;
173
+ VALUE key = Qnil;
174
+ VALUE key_cert = Qnil;
175
+ int auth_client = 0;
176
+ int num_key_certs = 0;
177
+ int i;
178
+
179
+ if (NIL_P(force_client_auth) ||
180
+ !(force_client_auth == Qfalse || force_client_auth == Qtrue)) {
181
+ rb_raise(rb_eTypeError,
182
+ "bad force_client_auth: got:<%s> want: <True|False|nil>",
183
+ rb_obj_classname(force_client_auth));
171
184
  return Qnil;
172
- } else if (pem_private_key == Qnil) {
173
- rb_raise(rb_eRuntimeError,
174
- "could not create a server credential: nil pem_private_key");
185
+ }
186
+ if (NIL_P(pem_key_certs) || TYPE(pem_key_certs) != T_ARRAY) {
187
+ rb_raise(rb_eTypeError, "bad pem_key_certs: got:<%s> want: <Array>",
188
+ rb_obj_classname(pem_key_certs));
189
+ return Qnil;
190
+ }
191
+ num_key_certs = RARRAY_LEN(pem_key_certs);
192
+ if (num_key_certs == 0) {
193
+ rb_raise(rb_eTypeError, "bad pem_key_certs: it had no elements");
175
194
  return Qnil;
176
195
  }
177
- key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
178
- key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
196
+ for (i = 0; i < num_key_certs; i++) {
197
+ key_cert = rb_ary_entry(pem_key_certs, i);
198
+ if (key_cert == Qnil) {
199
+ rb_raise(rb_eTypeError,
200
+ "could not create a server credential: nil key_cert");
201
+ return Qnil;
202
+ } else if (TYPE(key_cert) != T_HASH) {
203
+ rb_raise(rb_eTypeError,
204
+ "could not create a server credential: want <Hash>, got <%s>",
205
+ rb_obj_classname(key_cert));
206
+ return Qnil;
207
+ } else if (rb_hash_aref(key_cert, sym_private_key) == Qnil) {
208
+ rb_raise(rb_eTypeError,
209
+ "could not create a server credential: want nil private key");
210
+ return Qnil;
211
+ } else if (rb_hash_aref(key_cert, sym_cert_chain) == Qnil) {
212
+ rb_raise(rb_eTypeError,
213
+ "could not create a server credential: want nil cert chain");
214
+ return Qnil;
215
+ }
216
+ }
217
+
218
+ auth_client = TYPE(force_client_auth) == T_TRUE;
219
+ key_cert_pairs = ALLOC_N(grpc_ssl_pem_key_cert_pair, num_key_certs);
220
+ for (i = 0; i < num_key_certs; i++) {
221
+ key_cert = rb_ary_entry(pem_key_certs, i);
222
+ key = rb_hash_aref(key_cert, sym_private_key);
223
+ cert = rb_hash_aref(key_cert, sym_cert_chain);
224
+ key_cert_pairs[i].private_key = RSTRING_PTR(key);
225
+ key_cert_pairs[i].cert_chain = RSTRING_PTR(cert);
226
+ }
227
+
228
+ TypedData_Get_Struct(self, grpc_rb_server_credentials,
229
+ &grpc_rb_server_credentials_data_type, wrapper);
230
+
179
231
  if (pem_root_certs == Qnil) {
180
- creds = grpc_ssl_server_credentials_create(NULL, &key_cert_pair, 1);
232
+ creds = grpc_ssl_server_credentials_create(NULL, key_cert_pairs,
233
+ num_key_certs,
234
+ auth_client, NULL);
181
235
  } else {
182
236
  creds = grpc_ssl_server_credentials_create(RSTRING_PTR(pem_root_certs),
183
- &key_cert_pair, 1);
237
+ key_cert_pairs, num_key_certs,
238
+ auth_client, NULL);
184
239
  }
240
+ xfree(key_cert_pairs);
185
241
  if (creds == NULL) {
186
242
  rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
243
+ return Qnil;
187
244
  }
188
245
  wrapper->wrapped = creds;
189
246
 
190
247
  /* Add the input objects as hidden fields to preserve them. */
191
- rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
192
- rb_ivar_set(self, id_pem_private_key, pem_private_key);
248
+ rb_ivar_set(self, id_pem_key_certs, pem_key_certs);
193
249
  rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
194
250
 
195
251
  return self;
@@ -209,9 +265,10 @@ void Init_grpc_server_credentials() {
209
265
  rb_define_method(grpc_rb_cServerCredentials, "initialize_copy",
210
266
  grpc_rb_server_credentials_init_copy, 1);
211
267
 
212
- id_pem_cert_chain = rb_intern("__pem_cert_chain");
213
- id_pem_private_key = rb_intern("__pem_private_key");
268
+ id_pem_key_certs = rb_intern("__pem_key_certs");
214
269
  id_pem_root_certs = rb_intern("__pem_root_certs");
270
+ sym_private_key = ID2SYM(rb_intern("private_key"));
271
+ sym_cert_chain = ID2SYM(rb_intern("cert_chain"));
215
272
  }
216
273
 
217
274
  /* Gets the wrapped grpc_server_credentials from the ruby wrapper */