grpc 1.42.0.pre1-x86_64-darwin
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 +7 -0
- data/etc/roots.pem +4337 -0
- data/grpc_c.32.ruby +0 -0
- data/grpc_c.64.ruby +0 -0
- data/src/ruby/bin/math_client.rb +140 -0
- data/src/ruby/bin/math_pb.rb +34 -0
- data/src/ruby/bin/math_server.rb +191 -0
- data/src/ruby/bin/math_services_pb.rb +51 -0
- data/src/ruby/bin/noproto_client.rb +93 -0
- data/src/ruby/bin/noproto_server.rb +97 -0
- data/src/ruby/ext/grpc/ext-export.clang +1 -0
- data/src/ruby/ext/grpc/ext-export.gcc +6 -0
- data/src/ruby/ext/grpc/extconf.rb +123 -0
- data/src/ruby/ext/grpc/rb_byte_buffer.c +65 -0
- data/src/ruby/ext/grpc/rb_byte_buffer.h +35 -0
- data/src/ruby/ext/grpc/rb_call.c +1051 -0
- data/src/ruby/ext/grpc/rb_call.h +57 -0
- data/src/ruby/ext/grpc/rb_call_credentials.c +341 -0
- data/src/ruby/ext/grpc/rb_call_credentials.h +31 -0
- data/src/ruby/ext/grpc/rb_channel.c +846 -0
- data/src/ruby/ext/grpc/rb_channel.h +34 -0
- data/src/ruby/ext/grpc/rb_channel_args.c +155 -0
- data/src/ruby/ext/grpc/rb_channel_args.h +38 -0
- data/src/ruby/ext/grpc/rb_channel_credentials.c +286 -0
- data/src/ruby/ext/grpc/rb_channel_credentials.h +37 -0
- data/src/ruby/ext/grpc/rb_completion_queue.c +101 -0
- data/src/ruby/ext/grpc/rb_completion_queue.h +36 -0
- data/src/ruby/ext/grpc/rb_compression_options.c +471 -0
- data/src/ruby/ext/grpc/rb_compression_options.h +29 -0
- data/src/ruby/ext/grpc/rb_enable_cpp.cc +22 -0
- data/src/ruby/ext/grpc/rb_event_thread.c +145 -0
- data/src/ruby/ext/grpc/rb_event_thread.h +21 -0
- data/src/ruby/ext/grpc/rb_grpc.c +333 -0
- data/src/ruby/ext/grpc/rb_grpc.h +77 -0
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +605 -0
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +913 -0
- data/src/ruby/ext/grpc/rb_loader.c +57 -0
- data/src/ruby/ext/grpc/rb_loader.h +25 -0
- data/src/ruby/ext/grpc/rb_server.c +385 -0
- data/src/ruby/ext/grpc/rb_server.h +32 -0
- data/src/ruby/ext/grpc/rb_server_credentials.c +259 -0
- data/src/ruby/ext/grpc/rb_server_credentials.h +37 -0
- data/src/ruby/ext/grpc/rb_xds_channel_credentials.c +218 -0
- data/src/ruby/ext/grpc/rb_xds_channel_credentials.h +37 -0
- data/src/ruby/ext/grpc/rb_xds_server_credentials.c +170 -0
- data/src/ruby/ext/grpc/rb_xds_server_credentials.h +37 -0
- data/src/ruby/lib/grpc/2.4/grpc_c.bundle +0 -0
- data/src/ruby/lib/grpc/2.5/grpc_c.bundle +0 -0
- data/src/ruby/lib/grpc/2.6/grpc_c.bundle +0 -0
- data/src/ruby/lib/grpc/2.7/grpc_c.bundle +0 -0
- data/src/ruby/lib/grpc/3.0/grpc_c.bundle +0 -0
- data/src/ruby/lib/grpc/core/status_codes.rb +135 -0
- data/src/ruby/lib/grpc/core/time_consts.rb +56 -0
- data/src/ruby/lib/grpc/errors.rb +277 -0
- data/src/ruby/lib/grpc/generic/active_call.rb +669 -0
- data/src/ruby/lib/grpc/generic/bidi_call.rb +233 -0
- data/src/ruby/lib/grpc/generic/client_stub.rb +503 -0
- data/src/ruby/lib/grpc/generic/interceptor_registry.rb +53 -0
- data/src/ruby/lib/grpc/generic/interceptors.rb +186 -0
- data/src/ruby/lib/grpc/generic/rpc_desc.rb +204 -0
- data/src/ruby/lib/grpc/generic/rpc_server.rb +551 -0
- data/src/ruby/lib/grpc/generic/service.rb +211 -0
- data/src/ruby/lib/grpc/google_rpc_status_utils.rb +40 -0
- data/src/ruby/lib/grpc/grpc.rb +24 -0
- data/src/ruby/lib/grpc/logconfig.rb +44 -0
- data/src/ruby/lib/grpc/notifier.rb +45 -0
- data/src/ruby/lib/grpc/structs.rb +15 -0
- data/src/ruby/lib/grpc/version.rb +18 -0
- data/src/ruby/lib/grpc.rb +37 -0
- data/src/ruby/pb/README.md +42 -0
- data/src/ruby/pb/generate_proto_ruby.sh +51 -0
- data/src/ruby/pb/grpc/health/checker.rb +75 -0
- data/src/ruby/pb/grpc/health/v1/health_pb.rb +31 -0
- data/src/ruby/pb/grpc/health/v1/health_services_pb.rb +62 -0
- data/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb +44 -0
- data/src/ruby/pb/grpc/testing/metrics_pb.rb +28 -0
- data/src/ruby/pb/grpc/testing/metrics_services_pb.rb +49 -0
- data/src/ruby/pb/src/proto/grpc/testing/empty_pb.rb +17 -0
- data/src/ruby/pb/src/proto/grpc/testing/messages_pb.rb +145 -0
- data/src/ruby/pb/src/proto/grpc/testing/test_pb.rb +16 -0
- data/src/ruby/pb/src/proto/grpc/testing/test_services_pb.rb +152 -0
- data/src/ruby/pb/test/client.rb +769 -0
- data/src/ruby/pb/test/server.rb +252 -0
- data/src/ruby/pb/test/xds_client.rb +415 -0
- data/src/ruby/spec/call_credentials_spec.rb +42 -0
- data/src/ruby/spec/call_spec.rb +180 -0
- data/src/ruby/spec/channel_connection_spec.rb +126 -0
- data/src/ruby/spec/channel_credentials_spec.rb +124 -0
- data/src/ruby/spec/channel_spec.rb +245 -0
- data/src/ruby/spec/client_auth_spec.rb +152 -0
- data/src/ruby/spec/client_server_spec.rb +664 -0
- data/src/ruby/spec/compression_options_spec.rb +149 -0
- data/src/ruby/spec/debug_message_spec.rb +134 -0
- data/src/ruby/spec/error_sanity_spec.rb +49 -0
- data/src/ruby/spec/errors_spec.rb +142 -0
- data/src/ruby/spec/generic/active_call_spec.rb +683 -0
- data/src/ruby/spec/generic/client_interceptors_spec.rb +153 -0
- data/src/ruby/spec/generic/client_stub_spec.rb +1083 -0
- data/src/ruby/spec/generic/interceptor_registry_spec.rb +65 -0
- data/src/ruby/spec/generic/rpc_desc_spec.rb +374 -0
- data/src/ruby/spec/generic/rpc_server_pool_spec.rb +127 -0
- data/src/ruby/spec/generic/rpc_server_spec.rb +748 -0
- data/src/ruby/spec/generic/server_interceptors_spec.rb +218 -0
- data/src/ruby/spec/generic/service_spec.rb +263 -0
- data/src/ruby/spec/google_rpc_status_utils_spec.rb +282 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options.proto +28 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import.proto +22 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_import2.proto +23 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/package_options_ruby_style.proto +41 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/same_package_service_name.proto +27 -0
- data/src/ruby/spec/pb/codegen/grpc/testing/same_ruby_package_service_name.proto +29 -0
- data/src/ruby/spec/pb/codegen/package_option_spec.rb +98 -0
- data/src/ruby/spec/pb/duplicate/codegen_spec.rb +57 -0
- data/src/ruby/spec/pb/health/checker_spec.rb +236 -0
- data/src/ruby/spec/server_credentials_spec.rb +104 -0
- data/src/ruby/spec/server_spec.rb +231 -0
- data/src/ruby/spec/spec_helper.rb +61 -0
- data/src/ruby/spec/support/helpers.rb +107 -0
- data/src/ruby/spec/support/services.rb +160 -0
- data/src/ruby/spec/testdata/README +1 -0
- data/src/ruby/spec/testdata/ca.pem +20 -0
- data/src/ruby/spec/testdata/client.key +28 -0
- data/src/ruby/spec/testdata/client.pem +20 -0
- data/src/ruby/spec/testdata/server1.key +28 -0
- data/src/ruby/spec/testdata/server1.pem +22 -0
- data/src/ruby/spec/time_consts_spec.rb +74 -0
- data/src/ruby/spec/user_agent_spec.rb +74 -0
- metadata +404 -0
@@ -0,0 +1,769 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2015 gRPC authors.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
# client is a testing tool that accesses a gRPC interop testing server and runs
|
18
|
+
# a test on it.
|
19
|
+
#
|
20
|
+
# Helps validate interoperation b/w different gRPC implementations.
|
21
|
+
#
|
22
|
+
# Usage: $ path/to/client.rb --server_host=<hostname> \
|
23
|
+
# --server_port=<port> \
|
24
|
+
# --test_case=<testcase_name>
|
25
|
+
|
26
|
+
# These lines are required for the generated files to load grpc
|
27
|
+
this_dir = File.expand_path(File.dirname(__FILE__))
|
28
|
+
lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib')
|
29
|
+
pb_dir = File.dirname(this_dir)
|
30
|
+
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
31
|
+
$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir)
|
32
|
+
|
33
|
+
require 'optparse'
|
34
|
+
require 'logger'
|
35
|
+
|
36
|
+
require_relative '../../lib/grpc'
|
37
|
+
require 'googleauth'
|
38
|
+
require 'google/protobuf'
|
39
|
+
|
40
|
+
require_relative '../src/proto/grpc/testing/empty_pb'
|
41
|
+
require_relative '../src/proto/grpc/testing/messages_pb'
|
42
|
+
require_relative '../src/proto/grpc/testing/test_services_pb'
|
43
|
+
|
44
|
+
AUTH_ENV = Google::Auth::CredentialsLoader::ENV_VAR
|
45
|
+
|
46
|
+
# RubyLogger defines a logger for gRPC based on the standard ruby logger.
|
47
|
+
module RubyLogger
|
48
|
+
def logger
|
49
|
+
LOGGER
|
50
|
+
end
|
51
|
+
|
52
|
+
LOGGER = Logger.new(STDOUT)
|
53
|
+
LOGGER.level = Logger::INFO
|
54
|
+
end
|
55
|
+
|
56
|
+
# GRPC is the general RPC module
|
57
|
+
module GRPC
|
58
|
+
# Inject the noop #logger if no module-level logger method has been injected.
|
59
|
+
extend RubyLogger
|
60
|
+
end
|
61
|
+
|
62
|
+
# AssertionError is use to indicate interop test failures.
|
63
|
+
class AssertionError < RuntimeError; end
|
64
|
+
|
65
|
+
# Fails with AssertionError if the block does evaluate to true
|
66
|
+
def assert(msg = 'unknown cause')
|
67
|
+
fail 'No assertion block provided' unless block_given?
|
68
|
+
fail AssertionError, msg unless yield
|
69
|
+
end
|
70
|
+
|
71
|
+
# loads the certificates used to access the test server securely.
|
72
|
+
def load_test_certs
|
73
|
+
this_dir = File.expand_path(File.dirname(__FILE__))
|
74
|
+
data_dir = File.join(File.dirname(File.dirname(this_dir)), 'spec/testdata')
|
75
|
+
files = ['ca.pem', 'server1.key', 'server1.pem']
|
76
|
+
files.map { |f| File.open(File.join(data_dir, f)).read }
|
77
|
+
end
|
78
|
+
|
79
|
+
# creates SSL Credentials from the test certificates.
|
80
|
+
def test_creds
|
81
|
+
certs = load_test_certs
|
82
|
+
GRPC::Core::ChannelCredentials.new(certs[0])
|
83
|
+
end
|
84
|
+
|
85
|
+
# creates SSL Credentials from the production certificates.
|
86
|
+
def prod_creds
|
87
|
+
GRPC::Core::ChannelCredentials.new()
|
88
|
+
end
|
89
|
+
|
90
|
+
# creates the SSL Credentials.
|
91
|
+
def ssl_creds(use_test_ca)
|
92
|
+
return test_creds if use_test_ca
|
93
|
+
prod_creds
|
94
|
+
end
|
95
|
+
|
96
|
+
# creates a test stub that accesses host:port securely.
|
97
|
+
def create_stub(opts)
|
98
|
+
address = "#{opts.server_host}:#{opts.server_port}"
|
99
|
+
|
100
|
+
# Provide channel args that request compression by default
|
101
|
+
# for compression interop tests
|
102
|
+
if ['client_compressed_unary',
|
103
|
+
'client_compressed_streaming'].include?(opts.test_case)
|
104
|
+
compression_options =
|
105
|
+
GRPC::Core::CompressionOptions.new(default_algorithm: :gzip)
|
106
|
+
compression_channel_args = compression_options.to_channel_arg_hash
|
107
|
+
else
|
108
|
+
compression_channel_args = {}
|
109
|
+
end
|
110
|
+
|
111
|
+
if opts.secure
|
112
|
+
creds = ssl_creds(opts.use_test_ca)
|
113
|
+
stub_opts = {
|
114
|
+
channel_args: {}
|
115
|
+
}
|
116
|
+
unless opts.server_host_override.empty?
|
117
|
+
stub_opts[:channel_args].merge!({
|
118
|
+
GRPC::Core::Channel::SSL_TARGET => opts.server_host_override
|
119
|
+
})
|
120
|
+
end
|
121
|
+
|
122
|
+
# Add service account creds if specified
|
123
|
+
wants_creds = %w(all compute_engine_creds service_account_creds)
|
124
|
+
if wants_creds.include?(opts.test_case)
|
125
|
+
unless opts.oauth_scope.nil?
|
126
|
+
auth_creds = Google::Auth.get_application_default(opts.oauth_scope)
|
127
|
+
call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc)
|
128
|
+
creds = creds.compose call_creds
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if opts.test_case == 'oauth2_auth_token'
|
133
|
+
auth_creds = Google::Auth.get_application_default(opts.oauth_scope)
|
134
|
+
kw = auth_creds.updater_proc.call({}) # gives as an auth token
|
135
|
+
|
136
|
+
# use a metadata update proc that just adds the auth token.
|
137
|
+
call_creds = GRPC::Core::CallCredentials.new(proc { |md| md.merge(kw) })
|
138
|
+
creds = creds.compose call_creds
|
139
|
+
end
|
140
|
+
|
141
|
+
if opts.test_case == 'jwt_token_creds' # don't use a scope
|
142
|
+
auth_creds = Google::Auth.get_application_default
|
143
|
+
call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc)
|
144
|
+
creds = creds.compose call_creds
|
145
|
+
end
|
146
|
+
|
147
|
+
GRPC.logger.info("... connecting securely to #{address}")
|
148
|
+
stub_opts[:channel_args].merge!(compression_channel_args)
|
149
|
+
if opts.test_case == "unimplemented_service"
|
150
|
+
Grpc::Testing::UnimplementedService::Stub.new(address, creds, **stub_opts)
|
151
|
+
else
|
152
|
+
Grpc::Testing::TestService::Stub.new(address, creds, **stub_opts)
|
153
|
+
end
|
154
|
+
else
|
155
|
+
GRPC.logger.info("... connecting insecurely to #{address}")
|
156
|
+
if opts.test_case == "unimplemented_service"
|
157
|
+
Grpc::Testing::UnimplementedService::Stub.new(
|
158
|
+
address,
|
159
|
+
:this_channel_is_insecure,
|
160
|
+
channel_args: compression_channel_args
|
161
|
+
)
|
162
|
+
else
|
163
|
+
Grpc::Testing::TestService::Stub.new(
|
164
|
+
address,
|
165
|
+
:this_channel_is_insecure,
|
166
|
+
channel_args: compression_channel_args
|
167
|
+
)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# produces a string of null chars (\0) of length l.
|
173
|
+
def nulls(l)
|
174
|
+
fail 'requires #{l} to be +ve' if l < 0
|
175
|
+
[].pack('x' * l).force_encoding('ascii-8bit')
|
176
|
+
end
|
177
|
+
|
178
|
+
# a PingPongPlayer implements the ping pong bidi test.
|
179
|
+
class PingPongPlayer
|
180
|
+
include Grpc::Testing
|
181
|
+
include Grpc::Testing::PayloadType
|
182
|
+
attr_accessor :queue
|
183
|
+
attr_accessor :canceller_op
|
184
|
+
|
185
|
+
# reqs is the enumerator over the requests
|
186
|
+
def initialize(msg_sizes)
|
187
|
+
@queue = Queue.new
|
188
|
+
@msg_sizes = msg_sizes
|
189
|
+
@canceller_op = nil # used to cancel after the first response
|
190
|
+
end
|
191
|
+
|
192
|
+
def each_item
|
193
|
+
return enum_for(:each_item) unless block_given?
|
194
|
+
req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters # short
|
195
|
+
count = 0
|
196
|
+
@msg_sizes.each do |m|
|
197
|
+
req_size, resp_size = m
|
198
|
+
req = req_cls.new(payload: Payload.new(body: nulls(req_size)),
|
199
|
+
response_type: :COMPRESSABLE,
|
200
|
+
response_parameters: [p_cls.new(size: resp_size)])
|
201
|
+
yield req
|
202
|
+
resp = @queue.pop
|
203
|
+
assert('payload type is wrong') { :COMPRESSABLE == resp.payload.type }
|
204
|
+
assert("payload body #{count} has the wrong length") do
|
205
|
+
resp_size == resp.payload.body.length
|
206
|
+
end
|
207
|
+
p "OK: ping_pong #{count}"
|
208
|
+
count += 1
|
209
|
+
unless @canceller_op.nil?
|
210
|
+
canceller_op.cancel
|
211
|
+
break
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class BlockingEnumerator
|
218
|
+
include Grpc::Testing
|
219
|
+
include Grpc::Testing::PayloadType
|
220
|
+
|
221
|
+
def initialize(req_size, sleep_time)
|
222
|
+
@req_size = req_size
|
223
|
+
@sleep_time = sleep_time
|
224
|
+
end
|
225
|
+
|
226
|
+
def each_item
|
227
|
+
return enum_for(:each_item) unless block_given?
|
228
|
+
req_cls = StreamingOutputCallRequest
|
229
|
+
req = req_cls.new(payload: Payload.new(body: nulls(@req_size)))
|
230
|
+
yield req
|
231
|
+
# Sleep until after the deadline should have passed
|
232
|
+
sleep(@sleep_time)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Intended to be used to wrap a call_op, and to adjust
|
237
|
+
# the write flag of the call_op in between messages yielded to it.
|
238
|
+
class WriteFlagSettingStreamingInputEnumerable
|
239
|
+
attr_accessor :call_op
|
240
|
+
|
241
|
+
def initialize(requests_and_write_flags)
|
242
|
+
@requests_and_write_flags = requests_and_write_flags
|
243
|
+
end
|
244
|
+
|
245
|
+
def each
|
246
|
+
@requests_and_write_flags.each do |request_and_flag|
|
247
|
+
@call_op.write_flag = request_and_flag[:write_flag]
|
248
|
+
yield request_and_flag[:request]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# defines methods corresponding to each interop test case.
|
254
|
+
class NamedTests
|
255
|
+
include Grpc::Testing
|
256
|
+
include Grpc::Testing::PayloadType
|
257
|
+
include GRPC::Core::MetadataKeys
|
258
|
+
|
259
|
+
def initialize(stub, args)
|
260
|
+
@stub = stub
|
261
|
+
@args = args
|
262
|
+
end
|
263
|
+
|
264
|
+
def empty_unary
|
265
|
+
resp = @stub.empty_call(Empty.new)
|
266
|
+
assert('empty_unary: invalid response') { resp.is_a?(Empty) }
|
267
|
+
end
|
268
|
+
|
269
|
+
def large_unary
|
270
|
+
perform_large_unary
|
271
|
+
end
|
272
|
+
|
273
|
+
def client_compressed_unary
|
274
|
+
# first request used also for the probe
|
275
|
+
req_size, wanted_response_size = 271_828, 314_159
|
276
|
+
expect_compressed = BoolValue.new(value: true)
|
277
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
|
278
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
279
|
+
response_size: wanted_response_size,
|
280
|
+
payload: payload,
|
281
|
+
expect_compressed: expect_compressed)
|
282
|
+
|
283
|
+
# send a probe to see if CompressedResponse is supported on the server
|
284
|
+
send_probe_for_compressed_request_support do
|
285
|
+
request_uncompressed_args = {
|
286
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
287
|
+
}
|
288
|
+
@stub.unary_call(req, metadata: request_uncompressed_args)
|
289
|
+
end
|
290
|
+
|
291
|
+
# make a call with a compressed message
|
292
|
+
resp = @stub.unary_call(req)
|
293
|
+
assert('Expected second unary call with compression to work') do
|
294
|
+
resp.payload.body.length == wanted_response_size
|
295
|
+
end
|
296
|
+
|
297
|
+
# make a call with an uncompressed message
|
298
|
+
stub_options = {
|
299
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
300
|
+
}
|
301
|
+
|
302
|
+
req = SimpleRequest.new(
|
303
|
+
response_type: :COMPRESSABLE,
|
304
|
+
response_size: wanted_response_size,
|
305
|
+
payload: payload,
|
306
|
+
expect_compressed: BoolValue.new(value: false)
|
307
|
+
)
|
308
|
+
|
309
|
+
resp = @stub.unary_call(req, metadata: stub_options)
|
310
|
+
assert('Expected second unary call with compression to work') do
|
311
|
+
resp.payload.body.length == wanted_response_size
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def service_account_creds
|
316
|
+
# ignore this test if the oauth options are not set
|
317
|
+
if @args.oauth_scope.nil?
|
318
|
+
p 'NOT RUN: service_account_creds; no service_account settings'
|
319
|
+
return
|
320
|
+
end
|
321
|
+
json_key = File.read(ENV[AUTH_ENV])
|
322
|
+
wanted_email = MultiJson.load(json_key)['client_email']
|
323
|
+
resp = perform_large_unary(fill_username: true,
|
324
|
+
fill_oauth_scope: true)
|
325
|
+
assert("#{__callee__}: bad username") { wanted_email == resp.username }
|
326
|
+
assert("#{__callee__}: bad oauth scope") do
|
327
|
+
@args.oauth_scope.include?(resp.oauth_scope)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def jwt_token_creds
|
332
|
+
json_key = File.read(ENV[AUTH_ENV])
|
333
|
+
wanted_email = MultiJson.load(json_key)['client_email']
|
334
|
+
resp = perform_large_unary(fill_username: true)
|
335
|
+
assert("#{__callee__}: bad username") { wanted_email == resp.username }
|
336
|
+
end
|
337
|
+
|
338
|
+
def compute_engine_creds
|
339
|
+
resp = perform_large_unary(fill_username: true,
|
340
|
+
fill_oauth_scope: true)
|
341
|
+
assert("#{__callee__}: bad username") do
|
342
|
+
@args.default_service_account == resp.username
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def oauth2_auth_token
|
347
|
+
resp = perform_large_unary(fill_username: true,
|
348
|
+
fill_oauth_scope: true)
|
349
|
+
json_key = File.read(ENV[AUTH_ENV])
|
350
|
+
wanted_email = MultiJson.load(json_key)['client_email']
|
351
|
+
assert("#{__callee__}: bad username") { wanted_email == resp.username }
|
352
|
+
assert("#{__callee__}: bad oauth scope") do
|
353
|
+
@args.oauth_scope.include?(resp.oauth_scope)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def per_rpc_creds
|
358
|
+
auth_creds = Google::Auth.get_application_default(@args.oauth_scope)
|
359
|
+
update_metadata = proc do |md|
|
360
|
+
kw = auth_creds.updater_proc.call({})
|
361
|
+
end
|
362
|
+
|
363
|
+
call_creds = GRPC::Core::CallCredentials.new(update_metadata)
|
364
|
+
|
365
|
+
resp = perform_large_unary(fill_username: true,
|
366
|
+
fill_oauth_scope: true,
|
367
|
+
credentials: call_creds)
|
368
|
+
json_key = File.read(ENV[AUTH_ENV])
|
369
|
+
wanted_email = MultiJson.load(json_key)['client_email']
|
370
|
+
assert("#{__callee__}: bad username") { wanted_email == resp.username }
|
371
|
+
assert("#{__callee__}: bad oauth scope") do
|
372
|
+
@args.oauth_scope.include?(resp.oauth_scope)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def client_streaming
|
377
|
+
msg_sizes = [27_182, 8, 1828, 45_904]
|
378
|
+
wanted_aggregate_size = 74_922
|
379
|
+
reqs = msg_sizes.map do |x|
|
380
|
+
req = Payload.new(body: nulls(x))
|
381
|
+
StreamingInputCallRequest.new(payload: req)
|
382
|
+
end
|
383
|
+
resp = @stub.streaming_input_call(reqs)
|
384
|
+
assert("#{__callee__}: aggregate payload size is incorrect") do
|
385
|
+
wanted_aggregate_size == resp.aggregated_payload_size
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def client_compressed_streaming
|
390
|
+
# first request used also by the probe
|
391
|
+
first_request = StreamingInputCallRequest.new(
|
392
|
+
payload: Payload.new(type: :COMPRESSABLE, body: nulls(27_182)),
|
393
|
+
expect_compressed: BoolValue.new(value: true)
|
394
|
+
)
|
395
|
+
|
396
|
+
# send a probe to see if CompressedResponse is supported on the server
|
397
|
+
send_probe_for_compressed_request_support do
|
398
|
+
request_uncompressed_args = {
|
399
|
+
COMPRESSION_REQUEST_ALGORITHM => 'identity'
|
400
|
+
}
|
401
|
+
@stub.streaming_input_call([first_request],
|
402
|
+
metadata: request_uncompressed_args)
|
403
|
+
end
|
404
|
+
|
405
|
+
second_request = StreamingInputCallRequest.new(
|
406
|
+
payload: Payload.new(type: :COMPRESSABLE, body: nulls(45_904)),
|
407
|
+
expect_compressed: BoolValue.new(value: false)
|
408
|
+
)
|
409
|
+
|
410
|
+
# Create the requests messages and the corresponding write flags
|
411
|
+
# for each message
|
412
|
+
requests = WriteFlagSettingStreamingInputEnumerable.new([
|
413
|
+
{ request: first_request,
|
414
|
+
write_flag: 0 },
|
415
|
+
{ request: second_request,
|
416
|
+
write_flag: GRPC::Core::WriteFlags::NO_COMPRESS }
|
417
|
+
])
|
418
|
+
|
419
|
+
# Create the call_op, pass it to the requests enumerable, and
|
420
|
+
# run the call
|
421
|
+
call_op = @stub.streaming_input_call(requests,
|
422
|
+
return_op: true)
|
423
|
+
requests.call_op = call_op
|
424
|
+
resp = call_op.execute
|
425
|
+
|
426
|
+
wanted_aggregate_size = 73_086
|
427
|
+
|
428
|
+
assert("#{__callee__}: aggregate payload size is incorrect") do
|
429
|
+
wanted_aggregate_size == resp.aggregated_payload_size
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def server_streaming
|
434
|
+
msg_sizes = [31_415, 9, 2653, 58_979]
|
435
|
+
response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) }
|
436
|
+
req = StreamingOutputCallRequest.new(response_type: :COMPRESSABLE,
|
437
|
+
response_parameters: response_spec)
|
438
|
+
resps = @stub.streaming_output_call(req)
|
439
|
+
resps.each_with_index do |r, i|
|
440
|
+
assert("#{__callee__}: too many responses") { i < msg_sizes.length }
|
441
|
+
assert("#{__callee__}: payload body #{i} has the wrong length") do
|
442
|
+
msg_sizes[i] == r.payload.body.length
|
443
|
+
end
|
444
|
+
assert("#{__callee__}: payload type is wrong") do
|
445
|
+
:COMPRESSABLE == r.payload.type
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
def ping_pong
|
451
|
+
msg_sizes = [[27_182, 31_415], [8, 9], [1828, 2653], [45_904, 58_979]]
|
452
|
+
ppp = PingPongPlayer.new(msg_sizes)
|
453
|
+
resps = @stub.full_duplex_call(ppp.each_item)
|
454
|
+
resps.each { |r| ppp.queue.push(r) }
|
455
|
+
end
|
456
|
+
|
457
|
+
def timeout_on_sleeping_server
|
458
|
+
enum = BlockingEnumerator.new(27_182, 2)
|
459
|
+
deadline = GRPC::Core::TimeConsts::from_relative_time(1)
|
460
|
+
resps = @stub.full_duplex_call(enum.each_item, deadline: deadline)
|
461
|
+
resps.each { } # wait to receive each request (or timeout)
|
462
|
+
fail 'Should have raised GRPC::DeadlineExceeded'
|
463
|
+
rescue GRPC::DeadlineExceeded
|
464
|
+
end
|
465
|
+
|
466
|
+
def empty_stream
|
467
|
+
ppp = PingPongPlayer.new([])
|
468
|
+
resps = @stub.full_duplex_call(ppp.each_item)
|
469
|
+
count = 0
|
470
|
+
resps.each do |r|
|
471
|
+
ppp.queue.push(r)
|
472
|
+
count += 1
|
473
|
+
end
|
474
|
+
assert("#{__callee__}: too many responses expected 0") do
|
475
|
+
count == 0
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def cancel_after_begin
|
480
|
+
msg_sizes = [27_182, 8, 1828, 45_904]
|
481
|
+
reqs = msg_sizes.map do |x|
|
482
|
+
req = Payload.new(body: nulls(x))
|
483
|
+
StreamingInputCallRequest.new(payload: req)
|
484
|
+
end
|
485
|
+
op = @stub.streaming_input_call(reqs, return_op: true)
|
486
|
+
op.cancel
|
487
|
+
op.execute
|
488
|
+
fail 'Should have raised GRPC:Cancelled'
|
489
|
+
rescue GRPC::Cancelled
|
490
|
+
assert("#{__callee__}: call operation should be CANCELLED") { op.cancelled? }
|
491
|
+
end
|
492
|
+
|
493
|
+
def cancel_after_first_response
|
494
|
+
msg_sizes = [[27_182, 31_415], [8, 9], [1828, 2653], [45_904, 58_979]]
|
495
|
+
ppp = PingPongPlayer.new(msg_sizes)
|
496
|
+
op = @stub.full_duplex_call(ppp.each_item, return_op: true)
|
497
|
+
ppp.canceller_op = op # causes ppp to cancel after the 1st message
|
498
|
+
op.execute.each { |r| ppp.queue.push(r) }
|
499
|
+
fail 'Should have raised GRPC:Cancelled'
|
500
|
+
rescue GRPC::Cancelled
|
501
|
+
assert("#{__callee__}: call operation should be CANCELLED") { op.cancelled? }
|
502
|
+
op.wait
|
503
|
+
end
|
504
|
+
|
505
|
+
def unimplemented_method
|
506
|
+
begin
|
507
|
+
resp = @stub.unimplemented_call(Empty.new)
|
508
|
+
rescue GRPC::Unimplemented => e
|
509
|
+
return
|
510
|
+
rescue Exception => e
|
511
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
512
|
+
end
|
513
|
+
fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
|
514
|
+
end
|
515
|
+
|
516
|
+
def unimplemented_service
|
517
|
+
begin
|
518
|
+
resp = @stub.unimplemented_call(Empty.new)
|
519
|
+
rescue GRPC::Unimplemented => e
|
520
|
+
return
|
521
|
+
rescue Exception => e
|
522
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
523
|
+
end
|
524
|
+
fail AssertionError, "GRPC::Unimplemented should have been raised. Was not."
|
525
|
+
end
|
526
|
+
|
527
|
+
def status_code_and_message
|
528
|
+
|
529
|
+
# Function wide constants.
|
530
|
+
message = "test status method"
|
531
|
+
code = GRPC::Core::StatusCodes::UNKNOWN
|
532
|
+
|
533
|
+
# Testing with UnaryCall.
|
534
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(1))
|
535
|
+
echo_status = EchoStatus.new(code: code, message: message)
|
536
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
537
|
+
response_size: 1,
|
538
|
+
payload: payload,
|
539
|
+
response_status: echo_status)
|
540
|
+
seen_correct_exception = false
|
541
|
+
begin
|
542
|
+
resp = @stub.unary_call(req)
|
543
|
+
rescue GRPC::Unknown => e
|
544
|
+
if e.details != message
|
545
|
+
fail AssertionError,
|
546
|
+
"Expected message #{message}. Received: #{e.details}"
|
547
|
+
end
|
548
|
+
seen_correct_exception = true
|
549
|
+
rescue Exception => e
|
550
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
551
|
+
end
|
552
|
+
|
553
|
+
if not seen_correct_exception
|
554
|
+
fail AssertionError, "Did not see expected status from UnaryCall"
|
555
|
+
end
|
556
|
+
|
557
|
+
# testing with FullDuplex
|
558
|
+
req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
|
559
|
+
duplex_req = req_cls.new(payload: Payload.new(body: nulls(1)),
|
560
|
+
response_type: :COMPRESSABLE,
|
561
|
+
response_parameters: [p_cls.new(size: 1)],
|
562
|
+
response_status: echo_status)
|
563
|
+
seen_correct_exception = false
|
564
|
+
begin
|
565
|
+
resp = @stub.full_duplex_call([duplex_req])
|
566
|
+
resp.each { |r| }
|
567
|
+
rescue GRPC::Unknown => e
|
568
|
+
if e.details != message
|
569
|
+
fail AssertionError,
|
570
|
+
"Expected message #{message}. Received: #{e.details}"
|
571
|
+
end
|
572
|
+
seen_correct_exception = true
|
573
|
+
rescue Exception => e
|
574
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
575
|
+
end
|
576
|
+
|
577
|
+
if not seen_correct_exception
|
578
|
+
fail AssertionError, "Did not see expected status from FullDuplexCall"
|
579
|
+
end
|
580
|
+
|
581
|
+
end
|
582
|
+
|
583
|
+
|
584
|
+
def custom_metadata
|
585
|
+
|
586
|
+
# Function wide constants
|
587
|
+
req_size, wanted_response_size = 271_828, 314_159
|
588
|
+
initial_metadata_key = "x-grpc-test-echo-initial"
|
589
|
+
initial_metadata_value = "test_initial_metadata_value"
|
590
|
+
trailing_metadata_key = "x-grpc-test-echo-trailing-bin"
|
591
|
+
trailing_metadata_value = "\x0a\x0b\x0a\x0b\x0a\x0b"
|
592
|
+
|
593
|
+
metadata = {
|
594
|
+
initial_metadata_key => initial_metadata_value,
|
595
|
+
trailing_metadata_key => trailing_metadata_value
|
596
|
+
}
|
597
|
+
|
598
|
+
# Testing with UnaryCall
|
599
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
|
600
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
601
|
+
response_size: wanted_response_size,
|
602
|
+
payload: payload)
|
603
|
+
|
604
|
+
op = @stub.unary_call(req, metadata: metadata, return_op: true)
|
605
|
+
op.execute
|
606
|
+
if not op.metadata.has_key?(initial_metadata_key)
|
607
|
+
fail AssertionError, "Expected initial metadata. None received"
|
608
|
+
elsif op.metadata[initial_metadata_key] != metadata[initial_metadata_key]
|
609
|
+
fail AssertionError,
|
610
|
+
"Expected initial metadata: #{metadata[initial_metadata_key]}. "\
|
611
|
+
"Received: #{op.metadata[initial_metadata_key]}"
|
612
|
+
end
|
613
|
+
if not op.trailing_metadata.has_key?(trailing_metadata_key)
|
614
|
+
fail AssertionError, "Expected trailing metadata. None received"
|
615
|
+
elsif op.trailing_metadata[trailing_metadata_key] !=
|
616
|
+
metadata[trailing_metadata_key]
|
617
|
+
fail AssertionError,
|
618
|
+
"Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
|
619
|
+
"Received: #{op.trailing_metadata[trailing_metadata_key]}"
|
620
|
+
end
|
621
|
+
|
622
|
+
# Testing with FullDuplex
|
623
|
+
req_cls, p_cls = StreamingOutputCallRequest, ResponseParameters
|
624
|
+
duplex_req = req_cls.new(payload: Payload.new(body: nulls(req_size)),
|
625
|
+
response_type: :COMPRESSABLE,
|
626
|
+
response_parameters: [p_cls.new(size: wanted_response_size)])
|
627
|
+
|
628
|
+
duplex_op = @stub.full_duplex_call([duplex_req], metadata: metadata,
|
629
|
+
return_op: true)
|
630
|
+
resp = duplex_op.execute
|
631
|
+
resp.each { |r| } # ensures that the server sends trailing data
|
632
|
+
duplex_op.wait
|
633
|
+
if not duplex_op.metadata.has_key?(initial_metadata_key)
|
634
|
+
fail AssertionError, "Expected initial metadata. None received"
|
635
|
+
elsif duplex_op.metadata[initial_metadata_key] !=
|
636
|
+
metadata[initial_metadata_key]
|
637
|
+
fail AssertionError,
|
638
|
+
"Expected initial metadata: #{metadata[initial_metadata_key]}. "\
|
639
|
+
"Received: #{duplex_op.metadata[initial_metadata_key]}"
|
640
|
+
end
|
641
|
+
if not duplex_op.trailing_metadata[trailing_metadata_key]
|
642
|
+
fail AssertionError, "Expected trailing metadata. None received"
|
643
|
+
elsif duplex_op.trailing_metadata[trailing_metadata_key] !=
|
644
|
+
metadata[trailing_metadata_key]
|
645
|
+
fail AssertionError,
|
646
|
+
"Expected trailing metadata: #{metadata[trailing_metadata_key]}. "\
|
647
|
+
"Received: #{duplex_op.trailing_metadata[trailing_metadata_key]}"
|
648
|
+
end
|
649
|
+
|
650
|
+
end
|
651
|
+
|
652
|
+
def all
|
653
|
+
all_methods = NamedTests.instance_methods(false).map(&:to_s)
|
654
|
+
all_methods.each do |m|
|
655
|
+
next if m == 'all' || m.start_with?('assert')
|
656
|
+
p "TESTCASE: #{m}"
|
657
|
+
method(m).call
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
private
|
662
|
+
|
663
|
+
def perform_large_unary(fill_username: false, fill_oauth_scope: false, **kw)
|
664
|
+
req_size, wanted_response_size = 271_828, 314_159
|
665
|
+
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
|
666
|
+
req = SimpleRequest.new(response_type: :COMPRESSABLE,
|
667
|
+
response_size: wanted_response_size,
|
668
|
+
payload: payload)
|
669
|
+
req.fill_username = fill_username
|
670
|
+
req.fill_oauth_scope = fill_oauth_scope
|
671
|
+
resp = @stub.unary_call(req, **kw)
|
672
|
+
assert('payload type is wrong') do
|
673
|
+
:COMPRESSABLE == resp.payload.type
|
674
|
+
end
|
675
|
+
assert('payload body has the wrong length') do
|
676
|
+
wanted_response_size == resp.payload.body.length
|
677
|
+
end
|
678
|
+
assert('payload body is invalid') do
|
679
|
+
nulls(wanted_response_size) == resp.payload.body
|
680
|
+
end
|
681
|
+
resp
|
682
|
+
end
|
683
|
+
|
684
|
+
# Send probing message for compressed request on the server, to see
|
685
|
+
# if it's implemented.
|
686
|
+
def send_probe_for_compressed_request_support(&send_probe)
|
687
|
+
bad_status_occurred = false
|
688
|
+
|
689
|
+
begin
|
690
|
+
send_probe.call
|
691
|
+
rescue GRPC::BadStatus => e
|
692
|
+
if e.code == GRPC::Core::StatusCodes::INVALID_ARGUMENT
|
693
|
+
bad_status_occurred = true
|
694
|
+
else
|
695
|
+
fail AssertionError, "Bad status received but code is #{e.code}"
|
696
|
+
end
|
697
|
+
rescue Exception => e
|
698
|
+
fail AssertionError, "Expected BadStatus. Received: #{e.inspect}"
|
699
|
+
end
|
700
|
+
|
701
|
+
assert('CompressedRequest probe failed') do
|
702
|
+
bad_status_occurred
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
end
|
707
|
+
|
708
|
+
# Args is used to hold the command line info.
|
709
|
+
Args = Struct.new(:default_service_account, :server_host, :server_host_override,
|
710
|
+
:oauth_scope, :server_port, :secure, :test_case,
|
711
|
+
:use_test_ca)
|
712
|
+
|
713
|
+
# validates the command line options, returning them as a Hash.
|
714
|
+
def parse_args
|
715
|
+
args = Args.new
|
716
|
+
args.server_host_override = ''
|
717
|
+
OptionParser.new do |opts|
|
718
|
+
opts.on('--oauth_scope scope',
|
719
|
+
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
|
720
|
+
opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
|
721
|
+
args['server_host'] = v
|
722
|
+
end
|
723
|
+
opts.on('--default_service_account email_address',
|
724
|
+
'email address of the default service account') do |v|
|
725
|
+
args['default_service_account'] = v
|
726
|
+
end
|
727
|
+
opts.on('--server_host_override HOST_OVERRIDE',
|
728
|
+
'override host via a HTTP header') do |v|
|
729
|
+
args['server_host_override'] = v
|
730
|
+
end
|
731
|
+
opts.on('--server_port SERVER_PORT', 'server port') do |v|
|
732
|
+
args['server_port'] = v
|
733
|
+
end
|
734
|
+
# instance_methods(false) gives only the methods defined in that class
|
735
|
+
test_cases = NamedTests.instance_methods(false).map(&:to_s)
|
736
|
+
test_case_list = test_cases.join(',')
|
737
|
+
opts.on('--test_case CODE', test_cases, {}, 'select a test_case',
|
738
|
+
" (#{test_case_list})") { |v| args['test_case'] = v }
|
739
|
+
opts.on('--use_tls USE_TLS', ['false', 'true'],
|
740
|
+
'require a secure connection?') do |v|
|
741
|
+
args['secure'] = v == 'true'
|
742
|
+
end
|
743
|
+
opts.on('--use_test_ca USE_TEST_CA', ['false', 'true'],
|
744
|
+
'if secure, use the test certificate?') do |v|
|
745
|
+
args['use_test_ca'] = v == 'true'
|
746
|
+
end
|
747
|
+
end.parse!
|
748
|
+
_check_args(args)
|
749
|
+
end
|
750
|
+
|
751
|
+
def _check_args(args)
|
752
|
+
%w(server_host server_port test_case).each do |a|
|
753
|
+
if args[a].nil?
|
754
|
+
fail(OptionParser::MissingArgument, "please specify --#{a}")
|
755
|
+
end
|
756
|
+
end
|
757
|
+
args
|
758
|
+
end
|
759
|
+
|
760
|
+
def main
|
761
|
+
opts = parse_args
|
762
|
+
stub = create_stub(opts)
|
763
|
+
NamedTests.new(stub, opts).method(opts['test_case']).call
|
764
|
+
p "OK: #{opts['test_case']}"
|
765
|
+
end
|
766
|
+
|
767
|
+
if __FILE__ == $0
|
768
|
+
main
|
769
|
+
end
|