grpc 0.6.0 → 0.6.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.rubocop_todo.yml +12 -20
  4. data/CHANGELOG.md +11 -0
  5. data/Rakefile +1 -0
  6. data/bin/apis/pubsub_demo.rb +3 -6
  7. data/bin/interop/interop_client.rb +43 -3
  8. data/bin/interop/interop_server.rb +1 -1
  9. data/bin/math_server.rb +1 -1
  10. data/bin/noproto_server.rb +1 -1
  11. data/ext/grpc/rb_byte_buffer.c +15 -189
  12. data/ext/grpc/rb_byte_buffer.h +4 -12
  13. data/ext/grpc/rb_call.c +514 -307
  14. data/ext/grpc/rb_call.h +4 -4
  15. data/ext/grpc/rb_channel.c +58 -34
  16. data/ext/grpc/rb_channel.h +0 -3
  17. data/ext/grpc/rb_channel_args.c +13 -4
  18. data/ext/grpc/rb_completion_queue.c +50 -23
  19. data/ext/grpc/rb_completion_queue.h +7 -3
  20. data/ext/grpc/rb_credentials.c +40 -28
  21. data/ext/grpc/rb_credentials.h +0 -4
  22. data/ext/grpc/rb_grpc.c +86 -67
  23. data/ext/grpc/rb_grpc.h +20 -10
  24. data/ext/grpc/rb_server.c +119 -26
  25. data/ext/grpc/rb_server.h +0 -4
  26. data/ext/grpc/rb_server_credentials.c +29 -16
  27. data/ext/grpc/rb_server_credentials.h +0 -4
  28. data/grpc.gemspec +11 -8
  29. data/lib/grpc.rb +1 -1
  30. data/lib/grpc/errors.rb +8 -7
  31. data/lib/grpc/generic/active_call.rb +104 -171
  32. data/lib/grpc/generic/bidi_call.rb +32 -60
  33. data/lib/grpc/generic/client_stub.rb +42 -31
  34. data/lib/grpc/generic/rpc_desc.rb +7 -12
  35. data/lib/grpc/generic/rpc_server.rb +253 -170
  36. data/lib/grpc/{core/event.rb → notifier.rb} +25 -9
  37. data/lib/grpc/version.rb +1 -1
  38. data/spec/call_spec.rb +23 -40
  39. data/spec/channel_spec.rb +11 -20
  40. data/spec/client_server_spec.rb +193 -175
  41. data/spec/credentials_spec.rb +2 -2
  42. data/spec/generic/active_call_spec.rb +59 -85
  43. data/spec/generic/client_stub_spec.rb +46 -64
  44. data/spec/generic/rpc_desc_spec.rb +50 -80
  45. data/spec/generic/rpc_server_pool_spec.rb +2 -3
  46. data/spec/generic/rpc_server_spec.rb +158 -29
  47. data/spec/server_spec.rb +1 -1
  48. data/spec/spec_helper.rb +8 -4
  49. metadata +27 -37
  50. data/ext/grpc/rb_event.c +0 -361
  51. data/ext/grpc/rb_event.h +0 -53
  52. data/ext/grpc/rb_metadata.c +0 -215
  53. data/ext/grpc/rb_metadata.h +0 -53
  54. data/spec/alloc_spec.rb +0 -44
  55. data/spec/byte_buffer_spec.rb +0 -67
  56. data/spec/event_spec.rb +0 -53
  57. data/spec/metadata_spec.rb +0 -64
@@ -37,7 +37,6 @@ describe GRPC::RpcDesc do
37
37
  INTERNAL = GRPC::Core::StatusCodes::INTERNAL
38
38
  UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN
39
39
  CallError = GRPC::Core::CallError
40
- EventError = GRPC::Core::EventError
41
40
 
42
41
  before(:each) do
43
42
  @request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode',
@@ -53,49 +52,49 @@ describe GRPC::RpcDesc do
53
52
  @ok_response = Object.new
54
53
  end
55
54
 
