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,10 +37,6 @@
37
37
  #include <ruby.h>
38
38
  #include <grpc/grpc.h>
39
39
 
40
- /* rb_cServer is the Server class whose instances proxy
41
- grpc_byte_buffer. */
42
- extern VALUE rb_cServer;
43
-
44
40
  /* Initializes the Server class. */
45
41
  void Init_grpc_server();
46
42
 
@@ -40,6 +40,10 @@
40
40
 
41
41
  #include "rb_grpc.h"
42
42
 
43
+ /* grpc_rb_cServerCredentials is the ruby class that proxies
44
+ grpc_server_credentials. */
45
+ static VALUE grpc_rb_cServerCredentials = Qnil;
46
+
43
47
  /* grpc_rb_server_credentials wraps a grpc_server_credentials. It provides a
44
48
  peer ruby object, 'mark' to minimize copying when a server credential is
45
49
  created from ruby. */
@@ -82,6 +86,14 @@ static void grpc_rb_server_credentials_mark(void *p) {
82
86
  }
83
87
  }
84
88
 
89
+ static const rb_data_type_t grpc_rb_server_credentials_data_type = {
90
+ "grpc_server_credentials",
91
+ {grpc_rb_server_credentials_mark, grpc_rb_server_credentials_free,
92
+ GRPC_RB_MEMSIZE_UNAVAILABLE},
93
+ NULL, NULL,
94
+ RUBY_TYPED_FREE_IMMEDIATELY
95
+ };
96
+
85
97
  /* Allocates ServerCredential instances.
86
98
 
87
99
  Provides safe initial defaults for the instance fields. */
@@ -89,8 +101,8 @@ static VALUE grpc_rb_server_credentials_alloc(VALUE cls) {
89
101
  grpc_rb_server_credentials *wrapper = ALLOC(grpc_rb_server_credentials);
90
102
  wrapper->wrapped = NULL;
91
103
  wrapper->mark = Qnil;
92
- return Data_Wrap_Struct(cls, grpc_rb_server_credentials_mark,
93
- grpc_rb_server_credentials_free, wrapper);
104
+ return TypedData_Wrap_Struct(cls, &grpc_rb_server_credentials_data_type,
105
+ wrapper);
94
106
  }
95
107
 
96
108
  /* Clones ServerCredentials instances.
@@ -109,11 +121,13 @@ static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
109
121
  if (TYPE(orig) != T_DATA ||
110
122
  RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_credentials_free) {
111
123
  rb_raise(rb_eTypeError, "not a %s",
112
- rb_obj_classname(rb_cServerCredentials));
124
+ rb_obj_classname(grpc_rb_cServerCredentials));
113
125
  }
114
126
 
115
- Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch);
116
- Data_Get_Struct(copy, grpc_rb_server_credentials, copy_ch);
127
+ TypedData_Get_Struct(orig, grpc_rb_server_credentials,
128
+ &grpc_rb_server_credentials_data_type, orig_ch);
129
+ TypedData_Get_Struct(copy, grpc_rb_server_credentials,
130
+ &grpc_rb_server_credentials_data_type, copy_ch);
117
131
 
118
132
  /* use ruby's MEMCPY to make a byte-for-byte copy of the server_credentials
119
133
  wrapper object. */
@@ -149,7 +163,8 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
149
163
  grpc_rb_server_credentials *wrapper = NULL;
150
164
  grpc_server_credentials *creds = NULL;
151
165
  grpc_ssl_pem_key_cert_pair key_cert_pair = {NULL, NULL};
152
- Data_Get_Struct(self, grpc_rb_server_credentials, wrapper);
166
+ TypedData_Get_Struct(self, grpc_rb_server_credentials,
167
+ &grpc_rb_server_credentials_data_type, wrapper);
153
168
  if (pem_cert_chain == Qnil) {
154
169
  rb_raise(rb_eRuntimeError,
155
170
  "could not create a server credential: nil pem_cert_chain");
@@ -180,21 +195,18 @@ static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
180
195
  return self;
181
196
  }
182
197
 
