mirror of https://github.com/grpc/grpc.git
Merge pull request #22876 from apolcyn/recv_status_error_message_ruby
Ruby: surface recv_status_op.error_string and into BadStatus exceptionspull/22473/head
commit
5a5105b89c
5 changed files with 249 additions and 47 deletions
@ -0,0 +1,134 @@ |
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# 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 |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# 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. |
||||
|
||||
require 'spec_helper' |
||||
|
||||
TEST_DEBUG_MESSAGE = 'raised by test server'.freeze |
||||
|
||||
# a test service that checks the cert of its peer |
||||
class DebugMessageTestService |
||||
include GRPC::GenericService |
||||
rpc :an_rpc_raises_abort, EchoMsg, EchoMsg |
||||
rpc :an_rpc_raises_standarderror, EchoMsg, EchoMsg |
||||
|
||||
def an_rpc_raises_abort(_req, _call) |
||||
fail GRPC::Aborted.new( |
||||
'aborted', |
||||
{}, |
||||
TEST_DEBUG_MESSAGE) |
||||
end |
||||
|
||||
def an_rpc_raises_standarderror(_req, _call) |
||||
fail(StandardError, TEST_DEBUG_MESSAGE) |
||||
end |
||||
end |
||||
|
||||
DebugMessageTestServiceStub = DebugMessageTestService.rpc_stub_class |
||||
|
||||
describe 'surfacing and transmitting of debug messages' do |
||||
RpcServer = GRPC::RpcServer |
||||
|
||||
before(:all) do |
||||
server_opts = { |
||||
poll_period: 1 |
||||
} |
||||
@srv = new_rpc_server_for_testing(**server_opts) |
||||
@port = @srv.add_http2_port('0.0.0.0:0', :this_port_is_insecure) |
||||
@srv.handle(DebugMessageTestService) |
||||
@srv_thd = Thread.new { @srv.run } |
||||
@srv.wait_till_running |
||||
end |
||||
|
||||
after(:all) do |
||||
expect(@srv.stopped?).to be(false) |
||||
@srv.stop |
||||
@srv_thd.join |
||||
end |
||||
|
||||
it 'debug error message is not present BadStatus exceptions that dont set it' do |
||||
exception_message = '' |
||||
begin |
||||
fail GRPC::Unavailable('unavailable', {}) |
||||
rescue StandardError => e |
||||
p "Got exception: #{e.message}" |
||||
exception_message = e.message |
||||
end |
||||
expect(exception_message.empty?).to be(false) |
||||
expect(exception_message.include?('debug_error_string')).to be(false) |
||||
end |
||||
|
||||
it 'debug error message is present in locally generated errors' do |
||||
# Create a secure channel. This is just one way to force a |
||||
# connection handshake error, which shoud result in C-core |
||||
# generating a status and error message and surfacing them up. |
||||
test_root = File.join(File.dirname(__FILE__), 'testdata') |
||||
files = ['ca.pem', 'client.key', 'client.pem'] |
||||
creds = files.map { |f| File.open(File.join(test_root, f)).read } |
||||
creds = GRPC::Core::ChannelCredentials.new(creds[0], creds[1], creds[2]) |
||||
stub = DebugMessageTestServiceStub.new( |
||||
"localhost:#{@port}", creds) |
||||
begin |
||||
stub.an_rpc_raises_abort(EchoMsg.new) |
||||
rescue StandardError => e |
||||
p "Got exception: #{e.message}" |
||||
exception_message = e.message |
||||
# check that the RPC did actually result in a BadStatus exception |
||||
expect(e.is_a?(GRPC::BadStatus)).to be(true) |
||||
end |
||||
# just check that the debug_error_string is non-empty (we know that |
||||
# it's a JSON object, so the first character is '{'). |
||||
expect(exception_message.include?('. debug_error_string:{')).to be(true) |
||||
end |
||||
|
||||
it 'debug message is not transmitted from server to client' do |
||||
# in order to not accidentally leak internal details about a |
||||
# server to untrusted clients, avoid including the debug_error_string |
||||
# field of a BadStatusException raised at a server in the |
||||
# RPC status that it sends to clients. |
||||
stub = DebugMessageTestServiceStub.new( |
||||
"localhost:#{@port}", :this_channel_is_insecure) |
||||
exception_message = '' |
||||
begin |
||||
stub.an_rpc_raises_abort(EchoMsg.new) |
||||
rescue StandardError => e |
||||
p "Got exception: #{e.message}" |
||||
exception_message = e.message |
||||
# check that the status was aborted is an indirect way to |
||||
# tell that the RPC did actually get handled by the server |
||||
expect(e.is_a?(GRPC::Aborted)).to be(true) |
||||
end |
||||
# just assert that the contents of the server-side BadStatus |
||||
# debug_error_string field were *not* propagated to the client. |
||||
expect(exception_message.include?('. debug_error_string:{')).to be(true) |
||||
expect(exception_message.include?(TEST_DEBUG_MESSAGE)).to be(false) |
||||
end |
||||
|
||||
it 'standard_error messages are transmitted from server to client' do |
||||
# this test exists mostly in order to understand the test case |
||||
# above, by comparison. |
||||
stub = DebugMessageTestServiceStub.new( |
||||
"localhost:#{@port}", :this_channel_is_insecure) |
||||
exception_message = '' |
||||
begin |
||||
stub.an_rpc_raises_standarderror(EchoMsg.new) |
||||
rescue StandardError => e |
||||
p "Got exception: #{e.message}" |
||||
exception_message = e.message |
||||
expect(e.is_a?(GRPC::BadStatus)).to be(true) |
||||
end |
||||
# assert that the contents of the StandardError exception message |
||||
# are propagated to the client. |
||||
expect(exception_message.include?(TEST_DEBUG_MESSAGE)).to be(true) |
||||
end |
||||
end |
Loading…
Reference in new issue