55
+ shared_examples 'it handles errors' do
56
+ it 'sends the specified status if BadStatus is raised' do
57
+ expect(@call).to receive(:remote_read).once.and_return(Object.new)
58
+ expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false,
59
+ {})
60
+ this_desc.run_server_method(@call, method(:bad_status))
61
+ end
62
+
63
+ it 'sends status UNKNOWN if other StandardErrors are raised' do
64
+ expect(@call).to receive(:remote_read).once.and_return(Object.new)
65
+ expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
66
+ false, {})
67
+ this_desc.run_server_method(@call, method(:other_error))
68
+ end
69
+
70
+ it 'absorbs CallError with no further action' do
71
+ expect(@call).to receive(:remote_read).once.and_raise(CallError)
72
+ blk = proc do
73
+ this_desc.run_server_method(@call, method(:fake_reqresp))
74
+ end
75
+ expect(&blk).to_not raise_error
76
+ end
77
+ end
78
+
56
79
  describe '#run_server_method' do
80
+ let(:fake_md) { { k1: 'v1', k2: 'v2' } }
57
81
  describe 'for request responses' do
82
+ let(:this_desc) { @request_response }
58
83
  before(:each) do
59
84
  @call = double('active_call')
60
85
  allow(@call).to receive(:single_req_view).and_return(@call)
61
- allow(@call).to receive(:gc)
62
- end
63
-
64
- it 'sends the specified status if BadStatus is raised' do
65
- expect(@call).to receive(:remote_read).once.and_return(Object.new)
66
- expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK')
67
- @request_response.run_server_method(@call, method(:bad_status))
68
- end
69
-
70
- it 'sends status UNKNOWN if other StandardErrors are raised' do
71
- expect(@call).to receive(:remote_read).once.and_return(Object.new)
72
- expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason)
73
- @request_response.run_server_method(@call, method(:other_error))
74
- end
75
-
76
- it 'absorbs EventError with no further action' do
77
- expect(@call).to receive(:remote_read).once.and_raise(EventError)
78
- blk = proc do
79
- @request_response.run_server_method(@call, method(:fake_reqresp))
80
- end
81
- expect(&blk).to_not raise_error
82
86
  end
83
87
 
84
- it 'absorbs CallError with no further action' do
85
- expect(@call).to receive(:remote_read).once.and_raise(CallError)
86
- blk = proc do
87
- @request_response.run_server_method(@call, method(:fake_reqresp))
88
- end
89
- expect(&blk).to_not raise_error
90
- end
88
+ it_behaves_like 'it handles errors'
91
89
 
92
90
  it 'sends a response and closes the stream if there no errors' do
93
91
  req = Object.new
94
92
  expect(@call).to receive(:remote_read).once.and_return(req)
95
93
  expect(@call).to receive(:remote_send).once.with(@ok_response)
96
- expect(@call).to receive(:send_status).once.with(OK, 'OK')
97
- expect(@call).to receive(:finished).once
98
- @request_response.run_server_method(@call, method(:fake_reqresp))
94
+ expect(@call).to receive(:output_metadata).and_return(fake_md)
95
+ expect(@call).to receive(:send_status).once.with(OK, 'OK', true,
96
+ **fake_md)
97
+ this_desc.run_server_method(@call, method(:fake_reqresp))
99
98
  end
100
99
  end
101
100
 
@@ -103,27 +102,20 @@ describe GRPC::RpcDesc do
103
102
  before(:each) do
104
103
  @call = double('active_call')
105
104
  allow(@call).to receive(:multi_req_view).and_return(@call)
106
- allow(@call).to receive(:gc)
107
105
  end
108
106
 
109
107
  it 'sends the specified status if BadStatus is raised' do
110
- expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK')
108
+ expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false,
109
+ {})
111
110
  @client_streamer.run_server_method(@call, method(:bad_status_alt))
112
111
  end
113
112
 
114
113
  it 'sends status UNKNOWN if other StandardErrors are raised' do
115
- expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason)
114
+ expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason,
115
+ false, {})
116
116
  @client_streamer.run_server_method(@call, method(:other_error_alt))
117
117
  end
118
118
 
119
- it 'absorbs EventError with no further action' do
120
- expect(@call).to receive(:remote_send).once.and_raise(EventError)
121
- blk = proc do
122
- @client_streamer.run_server_method(@call, method(:fake_clstream))
123
- end
124
- expect(&blk).to_not raise_error
125
- end
126
-
127
119
  it 'absorbs CallError with no further action' do
128
120
  expect(@call).to receive(:remote_send).once.and_raise(CallError)
129
121
  blk = proc do
@@ -134,53 +126,29 @@ describe GRPC::RpcDesc do
134
126
 
