grpc 1.0.1-x86-linux → 1.2.0.pre1-x86-linux
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.
- checksums.yaml +4 -4
- data/etc/roots.pem +228 -213
- data/grpc_c.32.ruby +0 -0
- data/grpc_c.64.ruby +0 -0
- data/src/ruby/ext/grpc/extconf.rb +4 -2
- data/src/ruby/ext/grpc/rb_byte_buffer.c +15 -7
- data/src/ruby/ext/grpc/rb_byte_buffer.h +3 -0
- data/src/ruby/ext/grpc/rb_call.c +62 -51
- data/src/ruby/ext/grpc/rb_channel.c +22 -7
- data/src/ruby/ext/grpc/rb_compression_options.c +469 -0
- data/src/ruby/ext/grpc/rb_compression_options.h +44 -0
- data/src/ruby/ext/grpc/rb_grpc.c +3 -1
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +232 -190
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +362 -299
- data/src/ruby/ext/grpc/rb_server.c +24 -16
- data/src/ruby/lib/grpc/2.0/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.1/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.2/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.3/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/2.4/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/errors.rb +154 -2
- data/src/ruby/lib/grpc/generic/active_call.rb +144 -63
- data/src/ruby/lib/grpc/generic/bidi_call.rb +18 -2
- data/src/ruby/lib/grpc/generic/client_stub.rb +8 -6
- data/src/ruby/lib/grpc/generic/rpc_desc.rb +39 -13
- data/src/ruby/lib/grpc/generic/rpc_server.rb +51 -24
- data/src/ruby/lib/grpc/generic/service.rb +3 -2
- data/src/ruby/lib/grpc/grpc_c.so +0 -0
- data/src/ruby/lib/grpc/version.rb +1 -1
- data/src/ruby/pb/grpc/health/checker.rb +3 -1
- data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +7 -0
- data/src/ruby/pb/test/client.rb +307 -7
- data/src/ruby/pb/test/server.rb +26 -1
- data/src/ruby/spec/compression_options_spec.rb +164 -0
- data/src/ruby/spec/error_sanity_spec.rb +64 -0
- data/src/ruby/spec/generic/active_call_spec.rb +290 -12
- data/src/ruby/spec/generic/client_stub_spec.rb +91 -41
- data/src/ruby/spec/generic/rpc_desc_spec.rb +36 -16
- data/src/ruby/spec/generic/rpc_server_pool_spec.rb +22 -28
- data/src/ruby/spec/generic/rpc_server_spec.rb +6 -6
- data/src/ruby/spec/pb/health/checker_spec.rb +27 -19
- data/src/ruby/spec/spec_helper.rb +2 -0
- metadata +20 -10
@@ -56,7 +56,8 @@ module GRPC
|
|
56
56
|
# @param unmarshal [Function] f(string)->obj that unmarshals responses
|
57
57
|
# @param metadata_received [true|false] indicates if metadata has already
|
58
58
|
# been received. Should always be true for server calls
|
59
|
-
def initialize(call, marshal, unmarshal, metadata_received: false
|
59
|
+
def initialize(call, marshal, unmarshal, metadata_received: false,
|
60
|
+
req_view: nil)
|
60
61
|
fail(ArgumentError, 'not a call') unless call.is_a? Core::Call
|
61
62
|
@call = call
|
62
63
|
@marshal = marshal
|
@@ -67,6 +68,7 @@ module GRPC
|
|
67
68
|
@writes_complete = false
|
68
69
|
@complete = false
|
69
70
|
@done_mutex = Mutex.new
|
71
|
+
@req_view = req_view
|
70
72
|
end
|
71
73
|
|
72
74
|
# Begins orchestration of the Bidi stream for a client sending requests.
|
@@ -95,7 +97,15 @@ module GRPC
|
|
95
97
|
#
|
96
98
|
# @param gen_each_reply [Proc] generates the BiDi stream replies.
|
97
99
|
def run_on_server(gen_each_reply)
|
98
|
-
|
100
|
+
# Pass in the optional call object parameter if possible
|
101
|
+
if gen_each_reply.arity == 1
|
102
|
+
replys = gen_each_reply.call(read_loop(is_client: false))
|
103
|
+
elsif gen_each_reply.arity == 2
|
104
|
+
replys = gen_each_reply.call(read_loop(is_client: false), @req_view)
|
105
|
+
else
|
106
|
+
fail 'Illegal arity of reply generator'
|
107
|
+
end
|
108
|
+
|
99
109
|
write_loop(replys, is_client: false)
|
100
110
|
end
|
101
111
|
|
@@ -141,6 +151,7 @@ module GRPC
|
|
141
151
|
payload = @marshal.call(req)
|
142
152
|
# Fails if status already received
|
143
153
|
begin
|
154
|
+
@req_view.send_initial_metadata unless @req_view.nil?
|
144
155
|
@call.run_batch(SEND_MESSAGE => payload)
|
145
156
|
rescue GRPC::Core::CallError => e
|
146
157
|
# This is almost definitely caused by a status arriving while still
|
@@ -189,6 +200,7 @@ module GRPC
|
|
189
200
|
if is_client
|
190
201
|
batch_result = @call.run_batch(RECV_STATUS_ON_CLIENT => nil)
|
191
202
|
@call.status = batch_result.status
|
203
|
+
@call.trailing_metadata = @call.status.metadata if @call.status
|
192
204
|
batch_result.check_status
|
193
205
|
GRPC.logger.debug("bidi-read-loop: done status #{@call.status}")
|
194
206
|
end
|
@@ -208,6 +220,10 @@ module GRPC
|
|
208
220
|
GRPC.logger.debug('bidi-read-loop: finished')
|
209
221
|
@reads_complete = true
|
210
222
|
finished
|
223
|
+
# Make sure that the write loop is done done before finishing the call.
|
224
|
+
# Note that blocking is ok at this point because we've already received
|
225
|
+
# a status
|
226
|
+
@enq_th.join if is_client
|
211
227
|
end
|
212
228
|
end
|
213
229
|
end
|
@@ -84,7 +84,7 @@ module GRPC
|
|
84
84
|
# channel:
|
85
85
|
#
|
86
86
|
# - :channel_override
|
87
|
-
# when present, this must be a pre-created GRPC::Channel. If it's
|
87
|
+
# when present, this must be a pre-created GRPC::Core::Channel. If it's
|
88
88
|
# present the host and arbitrary keyword arg areignored, and the RPC
|
89
89
|
# connection uses this channel.
|
90
90
|
#
|
@@ -168,6 +168,7 @@ module GRPC
|
|
168
168
|
|
169
169
|
# return the operation view of the active_call; define #execute as a
|
170
170
|
# new method for this instance that invokes #request_response.
|
171
|
+
c.merge_metadata_to_send(metadata)
|
171
172
|
op = c.operation
|
172
173
|
op.define_singleton_method(:execute) do
|
173
174
|
c.request_response(req, metadata: metadata)
|
@@ -231,9 +232,10 @@ module GRPC
|
|
231
232
|
|
232
233
|
# return the operation view of the active_call; define #execute as a
|
233
234
|
# new method for this instance that invokes #client_streamer.
|
235
|
+
c.merge_metadata_to_send(metadata)
|
234
236
|
op = c.operation
|
235
237
|
op.define_singleton_method(:execute) do
|
236
|
-
c.client_streamer(requests
|
238
|
+
c.client_streamer(requests)
|
237
239
|
end
|
238
240
|
op
|
239
241
|
end
|
@@ -309,9 +311,10 @@ module GRPC
|
|
309
311
|
|
310
312
|
# return the operation view of the active_call; define #execute
|
311
313
|
# as a new method for this instance that invokes #server_streamer
|
314
|
+
c.merge_metadata_to_send(metadata)
|
312
315
|
op = c.operation
|
313
316
|
op.define_singleton_method(:execute) do
|
314
|
-
c.server_streamer(req,
|
317
|
+
c.server_streamer(req, &blk)
|
315
318
|
end
|
316
319
|
op
|
317
320
|
end
|
@@ -417,15 +420,15 @@ module GRPC
|
|
417
420
|
deadline: deadline,
|
418
421
|
parent: parent,
|
419
422
|
credentials: credentials)
|
420
|
-
|
421
423
|
return c.bidi_streamer(requests, metadata: metadata,
|
422
424
|
&blk) unless return_op
|
423
425
|
|
424
426
|
# return the operation view of the active_call; define #execute
|
425
427
|
# as a new method for this instance that invokes #bidi_streamer
|
428
|
+
c.merge_metadata_to_send(metadata)
|
426
429
|
op = c.operation
|
427
430
|
op.define_singleton_method(:execute) do
|
428
|
-
c.bidi_streamer(requests,
|
431
|
+
c.bidi_streamer(requests, &blk)
|
429
432
|
end
|
430
433
|
op
|
431
434
|
end
|
@@ -445,7 +448,6 @@ module GRPC
|
|
445
448
|
deadline: nil,
|
446
449
|
parent: nil,
|
447
450
|
credentials: nil)
|
448
|
-
|
449
451
|
deadline = from_relative_time(@timeout) if deadline.nil?
|
450
452
|
# Provide each new client call with its own completion queue
|
451
453
|
call = @ch.create_call(parent, # parent call
|
@@ -62,25 +62,44 @@ module GRPC
|
|
62
62
|
proc { |o| unmarshal_class.method(unmarshal_method).call(o) }
|
63
63
|
end
|
64
64
|
|
65
|
+
def handle_request_response(active_call, mth)
|
66
|
+
req = active_call.remote_read
|
67
|
+
resp = mth.call(req, active_call.single_req_view)
|
68
|
+
active_call.server_unary_response(
|
69
|
+
resp, trailing_metadata: active_call.output_metadata)
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_client_streamer(active_call, mth)
|
73
|
+
resp = mth.call(active_call.multi_req_view)
|
74
|
+
active_call.server_unary_response(
|
75
|
+
resp, trailing_metadata: active_call.output_metadata)
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_server_streamer(active_call, mth)
|
79
|
+
req = active_call.remote_read
|
80
|
+
replys = mth.call(req, active_call.single_req_view)
|
81
|
+
replys.each { |r| active_call.remote_send(r) }
|
82
|
+
send_status(active_call, OK, 'OK', active_call.output_metadata)
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle_bidi_streamer(active_call, mth)
|
86
|
+
active_call.run_server_bidi(mth)
|
87
|
+
send_status(active_call, OK, 'OK', active_call.output_metadata)
|
88
|
+
end
|
89
|
+
|
65
90
|
def run_server_method(active_call, mth)
|
66
91
|
# While a server method is running, it might be cancelled, its deadline
|
67
92
|
# might be reached, the handler could throw an unknown error, or a
|
68
93
|
# well-behaved handler could throw a StatusError.
|
69
94
|
if request_response?
|
70
|
-
|
71
|
-
resp = mth.call(req, active_call.single_req_view)
|
72
|
-
active_call.remote_send(resp)
|
95
|
+
handle_request_response(active_call, mth)
|
73
96
|
elsif client_streamer?
|
74
|
-
|
75
|
-
active_call.remote_send(resp)
|
97
|
+
handle_client_streamer(active_call, mth)
|
76
98
|
elsif server_streamer?
|
77
|
-
|
78
|
-
replys = mth.call(req, active_call.single_req_view)
|
79
|
-
replys.each { |r| active_call.remote_send(r) }
|
99
|
+
handle_server_streamer(active_call, mth)
|
80
100
|
else # is a bidi_stream
|
81
|
-
active_call
|
101
|
+
handle_bidi_streamer(active_call, mth)
|
82
102
|
end
|
83
|
-
send_status(active_call, OK, 'OK', active_call.output_metadata)
|
84
103
|
rescue BadStatus => e
|
85
104
|
# this is raised by handlers that want GRPC to send an application error
|
86
105
|
# code and detail message and some additional app-specific metadata.
|
@@ -91,7 +110,7 @@ module GRPC
|
|
91
110
|
# Log it, but don't notify the other endpoint..
|
92
111
|
GRPC.logger.warn("failed call: #{active_call}\n#{e}")
|
93
112
|
rescue Core::OutOfTime
|
94
|
-
# This is raised when active_call#method.call
|
113
|
+
# This is raised when active_call#method.call exceeds the deadline
|
95
114
|
# event. Send a status of deadline exceeded
|
96
115
|
GRPC.logger.warn("late call: #{active_call}")
|
97
116
|
send_status(active_call, DEADLINE_EXCEEDED, 'late')
|
@@ -100,11 +119,18 @@ module GRPC
|
|
100
119
|
# Send back a UNKNOWN status to the client
|
101
120
|
GRPC.logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
|
102
121
|
GRPC.logger.warn(e)
|
103
|
-
send_status(active_call, UNKNOWN,
|
122
|
+
send_status(active_call, UNKNOWN, "#{e.class}: #{e.message}")
|
104
123
|
end
|
105
124
|
|
106
125
|
def assert_arity_matches(mth)
|
107
|
-
|
126
|
+
# A bidi handler function can optionally be passed a second
|
127
|
+
# call object parameter for access to metadata, cancelling, etc.
|
128
|
+
if bidi_streamer?
|
129
|
+
if mth.arity != 2 && mth.arity != 1
|
130
|
+
fail arity_error(mth, 2, "should be #{mth.name}(req, call) or " \
|
131
|
+
"#{mth.name}(req)")
|
132
|
+
end
|
133
|
+
elsif request_response? || server_streamer?
|
108
134
|
if mth.arity != 2
|
109
135
|
fail arity_error(mth, 2, "should be #{mth.name}(req, call)")
|
110
136
|
end
|
@@ -48,6 +48,10 @@ module GRPC
|
|
48
48
|
@stop_cond = ConditionVariable.new
|
49
49
|
@workers = []
|
50
50
|
@keep_alive = keep_alive
|
51
|
+
|
52
|
+
# Each worker thread has its own queue to push and pull jobs
|
53
|
+
# these queues are put into @ready_queues when that worker is idle
|
54
|
+
@ready_workers = Queue.new
|
51
55
|
end
|
52
56
|
|
53
57
|
# Returns the number of jobs waiting
|
@@ -55,6 +59,13 @@ module GRPC
|
|
55
59
|
@jobs.size
|
56
60
|
end
|
57
61
|
|
62
|
+
def ready_for_work?
|
63
|
+
# Busy worker threads are either doing work, or have a single job
|
64
|
+
# waiting on them. Workers that are idle with no jobs waiting
|
65
|
+
# have their "queues" in @ready_workers
|
66
|
+
!@ready_workers.empty?
|
67
|
+
end
|
68
|
+
|
58
69
|
# Runs the given block on the queue with the provided args.
|
59
70
|
#
|
60
71
|
# @param args the args passed blk when it is called
|
@@ -67,7 +78,11 @@ module GRPC
|
|
67
78
|
return
|
68
79
|
end
|
69
80
|
GRPC.logger.info('schedule another job')
|
70
|
-
|
81
|
+
fail 'No worker threads available' if @ready_workers.empty?
|
82
|
+
worker_queue = @ready_workers.pop
|
83
|
+
|
84
|
+
fail 'worker already has a task waiting' unless worker_queue.empty?
|
85
|
+
worker_queue << [blk, args]
|
71
86
|
end
|
72
87
|
end
|
73
88
|
|
@@ -77,9 +92,11 @@ module GRPC
|
|
77
92
|
fail 'already stopped' if @stopped
|
78
93
|
end
|
79
94
|
until @workers.size == @size.to_i
|
80
|
-
|
95
|
+
new_worker_queue = Queue.new
|
96
|
+
@ready_workers << new_worker_queue
|
97
|
+
next_thread = Thread.new(new_worker_queue) do |jobs|
|
81
98
|
catch(:exit) do # allows { throw :exit } to kill a thread
|
82
|
-
loop_execute_jobs
|
99
|
+
loop_execute_jobs(jobs)
|
83
100
|
end
|
84
101
|
remove_current_thread
|
85
102
|
end
|
@@ -90,7 +107,7 @@ module GRPC
|
|
90
107
|
# Stops the jobs in the pool
|
91
108
|
def stop
|
92
109
|
GRPC.logger.info('stopping, will wait for all the workers to exit')
|
93
|
-
|
110
|
+
schedule { throw :exit } while ready_for_work?
|
94
111
|
@stop_mutex.synchronize do # wait @keep_alive for works to stop
|
95
112
|
@stopped = true
|
96
113
|
@stop_cond.wait(@stop_mutex, @keep_alive) if @workers.size > 0
|
@@ -125,15 +142,18 @@ module GRPC
|
|
125
142
|
end
|
126
143
|
end
|
127
144
|
|
128
|
-
def loop_execute_jobs
|
145
|
+
def loop_execute_jobs(worker_queue)
|
129
146
|
loop do
|
130
147
|
begin
|
131
|
-
blk, args =
|
148
|
+
blk, args = worker_queue.pop
|
132
149
|
blk.call(*args)
|
133
150
|
rescue StandardError => e
|
134
151
|
GRPC.logger.warn('Error in worker thread')
|
135
152
|
GRPC.logger.warn(e)
|
136
153
|
end
|
154
|
+
# there shouldn't be any work given to this thread while its busy
|
155
|
+
fail('received a task while busy') unless worker_queue.empty?
|
156
|
+
@ready_workers << worker_queue
|
137
157
|
end
|
138
158
|
end
|
139
159
|
end
|
@@ -147,10 +167,10 @@ module GRPC
|
|
147
167
|
|
148
168
|
def_delegators :@server, :add_http2_port
|
149
169
|
|
150
|
-
# Default thread pool size is
|
151
|
-
DEFAULT_POOL_SIZE =
|
170
|
+
# Default thread pool size is 30
|
171
|
+
DEFAULT_POOL_SIZE = 30
|
152
172
|
|
153
|
-
#
|
173
|
+
# Deprecated due to internal changes to the thread pool
|
154
174
|
DEFAULT_MAX_WAITING_REQUESTS = 20
|
155
175
|
|
156
176
|
# Default poll period is 1s
|
@@ -175,11 +195,11 @@ module GRPC
|
|
175
195
|
# instance.
|
176
196
|
#
|
177
197
|
# * pool_size: the size of the thread pool the server uses to run its
|
178
|
-
# threads
|
198
|
+
# threads. No more concurrent requests can be made than the size
|
199
|
+
# of the thread pool
|
179
200
|
#
|
180
|
-
# * max_waiting_requests:
|
181
|
-
#
|
182
|
-
# with not available to new requests
|
201
|
+
# * max_waiting_requests: Deprecated due to internal changes to the thread
|
202
|
+
# pool. This is still an argument for compatibility but is ignored.
|
183
203
|
#
|
184
204
|
# * poll_period: when present, the server polls for new events with this
|
185
205
|
# period
|
@@ -330,13 +350,14 @@ module GRPC
|
|
330
350
|
|
331
351
|
# Sends RESOURCE_EXHAUSTED if there are too many unprocessed jobs
|
332
352
|
def available?(an_rpc)
|
333
|
-
|
334
|
-
GRPC.logger.
|
335
|
-
return an_rpc if @pool.jobs_waiting <= @max_waiting_requests
|
336
|
-
GRPC.logger.warn("NOT AVAILABLE: too many jobs_waiting: #{an_rpc}")
|
353
|
+
return an_rpc if @pool.ready_for_work?
|
354
|
+
GRPC.logger.warn('no free worker threads currently')
|
337
355
|
noop = proc { |x| x }
|
356
|
+
|
357
|
+
# Create a new active call that knows that metadata hasn't been
|
358
|
+
# sent yet
|
338
359
|
c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
|
339
|
-
metadata_received: true)
|
360
|
+
metadata_received: true, started: false)
|
340
361
|
c.send_status(GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED, '')
|
341
362
|
nil
|
342
363
|
end
|
@@ -347,8 +368,11 @@ module GRPC
|
|
347
368
|
return an_rpc if rpc_descs.key?(mth)
|
348
369
|
GRPC.logger.warn("UNIMPLEMENTED: #{an_rpc}")
|
349
370
|
noop = proc { |x| x }
|
371
|
+
|
372
|
+
# Create a new active call that knows that
|
373
|
+
# metadata hasn't been sent yet
|
350
374
|
c = ActiveCall.new(an_rpc.call, noop, noop, an_rpc.deadline,
|
351
|
-
metadata_received: true)
|
375
|
+
metadata_received: true, started: false)
|
352
376
|
c.send_status(GRPC::Core::StatusCodes::UNIMPLEMENTED, '')
|
353
377
|
nil
|
354
378
|
end
|
@@ -395,17 +419,20 @@ module GRPC
|
|
395
419
|
unless @connect_md_proc.nil?
|
396
420
|
connect_md = @connect_md_proc.call(an_rpc.method, an_rpc.metadata)
|
397
421
|
end
|
398
|
-
an_rpc.call.run_batch(SEND_INITIAL_METADATA => connect_md)
|
399
422
|
|
400
423
|
return nil unless available?(an_rpc)
|
401
424
|
return nil unless implemented?(an_rpc)
|
402
425
|
|
403
|
-
# Create the ActiveCall
|
426
|
+
# Create the ActiveCall. Indicate that metadata hasnt been sent yet.
|
404
427
|
GRPC.logger.info("deadline is #{an_rpc.deadline}; (now=#{Time.now})")
|
405
428
|
rpc_desc = rpc_descs[an_rpc.method.to_sym]
|
406
|
-
c = ActiveCall.new(an_rpc.call,
|
407
|
-
rpc_desc.
|
408
|
-
|
429
|
+
c = ActiveCall.new(an_rpc.call,
|
430
|
+
rpc_desc.marshal_proc,
|
431
|
+
rpc_desc.unmarshal_proc(:input),
|
432
|
+
an_rpc.deadline,
|
433
|
+
metadata_received: true,
|
434
|
+
started: false,
|
435
|
+
metadata_to_send: connect_md)
|
409
436
|
mth = an_rpc.method.to_sym
|
410
437
|
[c, mth]
|
411
438
|
end
|
@@ -110,8 +110,9 @@ module GRPC
|
|
110
110
|
rpc_descs[name] = RpcDesc.new(name, input, output,
|
111
111
|
marshal_class_method,
|
112
112
|
unmarshal_class_method)
|
113
|
-
define_method(name) do
|
114
|
-
fail GRPC::BadStatus
|
113
|
+
define_method(GenericService.underscore(name.to_s).to_sym) do |_, _|
|
114
|
+
fail GRPC::BadStatus.new_status_exception(
|
115
|
+
GRPC::Core::StatusCodes::UNIMPLEMENTED)
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
data/src/ruby/lib/grpc/grpc_c.so
CHANGED
Binary file
|
@@ -52,7 +52,9 @@ module Grpc
|
|
52
52
|
@status_mutex.synchronize do
|
53
53
|
status = @statuses["#{req.service}"]
|
54
54
|
end
|
55
|
-
|
55
|
+
if status.nil?
|
56
|
+
fail GRPC::BadStatus.new_status_exception(StatusCodes::NOT_FOUND)
|
57
|
+
end
|
56
58
|
HealthCheckResponse.new(status: status)
|
57
59
|
end
|
58
60
|
|
@@ -54,6 +54,10 @@ module Grpc
|
|
54
54
|
rpc :EmptyCall, Empty, Empty
|
55
55
|
# One request followed by one response.
|
56
56
|
rpc :UnaryCall, SimpleRequest, SimpleResponse
|
57
|
+
# One request followed by one response. Response has cache control
|
58
|
+
# headers set such that a caching HTTP proxy (such as GFE) can
|
59
|
+
# satisfy subsequent requests.
|
60
|
+
rpc :CacheableUnaryCall, SimpleRequest, SimpleResponse
|
57
61
|
# One request followed by a sequence of responses (streamed download).
|
58
62
|
# The server returns the payload with client desired type and sizes.
|
59
63
|
rpc :StreamingOutputCall, StreamingOutputCallRequest, stream(StreamingOutputCallResponse)
|
@@ -69,6 +73,9 @@ module Grpc
|
|
69
73
|
# stream of responses are returned to the client when the server starts with
|
70
74
|
# first request.
|
71
75
|
rpc :HalfDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
|
76
|
+
# The test server will not implement this method. It will be used
|
77
|
+
# to test the behavior when clients call unimplemented methods.
|
78
|
+
rpc :UnimplementedCall, Empty, Empty
|
72
79
|
end
|
73
80
|
|
74
81
|
Stub = Service.rpc_stub_class
|
data/src/ruby/pb/test/client.rb
CHANGED
@@ -111,6 +111,18 @@ end
|
|
111
111
|
# creates a test stub that accesses host:port securely.
|
112
112
|
def create_stub(opts)
|
113
113
|
address = "#{opts.host}:#{opts.port}"
|
114
|
+
|
115
|
+
# Provide channel args that request compression by default
|
116
|
+
# for compression interop tests
|
117
|
+
if ['client_compressed_unary',
|
118
|
+
'client_compressed_streaming'].include?(opts.test_case)
|
119
|
+
compression_options =
|
120
|
+
GRPC::Core::CompressionOptions.new(default_algorithm: :gzip)
|
121
|
+
compression_channel_args = compression_options.to_channel_arg_hash
|
122
|
+
else
|
123
|
+
compression_channel_args = {}
|
124
|
+
end
|
125
|
+
|
114
126
|
if opts.secure
|
115
127
|
creds = ssl_creds(opts.use_test_ca)
|
116
128
|
stub_opts = {
|
@@ -145,10 +157,27 @@ def create_stub(opts)
|
|
145
157
|
end
|
146
158
|
|
147
159
|
GRPC.logger.info("... connecting securely to #{address}")
|
148
|
-
|
160
|
+
stub_opts[:channel_args].merge!(compression_channel_args)
|
161
|
+
if opts.test_case == "unimplemented_service"
|
162
|
+
Grpc::Testing::UnimplementedService::Stub.new(address, creds, **stub_opts)
|
163
|
+
else
|
164
|
+
Grpc::Testing::TestService::Stub.new(address, creds, **stub_opts)
|
165
|
+
end
|
149
166
|
else
|
150
167
|
GRPC.logger.info("... connecting insecurely to #{address}")
|
151
|
-
|
168
|
+
if opts.test_case == "unimplemented_service"
|
169
|
+
Grpc::Testing::UnimplementedService::Stub.new(
|
170
|
+
address,
|
171
|
+
:this_channel_is_insecure,
|
172
|
+
channel_args: compression_channel_args
|
173
|
+
)
|
174
|
+
else
|
175
|
+
Grpc::Testing::TestService::Stub.new(
|
176
|
+
address,
|
177
|
+
:this_channel_is_insecure,
|
178
|
+
channel_args: compression_channel_args
|
179
|
+
)
|
180
|
+
end
|
152
181
|
end
|
153
182
|
end
|
154
183
|
|
@@ -216,10 +245,28 @@ class BlockingEnumerator
|
|
216
245
|
end
|
217
246
|
end
|
218
247
|
|
248
|
+
# Intended to be used to wrap a call_op, and to adjust
|
249
|
+
# the write flag of the call_op in between messages yielded to it.
|
250
|
+
class WriteFlagSettingStreamingInputEnumerable
|
251
|
+
attr_accessor :call_op
|
252
|
+
|
253
|
+
def initialize(requests_and_write_flags)
|
254
|
+
@requests_and_write_flags = requests_and_write_flags
|
255
|
+
end
|
256
|
+
|
257
|
+
def each
|
258
|
+
@requests_and_write_flags.each do |request_and_flag|
|
259
|
+
@call_op.write_flag = request_and_flag[:write_flag]
|
260
|
+
yield request_and_flag[:request]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
219
265
|
# defines methods corresponding to each interop test case.
|
220
266
|
class NamedTests
|
221
267
|
include Grpc::Testing
|
222
268
|
include Grpc::Testing::PayloadType
|
269
|
+
include GRPC::Core::MetadataKeys
|
223
270
|
|
224
271
|
def initialize(stub, args)
|
225
272
|
@stub = stub
|
@@ -235,6 +282,48 @@ class NamedTests
|
|
235
282
|
perform_large_unary
|
236
283
|
end
|
237
284
|
|
285
|
+
def client_compressed_unary
|
286
|
+
# first request used also for the probe
|
287
|
+
req_size, wanted_response_size = 271_828, 314_159
|
288
|
+
expect_compressed = BoolValue.new(value: true)
|
289
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
|
290
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
291
|
+
response_size: wanted_response_size,
|
292
|
+
payload: payload,
|
293
|
+
expect_compressed: expect_compressed)
|
294
|
+
|
295
|
+
# send a probe to see if CompressedResponse is supported on the server
|
296
|
+
send_probe_for_compressed_request_support do
|
297
|
+
request_uncompressed_args = {
|
298
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
299
|
+
}
|
300
|
+
@stub.unary_call(req, metadata: request_uncompressed_args)
|
301
|
+
end
|
302
|
+
|
303
|
+
# make a call with a compressed message
|
304
|
+
resp = @stub.unary_call(req)
|
305
|
+
assert('Expected second unary call with compression to work') do
|
306
|
+
resp.payload.body.length == wanted_response_size
|
307
|
+
end
|
308
|
+
|
309
|
+
# make a call with an uncompressed message
|
310
|
+
stub_options = {
|
311
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
312
|
+
}
|
313
|
+
|
314
|
+
req = SimpleRequest.new(
|
315
|
+
response_type: :COMPRESSABLE,
|
316
|
+
response_size: wanted_response_size,
|
317
|
+
payload: payload,
|
318
|
+
expect_compressed: BoolValue.new(value: false)
|
319
|
+
)
|
320
|
+
|
321
|
+
resp = @stub.unary_call(req, metadata: stub_options)
|
322
|
+
assert('Expected second unary call with compression to work') do
|
323
|
+
resp.payload.body.length == wanted_response_size
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
238
327
|
def service_account_creds
|
239
328
|
# ignore this test if the oauth options are not set
|
240
329
|
if @args.oauth_scope.nil?
|
@@ -309,6 +398,50 @@ class NamedTests
|
|
309
398
|
end
|
310
399
|
end
|
311
400
|
|
401
|
+
def client_compressed_streaming
|
402
|
+
# first request used also by the probe
|
403
|
+
first_request = StreamingInputCallRequest.new(
|
404
|
+
payload: Payload.new(type: :COMPRESSABLE, body: nulls(27_182)),
|
405
|
+
expect_compressed: BoolValue.new(value: true)
|
406
|
+
)
|
407
|
+
|
408
|
+
# send a probe to see if CompressedResponse is supported on the server
|
409
|
+
send_probe_for_compressed_request_support do
|
410
|
+
request_uncompressed_args = {
|
411
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
412
|
+
}
|
413
|
+
@stub.streaming_input_call([first_request],
|
414
|
+
metadata: request_uncompressed_args)
|
415
|
+
end
|
416
|
+
|
417
|
+
second_request = StreamingInputCallRequest.new(
|
418
|
+
payload: Payload.new(type: :COMPRESSABLE, body: nulls(45_904)),
|
419
|
+
expect_compressed: BoolValue.new(value: false)
|
420
|
+
)
|
421
|
+
|
422
|
+
# Create the requests messages and the corresponding write flags
|
423
|
+
# for each message
|
424
|
+
requests = WriteFlagSettingStreamingInputEnumerable.new([
|
425
|
+
{ request: first_request,
|
426
|
+
write_flag: 0 },
|
427
|
+
{ request: second_request,
|
428
|
+
write_flag: GRPC::Core::WriteFlags::NO_COMPRESS }
|
429
|
+
])
|
430
|
+
|
431
|
+
# Create the call_op, pass it to the requests enumerable, and
|
432
|
+
# run the call
|
433
|
+
call_op = @stub.streaming_input_call(requests,
|
434
|
+
return_op: true)
|
435
|
+
requests.call_op = call_op
|
436
|
+
resp = call_op.execute
|
437
|
+
|
438
|
+
wanted_aggregate_size = 73_086
|
439
|
+
|
440
|
+
assert("#{__callee__}: aggregate payload size is incorrect") do
|
441
|
+
wanted_aggregate_size == resp.aggregated_payload_size
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
312
445
|
def server_streaming
|
313
446
|
msg_sizes = [31_415, 9, 2653, 58_979]
|
314
447
|
response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) }
|
@@ -338,11 +471,8 @@ class NamedTests
|
|
338
471
|
deadline = GRPC::Core::TimeConsts::from_relative_time(1)
|
339
472
|
resps = @stub.full_duplex_call(enum.each_item, deadline: deadline)
|
340
473
|
resps.each { } # wait to receive each request (or timeout)
|
341
|
-
fail 'Should have raised GRPC::
|
342
|
-
rescue GRPC::
|
343
|
-
assert("#{__callee__}: status was wrong") do
|
344
|
-
e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
|
345
|
-
end
|
474
|
+
fail 'Should have raised GRPC::DeadlineExceeded'
|
475
|
+
rescue GRPC::DeadlineExceeded
|
346
476
|
end
|
347
477
|
|
348
478
|
def empty_stream
|
@@ -384,6 +514,153 @@ class NamedTests
|
|
384
514
|
op.wait
|
385
515
|
end
|
386
516
|
|
517
|
+
def unimplemented_method
|
518
|
+
begin
|
519
|
+
resp = @stub.unimplemented_call(Empty.new)
|
520
|
+
rescue GRPC::Unimplemented => e
|
521
|
+
return
|
522
|
+
rescue Exception => e
|
523
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
524
|
+
end
|
525
|
+
fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
|
526
|
+
end
|
527
|
+
|
528
|
+
def unimplemented_service
|
529
|
+
begin
|
530
|
+
resp = @stub.unimplemented_call(Empty.new)
|
531
|
+
rescue GRPC::Unimplemented => e
|
532
|
+
return
|
533
|
+
rescue Exception => e
|
534
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
535
|
+
end
|
536
|
+
fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
|
537
|
+
end
|
538
|
+
|
539
|
+
def status_code_and_message
|
540
|
+
|
541
|
+
# Function wide constants.
|
542
|
+
message = "test status method"
|
543
|
+
code = GRPC::Core::StatusCodes::UNKNOWN
|
544
|
+
|
545
|
+
# Testing with UnaryCall.
|
546
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(1))
|
547
|
+
echo_status = EchoStatus.new(code: code, message: message)
|
548
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
549
|
+
response_size: 1,
|
550
|
+
payload: payload,
|
551
|
+
response_status: echo_status)
|
552
|
+
seen_correct_exception = false
|
553
|
+
begin
|
554
|
+
resp = @stub.unary_call(req)
|
555
|
+
rescue GRPC::Unknown => e
|
556
|
+
if e.details != message
|
557
|
+
fail AssertionError,
|
558
|
+
"Expected message #{message}. Received: #{e.details}"
|
559
|
+
end
|
560
|
+
seen_correct_exception = true
|
561
|
+
rescue Exception => e
|
562
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
563
|
+
end
|
564
|
+
|
565
|
+
if not seen_correct_exception
|
566
|
+
fail AssertionError, "Did not see expected status from UnaryCall"
|
567
|
+
end
|
568
|
+
|
569
|
+
# testing with FullDuplex
|
570
|
+
req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
|
571
|
+
duplex_req = req_cls.new(payload: Payload.new(body: nulls(1)),
|
572
|
+
response_type: :COMPRESSABLE,
|
573
|
+
response_parameters: [p_cls.new(size: 1)],
|
574
|
+
response_status: echo_status)
|
575
|
+
seen_correct_exception = false
|
576
|
+
begin
|
577
|
+
resp = @stub.full_duplex_call([duplex_req])
|
578
|
+
resp.each { |r| }
|
579
|
+
rescue GRPC::Unknown => e
|
580
|
+
if e.details != message
|
581
|
+
fail AssertionError,
|
582
|
+
"Expected message #{message}. Received: #{e.details}"
|
583
|
+
end
|
584
|
+
seen_correct_exception = true
|
585
|
+
rescue Exception => e
|
586
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
587
|
+
end
|
588
|
+
|
589
|
+
if not seen_correct_exception
|
590
|
+
fail AssertionError, "Did not see expected status from FullDuplexCall"
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
|
595
|
+
|
596
|
+
def custom_metadata
|
597
|
+
|
598
|
+
# Function wide constants
|
599
|
+
req_size, wanted_response_size = 271_828, 314_159
|
600
|
+
initial_metadata_key = "x-grpc-test-echo-initial"
|
601
|
+
initial_metadata_value = "test_initial_metadata_value"
|
602
|
+
trailing_metadata_key = "x-grpc-test-echo-trailing-bin"
|
603
|
+
trailing_metadata_value = "\x0a\x0b\x0a\x0b\x0a\x0b"
|
604
|
+
|
605
|
+
metadata = {
|
606
|
+
initial_metadata_key => initial_metadata_value,
|
607
|
+
trailing_metadata_key => trailing_metadata_value
|
608
|
+
}
|
609
|
+
|
610
|
+
# Testing with UnaryCall
|
611
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
|
612
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
613
|
+
response_size: wanted_response_size,
|
614
|
+
payload: payload)
|
615
|
+
|
616
|
+
op = @stub.unary_call(req, metadata: metadata, return_op: true)
|
617
|
+
op.execute
|
618
|
+
if not op.metadata.has_key?(initial_metadata_key)
|
619
|
+
fail AssertionError, "Expected initial metadata. None received"
|
620
|
+
elsif op.metadata[initial_metadata_key] != metadata[initial_metadata_key]
|
621
|
+
fail AssertionError,
|
622
|
+
"Expected initial metadata: #{metadata[initial_metadata_key]}. "\
|
623
|
+
"Received: #{op.metadata[initial_metadata_key]}"
|
624
|
+
end
|
625
|
+
if not op.trailing_metadata.has_key?(trailing_metadata_key)
|
626
|
+
fail AssertionError, "Expected trailing metadata. None received"
|
627
|
+
elsif op.trailing_metadata[trailing_metadata_key] !=
|
628
|
+
metadata[trailing_metadata_key]
|
629
|
+
fail AssertionError,
|
630
|
+
"Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
|
631
|
+
"Received: #{op.trailing_metadata[trailing_metadata_key]}"
|
632
|
+
end
|
633
|
+
|
634
|
+
# Testing with FullDuplex
|
635
|
+
req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
|
636
|
+
duplex_req = req_cls.new(payload: Payload.new(body: nulls(req_size)),
|
637
|
+
response_type: :COMPRESSABLE,
|
638
|
+
response_parameters: [p_cls.new(size: wanted_response_size)])
|
639
|
+
|
640
|
+
duplex_op = @stub.full_duplex_call([duplex_req], metadata: metadata,
|
641
|
+
return_op: true)
|
642
|
+
resp = duplex_op.execute
|
643
|
+
resp.each { |r| } # ensures that the server sends trailing data
|
644
|
+
duplex_op.wait
|
645
|
+
if not duplex_op.metadata.has_key?(initial_metadata_key)
|
646
|
+
fail AssertionError, "Expected initial metadata. None received"
|
647
|
+
elsif duplex_op.metadata[initial_metadata_key] !=
|
648
|
+
metadata[initial_metadata_key]
|
649
|
+
fail AssertionError,
|
650
|
+
"Expected initial metadata: #{metadata[initial_metadata_key]}. "\
|
651
|
+
"Received: #{duplex_op.metadata[initial_metadata_key]}"
|
652
|
+
end
|
653
|
+
if not duplex_op.trailing_metadata[trailing_metadata_key]
|
654
|
+
fail AssertionError, "Expected trailing metadata. None received"
|
655
|
+
elsif duplex_op.trailing_metadata[trailing_metadata_key] !=
|
656
|
+
metadata[trailing_metadata_key]
|
657
|
+
fail AssertionError,
|
658
|
+
"Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
|
659
|
+
"Received: #{duplex_op.trailing_metadata[trailing_metadata_key]}"
|
660
|
+
end
|
661
|
+
|
662
|
+
end
|
663
|
+
|
387
664
|
def all
|
388
665
|
all_methods = NamedTests.instance_methods(false).map(&:to_s)
|
389
666
|
all_methods.each do |m|
|
@@ -415,6 +692,29 @@ class NamedTests
|
|
415
692
|
end
|
416
693
|
resp
|
417
694
|
end
|
695
|
+
|
696
|
+
# Send probing message for compressed request on the server, to see
|
697
|
+
# if it's implemented.
|
698
|
+
def send_probe_for_compressed_request_support(&send_probe)
|
699
|
+
bad_status_occured = false
|
700
|
+
|
701
|
+
begin
|
702
|
+
send_probe.call
|
703
|
+
rescue GRPC::BadStatus => e
|
704
|
+
if e.code == GRPC::Core::StatusCodes::INVALID_ARGUMENT
|
705
|
+
bad_status_occured = true
|
706
|
+
else
|
707
|
+
fail AssertionError, "Bad status received but code is #{e.code}"
|
708
|
+
end
|
709
|
+
rescue Exception => e
|
710
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
711
|
+
end
|
712
|
+
|
713
|
+
assert('CompressedRequest probe failed') do
|
714
|
+
bad_status_occured
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
418
718
|
end
|
419
719
|
|
420
720
|
# Args is used to hold the command line info.
|