183
- /* rb_cServerCredentials is the ruby class that proxies
184
- grpc_server_credentials. */
185
- VALUE rb_cServerCredentials = Qnil;
186
-
187
198
  void Init_grpc_server_credentials() {
188
- rb_cServerCredentials =
189
- rb_define_class_under(rb_mGrpcCore, "ServerCredentials", rb_cObject);
199
+ grpc_rb_cServerCredentials =
200
+ rb_define_class_under(grpc_rb_mGrpcCore, "ServerCredentials", rb_cObject);
190
201
 
191
202
  /* Allocates an object managed by the ruby runtime */
192
- rb_define_alloc_func(rb_cServerCredentials, grpc_rb_server_credentials_alloc);
203
+ rb_define_alloc_func(grpc_rb_cServerCredentials,
204
+ grpc_rb_server_credentials_alloc);
193
205
 
194
206
  /* Provides a ruby constructor and support for dup/clone. */
195
- rb_define_method(rb_cServerCredentials, "initialize",
207
+ rb_define_method(grpc_rb_cServerCredentials, "initialize",
196
208
  grpc_rb_server_credentials_init, 3);
197
- rb_define_method(rb_cServerCredentials, "initialize_copy",
209
+ rb_define_method(grpc_rb_cServerCredentials, "initialize_copy",
198
210
  grpc_rb_server_credentials_init_copy, 1);
199
211
 
200
212
  id_pem_cert_chain = rb_intern("__pem_cert_chain");
@@ -205,6 +217,7 @@ void Init_grpc_server_credentials() {
205
217
  /* Gets the wrapped grpc_server_credentials from the ruby wrapper */
206
218
  grpc_server_credentials *grpc_rb_get_wrapped_server_credentials(VALUE v) {
207
219
  grpc_rb_server_credentials *wrapper = NULL;
208
- Data_Get_Struct(v, grpc_rb_server_credentials, wrapper);
220
+ TypedData_Get_Struct(v, grpc_rb_server_credentials,
221
+ &grpc_rb_server_credentials_data_type, wrapper);
209
222
  return wrapper->wrapped;
210
223
  }
@@ -37,10 +37,6 @@
37
37
  #include <ruby.h>
38
38
  #include <grpc/grpc_security.h>
39
39
 
40
- /* rb_cServerCredentials is the ruby class whose instances proxy
41
- grpc_server_credentials. */
42
- extern VALUE rb_cServerCredentials;
43
-
44
40
  /* Initializes the ruby ServerCredentials class. */
45
41
  void Init_grpc_server_credentials();
46
42
 
@@ -13,6 +13,9 @@ Gem::Specification.new do |s|
13
13
  s.description = 'Send RPCs from Ruby using GRPC'
14
14
  s.license = 'BSD-3-Clause'
15
15
 
16
+ s.required_ruby_version = '>= 2.0.0'
17
+ s.requirements << 'libgrpc ~> 0.6.0 needs to be installed'
18
+
16
19
  s.files = `git ls-files`.split("\n")
17
20
  s.test_files = `git ls-files -- spec/*`.split("\n")
18
21
  s.executables = `git ls-files -- bin/*.rb`.split("\n").map do |f|
@@ -22,16 +25,16 @@ Gem::Specification.new do |s|
22
25
  s.platform = Gem::Platform::RUBY
23
26
 
24
27
  s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
25
- s.add_dependency 'googleauth', '~> 0.1'
26
- s.add_dependency 'logging', '~> 1.8'
28
+ s.add_dependency 'googleauth', '~> 0.4' # reqd for interop tests
29
+ s.add_dependency 'logging', '~> 2.0'
27
30
  s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
28
- s.add_dependency 'xray', '~> 1.1'
29
31
 
30
- s.add_development_dependency 'bundler', '~> 1.7'
31
- s.add_development_dependency 'rake', '~> 10.0'
32
- s.add_development_dependency 'rake-compiler', '~> 0'
33
- s.add_development_dependency 'rubocop', '~> 0.28.0'
34
- s.add_development_dependency 'rspec', '~> 3.0'
32
+ s.add_development_dependency 'simplecov', '~> 0.9'
33
+ s.add_development_dependency 'bundler', '~> 1.9'
34
+ s.add_development_dependency 'rake', '~> 10.4'
35
+ s.add_development_dependency 'rake-compiler', '~> 0.9'
36
+ s.add_development_dependency 'rspec', '~> 3.2'
37
+ s.add_development_dependency 'rubocop', '~> 0.30'
35
38
 
36
39
  s.extensions = %w(ext/grpc/extconf.rb)
37
40
  end
@@ -30,8 +30,8 @@
30
30
  require 'grpc/errors'
31
31
  require 'grpc/grpc'
32
32
  require 'grpc/logconfig'
33
+ require 'grpc/notifier'
33
34
  require 'grpc/version'
34
- require 'grpc/core/event'
35
35
  require 'grpc/core/time_consts'
36
36
  require 'grpc/generic/active_call'
37
37
  require 'grpc/generic/client_stub'
@@ -31,23 +31,20 @@ require 'grpc'
31
31
 
32
32
  # GRPC contains the General RPC module.
33
33
  module GRPC
34
- # OutOfTime is an exception class that indicates that an RPC exceeded its
35
- # deadline.
36
- OutOfTime = Class.new(StandardError)
37
-
38
34
  # BadStatus is an exception class that indicates that an error occurred at
39
35
  # either end of a GRPC connection. When raised, it indicates that a status
40
36
  # error should be returned to the other end of a GRPC connection; when
41
37
  # caught it means that this end received a status error.
42
38
  class BadStatus < StandardError
43
- attr_reader :code, :details
39
+ attr_reader :code, :details, :metadata
44
40
 
45
41
  # @param code [Numeric] the status code
46
42
  # @param details [String] the details of the exception
47
- def initialize(code, details = 'unknown cause')
43
+ def initialize(code, details = 'unknown cause', **kw)
48
44
  super("#{code}:#{details}")
49
45
  @code = code
50
46
  @details = details
47
+ @metadata = kw
51
48
  end
52
49
 
53
50
  # Converts the exception to a GRPC::Status for use in the networking
@@ -55,7 +52,11 @@ module GRPC
55
52
  #
56
53
  # @return [Status] with the same code and details
57
54
  def to_status
58
- Status.new(code, details)
55
+ Struct::Status.new(code, details, @metadata)
59
56
  end
60
57
  end
58
+
59
+ # Cancelled is an exception class that indicates that an rpc was cancelled.
60
+ class Cancelled < StandardError
61
+ end
61
62
  end
@@ -30,10 +30,23 @@
30
30
  require 'forwardable'
31
31
  require 'grpc/generic/bidi_call'
32
32
 
33
- def assert_event_type(ev, want)
34
- fail OutOfTime if ev.nil?
35
- got = ev.type
36
- fail "Unexpected rpc event: got #{got}, want #{want}" unless got == want
33
+ class Struct
34
+ # BatchResult is the struct returned by calls to call#start_batch.
35
+ class BatchResult
36
+ # check_status returns the status, raising an error if the status
37
+ # is non-nil and not OK.
38
+ def check_status
39
+ return nil if status.nil?
40
+ fail GRPC::Cancelled if status.code == GRPC::Core::StatusCodes::CANCELLED
41
+ if status.code != GRPC::Core::StatusCodes::OK
42
+ # raise BadStatus, propagating the metadata if present.
43
+ md = status.metadata
44
+ with_sym_keys = Hash[md.each_pair.collect { |x, y| [x.to_sym, y] }]
45
+ fail GRPC::BadStatus.new(status.code, status.details, **with_sym_keys)
46
+ end
47
+ status
48
+ end
49
+ end
37
50
  end
38
51
 
39
52
  # GRPC contains the General RPC module.
@@ -41,10 +54,12 @@ module GRPC
41
54
  # The ActiveCall class provides simple methods for sending marshallable
42
55
  # data to a call
43
56
  class ActiveCall
44
- include Core::CompletionType
45
57
  include Core::StatusCodes
46
58
  include Core::TimeConsts
59
+ include Core::CallOps
60
+ extend Forwardable
47
61
  attr_reader(:deadline)
62
+ def_delegators :@call, :cancel, :metadata
48
63
 
49
64
  # client_invoke begins a client invocation.
50
65
  #
@@ -61,15 +76,14 @@ module GRPC
61
76
  # @param q [CompletionQueue] the completion queue
62
77
  # @param deadline [Fixnum,TimeSpec] the deadline
63
78
  def self.client_invoke(call, q, _deadline, **kw)
64
- fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
79
+ fail(TypeError, '!Core::Call') unless call.is_a? Core::Call
65
80
  unless q.is_a? Core::CompletionQueue
66
- fail(ArgumentError, 'not a CompletionQueue')
81
+ fail(TypeError, '!Core::CompletionQueue')
67
82
  end
68
- call.add_metadata(kw) if kw.length > 0
69
- client_metadata_read = Object.new
70
- finished_tag = Object.new
71
- call.invoke(q, client_metadata_read, finished_tag)
72
- [finished_tag, client_metadata_read]
83
+ metadata_tag = Object.new
84
+ call.run_batch(q, metadata_tag, INFINITE_FUTURE,
85
+ SEND_INITIAL_METADATA => kw)
86
+ metadata_tag
73
87
  end
74
88
 
75
89
  # Creates an ActiveCall.
@@ -91,69 +105,27 @@ module GRPC
91
105
  # @param marshal [Function] f(obj)->string that marshal requests
92
106
  # @param unmarshal [Function] f(string)->obj that unmarshals responses
93
107
  # @param deadline [Fixnum] the deadline for the call to complete
94
- # @param finished_tag [Object] the object used as the call's finish tag,
95
- # if the call has begun
96
- # @param read_metadata_tag [Object] the object used as the call's finish
97
- # tag, if the call has begun
108
+ # @param metadata_tag [Object] the object use obtain metadata for clients
98
109
  # @param started [true|false] indicates if the call has begun
99
- def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil,
100
- read_metadata_tag: nil, started: true)
101
- fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
110
+ def initialize(call, q, marshal, unmarshal, deadline, started: true,
111
+ metadata_tag: nil)
112
+ fail(TypeError, '!Core::Call') unless call.is_a? Core::Call
102
113
  unless q.is_a? Core::CompletionQueue
103
- fail(ArgumentError, 'not a CompletionQueue')
114
+ fail(TypeError, '!Core::CompletionQueue')
104
115
  end
105
116
  @call = call
106
117
  @cq = q
107
118
  @deadline = deadline
108
- @finished_tag = finished_tag
109
- @read_metadata_tag = read_metadata_tag
110
119
  @marshal = marshal
111
120
  @started = started
112
121
  @unmarshal = unmarshal
122
+ @metadata_tag = metadata_tag
113
123
  end
114
124
 
115
- # Obtains the status of the call.
116
- #
117
- # this value is nil until the call completes
118
- # @return this call's status
119
- def status
120
- @call.status
121
- end
122
-
123
- # Obtains the metadata of the call.
124
- #
125
- # At the start of the call this will be nil. During the call this gets
126
- # some values as soon as the other end of the connection acknowledges the
127
- # request.
128
- #
129
- # @return this calls's metadata
130
- def metadata
131
- @call.metadata
132
- end
133
-
134
- # Cancels the call.
135
- #
136
- # Cancels the call. The call does not return any result, but once this it
137
- # has been called, the call should eventually terminate. Due to potential
138
- # races between the execution of the cancel and the in-flight request, the
139
- # result of the call after calling #cancel is indeterminate:
140
- #
141
- # - the call may terminate with a BadStatus exception, with code=CANCELLED
142
- # - the call may terminate with OK Status, and return a response
143
- # - the call may terminate with a different BadStatus exception if that
144
- # was happening
145
- def cancel
146
- @call.cancel
147
- end
148
-
149
- # indicates if the call is shutdown
150
- def shutdown
151
- @shutdown ||= false
152
- end
153
-
154
- # indicates if the call is cancelled.
155
- def cancelled
156
- @cancelled ||= false
125
+ # output_metadata are provides access to hash that can be used to
126
+ # save metadata to be sent as trailer
127
+ def output_metadata
128
+ @output_metadata ||= {}
157
129
  end
158
130
 
159
131
  # multi_req_view provides a restricted view of this ActiveCall for use
@@ -176,128 +148,94 @@ module GRPC
176
148
 
177
149
  # writes_done indicates that all writes are completed.
178
150
  #
179
- # It blocks until the remote endpoint acknowledges by sending a FINISHED
180
- # event, unless assert_finished is set to false. Any calls to
181
- # #remote_send after this call will fail.
151
+ # It blocks until the remote endpoint acknowledges with at status unless
152
+ # assert_finished is set to false. Any calls to #remote_send after this
153
+ # call will fail.
182
154
  #
183
155
  # @param assert_finished [true, false] when true(default), waits for
184
156
  # FINISHED.
185
157
  def writes_done(assert_finished = true)
186
- @call.writes_done(self)
187
- ev = @cq.pluck(self, INFINITE_FUTURE)
188
- begin
189
- assert_event_type(ev, FINISH_ACCEPTED)
190
- logger.debug("Writes done: waiting for finish? #{assert_finished}")
191
- ensure
192
- ev.close
193
- end
194
-
158
+ ops = {
159
+ SEND_CLOSE_FROM_CLIENT => nil
160
+ }
161
+ ops[RECV_STATUS_ON_CLIENT] = nil if assert_finished
162
+ batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
195
163
  return unless assert_finished
196
- ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
197
- fail 'unexpected nil event' if ev.nil?
198
- ev.close
199
- @call.status
164
+ batch_result.check_status
200
165
  end
201
166
 
202
- # finished waits until the call is completed.
167
+ # finished waits until a client call is completed.
203
168
  #
204
- # It blocks until the remote endpoint acknowledges by sending a FINISHED
205
- # event.
169
+ # It blocks until the remote endpoint acknowledges by sending a status.
206
170
  def finished
207
- ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
208
- begin
209
- fail "unexpected event: #{ev.inspect}" unless ev.type == FINISHED
171
+ batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE,
172
+ RECV_STATUS_ON_CLIENT => nil)
173
+ unless batch_result.status.nil?
210
174
  if @call.metadata.nil?
211
- @call.metadata = ev.result.metadata
175
+ @call.metadata = batch_result.status.metadata
212
176
  else
213
- @call.metadata.merge!(ev.result.metadata)
177
+ @call.metadata.merge!(batch_result.status.metadata)
214
178
  end
215
-
216
- if ev.result.code != Core::StatusCodes::OK
217
- fail BadStatus.new(ev.result.code, ev.result.details)
218
- end
219
- res = ev.result
220
- ensure
221
- ev.close
222
179
  end
223
- res
180
+ batch_result.check_status
224
181
  end
225
182
 
226
183
  # remote_send sends a request to the remote endpoint.
227
184
  #
228
- # It blocks until the remote endpoint acknowledges by sending a
229
- # WRITE_ACCEPTED. req can be marshalled already.
185
+ # It blocks until the remote endpoint accepts the message.
230
186
  #
231
187
  # @param req [Object, String] the object to send or it's marshal form.
232
188
  # @param marshalled [false, true] indicates if the object is already
233
189
  # marshalled.
234
190
  def remote_send(req, marshalled = false)
235
- assert_queue_is_ready
236
- logger.debug("sending #{req.inspect}, marshalled? #{marshalled}")
191
+ logger.debug("sending #{req}, marshalled? #{marshalled}")
237
192
  if marshalled
238
193
  payload = req
239
194
  else
240
195
  payload = @marshal.call(req)
241
196
  end
242
- @call.start_write(Core::ByteBuffer.new(payload), self)
243
-
244
- # call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return
245
- # until the flow control allows another send on this call.
246
- ev = @cq.pluck(self, INFINITE_FUTURE)
247
- begin
248
- assert_event_type(ev, WRITE_ACCEPTED)
249
- ensure
250
- ev.close
251
- end
197
+ @call.run_batch(@cq, self, INFINITE_FUTURE, SEND_MESSAGE => payload)
252
198
  end
253
199
 
254
- # send_status sends a status to the remote endpoint
200
+ # send_status sends a status to the remote endpoint.
255
201
  #
256
202
  # @param code [int] the status code to send
257
203
  # @param details [String] details
258
204
  # @param assert_finished [true, false] when true(default), waits for
259
205
  # FINISHED.
260
- def send_status(code = OK, details = '', assert_finished = false)
261
- assert_queue_is_ready
262
- @call.start_write_status(code, details, self)
263
- ev = @cq.pluck(self, INFINITE_FUTURE)
264
- begin
265
- assert_event_type(ev, FINISH_ACCEPTED)
266
- ensure
267
- ev.close
268
- end
269
- logger.debug("Status sent: #{code}:'#{details}'")
270
- return finished if assert_finished
206
+ #
207
+ # == Keyword Arguments ==
208
+ # any keyword arguments are treated as metadata to be sent to the server
209
+ # if a keyword value is a list, multiple metadata for it's key are sent
210
+ def send_status(code = OK, details = '', assert_finished = false, **kw)
211
+ ops = {
212
+ SEND_STATUS_FROM_SERVER => Struct::Status.new(code, details, kw)
213
+ }
214
+ ops[RECV_CLOSE_ON_SERVER] = nil if assert_finished
215
+ @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
271
216
  nil
272
217
  end
273
218
 
274
219
  # remote_read reads a response from the remote endpoint.
275
220
  #
276
- # It blocks until the remote endpoint sends a READ or FINISHED event. On
277
- # a READ, it returns the response after unmarshalling it. On
278
- # FINISHED, it returns nil if the status is OK, otherwise raising
279
- # BadStatus
221
+ # It blocks until the remote endpoint replies with a message or status.
222
+ # On receiving a message, it returns the response after unmarshalling it.
223
+ # On receiving a status, it returns nil if the status is OK, otherwise
224
+ # raising BadStatus
280
225
  def remote_read
281
- if @call.metadata.nil? && !@read_metadata_tag.nil?
282
- ev = @cq.pluck(@read_metadata_tag, INFINITE_FUTURE)
283
- assert_event_type(ev, CLIENT_METADATA_READ)
284
- @call.metadata = ev.result
285
- @read_metadata_tag = nil
226
+ ops = { RECV_MESSAGE => nil }
227
+ ops[RECV_INITIAL_METADATA] = nil unless @metadata_tag.nil?
228
+ batch_result = @call.run_batch(@cq, self, INFINITE_FUTURE, ops)
229
+ unless @metadata_tag.nil?
230
+ @call.metadata = batch_result.metadata
231
+ @metadata_tag = nil
286
232
  end
287
-
288
- @call.start_read(self)
289
- ev = @cq.pluck(self, INFINITE_FUTURE)
290
- begin
291
- assert_event_type(ev, READ)
292
- logger.debug("received req: #{ev.result.inspect}")
293
- unless ev.result.nil?
294
- logger.debug("received req.to_s: #{ev.result}")
295
- res = @unmarshal.call(ev.result.to_s)
296
- logger.debug("received_req (unmarshalled): #{res.inspect}")
297
- return res
298
- end
299
- ensure
300
- ev.close
233
+ logger.debug("received req: #{batch_result}")
234
+ unless batch_result.nil? || batch_result.message.nil?
235
+ logger.debug("received req.to_s: #{batch_result.message}")
236
+ res = @unmarshal.call(batch_result.message)
237
+ logger.debug("received_req (unmarshalled): #{res.inspect}")
238
+ return res
301
239
  end
302
240
  logger.debug('found nil; the final response has been sent')
303
241
  nil
@@ -324,7 +262,6 @@ module GRPC
324
262
  return enum_for(:each_remote_read) unless block_given?
325
263
  loop do
326
264
  resp = remote_read
327
- break if resp.is_a? Struct::Status # is an OK status
328
265
  break if resp.nil? # the last response was received
329
266
  yield resp
330
267
  end
@@ -379,6 +316,9 @@ module GRPC
379
316
  response = remote_read
380
317
  finished unless response.is_a? Struct::Status
381
318
  response
319
+ rescue GRPC::Core::CallError => e
320
+ finished # checks for Cancelled
321
+ raise e
382
322
  end
383
323
 
384
324
  # client_streamer sends a stream of requests to a GRPC server, and
@@ -402,6 +342,9 @@ module GRPC
402
342
  response = remote_read
403
343
  finished unless response.is_a? Struct::Status
404
344
  response
345
+ rescue GRPC::Core::CallError => e
346
+ finished # checks for Cancelled
347
+ raise e
405
348
  end
406
349
 
407
350
  # server_streamer sends one request to the GRPC server, which yields a
@@ -428,6 +371,9 @@ module GRPC
428
371
  replies = enum_for(:each_remote_read_then_finish)
429
372
  return replies unless block_given?
430
373
  replies.each { |r| yield r }
374
+ rescue GRPC::Core::CallError => e
375
+ finished # checks for Cancelled
376
+ raise e
431
377
  end
432
378
 
433
379
  # bidi_streamer sends a stream of requests to the GRPC server, and yields
@@ -461,9 +407,11 @@ module GRPC
461
407
  # @return [Enumerator, nil] a response Enumerator
462
408
  def bidi_streamer(requests, **kw, &blk)
463
409
  start_call(**kw) unless @started
464
- bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline,
465
- @finished_tag)
410
+ bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline)
466
411
  bd.run_on_client(requests, &blk)