135
127
  it 'sends a response and closes the stream if there no errors' do
136
128
  expect(@call).to receive(:remote_send).once.with(@ok_response)
137
- expect(@call).to receive(:send_status).once.with(OK, 'OK')
138
- expect(@call).to receive(:finished).once
129
+ expect(@call).to receive(:output_metadata).and_return(fake_md)
130
+ expect(@call).to receive(:send_status).once.with(OK, 'OK', true,
131
+ **fake_md)
139
132
  @client_streamer.run_server_method(@call, method(:fake_clstream))
140
133
  end
141
134
  end
142
135
 
143
136
  describe 'for server streaming' do
137
+ let(:this_desc) { @request_response }
144
138
  before(:each) do
145
139
  @call = double('active_call')
146
140
  allow(@call).to receive(:single_req_view).and_return(@call)
147
- allow(@call).to receive(:gc)
148
- end
149
-
150
- it 'sends the specified status if BadStatus is raised' do
151
- expect(@call).to receive(:remote_read).once.and_return(Object.new)
152
- expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK')
153
- @server_streamer.run_server_method(@call, method(:bad_status))
154
- end
155
-
156
- it 'sends status UNKNOWN if other StandardErrors are raised' do
157
- expect(@call).to receive(:remote_read).once.and_return(Object.new)
158
- expect(@call).to receive(:send_status) .once.with(UNKNOWN, @no_reason)
159
- @server_streamer.run_server_method(@call, method(:other_error))
160
- end
161
-
162
- it 'absorbs EventError with no further action' do
163
- expect(@call).to receive(:remote_read).once.and_raise(EventError)
164
- blk = proc do
165
- @server_streamer.run_server_method(@call, method(:fake_svstream))
166
- end
167
- expect(&blk).to_not raise_error
168
141
  end
169
142
 
170
- it 'absorbs CallError with no further action' do
171
- expect(@call).to receive(:remote_read).once.and_raise(CallError)
172
- blk = proc do
173
- @server_streamer.run_server_method(@call, method(:fake_svstream))
174
- end
175
- expect(&blk).to_not raise_error
176
- end
143
+ it_behaves_like 'it handles errors'
177
144
 
178
145
  it 'sends a response and closes the stream if there no errors' do
179
146
  req = Object.new
180
147
  expect(@call).to receive(:remote_read).once.and_return(req)
181
148
  expect(@call).to receive(:remote_send).twice.with(@ok_response)
182
- expect(@call).to receive(:send_status).once.with(OK, 'OK')
183
- expect(@call).to receive(:finished).once
149
+ expect(@call).to receive(:output_metadata).and_return(fake_md)
150
+ expect(@call).to receive(:send_status).once.with(OK, 'OK', true,
151
+ **fake_md)
184
152
  @server_streamer.run_server_method(@call, method(:fake_svstream))
185
153
  end
186
154
  end
@@ -191,26 +159,28 @@ describe GRPC::RpcDesc do
191
159
  enq_th, rwl_th = double('enqueue_th'), ('read_write_loop_th')
192
160
  allow(enq_th).to receive(:join)
193
161
  allow(rwl_th).to receive(:join)
194
- allow(@call).to receive(:gc)
195
162
  end
196
163
 
197
164
  it 'sends the specified status if BadStatus is raised' do
198
165
  e = GRPC::BadStatus.new(@bs_code, 'NOK')
199
166
  expect(@call).to receive(:run_server_bidi).and_raise(e)
200
- expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK')
167
+ expect(@call).to receive(:send_status).once.with(@bs_code, 'NOK', false,
168
+ {})
201
169
  @bidi_streamer.run_server_method(@call, method(:bad_status_alt))
202
170
  end
203
171
 
204
172
  it 'sends status UNKNOWN if other StandardErrors are raised' do
205
173
  expect(@call).to receive(:run_server_bidi).and_raise(StandardError)
206
- expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason)
174
+ expect(@call).to receive(:send_status).once.with(UNKNOWN, @no_reason,
175
+ false, {})
207
176
  @bidi_streamer.run_server_method(@call, method(:other_error_alt))
208
177
  end
209
178
 
210
179
  it 'closes the stream if there no errors' do
211
180
  expect(@call).to receive(:run_server_bidi)
