Newer
Older
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include GRPC::Core
shared_context 'setup: tags' do
let(:sent_message) { 'sent message' }
let(:reply_text) { 'the reply' }
def server_allows_client_to_proceed(metadata = {})
murgatroid99
committed
recvd_rpc = @server.request_call
expect(recvd_rpc).to_not eq nil
server_call = recvd_rpc.call
ops = { CallOps::SEND_INITIAL_METADATA => metadata }
murgatroid99
committed
svr_batch = server_call.run_batch(ops)
expect(svr_batch.send_metadata).to be true
server_call
end
murgatroid99
committed
@ch.create_call(nil, nil, '/method', nil, deadline)
end
end
shared_examples 'basic GRPC message delivery is OK' do
include_context 'setup: tags'
context 'the test channel' do
it 'should have a target' do
expect(@ch.target).to be_a(String)
end
end
context 'a client call' do
it 'should have a peer' do
expect(new_client_call.peer).to be_a(String)
end
end
it 'calls have peer info' do
call = new_client_call
expect(call.peer).to be_a(String)
end
it 'servers receive requests from clients and can respond' do
server_call = nil
server_thread = Thread.new do
server_call = server_allows_client_to_proceed
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::SEND_MESSAGE => sent_message
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message
server_ops = {
CallOps::RECV_MESSAGE => nil
}
murgatroid99
committed
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.message).to eq(sent_message)
end
it 'responses written by servers are received by the client' do
call = new_client_call
server_call = nil
server_thread = Thread.new do
server_call = server_allows_client_to_proceed
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::SEND_MESSAGE => sent_message
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message
server_ops = {
CallOps::RECV_MESSAGE => nil,
CallOps::SEND_MESSAGE => reply_text
}
murgatroid99
committed
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.message).to eq(sent_message)
expect(svr_batch.send_message).to be true
it 'compressed messages can be sent and received' do
call = new_client_call
server_call = nil
long_request_str = '0' * 2000
long_response_str = '1' * 2000
md = { 'grpc-internal-encoding-request' => 'gzip' }
server_thread = Thread.new do
server_call = server_allows_client_to_proceed(md)
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => md,
CallOps::SEND_MESSAGE => long_request_str
}
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message
server_thread.join
server_ops = {
CallOps::RECV_MESSAGE => nil,
CallOps::SEND_MESSAGE => long_response_str
}
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.message).to eq(long_request_str)
expect(svr_batch.send_message).to be true
client_ops = {
CallOps::SEND_CLOSE_FROM_CLIENT => nil,
CallOps::RECV_INITIAL_METADATA => nil,
CallOps::RECV_MESSAGE => nil
}
batch_result = call.run_batch(client_ops)
expect(batch_result.send_close).to be true
expect(batch_result.message).to eq long_response_str
end
it 'servers can ignore a client write and send a status' do
call = new_client_call
server_call = nil
server_thread = Thread.new do
server_call = server_allows_client_to_proceed
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::SEND_MESSAGE => sent_message
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message
the_status = Struct::Status.new(StatusCodes::OK, 'OK')
server_ops = {
CallOps::SEND_STATUS_FROM_SERVER => the_status
}
murgatroid99
committed
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.message).to eq nil
expect(svr_batch.send_status).to be true
end
it 'completes calls by sending status to client and server' do
call = new_client_call
server_call = nil
server_thread = Thread.new do
server_call = server_allows_client_to_proceed
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::SEND_MESSAGE => sent_message
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.send_message).to be true
# confirm the server can read the inbound message and respond
the_status = Struct::Status.new(StatusCodes::OK, 'OK', {})
server_ops = {
CallOps::RECV_MESSAGE => nil,
CallOps::SEND_MESSAGE => reply_text,
CallOps::SEND_STATUS_FROM_SERVER => the_status
}
murgatroid99
committed
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.message).to eq sent_message
expect(svr_batch.send_status).to be true
expect(svr_batch.send_message).to be true
# confirm the client can receive the server response and status.
client_ops = {
CallOps::SEND_CLOSE_FROM_CLIENT => nil,
CallOps::RECV_INITIAL_METADATA => nil,
CallOps::RECV_MESSAGE => nil,
CallOps::RECV_STATUS_ON_CLIENT => nil
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_close).to be true
expect(batch_result.message).to eq reply_text
expect(batch_result.status).to eq the_status
# confirm the server can receive the client close.
server_ops = {
CallOps::RECV_CLOSE_ON_SERVER => nil
}
murgatroid99
committed
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.send_close).to be true
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def client_cancel_test(cancel_proc, expected_code,
expected_details)
call = new_client_call
server_call = nil
server_thread = Thread.new do
server_call = server_allows_client_to_proceed
end
client_ops = {
CallOps::SEND_INITIAL_METADATA => {},
CallOps::RECV_INITIAL_METADATA => nil
}
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
expect(batch_result.metadata).to eq({})
cancel_proc.call(call)
server_thread.join
server_ops = {
CallOps::RECV_CLOSE_ON_SERVER => nil
}
svr_batch = server_call.run_batch(server_ops)
expect(svr_batch.send_close).to be true
client_ops = {
CallOps::RECV_STATUS_ON_CLIENT => {}
}
batch_result = call.run_batch(client_ops)
expect(batch_result.status.code).to be expected_code
expect(batch_result.status.details).to eq expected_details
end
it 'clients can cancel a call on the server' do
expected_code = StatusCodes::CANCELLED
expected_details = 'Cancelled'
cancel_proc = proc { |call| call.cancel }
client_cancel_test(cancel_proc, expected_code, expected_details)
end
it 'cancel_with_status unknown status' do
code = StatusCodes::UNKNOWN
details = 'test unknown reason'
cancel_proc = proc { |call| call.cancel_with_status(code, details) }
client_cancel_test(cancel_proc, code, details)
end
it 'cancel_with_status unknown status' do
code = StatusCodes::FAILED_PRECONDITION
details = 'test failed precondition reason'
cancel_proc = proc { |call| call.cancel_with_status(code, details) }
client_cancel_test(cancel_proc, code, details)
end
end
shared_examples 'GRPC metadata delivery works OK' do
include_context 'setup: tags'
describe 'from client => server' do
before(:example) do
n = 7 # arbitrary number of metadata
diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
symbol_key = { a_key: 'a val' }
@valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
@bad_keys = []
@bad_keys << { Object.new => 'a value' }
@bad_keys << { 1 => 'a value' }
end
it 'raises an exception if a metadata key is invalid' do
@bad_keys.each do |md|
call = new_client_call
client_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
blk = proc do
murgatroid99
committed
call.run_batch(client_ops)
end
expect(&blk).to raise_error
end
end
it 'sends all the metadata pairs when keys and values are valid' do
@valid_metadata.each do |md|
recvd_rpc = nil
rcv_thread = Thread.new do
murgatroid99
committed
recvd_rpc = @server.request_call
client_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.send_metadata).to be true
# confirm the server can receive the client metadata
expect(recvd_rpc).to_not eq nil
recvd_md = recvd_rpc.metadata
replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
end
end
end
describe 'from server => client' do
before(:example) do
n = 7 # arbitrary number of metadata
diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
symbol_key = { a_key: 'a val' }
@valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
@bad_keys = []
@bad_keys << { Object.new => 'a value' }
@bad_keys << { 1 => 'a value' }
end
it 'raises an exception if a metadata key is invalid' do
@bad_keys.each do |md|
recvd_rpc = nil
rcv_thread = Thread.new do
murgatroid99
committed
recvd_rpc = @server.request_call
# client signals that it's done sending metadata to allow server to
# respond
client_ops = {
CallOps::SEND_INITIAL_METADATA => nil
}
murgatroid99
committed
call.run_batch(client_ops)
# server gets the invocation
expect(recvd_rpc).to_not eq nil
server_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
blk = proc do
murgatroid99
committed
recvd_rpc.call.run_batch(server_ops)
end
expect(&blk).to raise_error
it 'sends an empty hash if no metadata is added' do
recvd_rpc = nil
rcv_thread = Thread.new do
murgatroid99
committed
recvd_rpc = @server.request_call
# client signals that it's done sending metadata to allow server to
# respond
client_ops = {
CallOps::SEND_INITIAL_METADATA => nil
}
murgatroid99
committed
call.run_batch(client_ops)
# server gets the invocation but sends no metadata back
expect(recvd_rpc).to_not eq nil
server_call = recvd_rpc.call
server_ops = {
CallOps::SEND_INITIAL_METADATA => nil
}
murgatroid99
committed
server_call.run_batch(server_ops)
# client receives nothing as expected
client_ops = {
CallOps::RECV_INITIAL_METADATA => nil
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
expect(batch_result.metadata).to eq({})
it 'sends all the pairs when keys and values are valid' do
@valid_metadata.each do |md|
recvd_rpc = nil
rcv_thread = Thread.new do
murgatroid99
committed
recvd_rpc = @server.request_call
# client signals that it's done sending metadata to allow server to
# respond
client_ops = {
CallOps::SEND_INITIAL_METADATA => nil
}
murgatroid99
committed
call.run_batch(client_ops)
# server gets the invocation but sends no metadata back
expect(recvd_rpc).to_not eq nil
server_call = recvd_rpc.call
server_ops = {
CallOps::SEND_INITIAL_METADATA => md
}
murgatroid99
committed
server_call.run_batch(server_ops)
# client receives nothing as expected
client_ops = {
CallOps::RECV_INITIAL_METADATA => nil
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
expect(batch_result.metadata).to eq(replace_symbols)
end
end
end
end
describe 'the http client/server' do
before(:example) do
server_host = '0.0.0.0:0'
murgatroid99
committed
@server = GRPC::Core::Server.new(nil)
server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
@ch = Channel.new("0.0.0.0:#{server_port}", nil, :this_channel_is_insecure)
end
after(:example) do
murgatroid99
committed
@server.close(deadline)
it_behaves_like 'basic GRPC message delivery is OK' do
end
it_behaves_like 'GRPC metadata delivery works OK' do
end
end
describe 'the secure http client/server' do
murgatroid99
committed
include_context 'setup: tags'
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.key', 'server1.pem']
files.map { |f| File.open(File.join(test_root, f)).read }
end
before(:example) do
certs = load_test_certs
server_host = '0.0.0.0:0'
server_creds = GRPC::Core::ServerCredentials.new(
nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
murgatroid99
committed
@server = GRPC::Core::Server.new(nil)
server_port = @server.add_http2_port(server_host, server_creds)
@server.start
args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
GRPC::Core::ChannelCredentials.new(certs[0], nil, nil))
end
after(:example) do
murgatroid99
committed
@server.close(deadline)
end
it_behaves_like 'basic GRPC message delivery is OK' do
end
it_behaves_like 'GRPC metadata delivery works OK' do
end
murgatroid99
committed
def credentials_update_test(creds_update_md)
auth_proc = proc { creds_update_md }
murgatroid99
committed
call_creds = GRPC::Core::CallCredentials.new(auth_proc)
initial_md_key = 'k2'
initial_md_val = 'v2'
initial_md = {}
initial_md[initial_md_key] = initial_md_val
expected_md = creds_update_md.clone
fail 'bad test param' unless expected_md[initial_md_key].nil?
expected_md[initial_md_key] = initial_md_val
murgatroid99
committed
recvd_rpc = nil
rcv_thread = Thread.new do
murgatroid99
committed
recvd_rpc = @server.request_call
murgatroid99
committed
end
call = new_client_call
call.set_credentials! call_creds
client_ops = {
CallOps::SEND_INITIAL_METADATA => initial_md
murgatroid99
committed
}
murgatroid99
committed
batch_result = call.run_batch(client_ops)
murgatroid99
committed
expect(batch_result.send_metadata).to be true
# confirm the server can receive the client metadata
rcv_thread.join
expect(recvd_rpc).to_not eq nil
recvd_md = recvd_rpc.metadata
replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }]
expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
end
it 'modifies metadata with CallCredentials' do
credentials_update_test('k1' => 'updated-v1')
end
it 'modifies large metadata with CallCredentials' do
val_array = %w(
'00000000000000000000000000000000000000000000000000000000000000',
'11111111111111111111111111111111111111111111111111111111111111',
)
md = {
k3: val_array,
k4: '0000000000000000000000000000000000000000000000000000000000',
keeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeey5: 'v1'
}
credentials_update_test(md)
end