412
+ rescue GRPC::Core::CallError => e
413
+ finished # checks for Cancelled
414
+ raise e
467
415
  end
468
416
 
469
417
  # run_server_bidi orchestrates a BiDi stream processing on a server.
@@ -478,16 +426,16 @@ module GRPC
478
426
  #
479
427
  # @param gen_each_reply [Proc] generates the BiDi stream replies
480
428
  def run_server_bidi(gen_each_reply)
481
- bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline,
482
- @finished_tag)
429
+ bd = BidiCall.new(@call, @cq, @marshal, @unmarshal, @deadline)
483
430
  bd.run_on_server(gen_each_reply)
484
431
  end
485
432
 
486
433
  private
487
434
 
435
+ # Starts the call if not already started
488
436
  def start_call(**kw)
489
- tags = ActiveCall.client_invoke(@call, @cq, @deadline, **kw)
490
- @finished_tag, @read_metadata_tag = tags
437
+ return if @started
438
+ @metadata_tag = ActiveCall.client_invoke(@call, @cq, @deadline, **kw)
491
439
  @started = true
492
440
  end
493
441
 
@@ -505,32 +453,17 @@ module GRPC
505
453
 
506
454
  # SingleReqView limits access to an ActiveCall's methods for use in server
507
455
  # handlers that receive just one request.