212
- expect(@call).to receive(:send_status).once.with(OK, 'OK')
213
- expect(@call).to receive(:finished).once
181
+ expect(@call).to receive(:output_metadata).and_return(fake_md)
182
+ expect(@call).to receive(:send_status).once.with(OK, 'OK', true,
183
+ **fake_md)
214
184
  @bidi_streamer.run_server_method(@call, method(:fake_bidistream))
215
185
  end
216
186
  end
@@ -28,11 +28,10 @@
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
30
  require 'grpc'
31
- require 'xray/thread_dump_signal_handler'
32
31
 
33
- Pool = GRPC::RpcServer::Pool
32
+ describe GRPC::Pool do
33
+ Pool = GRPC::Pool
34
34
 
35
- describe Pool do
36
35
  describe '#new' do
37
36
  it 'raises if a non-positive size is used' do
38
37
  expect { Pool.new(0) }.to raise_error
@@ -28,7 +28,6 @@
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
30
  require 'grpc'
31
- require 'xray/thread_dump_signal_handler'
32
31
 
33
32
  def load_test_certs
34
33
  test_root = File.join(File.dirname(File.dirname(__FILE__)), 'testdata')
@@ -58,18 +57,20 @@ class NoRpcImplementation
58
57
  rpc :an_rpc, EchoMsg, EchoMsg
59
58
  end
60
59
 
61
- # A test service with an implementation.
60
+ # A test service with an echo implementation.
62
61
  class EchoService
63
62
  include GRPC::GenericService
64
63
  rpc :an_rpc, EchoMsg, EchoMsg
65
64
  attr_reader :received_md
66
65
 
67
- def initialize(_default_var = 'ignored')
66
+ def initialize(**kw)
67
+ @trailing_metadata = kw
68
68
  @received_md = []
69
69
  end
70
70
 
71
71
  def an_rpc(req, call)
72
72
  logger.info('echo service received a request')
73
+ call.output_metadata.update(@trailing_metadata)
73
74
  @received_md << call.metadata unless call.metadata.nil?
74
75
  req
75
76
  end
@@ -77,6 +78,25 @@ end
77
78
 
78
79
  EchoStub = EchoService.rpc_stub_class
79
80
 
81
+ # A test service with an implementation that fails with BadStatus
82
+ class FailingService
83
+ include GRPC::GenericService
84
+ rpc :an_rpc, EchoMsg, EchoMsg
85
+ attr_reader :details, :code, :md
86
+
87
+ def initialize(_default_var = 'ignored')
88
+ @details = 'app error'
89
+ @code = 101
90
+ @md = { failed_method: 'an_rpc' }
91
+ end
92
+
93
+ def an_rpc(_req, _call)
94
+ fail GRPC::BadStatus.new(@code, @details, **@md)
95
+ end
96
+ end
97
+
98
+ FailingStub = FailingService.rpc_stub_class
99
+
80
100
  # A slow test service.
81
101
  class SlowService
82
102
  include GRPC::GenericService
@@ -301,21 +321,20 @@ describe GRPC::RpcServer do
301
321
  end
302
322
 
303
323
  describe '#run' do
304
- before(:each) do
305
- @client_opts = {
306
- channel_override: @ch
307
- }
308
- @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
309
- @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
310
- server_opts = {
311
- server_override: @server,
312
- completion_queue_override: @server_queue,
313
- poll_period: 1
314
- }
315
- @srv = RpcServer.new(**server_opts)
316
- end
324
+ let(:client_opts) { { channel_override: @ch } }
325
+ let(:marshal) { EchoService.rpc_descs[:an_rpc].marshal_proc }
326
+ let(:unmarshal) { EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output) }
327
+
328
+ context 'with no connect_metadata' do
329
+ before(:each) do
330
+ server_opts = {
331
+ server_override: @server,
332
+ completion_queue_override: @server_queue,
333
+ poll_period: 1
334
+ }
335
+ @srv = RpcServer.new(**server_opts)
336
+ end
317
337
 
318
- describe 'when running' do
319
338
  it 'should return NOT_FOUND status on unknown methods', server: true do
320
339
  @srv.handle(EchoService)
321
340
  t = Thread.new { @srv.run }
@@ -323,8 +342,8 @@ describe GRPC::RpcServer do
323
342
  req = EchoMsg.new
324
343
  blk = proc do