508
- SingleReqView = view_class(:cancelled, :deadline, :metadata)
456
+ SingleReqView = view_class(:cancelled, :deadline, :metadata,
457
+ :output_metadata)
509
458
 
510
459
  # MultiReqView limits access to an ActiveCall's methods for use in
511
460
  # server client_streamer handlers.
512
461
  MultiReqView = view_class(:cancelled, :deadline, :each_queued_msg,
513
- :each_remote_read, :metadata)
462
+ :each_remote_read, :metadata, :output_metadata)
514
463
 
515
464
  # Operation limits access to an ActiveCall's methods for use as
516
465
  # a Operation on the client.
517
466
  Operation = view_class(:cancel, :cancelled, :deadline, :execute,
518
- :metadata, :status)
519
-
520
- # confirms that no events are enqueued, and that the queue is not
521
- # shutdown.
522
- def assert_queue_is_ready
523
- ev = nil
524
- begin
525
- ev = @cq.pluck(self, ZERO)
526
- fail "unexpected event #{ev.inspect}" unless ev.nil?
527
- rescue OutOfTime
528
- logging.debug('timed out waiting for next event')
529
- # expected, nothing should be on the queue and the deadline was ZERO,
530
- # except things using another tag
531
- ensure
532
- ev.close unless ev.nil?
533
- end
534
- end
467
+ :metadata, :status, :start_call)
535
468
  end
536
469
  end