325
344
  cq = GRPC::Core::CompletionQueue.new
326
- stub = GRPC::ClientStub.new(@host, cq, **@client_opts)
327
- stub.request_response('/unknown', req, @marshal, @unmarshal)
345
+ stub = GRPC::ClientStub.new(@host, cq, **client_opts)
346
+ stub.request_response('/unknown', req, marshal, unmarshal)
328
347
  end
329
348
  expect(&blk).to raise_error GRPC::BadStatus
330
349
  @srv.stop
@@ -337,7 +356,7 @@ describe GRPC::RpcServer do
337
356
  @srv.wait_till_running
338
357
  req = EchoMsg.new
339
358
  n = 5 # arbitrary
340
- stub = EchoStub.new(@host, **@client_opts)
359
+ stub = EchoStub.new(@host, **client_opts)
341
360
  n.times { expect(stub.an_rpc(req)).to be_a(EchoMsg) }
342
361
  @srv.stop
343
362
  t.join
@@ -349,7 +368,7 @@ describe GRPC::RpcServer do
349
368
  t = Thread.new { @srv.run }
350
369
  @srv.wait_till_running
351
370
  req = EchoMsg.new
352
- stub = EchoStub.new(@host, **@client_opts)
371
+ stub = EchoStub.new(@host, **client_opts)
353
372
  expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
354
373
  wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
355
374
  expect(service.received_md).to eq(wanted_md)
@@ -363,8 +382,8 @@ describe GRPC::RpcServer do
363
382
  t = Thread.new { @srv.run }
364
383
  @srv.wait_till_running
365
384
  req = EchoMsg.new
366
- stub = SlowStub.new(@host, **@client_opts)
367
- deadline = service.delay + 0.5 # wait for long enough
385
+ stub = SlowStub.new(@host, **client_opts)
386
+ deadline = service.delay + 1.0 # wait for long enough
368
387
  expect(stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
369
388
  wanted_md = [{ 'k1' => 'v1', 'k2' => 'v2' }]
370
389
  expect(service.received_md).to eq(wanted_md)
@@ -378,7 +397,7 @@ describe GRPC::RpcServer do
378
397
  t = Thread.new { @srv.run }
379
398
  @srv.wait_till_running
380
399
  req = EchoMsg.new
381
- stub = SlowStub.new(@host, **@client_opts)
400
+ stub = SlowStub.new(@host, **client_opts)
382
401
  deadline = 0.1 # too short for SlowService to respond
383
402
  blk = proc { stub.an_rpc(req, deadline, k1: 'v1', k2: 'v2') }
384
403
  expect(&blk).to raise_error GRPC::BadStatus
@@ -388,19 +407,37 @@ describe GRPC::RpcServer do
388
407
  t.join
389
408
  end
390
409
 
410
+ it 'should handle cancellation correctly', server: true do
411
+ service = SlowService.new
412
+ @srv.handle(service)
413
+ t = Thread.new { @srv.run }
414
+ @srv.wait_till_running
415
+ req = EchoMsg.new
416
+ stub = SlowStub.new(@host, **client_opts)
417
+ op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
418
+ Thread.new do # cancel the call
419
+ sleep 0.1
420
+ op.cancel
421
+ end
422
+ expect { op.execute }.to raise_error GRPC::Cancelled
423
+ @srv.stop
424
+ t.join
425
+ end
426
+
391
427
  it 'should receive updated metadata', server: true do
392
428
  service = EchoService.new
393
429
  @srv.handle(service)
394
430
  t = Thread.new { @srv.run }
395
431
  @srv.wait_till_running
396
432
  req = EchoMsg.new
397
- @client_opts[:update_metadata] = proc do |md|
433
+ client_opts[:update_metadata] = proc do |md|
398
434
  md[:k1] = 'updated-v1'
399
435
  md
400
436
  end
401
- stub = EchoStub.new(@host, **@client_opts)
437
+ stub = EchoStub.new(@host, **client_opts)
402
438
  expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
403
- wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2' }]
439
+ wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2',
440
+ 'jwt_aud_uri' => "https://#{@host}/EchoService" }]
404
441
  expect(service.received_md).to eq(wanted_md)
405
442
  @srv.stop
406
443
  t.join
@@ -415,7 +452,7 @@ describe GRPC::RpcServer do
415
452
  threads = []
416
453
  n.times do
417
454
  threads << Thread.new do
418
- stub = EchoStub.new(@host, **@client_opts)
455
+ stub = EchoStub.new(@host, **client_opts)
419
456
  q << stub.an_rpc(req)
420
457
  end
421
458
  end
@@ -443,7 +480,7 @@ describe GRPC::RpcServer do
443
480
  one_failed_as_unavailable = false
444
481
  n.times do
445
482
  threads << Thread.new do
446
- stub = SlowStub.new(@host, **@client_opts)
483
+ stub = SlowStub.new(@host, **client_opts)
447
484
  begin
448
485
  stub.an_rpc(req)
449
486
  rescue GRPC::BadStatus => e
@@ -456,5 +493,97 @@ describe GRPC::RpcServer do
456
493
  expect(one_failed_as_unavailable).to be(true)
457
494
  end
458
495
  end
496
+
497
+ context 'with connect metadata' do
498
+ let(:test_md_proc) do
499
+ proc do |mth, md|
500
+ res = md.clone
501
+ res['method'] = mth
502
+ res['connect_k1'] = 'connect_v1'
503
+ res
504
+ end
505
+ end
506
+ before(:each) do
507
+ server_opts = {
508
+ server_override: @server,
509
+ completion_queue_override: @server_queue,
510
+ poll_period: 1,
511
+ connect_md_proc: test_md_proc
512
+ }
513
+ @srv = RpcServer.new(**server_opts)
514
+ end
515
+
516
+ it 'should send connect metadata to the client', server: true do
517
+ service = EchoService.new
518
+ @srv.handle(service)
519
+ t = Thread.new { @srv.run }
520
+ @srv.wait_till_running
521
+ req = EchoMsg.new
522
+ stub = EchoStub.new(@host, **client_opts)
523
+ op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
524
+ expect(op.metadata).to be nil
525
+ expect(op.execute).to be_a(EchoMsg)
526
+ wanted_md = {
527
+ 'k1' => 'v1',
528
+ 'k2' => 'v2',
529
+ 'method' => '/EchoService/an_rpc',
530
+ 'connect_k1' => 'connect_v1'
531
+ }
532
+ expect(op.metadata).to eq(wanted_md)
533
+ @srv.stop
534
+ t.join
535
+ end
536
+ end
537
+
538
+ context 'with trailing metadata' do
539
+ before(:each) do
540
+ server_opts = {
541
+ server_override: @server,
542
+ completion_queue_override: @server_queue,
543
+ poll_period: 1
544
+ }
545
+ @srv = RpcServer.new(**server_opts)
546
+ end
547
+
548
+ it 'should be added to BadStatus when requests fail', server: true do
549
+ service = FailingService.new
550
+ @srv.handle(service)
551
+ t = Thread.new { @srv.run }
552
+ @srv.wait_till_running
553
+ req = EchoMsg.new
554
+ stub = FailingStub.new(@host, **client_opts)
555
+ blk = proc { stub.an_rpc(req) }
556
+
557
+ # confirm it raise the expected error
558
+ expect(&blk).to raise_error GRPC::BadStatus
559
+
560
+ # call again and confirm exception contained the trailing metadata.
561
+ begin
562
+ blk.call
563
+ rescue GRPC::BadStatus => e
564
+ expect(e.code).to eq(service.code)
565
+ expect(e.details).to eq(service.details)
566
+ expect(e.metadata).to eq(service.md)
567
+ end
568
+ @srv.stop
569
+ t.join
570
+ end
571
+
572
+ it 'should be received by the client', server: true do
573
+ wanted_trailers = { 'k1' => 'out_v1', 'k2' => 'out_v2' }
574
+ service = EchoService.new(k1: 'out_v1', k2: 'out_v2')
575
+ @srv.handle(service)
576
+ t = Thread.new { @srv.run }
577
+ @srv.wait_till_running
578
+ req = EchoMsg.new
579
+ stub = EchoStub.new(@host, **client_opts)
580
+ op = stub.an_rpc(req, k1: 'v1', k2: 'v2', return_op: true)
581
+ expect(op.metadata).to be nil
582
+ expect(op.execute).to be_a(EchoMsg)
583
+ expect(op.metadata).to eq(wanted_trailers)
584
+ @srv.stop
585
+ t.join
586
+ end
587
+ end
459
588
  end
460
589
  end