diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/libs/grpc/test/cpp/util | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/libs/grpc/test/cpp/util')
41 files changed, 7141 insertions, 0 deletions
diff --git a/contrib/libs/grpc/test/cpp/util/.yandex_meta/licenses.list.txt b/contrib/libs/grpc/test/cpp/util/.yandex_meta/licenses.list.txt new file mode 100644 index 0000000000..d2dadabed9 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/.yandex_meta/licenses.list.txt @@ -0,0 +1,32 @@ +====================Apache-2.0==================== + * 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. + + +====================COPYRIGHT==================== + * Copyright 2015 gRPC authors. + + +====================COPYRIGHT==================== + * Copyright 2015-2016 gRPC authors. + + +====================COPYRIGHT==================== + * Copyright 2016 gRPC authors. + + +====================COPYRIGHT==================== + * Copyright 2017 gRPC authors. + + +====================COPYRIGHT==================== + * Copyright 2018 gRPC authors. diff --git a/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.cc b/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.cc new file mode 100644 index 0000000000..5971b53075 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.cc @@ -0,0 +1,57 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/byte_buffer_proto_helper.h" + +namespace grpc { +namespace testing { + +bool ParseFromByteBuffer(ByteBuffer* buffer, grpc::protobuf::Message* message) { + std::vector<Slice> slices; + (void)buffer->Dump(&slices); + TString buf; + buf.reserve(buffer->Length()); + for (auto s = slices.begin(); s != slices.end(); s++) { + buf.append(reinterpret_cast<const char*>(s->begin()), s->size()); + } + return message->ParseFromString(buf); +} + +std::unique_ptr<ByteBuffer> SerializeToByteBuffer( + grpc::protobuf::Message* message) { + TString buf; + message->SerializeToString(&buf); + Slice slice(buf); + return std::unique_ptr<ByteBuffer>(new ByteBuffer(&slice, 1)); +} + +bool SerializeToByteBufferInPlace(grpc::protobuf::Message* message, + ByteBuffer* buffer) { + TString buf; + if (!message->SerializeToString(&buf)) { + return false; + } + buffer->Clear(); + Slice slice(buf); + ByteBuffer tmp(&slice, 1); + buffer->Swap(&tmp); + return true; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.h b/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.h new file mode 100644 index 0000000000..3d01fb2468 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/byte_buffer_proto_helper.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H +#define GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H + +#include <memory> + +#include <grpcpp/impl/codegen/config_protobuf.h> +#include <grpcpp/support/byte_buffer.h> + +namespace grpc { +namespace testing { + +bool ParseFromByteBuffer(ByteBuffer* buffer, + ::grpc::protobuf::Message* message); + +std::unique_ptr<ByteBuffer> SerializeToByteBuffer( + ::grpc::protobuf::Message* message); + +bool SerializeToByteBufferInPlace(::grpc::protobuf::Message* message, + ByteBuffer* buffer); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_BYTE_BUFFER_PROTO_HELPER_H diff --git a/contrib/libs/grpc/test/cpp/util/byte_buffer_test.cc b/contrib/libs/grpc/test/cpp/util/byte_buffer_test.cc new file mode 100644 index 0000000000..c63f351a8f --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/byte_buffer_test.cc @@ -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. + * + */ + +#include <grpc++/support/byte_buffer.h> +#include <grpcpp/impl/grpc_library.h> + +#include <cstring> +#include <vector> + +#include <grpc/grpc.h> +#include <grpc/slice.h> +#include <grpcpp/support/slice.h> +#include <gtest/gtest.h> + +#include "test/core/util/test_config.h" + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; + +namespace { + +const char* kContent1 = "hello xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char* kContent2 = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy world"; + +class ByteBufferTest : public ::testing::Test { + protected: + static void SetUpTestCase() { grpc_init(); } + + static void TearDownTestCase() { grpc_shutdown(); } +}; + +TEST_F(ByteBufferTest, CopyCtor) { + ByteBuffer buffer1; + EXPECT_FALSE(buffer1.Valid()); + const ByteBuffer& buffer2 = buffer1; + EXPECT_FALSE(buffer2.Valid()); +} + +TEST_F(ByteBufferTest, CreateFromSingleSlice) { + Slice s(kContent1); + ByteBuffer buffer(&s, 1); + EXPECT_EQ(strlen(kContent1), buffer.Length()); +} + +TEST_F(ByteBufferTest, CreateFromVector) { + std::vector<Slice> slices; + slices.emplace_back(kContent1); + slices.emplace_back(kContent2); + ByteBuffer buffer(&slices[0], 2); + EXPECT_EQ(strlen(kContent1) + strlen(kContent2), buffer.Length()); +} + +TEST_F(ByteBufferTest, Clear) { + Slice s(kContent1); + ByteBuffer buffer(&s, 1); + buffer.Clear(); + EXPECT_EQ(static_cast<size_t>(0), buffer.Length()); +} + +TEST_F(ByteBufferTest, Length) { + std::vector<Slice> slices; + slices.emplace_back(kContent1); + slices.emplace_back(kContent2); + ByteBuffer buffer(&slices[0], 2); + EXPECT_EQ(strlen(kContent1) + strlen(kContent2), buffer.Length()); +} + +bool SliceEqual(const Slice& a, grpc_slice b) { + if (a.size() != GRPC_SLICE_LENGTH(b)) { + return false; + } + for (size_t i = 0; i < a.size(); i++) { + if (a.begin()[i] != GRPC_SLICE_START_PTR(b)[i]) { + return false; + } + } + return true; +} + +TEST_F(ByteBufferTest, Dump) { + grpc_slice hello = grpc_slice_from_copied_string(kContent1); + grpc_slice world = grpc_slice_from_copied_string(kContent2); + std::vector<Slice> slices; + slices.push_back(Slice(hello, Slice::STEAL_REF)); + slices.push_back(Slice(world, Slice::STEAL_REF)); + ByteBuffer buffer(&slices[0], 2); + slices.clear(); + (void)buffer.Dump(&slices); + EXPECT_TRUE(SliceEqual(slices[0], hello)); + EXPECT_TRUE(SliceEqual(slices[1], world)); +} + +TEST_F(ByteBufferTest, SerializationMakesCopy) { + grpc_slice hello = grpc_slice_from_copied_string(kContent1); + grpc_slice world = grpc_slice_from_copied_string(kContent2); + std::vector<Slice> slices; + slices.push_back(Slice(hello, Slice::STEAL_REF)); + slices.push_back(Slice(world, Slice::STEAL_REF)); + ByteBuffer send_buffer; + bool owned = false; + ByteBuffer buffer(&slices[0], 2); + slices.clear(); + auto status = SerializationTraits<ByteBuffer, void>::Serialize( + buffer, &send_buffer, &owned); + EXPECT_TRUE(status.ok()); + EXPECT_TRUE(owned); + EXPECT_TRUE(send_buffer.Valid()); +} + +} // namespace +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.cc b/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.cc new file mode 100644 index 0000000000..d4b4026774 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.cc @@ -0,0 +1,115 @@ +/* + * + * Copyright 2018 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. + * + */ + +#include <grpc/support/port_platform.h> + +#include "test/cpp/util/channel_trace_proto_helper.h" + +#include <grpc/grpc.h> +#include <grpc/support/log.h> +#include <grpcpp/impl/codegen/config.h> +#include <grpcpp/impl/codegen/config_protobuf.h> +#include <gtest/gtest.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/json/json.h" +#include "src/proto/grpc/channelz/channelz.pb.h" + +namespace grpc { + +namespace { + +// Generic helper that takes in a json string, converts it to a proto, and +// then back to json. This ensures that the json string was correctly formatted +// according to https://developers.google.com/protocol-buffers/docs/proto3#json +template <typename Message> +void VaidateProtoJsonTranslation(const TString& json_str) { + Message msg; + grpc::protobuf::json::JsonParseOptions parse_options; + // If the following line is failing, then uncomment the last line of the + // comment, and uncomment the lines that print the two strings. You can + // then compare the output, and determine what fields are missing. + // + // parse_options.ignore_unknown_fields = true; + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, &msg, parse_options); + EXPECT_TRUE(s.ok()); + TString proto_json_str; + grpc::protobuf::json::JsonPrintOptions print_options; + // We usually do not want this to be true, however it can be helpful to + // uncomment and see the output produced then all fields are printed. + // print_options.always_print_primitive_fields = true; + s = grpc::protobuf::json::MessageToJsonString(msg, &proto_json_str); + EXPECT_TRUE(s.ok()); + // Parse JSON and re-dump to string, to make sure formatting is the + // same as what would be generated by our JSON library. + grpc_error* error = GRPC_ERROR_NONE; + grpc_core::Json parsed_json = + grpc_core::Json::Parse(proto_json_str.c_str(), &error); + ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error); + ASSERT_EQ(parsed_json.type(), grpc_core::Json::Type::OBJECT); + proto_json_str = parsed_json.Dump(); + // uncomment these to compare the json strings. + // gpr_log(GPR_ERROR, "tracer json: %s", json_str.c_str()); + // gpr_log(GPR_ERROR, "proto json: %s", proto_json_str.c_str()); + EXPECT_EQ(json_str, proto_json_str); +} + +} // namespace + +namespace testing { + +void ValidateChannelTraceProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>(json_c_str); +} + +void ValidateChannelProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(json_c_str); +} + +void ValidateGetTopChannelsResponseProtoJsonTranslation( + const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::GetTopChannelsResponse>( + json_c_str); +} + +void ValidateGetChannelResponseProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::GetChannelResponse>( + json_c_str); +} + +void ValidateGetServerResponseProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::GetServerResponse>( + json_c_str); +} + +void ValidateSubchannelProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::Subchannel>(json_c_str); +} + +void ValidateServerProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::Server>(json_c_str); +} + +void ValidateGetServersResponseProtoJsonTranslation(const char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::GetServersResponse>( + json_c_str); +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.h b/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.h new file mode 100644 index 0000000000..664e899deb --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/channel_trace_proto_helper.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2018 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H +#define GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H + +namespace grpc { +namespace testing { + +void ValidateChannelTraceProtoJsonTranslation(const char* json_c_str); +void ValidateChannelProtoJsonTranslation(const char* json_c_str); +void ValidateGetTopChannelsResponseProtoJsonTranslation(const char* json_c_str); +void ValidateGetChannelResponseProtoJsonTranslation(const char* json_c_str); +void ValidateGetServerResponseProtoJsonTranslation(const char* json_c_str); +void ValidateSubchannelProtoJsonTranslation(const char* json_c_str); +void ValidateServerProtoJsonTranslation(const char* json_c_str); +void ValidateGetServersResponseProtoJsonTranslation(const char* json_c_str); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_CHANNEL_TRACE_PROTO_HELPER_H diff --git a/contrib/libs/grpc/test/cpp/util/channelz_sampler.cc b/contrib/libs/grpc/test/cpp/util/channelz_sampler.cc new file mode 100644 index 0000000000..e6bde68556 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/channelz_sampler.cc @@ -0,0 +1,588 @@ +/* + * + * 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. + * + */ +#include <unistd.h> + +#include <cstdlib> +#include <fstream> +#include <iostream> +#include <memory> +#include <ostream> +#include <queue> +#include <util/generic/string.h> + +#include "y_absl/strings/str_format.h" +#include "y_absl/strings/str_join.h" +#include "gflags/gflags.h" +#include "google/protobuf/text_format.h" +#include "grpc/grpc.h" +#include "grpc/support/port_platform.h" +#include "grpcpp/channel.h" +#include "grpcpp/client_context.h" +#include "grpcpp/create_channel.h" +#include "grpcpp/ext/channelz_service_plugin.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/security/credentials.h" +#include "grpcpp/security/server_credentials.h" +#include "grpcpp/server.h" +#include "grpcpp/server_builder.h" +#include "grpcpp/server_context.h" +#include "src/core/lib/json/json.h" +#include "src/cpp/server/channelz/channelz_service.h" +#include "src/proto/grpc/channelz/channelz.pb.h" +#include "test/core/util/test_config.h" +#include "test/cpp/util/test_config.h" +#include "test/cpp/util/test_credentials_provider.h" + +DEFINE_string(server_address, "", "channelz server address"); +DEFINE_string(custom_credentials_type, "", "custom credentials type"); +DEFINE_int64(sampling_times, 1, "number of sampling"); +DEFINE_int64(sampling_interval_seconds, 0, "sampling interval in seconds"); +DEFINE_string(output_json, "", "output filename in json format"); + +namespace { +using grpc::ClientContext; +using grpc::Status; +using grpc::StatusCode; +using grpc::channelz::v1::GetChannelRequest; +using grpc::channelz::v1::GetChannelResponse; +using grpc::channelz::v1::GetServerRequest; +using grpc::channelz::v1::GetServerResponse; +using grpc::channelz::v1::GetServerSocketsRequest; +using grpc::channelz::v1::GetServerSocketsResponse; +using grpc::channelz::v1::GetServersRequest; +using grpc::channelz::v1::GetServersResponse; +using grpc::channelz::v1::GetSocketRequest; +using grpc::channelz::v1::GetSocketResponse; +using grpc::channelz::v1::GetSubchannelRequest; +using grpc::channelz::v1::GetSubchannelResponse; +using grpc::channelz::v1::GetTopChannelsRequest; +using grpc::channelz::v1::GetTopChannelsResponse; +} // namespace + +class ChannelzSampler final { + public: + // Get server_id of a server + int64_t GetServerID(const grpc::channelz::v1::Server& server) { + return server.ref().server_id(); + } + + // Get channel_id of a channel + inline int64_t GetChannelID(const grpc::channelz::v1::Channel& channel) { + return channel.ref().channel_id(); + } + + // Get subchannel_id of a subchannel + inline int64_t GetSubchannelID( + const grpc::channelz::v1::Subchannel& subchannel) { + return subchannel.ref().subchannel_id(); + } + + // Get socket_id of a socket + inline int64_t GetSocketID(const grpc::channelz::v1::Socket& socket) { + return socket.ref().socket_id(); + } + + // Get name of a server + inline TString GetServerName(const grpc::channelz::v1::Server& server) { + return server.ref().name(); + } + + // Get name of a channel + inline TString GetChannelName( + const grpc::channelz::v1::Channel& channel) { + return channel.ref().name(); + } + + // Get name of a subchannel + inline TString GetSubchannelName( + const grpc::channelz::v1::Subchannel& subchannel) { + return subchannel.ref().name(); + } + + // Get name of a socket + inline TString GetSocketName(const grpc::channelz::v1::Socket& socket) { + return socket.ref().name(); + } + + // Get a channel based on channel_id + grpc::channelz::v1::Channel GetChannelRPC(int64_t channel_id) { + GetChannelRequest get_channel_request; + get_channel_request.set_channel_id(channel_id); + GetChannelResponse get_channel_response; + ClientContext get_channel_context; + get_channel_context.set_deadline( + grpc_timeout_seconds_to_deadline(rpc_timeout_seconds_)); + Status status = channelz_stub_->GetChannel( + &get_channel_context, get_channel_request, &get_channel_response); + if (!status.ok()) { + gpr_log(GPR_ERROR, "GetChannelRPC failed: %s", + get_channel_context.debug_error_string().c_str()); + GPR_ASSERT(0); + } + return get_channel_response.channel(); + } + + // Get a subchannel based on subchannel_id + grpc::channelz::v1::Subchannel GetSubchannelRPC(int64_t subchannel_id) { + GetSubchannelRequest get_subchannel_request; + get_subchannel_request.set_subchannel_id(subchannel_id); + GetSubchannelResponse get_subchannel_response; + ClientContext get_subchannel_context; + get_subchannel_context.set_deadline( + grpc_timeout_seconds_to_deadline(rpc_timeout_seconds_)); + Status status = channelz_stub_->GetSubchannel(&get_subchannel_context, + get_subchannel_request, + &get_subchannel_response); + if (!status.ok()) { + gpr_log(GPR_ERROR, "GetSubchannelRPC failed: %s", + get_subchannel_context.debug_error_string().c_str()); + GPR_ASSERT(0); + } + return get_subchannel_response.subchannel(); + } + + // get a socket based on socket_id + grpc::channelz::v1::Socket GetSocketRPC(int64_t socket_id) { + GetSocketRequest get_socket_request; + get_socket_request.set_socket_id(socket_id); + GetSocketResponse get_socket_response; + ClientContext get_socket_context; + get_socket_context.set_deadline( + grpc_timeout_seconds_to_deadline(rpc_timeout_seconds_)); + Status status = channelz_stub_->GetSocket( + &get_socket_context, get_socket_request, &get_socket_response); + if (!status.ok()) { + gpr_log(GPR_ERROR, "GetSocketRPC failed: %s", + get_socket_context.debug_error_string().c_str()); + GPR_ASSERT(0); + } + return get_socket_response.socket(); + } + + // get the descedent channels/subchannels/sockets of a channel + // push descedent channels/subchannels to queue for layer traverse + // store descedent channels/subchannels/sockets for dumping data + void GetChannelDescedence( + const grpc::channelz::v1::Channel& channel, + std::queue<grpc::channelz::v1::Channel>& channel_queue, + std::queue<grpc::channelz::v1::Subchannel>& subchannel_queue) { + std::cout << " Channel ID" << GetChannelID(channel) << "_" + << GetChannelName(channel) << " descendence - "; + if (channel.channel_ref_size() > 0 || channel.subchannel_ref_size() > 0) { + if (channel.channel_ref_size() > 0) { + std::cout << "channel: "; + for (const auto& _channelref : channel.channel_ref()) { + int64_t ch_id = _channelref.channel_id(); + std::cout << "ID" << ch_id << "_" << _channelref.name() << " "; + grpc::channelz::v1::Channel ch = GetChannelRPC(ch_id); + channel_queue.push(ch); + if (CheckID(ch_id)) { + all_channels_.push_back(ch); + StoreChannelInJson(ch); + } + } + if (channel.subchannel_ref_size() > 0) { + std::cout << ", "; + } + } + if (channel.subchannel_ref_size() > 0) { + std::cout << "subchannel: "; + for (const auto& _subchannelref : channel.subchannel_ref()) { + int64_t subch_id = _subchannelref.subchannel_id(); + std::cout << "ID" << subch_id << "_" << _subchannelref.name() << " "; + grpc::channelz::v1::Subchannel subch = GetSubchannelRPC(subch_id); + subchannel_queue.push(subch); + if (CheckID(subch_id)) { + all_subchannels_.push_back(subch); + StoreSubchannelInJson(subch); + } + } + } + } else if (channel.socket_ref_size() > 0) { + std::cout << "socket: "; + for (const auto& _socketref : channel.socket_ref()) { + int64_t so_id = _socketref.socket_id(); + std::cout << "ID" << so_id << "_" << _socketref.name() << " "; + grpc::channelz::v1::Socket so = GetSocketRPC(so_id); + if (CheckID(so_id)) { + all_sockets_.push_back(so); + StoreSocketInJson(so); + } + } + } + std::cout << std::endl; + } + + // get the descedent channels/subchannels/sockets of a subchannel + // push descedent channels/subchannels to queue for layer traverse + // store descedent channels/subchannels/sockets for dumping data + void GetSubchannelDescedence( + grpc::channelz::v1::Subchannel& subchannel, + std::queue<grpc::channelz::v1::Channel>& channel_queue, + std::queue<grpc::channelz::v1::Subchannel>& subchannel_queue) { + std::cout << " Subchannel ID" << GetSubchannelID(subchannel) << "_" + << GetSubchannelName(subchannel) << " descendence - "; + if (subchannel.channel_ref_size() > 0 || + subchannel.subchannel_ref_size() > 0) { + if (subchannel.channel_ref_size() > 0) { + std::cout << "channel: "; + for (const auto& _channelref : subchannel.channel_ref()) { + int64_t ch_id = _channelref.channel_id(); + std::cout << "ID" << ch_id << "_" << _channelref.name() << " "; + grpc::channelz::v1::Channel ch = GetChannelRPC(ch_id); + channel_queue.push(ch); + if (CheckID(ch_id)) { + all_channels_.push_back(ch); + StoreChannelInJson(ch); + } + } + if (subchannel.subchannel_ref_size() > 0) { + std::cout << ", "; + } + } + if (subchannel.subchannel_ref_size() > 0) { + std::cout << "subchannel: "; + for (const auto& _subchannelref : subchannel.subchannel_ref()) { + int64_t subch_id = _subchannelref.subchannel_id(); + std::cout << "ID" << subch_id << "_" << _subchannelref.name() << " "; + grpc::channelz::v1::Subchannel subch = GetSubchannelRPC(subch_id); + subchannel_queue.push(subch); + if (CheckID(subch_id)) { + all_subchannels_.push_back(subch); + StoreSubchannelInJson(subch); + } + } + } + } else if (subchannel.socket_ref_size() > 0) { + std::cout << "socket: "; + for (const auto& _socketref : subchannel.socket_ref()) { + int64_t so_id = _socketref.socket_id(); + std::cout << "ID" << so_id << "_" << _socketref.name() << " "; + grpc::channelz::v1::Socket so = GetSocketRPC(so_id); + if (CheckID(so_id)) { + all_sockets_.push_back(so); + StoreSocketInJson(so); + } + } + } + std::cout << std::endl; + } + + // Set up the channelz sampler client + // Initialize json as an array + void Setup(const TString& custom_credentials_type, + const TString& server_address) { + json_ = grpc_core::Json::Array(); + rpc_timeout_seconds_ = 20; + grpc::ChannelArguments channel_args; + std::shared_ptr<grpc::ChannelCredentials> channel_creds = + grpc::testing::GetCredentialsProvider()->GetChannelCredentials( + custom_credentials_type, &channel_args); + if (!channel_creds) { + gpr_log(GPR_ERROR, + "Wrong user credential type: %s. Allowed credential types: " + "INSECURE_CREDENTIALS, ssl, alts, google_default_credentials.", + custom_credentials_type.c_str()); + GPR_ASSERT(0); + } + std::shared_ptr<grpc::Channel> channel = + CreateChannel(server_address, channel_creds); + channelz_stub_ = grpc::channelz::v1::Channelz::NewStub(channel); + } + + // Get all servers, keep querying until getting all + // Store servers for dumping data + // Need to check id repeating for servers + void GetServersRPC() { + int64_t server_start_id = 0; + while (true) { + GetServersRequest get_servers_request; + GetServersResponse get_servers_response; + ClientContext get_servers_context; + get_servers_context.set_deadline( + grpc_timeout_seconds_to_deadline(rpc_timeout_seconds_)); + get_servers_request.set_start_server_id(server_start_id); + Status status = channelz_stub_->GetServers( + &get_servers_context, get_servers_request, &get_servers_response); + if (!status.ok()) { + if (status.error_code() == StatusCode::UNIMPLEMENTED) { + gpr_log(GPR_ERROR, + "Error status UNIMPLEMENTED. Please check and make sure " + "channelz has been registered on the server being queried."); + } else { + gpr_log(GPR_ERROR, + "GetServers RPC with GetServersRequest.server_start_id=%d, " + "failed: %s", + int(server_start_id), + get_servers_context.debug_error_string().c_str()); + } + GPR_ASSERT(0); + } + for (const auto& _server : get_servers_response.server()) { + all_servers_.push_back(_server); + StoreServerInJson(_server); + } + if (!get_servers_response.end()) { + server_start_id = GetServerID(all_servers_.back()) + 1; + } else { + break; + } + } + std::cout << "Number of servers = " << all_servers_.size() << std::endl; + } + + // Get sockets that belongs to servers + // Store sockets for dumping data + void GetSocketsOfServers() { + for (const auto& _server : all_servers_) { + std::cout << "Server ID" << GetServerID(_server) << "_" + << GetServerName(_server) << " listen_socket - "; + for (const auto& _socket : _server.listen_socket()) { + int64_t so_id = _socket.socket_id(); + std::cout << "ID" << so_id << "_" << _socket.name() << " "; + if (CheckID(so_id)) { + grpc::channelz::v1::Socket so = GetSocketRPC(so_id); + all_sockets_.push_back(so); + StoreSocketInJson(so); + } + } + std::cout << std::endl; + } + } + + // Get all top channels, keep querying until getting all + // Store channels for dumping data + // No need to check id repeating for top channels + void GetTopChannelsRPC() { + int64_t channel_start_id = 0; + while (true) { + GetTopChannelsRequest get_top_channels_request; + GetTopChannelsResponse get_top_channels_response; + ClientContext get_top_channels_context; + get_top_channels_context.set_deadline( + grpc_timeout_seconds_to_deadline(rpc_timeout_seconds_)); + get_top_channels_request.set_start_channel_id(channel_start_id); + Status status = channelz_stub_->GetTopChannels( + &get_top_channels_context, get_top_channels_request, + &get_top_channels_response); + if (!status.ok()) { + gpr_log(GPR_ERROR, + "GetTopChannels RPC with " + "GetTopChannelsRequest.channel_start_id=%d failed: %s", + int(channel_start_id), + get_top_channels_context.debug_error_string().c_str()); + GPR_ASSERT(0); + } + for (const auto& _topchannel : get_top_channels_response.channel()) { + top_channels_.push_back(_topchannel); + all_channels_.push_back(_topchannel); + StoreChannelInJson(_topchannel); + } + if (!get_top_channels_response.end()) { + channel_start_id = GetChannelID(top_channels_.back()) + 1; + } else { + break; + } + } + std::cout << std::endl + << "Number of top channels = " << top_channels_.size() + << std::endl; + } + + // layer traverse for each top channel + void TraverseTopChannels() { + for (const auto& _topchannel : top_channels_) { + int tree_depth = 0; + std::queue<grpc::channelz::v1::Channel> channel_queue; + std::queue<grpc::channelz::v1::Subchannel> subchannel_queue; + std::cout << "Tree depth = " << tree_depth << std::endl; + GetChannelDescedence(_topchannel, channel_queue, subchannel_queue); + while (!channel_queue.empty() || !subchannel_queue.empty()) { + ++tree_depth; + std::cout << "Tree depth = " << tree_depth << std::endl; + int ch_q_size = channel_queue.size(); + int subch_q_size = subchannel_queue.size(); + for (int i = 0; i < ch_q_size; ++i) { + grpc::channelz::v1::Channel ch = channel_queue.front(); + channel_queue.pop(); + GetChannelDescedence(ch, channel_queue, subchannel_queue); + } + for (int i = 0; i < subch_q_size; ++i) { + grpc::channelz::v1::Subchannel subch = subchannel_queue.front(); + subchannel_queue.pop(); + GetSubchannelDescedence(subch, channel_queue, subchannel_queue); + } + } + std::cout << std::endl; + } + } + + // dump data of all entities to stdout + void DumpStdout() { + TString data_str; + for (const auto& _channel : all_channels_) { + std::cout << "channel ID" << GetChannelID(_channel) << "_" + << GetChannelName(_channel) << " data:" << std::endl; + // TODO(mohanli): TextFormat::PrintToString records time as seconds and + // nanos. Need a more human readable way. + ::google::protobuf::TextFormat::PrintToString(_channel.data(), &data_str); + printf("%s\n", data_str.c_str()); + } + for (const auto& _subchannel : all_subchannels_) { + std::cout << "subchannel ID" << GetSubchannelID(_subchannel) << "_" + << GetSubchannelName(_subchannel) << " data:" << std::endl; + ::google::protobuf::TextFormat::PrintToString(_subchannel.data(), + &data_str); + printf("%s\n", data_str.c_str()); + } + for (const auto& _server : all_servers_) { + std::cout << "server ID" << GetServerID(_server) << "_" + << GetServerName(_server) << " data:" << std::endl; + ::google::protobuf::TextFormat::PrintToString(_server.data(), &data_str); + printf("%s\n", data_str.c_str()); + } + for (const auto& _socket : all_sockets_) { + std::cout << "socket ID" << GetSocketID(_socket) << "_" + << GetSocketName(_socket) << " data:" << std::endl; + ::google::protobuf::TextFormat::PrintToString(_socket.data(), &data_str); + printf("%s\n", data_str.c_str()); + } + } + + // Store a channel in Json + void StoreChannelInJson(const grpc::channelz::v1::Channel& channel) { + TString id = grpc::to_string(GetChannelID(channel)); + TString type = "Channel"; + TString description; + ::google::protobuf::TextFormat::PrintToString(channel.data(), &description); + grpc_core::Json description_json = grpc_core::Json(description); + StoreEntityInJson(id, type, description_json); + } + + // Store a subchannel in Json + void StoreSubchannelInJson(const grpc::channelz::v1::Subchannel& subchannel) { + TString id = grpc::to_string(GetSubchannelID(subchannel)); + TString type = "Subchannel"; + TString description; + ::google::protobuf::TextFormat::PrintToString(subchannel.data(), + &description); + grpc_core::Json description_json = grpc_core::Json(description); + StoreEntityInJson(id, type, description_json); + } + + // Store a server in Json + void StoreServerInJson(const grpc::channelz::v1::Server& server) { + TString id = grpc::to_string(GetServerID(server)); + TString type = "Server"; + TString description; + ::google::protobuf::TextFormat::PrintToString(server.data(), &description); + grpc_core::Json description_json = grpc_core::Json(description); + StoreEntityInJson(id, type, description_json); + } + + // Store a socket in Json + void StoreSocketInJson(const grpc::channelz::v1::Socket& socket) { + TString id = grpc::to_string(GetSocketID(socket)); + TString type = "Socket"; + TString description; + ::google::protobuf::TextFormat::PrintToString(socket.data(), &description); + grpc_core::Json description_json = grpc_core::Json(description); + StoreEntityInJson(id, type, description_json); + } + + // Store an entity in Json + void StoreEntityInJson(TString& id, TString& type, + const grpc_core::Json& description) { + TString start, finish; + gpr_timespec ago = gpr_time_sub( + now_, + gpr_time_from_seconds(FLAGS_sampling_interval_seconds, GPR_TIMESPAN)); + std::stringstream ss; + const time_t time_now = now_.tv_sec; + ss << std::put_time(std::localtime(&time_now), "%F %T"); + finish = ss.str(); // example: "2019-02-01 12:12:18" + ss.str(""); + const time_t time_ago = ago.tv_sec; + ss << std::put_time(std::localtime(&time_ago), "%F %T"); + start = ss.str(); + grpc_core::Json obj = + grpc_core::Json::Object{{"Task", y_absl::StrFormat("%s_ID%s", type, id)}, + {"Start", start}, + {"Finish", finish}, + {"ID", id}, + {"Type", type}, + {"Description", description}}; + json_.mutable_array()->push_back(obj); + } + + // Dump data in json + TString DumpJson() { return json_.Dump(); } + + // Check if one entity has been recorded + bool CheckID(int64_t id) { + if (id_set_.count(id) == 0) { + id_set_.insert(id); + return true; + } else { + return false; + } + } + + // Record current time + void RecordNow() { now_ = gpr_now(GPR_CLOCK_REALTIME); } + + private: + std::unique_ptr<grpc::channelz::v1::Channelz::Stub> channelz_stub_; + std::vector<grpc::channelz::v1::Channel> top_channels_; + std::vector<grpc::channelz::v1::Server> all_servers_; + std::vector<grpc::channelz::v1::Channel> all_channels_; + std::vector<grpc::channelz::v1::Subchannel> all_subchannels_; + std::vector<grpc::channelz::v1::Socket> all_sockets_; + std::unordered_set<int64_t> id_set_; + grpc_core::Json json_; + int64_t rpc_timeout_seconds_; + gpr_timespec now_; +}; + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + grpc::testing::InitTest(&argc, &argv, true); + std::ofstream output_file(FLAGS_output_json); + for (int i = 0; i < FLAGS_sampling_times; ++i) { + ChannelzSampler channelz_sampler; + channelz_sampler.Setup(FLAGS_custom_credentials_type, FLAGS_server_address); + std::cout << "Wait for sampling interval " + << FLAGS_sampling_interval_seconds << "s..." << std::endl; + const gpr_timespec kDelay = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_seconds(FLAGS_sampling_interval_seconds, GPR_TIMESPAN)); + gpr_sleep_until(kDelay); + std::cout << "##### " << i << "th sampling #####" << std::endl; + channelz_sampler.RecordNow(); + channelz_sampler.GetServersRPC(); + channelz_sampler.GetSocketsOfServers(); + channelz_sampler.GetTopChannelsRPC(); + channelz_sampler.TraverseTopChannels(); + channelz_sampler.DumpStdout(); + if (!FLAGS_output_json.empty()) { + output_file << channelz_sampler.DumpJson() << "\n" << std::flush; + } + } + output_file.close(); + return 0; +} diff --git a/contrib/libs/grpc/test/cpp/util/channelz_sampler_test.cc b/contrib/libs/grpc/test/cpp/util/channelz_sampler_test.cc new file mode 100644 index 0000000000..d81dbb0d05 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/channelz_sampler_test.cc @@ -0,0 +1,176 @@ +/* + * + * Copyright 2016 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. + * + */ +#include <stdlib.h> +#include <unistd.h> + +#include <cstdlib> +#include <iostream> +#include <memory> +#include <util/generic/string.h> +#include <thread> + +#include "grpc/grpc.h" +#include "grpc/support/alloc.h" +#include "grpc/support/port_platform.h" +#include "grpcpp/channel.h" +#include "grpcpp/client_context.h" +#include "grpcpp/create_channel.h" +#include "grpcpp/ext/channelz_service_plugin.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/security/credentials.h" +#include "grpcpp/security/server_credentials.h" +#include "grpcpp/server.h" +#include "grpcpp/server_builder.h" +#include "grpcpp/server_context.h" +#include "gtest/gtest.h" +#include "src/core/lib/gpr/env.h" +#include "src/cpp/server/channelz/channelz_service.h" +#include "src/proto/grpc/testing/test.grpc.pb.h" +#include "test/core/util/test_config.h" +#include "test/cpp/util/subprocess.h" +#include "test/cpp/util/test_credentials_provider.h" + +static TString g_root; + +namespace { +using grpc::ClientContext; +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +} // namespace + +// Test variables +TString server_address("0.0.0.0:10000"); +TString custom_credentials_type("INSECURE_CREDENTIALS"); +TString sampling_times = "2"; +TString sampling_interval_seconds = "3"; +TString output_json("output.json"); + +// Creata an echo server +class EchoServerImpl final : public grpc::testing::TestService::Service { + Status EmptyCall(::grpc::ServerContext* context, + const grpc::testing::Empty* request, + grpc::testing::Empty* response) { + return Status::OK; + } +}; + +// Run client in a thread +void RunClient(const TString& client_id, gpr_event* done_ev) { + grpc::ChannelArguments channel_args; + std::shared_ptr<grpc::ChannelCredentials> channel_creds = + grpc::testing::GetCredentialsProvider()->GetChannelCredentials( + custom_credentials_type, &channel_args); + std::unique_ptr<grpc::testing::TestService::Stub> stub = + grpc::testing::TestService::NewStub( + grpc::CreateChannel(server_address, channel_creds)); + gpr_log(GPR_INFO, "Client %s is echoing!", client_id.c_str()); + while (true) { + if (gpr_event_wait(done_ev, grpc_timeout_seconds_to_deadline(1)) != + nullptr) { + return; + } + grpc::testing::Empty request; + grpc::testing::Empty response; + ClientContext context; + Status status = stub->EmptyCall(&context, request, &response); + if (!status.ok()) { + gpr_log(GPR_ERROR, "Client echo failed."); + GPR_ASSERT(0); + } + } +} + +// Create the channelz to test the connection to the server +bool WaitForConnection(int wait_server_seconds) { + grpc::ChannelArguments channel_args; + std::shared_ptr<grpc::ChannelCredentials> channel_creds = + grpc::testing::GetCredentialsProvider()->GetChannelCredentials( + custom_credentials_type, &channel_args); + auto channel = grpc::CreateChannel(server_address, channel_creds); + return channel->WaitForConnected( + grpc_timeout_seconds_to_deadline(wait_server_seconds)); +} + +// Test the channelz sampler +TEST(ChannelzSamplerTest, SimpleTest) { + // start server + ::grpc::channelz::experimental::InitChannelzService(); + EchoServerImpl service; + grpc::ServerBuilder builder; + auto server_creds = + grpc::testing::GetCredentialsProvider()->GetServerCredentials( + custom_credentials_type); + builder.AddListeningPort(server_address, server_creds); + builder.RegisterService(&service); + std::unique_ptr<Server> server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Server listening on %s", server_address.c_str()); + const int kWaitForServerSeconds = 10; + ASSERT_TRUE(WaitForConnection(kWaitForServerSeconds)); + // client threads + gpr_event done_ev1, done_ev2; + gpr_event_init(&done_ev1); + gpr_event_init(&done_ev2); + std::thread client_thread_1(RunClient, "1", &done_ev1); + std::thread client_thread_2(RunClient, "2", &done_ev2); + // Run the channelz sampler + grpc::SubProcess* test_driver = new grpc::SubProcess( + {g_root + "/channelz_sampler", "--server_address=" + server_address, + "--custom_credentials_type=" + custom_credentials_type, + "--sampling_times=" + sampling_times, + "--sampling_interval_seconds=" + sampling_interval_seconds, + "--output_json=" + output_json}); + int status = test_driver->Join(); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + gpr_log(GPR_ERROR, + "Channelz sampler test test-runner exited with code %d", + WEXITSTATUS(status)); + GPR_ASSERT(0); // log the line number of the assertion failure + } + } else if (WIFSIGNALED(status)) { + gpr_log(GPR_ERROR, "Channelz sampler test test-runner ended from signal %d", + WTERMSIG(status)); + GPR_ASSERT(0); + } else { + gpr_log(GPR_ERROR, + "Channelz sampler test test-runner ended with unknown status %d", + status); + GPR_ASSERT(0); + } + delete test_driver; + gpr_event_set(&done_ev1, (void*)1); + gpr_event_set(&done_ev2, (void*)1); + client_thread_1.join(); + client_thread_2.join(); +} + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + TString me = argv[0]; + auto lslash = me.rfind('/'); + if (lslash != TString::npos) { + g_root = me.substr(0, lslash); + } else { + g_root = "."; + } + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/contrib/libs/grpc/test/cpp/util/cli_call.cc b/contrib/libs/grpc/test/cpp/util/cli_call.cc new file mode 100644 index 0000000000..5b3631667f --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/cli_call.cc @@ -0,0 +1,229 @@ +/* + * + * 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. + * + */ + +#include "test/cpp/util/cli_call.h" + +#include <grpc/grpc.h> +#include <grpc/slice.h> +#include <grpc/support/log.h> +#include <grpcpp/channel.h> +#include <grpcpp/client_context.h> +#include <grpcpp/support/byte_buffer.h> + +#include <cmath> +#include <iostream> +#include <utility> + +namespace grpc { +namespace testing { +namespace { +void* tag(int i) { return (void*)static_cast<intptr_t>(i); } +} // namespace + +Status CliCall::Call(const std::shared_ptr<grpc::Channel>& channel, + const TString& method, const TString& request, + TString* response, + const OutgoingMetadataContainer& metadata, + IncomingMetadataContainer* server_initial_metadata, + IncomingMetadataContainer* server_trailing_metadata) { + CliCall call(channel, method, metadata); + call.Write(request); + call.WritesDone(); + if (!call.Read(response, server_initial_metadata)) { + fprintf(stderr, "Failed to read response.\n"); + } + return call.Finish(server_trailing_metadata); +} + +CliCall::CliCall(const std::shared_ptr<grpc::Channel>& channel, + const TString& method, + const OutgoingMetadataContainer& metadata, CliArgs args) + : stub_(new grpc::GenericStub(channel)) { + gpr_mu_init(&write_mu_); + gpr_cv_init(&write_cv_); + if (!metadata.empty()) { + for (OutgoingMetadataContainer::const_iterator iter = metadata.begin(); + iter != metadata.end(); ++iter) { + ctx_.AddMetadata(iter->first, iter->second); + } + } + + // Set deadline if timeout > 0 (default value -1 if no timeout specified) + if (args.timeout > 0) { + int64_t timeout_in_ns = ceil(args.timeout * 1e9); + + // Convert timeout (in nanoseconds) to a deadline + auto deadline = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_nanos(timeout_in_ns, GPR_TIMESPAN)); + ctx_.set_deadline(deadline); + } else if (args.timeout != -1) { + fprintf( + stderr, + "WARNING: Non-positive timeout value, skipping setting deadline.\n"); + } + + call_ = stub_->PrepareCall(&ctx_, method, &cq_); + call_->StartCall(tag(1)); + void* got_tag; + bool ok; + cq_.Next(&got_tag, &ok); + GPR_ASSERT(ok); +} + +CliCall::~CliCall() { + gpr_cv_destroy(&write_cv_); + gpr_mu_destroy(&write_mu_); +} + +void CliCall::Write(const TString& request) { + void* got_tag; + bool ok; + + gpr_slice s = gpr_slice_from_copied_buffer(request.data(), request.size()); + grpc::Slice req_slice(s, grpc::Slice::STEAL_REF); + grpc::ByteBuffer send_buffer(&req_slice, 1); + call_->Write(send_buffer, tag(2)); + cq_.Next(&got_tag, &ok); + GPR_ASSERT(ok); +} + +bool CliCall::Read(TString* response, + IncomingMetadataContainer* server_initial_metadata) { + void* got_tag; + bool ok; + + grpc::ByteBuffer recv_buffer; + call_->Read(&recv_buffer, tag(3)); + + if (!cq_.Next(&got_tag, &ok) || !ok) { + return false; + } + std::vector<grpc::Slice> slices; + GPR_ASSERT(recv_buffer.Dump(&slices).ok()); + + response->clear(); + for (size_t i = 0; i < slices.size(); i++) { + response->append(reinterpret_cast<const char*>(slices[i].begin()), + slices[i].size()); + } + if (server_initial_metadata) { + *server_initial_metadata = ctx_.GetServerInitialMetadata(); + } + return true; +} + +void CliCall::WritesDone() { + void* got_tag; + bool ok; + + call_->WritesDone(tag(4)); + cq_.Next(&got_tag, &ok); + GPR_ASSERT(ok); +} + +void CliCall::WriteAndWait(const TString& request) { + grpc::Slice req_slice(request); + grpc::ByteBuffer send_buffer(&req_slice, 1); + + gpr_mu_lock(&write_mu_); + call_->Write(send_buffer, tag(2)); + write_done_ = false; + while (!write_done_) { + gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&write_mu_); +} + +void CliCall::WritesDoneAndWait() { + gpr_mu_lock(&write_mu_); + call_->WritesDone(tag(4)); + write_done_ = false; + while (!write_done_) { + gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&write_mu_); +} + +bool CliCall::ReadAndMaybeNotifyWrite( + TString* response, IncomingMetadataContainer* server_initial_metadata) { + void* got_tag; + bool ok; + grpc::ByteBuffer recv_buffer; + + call_->Read(&recv_buffer, tag(3)); + bool cq_result = cq_.Next(&got_tag, &ok); + + while (got_tag != tag(3)) { + gpr_mu_lock(&write_mu_); + write_done_ = true; + gpr_cv_signal(&write_cv_); + gpr_mu_unlock(&write_mu_); + + cq_result = cq_.Next(&got_tag, &ok); + if (got_tag == tag(2)) { + GPR_ASSERT(ok); + } + } + + if (!cq_result || !ok) { + // If the RPC is ended on the server side, we should still wait for the + // pending write on the client side to be done. + if (!ok) { + gpr_mu_lock(&write_mu_); + if (!write_done_) { + cq_.Next(&got_tag, &ok); + GPR_ASSERT(got_tag != tag(2)); + write_done_ = true; + gpr_cv_signal(&write_cv_); + } + gpr_mu_unlock(&write_mu_); + } + return false; + } + + std::vector<grpc::Slice> slices; + GPR_ASSERT(recv_buffer.Dump(&slices).ok()); + response->clear(); + for (size_t i = 0; i < slices.size(); i++) { + response->append(reinterpret_cast<const char*>(slices[i].begin()), + slices[i].size()); + } + if (server_initial_metadata) { + *server_initial_metadata = ctx_.GetServerInitialMetadata(); + } + return true; +} + +Status CliCall::Finish(IncomingMetadataContainer* server_trailing_metadata) { + void* got_tag; + bool ok; + grpc::Status status; + + call_->Finish(&status, tag(5)); + cq_.Next(&got_tag, &ok); + GPR_ASSERT(ok); + if (server_trailing_metadata) { + *server_trailing_metadata = ctx_.GetServerTrailingMetadata(); + } + + return status; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/cli_call.h b/contrib/libs/grpc/test/cpp/util/cli_call.h new file mode 100644 index 0000000000..79d00d99f4 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/cli_call.h @@ -0,0 +1,109 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_CLI_CALL_H +#define GRPC_TEST_CPP_UTIL_CLI_CALL_H + +#include <grpcpp/channel.h> +#include <grpcpp/completion_queue.h> +#include <grpcpp/generic/generic_stub.h> +#include <grpcpp/support/status.h> +#include <grpcpp/support/string_ref.h> + +#include <map> + +namespace grpc { + +class ClientContext; + +struct CliArgs { + double timeout = -1; +}; + +namespace testing { + +// CliCall handles the sending and receiving of generic messages given the name +// of the remote method. This class is only used by GrpcTool. Its thread-safe +// and thread-unsafe methods should not be used together. +class CliCall final { + public: + typedef std::multimap<TString, TString> OutgoingMetadataContainer; + typedef std::multimap<grpc::string_ref, grpc::string_ref> + IncomingMetadataContainer; + + CliCall(const std::shared_ptr<grpc::Channel>& channel, + const TString& method, const OutgoingMetadataContainer& metadata, + CliArgs args); + CliCall(const std::shared_ptr<grpc::Channel>& channel, + const TString& method, const OutgoingMetadataContainer& metadata) + : CliCall(channel, method, metadata, CliArgs{}) {} + + ~CliCall(); + + // Perform an unary generic RPC. + static Status Call(const std::shared_ptr<grpc::Channel>& channel, + const TString& method, const TString& request, + TString* response, + const OutgoingMetadataContainer& metadata, + IncomingMetadataContainer* server_initial_metadata, + IncomingMetadataContainer* server_trailing_metadata); + + // Send a generic request message in a synchronous manner. NOT thread-safe. + void Write(const TString& request); + + // Send a generic request message in a synchronous manner. NOT thread-safe. + void WritesDone(); + + // Receive a generic response message in a synchronous manner.NOT thread-safe. + bool Read(TString* response, + IncomingMetadataContainer* server_initial_metadata); + + // Thread-safe write. Must be used with ReadAndMaybeNotifyWrite. Send out a + // generic request message and wait for ReadAndMaybeNotifyWrite to finish it. + void WriteAndWait(const TString& request); + + // Thread-safe WritesDone. Must be used with ReadAndMaybeNotifyWrite. Send out + // WritesDone for gereneric request messages and wait for + // ReadAndMaybeNotifyWrite to finish it. + void WritesDoneAndWait(); + + // Thread-safe Read. Blockingly receive a generic response message. Notify + // writes if they are finished when this read is waiting for a resposne. + bool ReadAndMaybeNotifyWrite( + TString* response, + IncomingMetadataContainer* server_initial_metadata); + + // Finish the RPC. + Status Finish(IncomingMetadataContainer* server_trailing_metadata); + + TString peer() const { return ctx_.peer(); } + + private: + std::unique_ptr<grpc::GenericStub> stub_; + grpc::ClientContext ctx_; + std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call_; + grpc::CompletionQueue cq_; + gpr_mu write_mu_; + gpr_cv write_cv_; // Protected by write_mu_; + bool write_done_; // Portected by write_mu_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_CLI_CALL_H diff --git a/contrib/libs/grpc/test/cpp/util/cli_call_test.cc b/contrib/libs/grpc/test/cpp/util/cli_call_test.cc new file mode 100644 index 0000000000..4f0544b2e5 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/cli_call_test.cc @@ -0,0 +1,128 @@ +/* + * + * 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. + * + */ + +#include "test/cpp/util/cli_call.h" + +#include <grpc/grpc.h> +#include <grpcpp/channel.h> +#include <grpcpp/client_context.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/server.h> +#include <grpcpp/server_builder.h> +#include <grpcpp/server_context.h> +#include <gtest/gtest.h> + +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/util/string_ref_helper.h" + +using grpc::testing::EchoRequest; +using grpc::testing::EchoResponse; + +namespace grpc { +namespace testing { + +class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { + public: + Status Echo(ServerContext* context, const EchoRequest* request, + EchoResponse* response) override { + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + response->set_message(request->message()); + return Status::OK; + } +}; + +class CliCallTest : public ::testing::Test { + protected: + CliCallTest() {} + + void SetUp() override { + int port = grpc_pick_unused_port_or_die(); + server_address_ << "localhost:" << port; + // Setup server + ServerBuilder builder; + builder.AddListeningPort(server_address_.str(), + InsecureServerCredentials()); + builder.RegisterService(&service_); + server_ = builder.BuildAndStart(); + } + + void TearDown() override { server_->Shutdown(); } + + void ResetStub() { + channel_ = grpc::CreateChannel(server_address_.str(), + InsecureChannelCredentials()); + stub_ = grpc::testing::EchoTestService::NewStub(channel_); + } + + std::shared_ptr<Channel> channel_; + std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_; + std::unique_ptr<Server> server_; + std::ostringstream server_address_; + TestServiceImpl service_; +}; + +// Send a rpc with a normal stub and then a CliCall. Verify they match. +TEST_F(CliCallTest, SimpleRpc) { + ResetStub(); + // Normal stub. + EchoRequest request; + EchoResponse response; + request.set_message("Hello"); + + ClientContext context; + context.AddMetadata("key1", "val1"); + Status s = stub_->Echo(&context, request, &response); + EXPECT_EQ(response.message(), request.message()); + EXPECT_TRUE(s.ok()); + + const TString kMethod("/grpc.testing.EchoTestService/Echo"); + TString request_bin, response_bin, expected_response_bin; + EXPECT_TRUE(request.SerializeToString(&request_bin)); + EXPECT_TRUE(response.SerializeToString(&expected_response_bin)); + std::multimap<TString, TString> client_metadata; + std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata, + server_trailing_metadata; + client_metadata.insert(std::pair<TString, TString>("key1", "val1")); + Status s2 = CliCall::Call(channel_, kMethod, request_bin, &response_bin, + client_metadata, &server_initial_metadata, + &server_trailing_metadata); + EXPECT_TRUE(s2.ok()); + + EXPECT_EQ(expected_response_bin, response_bin); + EXPECT_EQ(context.GetServerInitialMetadata(), server_initial_metadata); + EXPECT_EQ(context.GetServerTrailingMetadata(), server_trailing_metadata); +} + +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contrib/libs/grpc/test/cpp/util/cli_credentials.cc b/contrib/libs/grpc/test/cpp/util/cli_credentials.cc new file mode 100644 index 0000000000..efd548eb9b --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/cli_credentials.cc @@ -0,0 +1,245 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/cli_credentials.h" + +#include <gflags/gflags.h> +#include <grpc/slice.h> +#include <grpc/support/log.h> +#include <grpcpp/impl/codegen/slice.h> + +#include "src/core/lib/iomgr/load_file.h" + +DEFINE_bool( + enable_ssl, false, + "Whether to use ssl/tls. Deprecated. Use --channel_creds_type=ssl."); +DEFINE_bool(use_auth, false, + "Whether to create default google credentials. Deprecated. Use " + "--channel_creds_type=gdc."); +DEFINE_string( + access_token, "", + "The access token that will be sent to the server to authenticate RPCs. " + "Deprecated. Use --call_creds=access_token=<token>."); +DEFINE_string( + ssl_target, "", + "If not empty, treat the server host name as this for ssl/tls certificate " + "validation."); +DEFINE_string( + ssl_client_cert, "", + "If not empty, load this PEM formatted client certificate file. Requires " + "use of --ssl_client_key."); +DEFINE_string( + ssl_client_key, "", + "If not empty, load this PEM formatted private key. Requires use of " + "--ssl_client_cert"); +DEFINE_string( + local_connect_type, "local_tcp", + "The type of local connections for which local channel credentials will " + "be applied. Should be local_tcp or uds."); +DEFINE_string( + channel_creds_type, "", + "The channel creds type: insecure, ssl, gdc (Google Default Credentials), " + "alts, or local."); +DEFINE_string( + call_creds, "", + "Call credentials to use: none (default), or access_token=<token>. If " + "provided, the call creds are composited on top of channel creds."); + +namespace grpc { +namespace testing { + +namespace { + +const char ACCESS_TOKEN_PREFIX[] = "access_token="; +constexpr int ACCESS_TOKEN_PREFIX_LEN = + sizeof(ACCESS_TOKEN_PREFIX) / sizeof(*ACCESS_TOKEN_PREFIX) - 1; + +bool IsAccessToken(const TString& auth) { + return auth.length() > ACCESS_TOKEN_PREFIX_LEN && + auth.compare(0, ACCESS_TOKEN_PREFIX_LEN, ACCESS_TOKEN_PREFIX) == 0; +} + +TString AccessToken(const TString& auth) { + if (!IsAccessToken(auth)) { + return ""; + } + return TString(auth.c_str(), ACCESS_TOKEN_PREFIX_LEN); +} + +} // namespace + +TString CliCredentials::GetDefaultChannelCredsType() const { + // Compatibility logic for --enable_ssl. + if (FLAGS_enable_ssl) { + fprintf(stderr, + "warning: --enable_ssl is deprecated. Use " + "--channel_creds_type=ssl.\n"); + return "ssl"; + } + // Compatibility logic for --use_auth. + if (FLAGS_access_token.empty() && FLAGS_use_auth) { + fprintf(stderr, + "warning: --use_auth is deprecated. Use " + "--channel_creds_type=gdc.\n"); + return "gdc"; + } + return "insecure"; +} + +TString CliCredentials::GetDefaultCallCreds() const { + if (!FLAGS_access_token.empty()) { + fprintf(stderr, + "warning: --access_token is deprecated. Use " + "--call_creds=access_token=<token>.\n"); + return TString("access_token=") + FLAGS_access_token; + } + return "none"; +} + +std::shared_ptr<grpc::ChannelCredentials> +CliCredentials::GetChannelCredentials() const { + if (FLAGS_channel_creds_type.compare("insecure") == 0) { + return grpc::InsecureChannelCredentials(); + } else if (FLAGS_channel_creds_type.compare("ssl") == 0) { + grpc::SslCredentialsOptions ssl_creds_options; + // TODO(@Capstan): This won't affect Google Default Credentials using SSL. + if (!FLAGS_ssl_client_cert.empty()) { + grpc_slice cert_slice = grpc_empty_slice(); + GRPC_LOG_IF_ERROR( + "load_file", + grpc_load_file(FLAGS_ssl_client_cert.c_str(), 1, &cert_slice)); + ssl_creds_options.pem_cert_chain = + grpc::StringFromCopiedSlice(cert_slice); + grpc_slice_unref(cert_slice); + } + if (!FLAGS_ssl_client_key.empty()) { + grpc_slice key_slice = grpc_empty_slice(); + GRPC_LOG_IF_ERROR( + "load_file", + grpc_load_file(FLAGS_ssl_client_key.c_str(), 1, &key_slice)); + ssl_creds_options.pem_private_key = + grpc::StringFromCopiedSlice(key_slice); + grpc_slice_unref(key_slice); + } + return grpc::SslCredentials(ssl_creds_options); + } else if (FLAGS_channel_creds_type.compare("gdc") == 0) { + return grpc::GoogleDefaultCredentials(); + } else if (FLAGS_channel_creds_type.compare("alts") == 0) { + return grpc::experimental::AltsCredentials( + grpc::experimental::AltsCredentialsOptions()); + } else if (FLAGS_channel_creds_type.compare("local") == 0) { + if (FLAGS_local_connect_type.compare("local_tcp") == 0) { + return grpc::experimental::LocalCredentials(LOCAL_TCP); + } else if (FLAGS_local_connect_type.compare("uds") == 0) { + return grpc::experimental::LocalCredentials(UDS); + } else { + fprintf(stderr, + "--local_connect_type=%s invalid; must be local_tcp or uds.\n", + FLAGS_local_connect_type.c_str()); + } + } + fprintf(stderr, + "--channel_creds_type=%s invalid; must be insecure, ssl, gdc, " + "alts, or local.\n", + FLAGS_channel_creds_type.c_str()); + return std::shared_ptr<grpc::ChannelCredentials>(); +} + +std::shared_ptr<grpc::CallCredentials> CliCredentials::GetCallCredentials() + const { + if (IsAccessToken(FLAGS_call_creds.c_str())) { + return grpc::AccessTokenCredentials(AccessToken(FLAGS_call_creds.c_str())); + } + if (FLAGS_call_creds.compare("none") == 0) { + // Nothing to do; creds, if any, are baked into the channel. + return std::shared_ptr<grpc::CallCredentials>(); + } + fprintf(stderr, + "--call_creds=%s invalid; must be none " + "or access_token=<token>.\n", + FLAGS_call_creds.c_str()); + return std::shared_ptr<grpc::CallCredentials>(); +} + +std::shared_ptr<grpc::ChannelCredentials> CliCredentials::GetCredentials() + const { + if (FLAGS_call_creds.empty()) { + FLAGS_call_creds = GetDefaultCallCreds(); + } else if (!FLAGS_access_token.empty() && !IsAccessToken(FLAGS_call_creds.c_str())) { + fprintf(stderr, + "warning: ignoring --access_token because --call_creds " + "already set to %s.\n", + FLAGS_call_creds.c_str()); + } + if (FLAGS_channel_creds_type.empty()) { + FLAGS_channel_creds_type = GetDefaultChannelCredsType(); + } else if (FLAGS_enable_ssl && FLAGS_channel_creds_type.compare("ssl") != 0) { + fprintf(stderr, + "warning: ignoring --enable_ssl because " + "--channel_creds_type already set to %s.\n", + FLAGS_channel_creds_type.c_str()); + } else if (FLAGS_use_auth && FLAGS_channel_creds_type.compare("gdc") != 0) { + fprintf(stderr, + "warning: ignoring --use_auth because " + "--channel_creds_type already set to %s.\n", + FLAGS_channel_creds_type.c_str()); + } + // Legacy transport upgrade logic for insecure requests. + if (IsAccessToken(FLAGS_call_creds.c_str()) && + FLAGS_channel_creds_type.compare("insecure") == 0) { + fprintf(stderr, + "warning: --channel_creds_type=insecure upgraded to ssl because " + "an access token was provided.\n"); + FLAGS_channel_creds_type = "ssl"; + } + std::shared_ptr<grpc::ChannelCredentials> channel_creds = + GetChannelCredentials(); + // Composite any call-type credentials on top of the base channel. + std::shared_ptr<grpc::CallCredentials> call_creds = GetCallCredentials(); + return (channel_creds == nullptr || call_creds == nullptr) + ? channel_creds + : grpc::CompositeChannelCredentials(channel_creds, call_creds); +} + +const TString CliCredentials::GetCredentialUsage() const { + return " --enable_ssl ; Set whether to use ssl " + "(deprecated)\n" + " --use_auth ; Set whether to create default google" + " credentials\n" + " ; (deprecated)\n" + " --access_token ; Set the access token in metadata," + " overrides --use_auth\n" + " ; (deprecated)\n" + " --ssl_target ; Set server host for ssl validation\n" + " --ssl_client_cert ; Client cert for ssl\n" + " --ssl_client_key ; Client private key for ssl\n" + " --local_connect_type ; Set to local_tcp or uds\n" + " --channel_creds_type ; Set to insecure, ssl, gdc, alts, or " + "local\n" + " --call_creds ; Set to none, or" + " access_token=<token>\n"; +} + +const TString CliCredentials::GetSslTargetNameOverride() const { + bool use_ssl = FLAGS_channel_creds_type.compare("ssl") == 0 || + FLAGS_channel_creds_type.compare("gdc") == 0; + return use_ssl ? FLAGS_ssl_target : ""; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/cli_credentials.h b/contrib/libs/grpc/test/cpp/util/cli_credentials.h new file mode 100644 index 0000000000..3e695692fa --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/cli_credentials.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H +#define GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H + +#include <grpcpp/security/credentials.h> +#include <grpcpp/support/config.h> + +namespace grpc { +namespace testing { + +class CliCredentials { + public: + virtual ~CliCredentials() {} + std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const; + virtual const TString GetCredentialUsage() const; + virtual const TString GetSslTargetNameOverride() const; + + protected: + // Returns the appropriate channel_creds_type value for the set of legacy + // flag arguments. + virtual TString GetDefaultChannelCredsType() const; + // Returns the appropriate call_creds value for the set of legacy flag + // arguments. + virtual TString GetDefaultCallCreds() const; + // Returns the base transport channel credentials. Child classes can override + // to support additional channel_creds_types unknown to this base class. + virtual std::shared_ptr<grpc::ChannelCredentials> GetChannelCredentials() + const; + // Returns call credentials to composite onto the base transport channel + // credentials. Child classes can override to support additional + // authentication flags unknown to this base class. + virtual std::shared_ptr<grpc::CallCredentials> GetCallCredentials() const; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_CLI_CREDENTIALS_H diff --git a/contrib/libs/grpc/test/cpp/util/config_grpc_cli.h b/contrib/libs/grpc/test/cpp/util/config_grpc_cli.h new file mode 100644 index 0000000000..358884196d --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/config_grpc_cli.h @@ -0,0 +1,70 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H +#define GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H + +#include <grpcpp/impl/codegen/config_protobuf.h> + +#ifndef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY +#include <google/protobuf/dynamic_message.h> +#define GRPC_CUSTOM_DYNAMICMESSAGEFACTORY \ + ::google::protobuf::DynamicMessageFactory +#endif + +#ifndef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE +#include <google/protobuf/descriptor.h> +#define GRPC_CUSTOM_DESCRIPTORPOOLDATABASE \ + ::google::protobuf::DescriptorPoolDatabase +#define GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE \ + ::google::protobuf::MergedDescriptorDatabase +#endif + +#ifndef GRPC_CUSTOM_TEXTFORMAT +#include <google/protobuf/text_format.h> +#define GRPC_CUSTOM_TEXTFORMAT ::google::protobuf::TextFormat +#endif + +#ifndef GRPC_CUSTOM_DISKSOURCETREE +#include <google/protobuf/compiler/importer.h> +#define GRPC_CUSTOM_DISKSOURCETREE ::google::protobuf::compiler::DiskSourceTree +#define GRPC_CUSTOM_IMPORTER ::google::protobuf::compiler::Importer +#define GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR \ + ::google::protobuf::compiler::MultiFileErrorCollector +#endif + +namespace grpc { +namespace protobuf { + +typedef GRPC_CUSTOM_DYNAMICMESSAGEFACTORY DynamicMessageFactory; + +typedef GRPC_CUSTOM_DESCRIPTORPOOLDATABASE DescriptorPoolDatabase; +typedef GRPC_CUSTOM_MERGEDDESCRIPTORDATABASE MergedDescriptorDatabase; + +typedef GRPC_CUSTOM_TEXTFORMAT TextFormat; + +namespace compiler { +typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree; +typedef GRPC_CUSTOM_IMPORTER Importer; +typedef GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR MultiFileErrorCollector; +} // namespace compiler + +} // namespace protobuf +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_CONFIG_GRPC_CLI_H diff --git a/contrib/libs/grpc/test/cpp/util/create_test_channel.cc b/contrib/libs/grpc/test/cpp/util/create_test_channel.cc new file mode 100644 index 0000000000..86d8e22af1 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/create_test_channel.cc @@ -0,0 +1,252 @@ +/* + * + * Copyright 2015-2016 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. + * + */ + +#include "test/cpp/util/create_test_channel.h" + +#include <gflags/gflags.h> + +#include <grpc/support/log.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/security/credentials.h> + +#include "test/cpp/util/test_credentials_provider.h" + +DEFINE_string( + grpc_test_use_grpclb_with_child_policy, "", + "If non-empty, set a static service config on channels created by " + "grpc::CreateTestChannel, that configures the grpclb LB policy " + "with a child policy being the value of this flag (e.g. round_robin " + "or pick_first)."); + +namespace grpc { + +namespace { + +const char kProdTlsCredentialsType[] = "prod_ssl"; + +class SslCredentialProvider : public testing::CredentialTypeProvider { + public: + std::shared_ptr<ChannelCredentials> GetChannelCredentials( + grpc::ChannelArguments* /*args*/) override { + return grpc::SslCredentials(SslCredentialsOptions()); + } + std::shared_ptr<ServerCredentials> GetServerCredentials() override { + return nullptr; + } +}; + +gpr_once g_once_init_add_prod_ssl_provider = GPR_ONCE_INIT; +// Register ssl with non-test roots type to the credentials provider. +void AddProdSslType() { + testing::GetCredentialsProvider()->AddSecureType( + kProdTlsCredentialsType, std::unique_ptr<testing::CredentialTypeProvider>( + new SslCredentialProvider)); +} + +void MaybeSetCustomChannelArgs(grpc::ChannelArguments* args) { + if (FLAGS_grpc_test_use_grpclb_with_child_policy.size() > 0) { + args->SetString("grpc.service_config", + "{\"loadBalancingConfig\":[{\"grpclb\":{\"childPolicy\":[{" + "\"" + + FLAGS_grpc_test_use_grpclb_with_child_policy + + "\":{}}]}}]}"); + } +} + +} // namespace + +// When cred_type is 'ssl', if server is empty, override_hostname is used to +// create channel. Otherwise, connect to server and override hostname if +// override_hostname is provided. +// When cred_type is not 'ssl', override_hostname is ignored. +// Set use_prod_root to true to use the SSL root for connecting to google. +// In this case, path to the roots pem file must be set via environment variable +// GRPC_DEFAULT_SSL_ROOTS_FILE_PATH. +// Otherwise, root for test SSL cert will be used. +// creds will be used to create a channel when cred_type is 'ssl'. +// Use examples: +// CreateTestChannel( +// "1.1.1.1:12345", "ssl", "override.hostname.com", false, creds); +// CreateTestChannel("test.google.com:443", "ssl", "", true, creds); +// same as above +// CreateTestChannel("", "ssl", "test.google.com:443", true, creds); +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& cred_type, + const TString& override_hostname, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + const ChannelArguments& args) { + return CreateTestChannel(server, cred_type, override_hostname, use_prod_roots, + creds, args, + /*interceptor_creators=*/{}); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + const ChannelArguments& args) { + return CreateTestChannel(server, override_hostname, security_type, + use_prod_roots, creds, args, + /*interceptor_creators=*/{}); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds) { + return CreateTestChannel(server, override_hostname, security_type, + use_prod_roots, creds, ChannelArguments()); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots) { + return CreateTestChannel(server, override_hostname, security_type, + use_prod_roots, std::shared_ptr<CallCredentials>()); +} + +// Shortcut for end2end and interop tests. +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, testing::transport_security security_type) { + return CreateTestChannel(server, "foo.test.google.fr", security_type, false); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& credential_type, + const std::shared_ptr<CallCredentials>& creds) { + ChannelArguments channel_args; + MaybeSetCustomChannelArgs(&channel_args); + std::shared_ptr<ChannelCredentials> channel_creds = + testing::GetCredentialsProvider()->GetChannelCredentials(credential_type, + &channel_args); + GPR_ASSERT(channel_creds != nullptr); + if (creds.get()) { + channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds); + } + return ::grpc::CreateCustomChannel(server, channel_creds, channel_args); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& cred_type, + const TString& override_hostname, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, const ChannelArguments& args, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators) { + ChannelArguments channel_args(args); + MaybeSetCustomChannelArgs(&channel_args); + std::shared_ptr<ChannelCredentials> channel_creds; + if (cred_type.empty()) { + if (interceptor_creators.empty()) { + return ::grpc::CreateCustomChannel(server, InsecureChannelCredentials(), + channel_args); + } else { + return experimental::CreateCustomChannelWithInterceptors( + server, InsecureChannelCredentials(), channel_args, + std::move(interceptor_creators)); + } + } else if (cred_type == testing::kTlsCredentialsType) { // cred_type == "ssl" + if (use_prod_roots) { + gpr_once_init(&g_once_init_add_prod_ssl_provider, &AddProdSslType); + channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials( + kProdTlsCredentialsType, &channel_args); + if (!server.empty() && !override_hostname.empty()) { + channel_args.SetSslTargetNameOverride(override_hostname); + } + } else { + // override_hostname is discarded as the provider handles it. + channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials( + testing::kTlsCredentialsType, &channel_args); + } + GPR_ASSERT(channel_creds != nullptr); + + const TString& connect_to = server.empty() ? override_hostname : server; + if (creds.get()) { + channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds); + } + if (interceptor_creators.empty()) { + return ::grpc::CreateCustomChannel(connect_to, channel_creds, + channel_args); + } else { + return experimental::CreateCustomChannelWithInterceptors( + connect_to, channel_creds, channel_args, + std::move(interceptor_creators)); + } + } else { + channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials( + cred_type, &channel_args); + GPR_ASSERT(channel_creds != nullptr); + + if (interceptor_creators.empty()) { + return ::grpc::CreateCustomChannel(server, channel_creds, channel_args); + } else { + return experimental::CreateCustomChannelWithInterceptors( + server, channel_creds, channel_args, std::move(interceptor_creators)); + } + } +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, const ChannelArguments& args, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators) { + TString credential_type = + security_type == testing::ALTS + ? testing::kAltsCredentialsType + : (security_type == testing::TLS ? testing::kTlsCredentialsType + : testing::kInsecureCredentialsType); + return CreateTestChannel(server, credential_type, override_hostname, + use_prod_roots, creds, args, + std::move(interceptor_creators)); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators) { + return CreateTestChannel(server, override_hostname, security_type, + use_prod_roots, creds, ChannelArguments(), + std::move(interceptor_creators)); +} + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& credential_type, + const std::shared_ptr<CallCredentials>& creds, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators) { + ChannelArguments channel_args; + MaybeSetCustomChannelArgs(&channel_args); + std::shared_ptr<ChannelCredentials> channel_creds = + testing::GetCredentialsProvider()->GetChannelCredentials(credential_type, + &channel_args); + GPR_ASSERT(channel_creds != nullptr); + if (creds.get()) { + channel_creds = grpc::CompositeChannelCredentials(channel_creds, creds); + } + return experimental::CreateCustomChannelWithInterceptors( + server, channel_creds, channel_args, std::move(interceptor_creators)); +} + +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/create_test_channel.h b/contrib/libs/grpc/test/cpp/util/create_test_channel.h new file mode 100644 index 0000000000..ed4ce6c11b --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/create_test_channel.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015-2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H +#define GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H + +#include <memory> + +#include <grpcpp/channel.h> +#include <grpcpp/impl/codegen/client_interceptor.h> +#include <grpcpp/security/credentials.h> +#include <grpcpp/support/channel_arguments.h> + +namespace grpc { +class Channel; + +namespace testing { + +typedef enum { INSECURE = 0, TLS, ALTS } transport_security; + +} // namespace testing + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, testing::transport_security security_type); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + const ChannelArguments& args); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& cred_type, + const TString& override_hostname, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + const ChannelArguments& args); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& credential_type, + const std::shared_ptr<CallCredentials>& creds); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& override_hostname, + testing::transport_security security_type, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, const ChannelArguments& args, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& cred_type, + const TString& override_hostname, bool use_prod_roots, + const std::shared_ptr<CallCredentials>& creds, const ChannelArguments& args, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators); + +std::shared_ptr<Channel> CreateTestChannel( + const TString& server, const TString& credential_type, + const std::shared_ptr<CallCredentials>& creds, + std::vector< + std::unique_ptr<experimental::ClientInterceptorFactoryInterface>> + interceptor_creators); + +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H diff --git a/contrib/libs/grpc/test/cpp/util/error_details_test.cc b/contrib/libs/grpc/test/cpp/util/error_details_test.cc new file mode 100644 index 0000000000..630ab1d98f --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/error_details_test.cc @@ -0,0 +1,125 @@ +/* + * + * Copyright 2017 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. + * + */ + +#include <grpcpp/support/error_details.h> +#include <gtest/gtest.h> + +#include "src/proto/grpc/status/status.pb.h" +#include "src/proto/grpc/testing/echo_messages.pb.h" +#include "test/core/util/test_config.h" + +namespace grpc { +namespace { + +TEST(ExtractTest, Success) { + google::rpc::Status expected; + expected.set_code(13); // INTERNAL + expected.set_message("I am an error message"); + testing::EchoRequest expected_details; + expected_details.set_message(TString(100, '\0')); + expected.add_details()->PackFrom(expected_details); + + google::rpc::Status to; + TString error_details = expected.SerializeAsString(); + Status from(static_cast<StatusCode>(expected.code()), expected.message(), + error_details); + EXPECT_TRUE(ExtractErrorDetails(from, &to).ok()); + EXPECT_EQ(expected.code(), to.code()); + EXPECT_EQ(expected.message(), to.message()); + EXPECT_EQ(1, to.details_size()); + testing::EchoRequest details; + to.details(0).UnpackTo(&details); + EXPECT_EQ(expected_details.message(), details.message()); +} + +TEST(ExtractTest, NullInput) { + EXPECT_EQ(StatusCode::FAILED_PRECONDITION, + ExtractErrorDetails(Status(), nullptr).error_code()); +} + +TEST(ExtractTest, Unparsable) { + TString error_details("I am not a status object"); + Status from(StatusCode::INTERNAL, "", error_details); + google::rpc::Status to; + EXPECT_EQ(StatusCode::INVALID_ARGUMENT, + ExtractErrorDetails(from, &to).error_code()); +} + +TEST(SetTest, Success) { + google::rpc::Status expected; + expected.set_code(13); // INTERNAL + expected.set_message("I am an error message"); + testing::EchoRequest expected_details; + expected_details.set_message(TString(100, '\0')); + expected.add_details()->PackFrom(expected_details); + + Status to; + Status s = SetErrorDetails(expected, &to); + EXPECT_TRUE(s.ok()); + EXPECT_EQ(expected.code(), to.error_code()); + EXPECT_EQ(expected.message(), to.error_message()); + EXPECT_EQ(expected.SerializeAsString(), to.error_details()); +} + +TEST(SetTest, NullInput) { + EXPECT_EQ(StatusCode::FAILED_PRECONDITION, + SetErrorDetails(google::rpc::Status(), nullptr).error_code()); +} + +TEST(SetTest, OutOfScopeErrorCode) { + google::rpc::Status expected; + expected.set_code(17); // Out of scope (UNAUTHENTICATED is 16). + expected.set_message("I am an error message"); + testing::EchoRequest expected_details; + expected_details.set_message(TString(100, '\0')); + expected.add_details()->PackFrom(expected_details); + + Status to; + Status s = SetErrorDetails(expected, &to); + EXPECT_TRUE(s.ok()); + EXPECT_EQ(StatusCode::UNKNOWN, to.error_code()); + EXPECT_EQ(expected.message(), to.error_message()); + EXPECT_EQ(expected.SerializeAsString(), to.error_details()); +} + +TEST(SetTest, ValidScopeErrorCode) { + for (int c = StatusCode::OK; c <= StatusCode::UNAUTHENTICATED; c++) { + google::rpc::Status expected; + expected.set_code(c); + expected.set_message("I am an error message"); + testing::EchoRequest expected_details; + expected_details.set_message(TString(100, '\0')); + expected.add_details()->PackFrom(expected_details); + + Status to; + Status s = SetErrorDetails(expected, &to); + EXPECT_TRUE(s.ok()); + EXPECT_EQ(c, to.error_code()); + EXPECT_EQ(expected.message(), to.error_message()); + EXPECT_EQ(expected.SerializeAsString(), to.error_details()); + } +} + +} // namespace +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contrib/libs/grpc/test/cpp/util/grpc_cli.cc b/contrib/libs/grpc/test/cpp/util/grpc_cli.cc new file mode 100644 index 0000000000..45c6b94f84 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/grpc_cli.cc @@ -0,0 +1,90 @@ +/* + + * 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. + * + */ + +/* + A command line tool to talk to a grpc server. + Run `grpc_cli help` command to see its usage information. + + Example of talking to grpc interop server: + grpc_cli call localhost:50051 UnaryCall "response_size:10" \ + --protofiles=src/proto/grpc/testing/test.proto --enable_ssl=false + + Options: + 1. --protofiles, use this flag to provide proto files if the server does + does not have the reflection service. + 2. --proto_path, if your proto file is not under current working directory, + use this flag to provide a search root. It should work similar to the + counterpart in protoc. This option is valid only when protofiles is + provided. + 3. --metadata specifies metadata to be sent to the server, such as: + --metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2" + 4. --enable_ssl, whether to use tls. + 5. --use_auth, if set to true, attach a GoogleDefaultCredentials to the call + 6. --infile, input filename (defaults to stdin) + 7. --outfile, output filename (defaults to stdout) + 8. --binary_input, use the serialized request as input. The serialized + request can be generated by calling something like: + protoc --proto_path=src/proto/grpc/testing/ \ + --encode=grpc.testing.SimpleRequest \ + src/proto/grpc/testing/messages.proto \ + < input.txt > input.bin + If this is used and no proto file is provided in the argument list, the + method string has to be exact in the form of /package.service/method. + 9. --binary_output, use binary format response as output, it can + be later decoded using protoc: + protoc --proto_path=src/proto/grpc/testing/ \ + --decode=grpc.testing.SimpleResponse \ + src/proto/grpc/testing/messages.proto \ + < output.bin > output.txt + 10. --default_service_config, optional default service config to use + on the channel. Note that this may be ignored if the name resolver + returns a service config. + 11. --display_peer_address, on CallMethod commands, log the peer socket + address of the connection that each RPC is made on to stderr. +*/ + +#include <fstream> +#include <functional> +#include <iostream> + +#include <gflags/gflags.h> +#include <grpcpp/support/config.h> +#include "test/cpp/util/cli_credentials.h" +#include "test/cpp/util/grpc_tool.h" +#include "test/cpp/util/test_config.h" + +DEFINE_string(outfile, "", "Output file (default is stdout)"); + +static bool SimplePrint(const TString& outfile, const TString& output) { + if (outfile.empty()) { + std::cout << output << std::flush; + } else { + std::ofstream output_file(outfile, std::ios::app | std::ios::binary); + output_file << output << std::flush; + output_file.close(); + } + return true; +} + +int main(int argc, char** argv) { + grpc::testing::InitTest(&argc, &argv, true); + + return grpc::testing::GrpcToolMainLib( + argc, (const char**)argv, grpc::testing::CliCredentials(), + std::bind(SimplePrint, TString(FLAGS_outfile.c_str()), std::placeholders::_1)); +} diff --git a/contrib/libs/grpc/test/cpp/util/grpc_tool.cc b/contrib/libs/grpc/test/cpp/util/grpc_tool.cc new file mode 100644 index 0000000000..30f3024e25 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/grpc_tool.cc @@ -0,0 +1,985 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/grpc_tool.h" + +#include <gflags/gflags.h> +#include <grpc/grpc.h> +#include <grpc/support/port_platform.h> +#include <grpcpp/channel.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/grpcpp.h> +#include <grpcpp/security/credentials.h> +#include <grpcpp/support/string_ref.h> + +#include <cstdio> +#include <fstream> +#include <iostream> +#include <memory> +#include <sstream> +#include <util/generic/string.h> +#include <thread> + +#include "test/cpp/util/cli_call.h" +#include "test/cpp/util/proto_file_parser.h" +#include "test/cpp/util/proto_reflection_descriptor_database.h" +#include "test/cpp/util/service_describer.h" + +#if GPR_WINDOWS +#include <io.h> +#else +#include <unistd.h> +#endif + +namespace grpc { +namespace testing { + +DEFINE_bool(l, false, "Use a long listing format"); +DEFINE_bool(remotedb, true, "Use server types to parse and format messages"); +DEFINE_string(metadata, "", + "Metadata to send to server, in the form of key1:val1:key2:val2"); +DEFINE_string(proto_path, ".", "Path to look for the proto file."); +DEFINE_string(protofiles, "", "Name of the proto file."); +DEFINE_bool(binary_input, false, "Input in binary format"); +DEFINE_bool(binary_output, false, "Output in binary format"); +DEFINE_string( + default_service_config, "", + "Default service config to use on the channel, if non-empty. Note " + "that this will be ignored if the name resolver returns a service " + "config."); +DEFINE_bool( + display_peer_address, false, + "Log the peer socket address of the connection that each RPC is made " + "on to stderr."); +DEFINE_bool(json_input, false, "Input in json format"); +DEFINE_bool(json_output, false, "Output in json format"); +DEFINE_string(infile, "", "Input file (default is stdin)"); +DEFINE_bool(batch, false, + "Input contains multiple requests. Please do not use this to send " + "more than a few RPCs. gRPC CLI has very different performance " + "characteristics compared with normal RPC calls which make it " + "unsuitable for loadtesting or significant production traffic."); +DEFINE_double(timeout, -1, + "Specify timeout in seconds, used to set the deadline for all " + "RPCs. The default value of -1 means no deadline has been set."); + +namespace { + +class GrpcTool { + public: + explicit GrpcTool(); + virtual ~GrpcTool() {} + + bool Help(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool CallMethod(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool ListServices(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool PrintType(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + // TODO(zyc): implement the following methods + // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback + // callback); + // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback + // callback); + bool ParseMessage(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool ToText(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + bool ToBinary(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + + void SetPrintCommandMode(int exit_status) { + print_command_usage_ = true; + usage_exit_status_ = exit_status; + } + + private: + void CommandUsage(const TString& usage) const; + bool print_command_usage_; + int usage_exit_status_; + const TString cred_usage_; +}; + +template <typename T> +std::function<bool(GrpcTool*, int, const char**, const CliCredentials&, + GrpcToolOutputCallback)> +BindWith5Args(T&& func) { + return std::bind(std::forward<T>(func), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5); +} + +template <typename T> +size_t ArraySize(T& a) { + return ((sizeof(a) / sizeof(*(a))) / + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))); +} + +void ParseMetadataFlag( + std::multimap<TString, TString>* client_metadata) { + if (FLAGS_metadata.empty()) { + return; + } + std::vector<TString> fields; + const char delim = ':'; + const char escape = '\\'; + size_t cur = -1; + std::stringstream ss; + while (++cur < FLAGS_metadata.length()) { + switch (FLAGS_metadata.at(cur)) { + case escape: + if (cur < FLAGS_metadata.length() - 1) { + char c = FLAGS_metadata.at(++cur); + if (c == delim || c == escape) { + ss << c; + continue; + } + } + fprintf(stderr, "Failed to parse metadata flag.\n"); + exit(1); + case delim: + fields.push_back(ss.str()); + ss.str(""); + ss.clear(); + break; + default: + ss << FLAGS_metadata.at(cur); + } + } + fields.push_back(ss.str()); + if (fields.size() % 2) { + fprintf(stderr, "Failed to parse metadata flag.\n"); + exit(1); + } + for (size_t i = 0; i < fields.size(); i += 2) { + client_metadata->insert( + std::pair<TString, TString>(fields[i], fields[i + 1])); + } +} + +template <typename T> +void PrintMetadata(const T& m, const TString& message) { + if (m.empty()) { + return; + } + fprintf(stderr, "%s\n", message.c_str()); + TString pair; + for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) { + pair.clear(); + pair.append(iter->first.data(), iter->first.size()); + pair.append(" : "); + pair.append(iter->second.data(), iter->second.size()); + fprintf(stderr, "%s\n", pair.c_str()); + } +} + +void ReadResponse(CliCall* call, const TString& method_name, + GrpcToolOutputCallback callback, ProtoFileParser* parser, + gpr_mu* parser_mu, bool print_mode) { + TString serialized_response_proto; + std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata; + + for (bool receive_initial_metadata = true; call->ReadAndMaybeNotifyWrite( + &serialized_response_proto, + receive_initial_metadata ? &server_initial_metadata : nullptr); + receive_initial_metadata = false) { + fprintf(stderr, "got response.\n"); + if (!FLAGS_binary_output) { + gpr_mu_lock(parser_mu); + serialized_response_proto = parser->GetFormattedStringFromMethod( + method_name, serialized_response_proto, false /* is_request */, + FLAGS_json_output); + if (parser->HasError() && print_mode) { + fprintf(stderr, "Failed to parse response.\n"); + } + gpr_mu_unlock(parser_mu); + } + if (receive_initial_metadata) { + PrintMetadata(server_initial_metadata, + "Received initial metadata from server:"); + } + if (!callback(serialized_response_proto) && print_mode) { + fprintf(stderr, "Failed to output response.\n"); + } + } +} + +std::shared_ptr<grpc::Channel> CreateCliChannel( + const TString& server_address, const CliCredentials& cred) { + grpc::ChannelArguments args; + if (!cred.GetSslTargetNameOverride().empty()) { + args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride()); + } + if (!FLAGS_default_service_config.empty()) { + args.SetString(GRPC_ARG_SERVICE_CONFIG, + FLAGS_default_service_config.c_str()); + } + return ::grpc::CreateCustomChannel(server_address, cred.GetCredentials(), + args); +} + +struct Command { + const char* command; + std::function<bool(GrpcTool*, int, const char**, const CliCredentials&, + GrpcToolOutputCallback)> + function; + int min_args; + int max_args; +}; + +const Command ops[] = { + {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX}, + {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3}, + {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3}, + {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3}, + {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2}, + {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3}, + {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3}, + {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3}, + {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3}, +}; + +void Usage(const TString& msg) { + fprintf( + stderr, + "%s\n" + " grpc_cli ls ... ; List services\n" + " grpc_cli call ... ; Call method\n" + " grpc_cli type ... ; Print type\n" + " grpc_cli parse ... ; Parse message\n" + " grpc_cli totext ... ; Convert binary message to text\n" + " grpc_cli tojson ... ; Convert binary message to json\n" + " grpc_cli tobinary ... ; Convert text message to binary\n" + " grpc_cli help ... ; Print this message, or per-command usage\n" + "\n", + msg.c_str()); + + exit(1); +} + +const Command* FindCommand(const TString& name) { + for (int i = 0; i < (int)ArraySize(ops); i++) { + if (name == ops[i].command) { + return &ops[i]; + } + } + return nullptr; +} +} // namespace + +int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + if (argc < 2) { + Usage("No command specified"); + } + + TString command = argv[1]; + argc -= 2; + argv += 2; + + const Command* cmd = FindCommand(command); + if (cmd != nullptr) { + GrpcTool grpc_tool; + if (argc < cmd->min_args || argc > cmd->max_args) { + // Force the command to print its usage message + fprintf(stderr, "\nWrong number of arguments for %s\n", command.c_str()); + grpc_tool.SetPrintCommandMode(1); + return cmd->function(&grpc_tool, -1, nullptr, cred, callback); + } + const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback); + return ok ? 0 : 1; + } else { + Usage("Invalid command '" + TString(command.c_str()) + "'"); + } + return 1; +} + +GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {} + +void GrpcTool::CommandUsage(const TString& usage) const { + if (print_command_usage_) { + fprintf(stderr, "\n%s%s\n", usage.c_str(), + (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : ""); + exit(usage_exit_status_); + } +} + +bool GrpcTool::Help(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Print help\n" + " grpc_cli help [subcommand]\n"); + + if (argc == 0) { + Usage(""); + } else { + const Command* cmd = FindCommand(argv[0]); + if (cmd == nullptr) { + Usage("Unknown command '" + TString(argv[0]) + "'"); + } + SetPrintCommandMode(0); + cmd->function(this, -1, nullptr, cred, callback); + } + return true; +} + +bool GrpcTool::ListServices(int argc, const char** argv, + const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "List services\n" + " grpc_cli ls <address> [<service>[/<method>]]\n" + " <address> ; host:port\n" + " <service> ; Exported service name\n" + " <method> ; Method name\n" + " --l ; Use a long listing format\n" + " --outfile ; Output filename (defaults to stdout)\n" + + cred.GetCredentialUsage()); + + TString server_address(argv[0]); + std::shared_ptr<grpc::Channel> channel = + CreateCliChannel(server_address, cred); + grpc::ProtoReflectionDescriptorDatabase desc_db(channel); + grpc::protobuf::DescriptorPool desc_pool(&desc_db); + + std::vector<TString> service_list; + if (!desc_db.GetServices(&service_list)) { + fprintf(stderr, "Received an error when querying services endpoint.\n"); + return false; + } + + // If no service is specified, dump the list of services. + TString output; + if (argc < 2) { + // List all services, if --l is passed, then include full description, + // otherwise include a summarized list only. + if (FLAGS_l) { + output = DescribeServiceList(service_list, desc_pool); + } else { + for (auto it = service_list.begin(); it != service_list.end(); it++) { + auto const& service = *it; + output.append(service); + output.append("\n"); + } + } + } else { + std::string service_name; + std::string method_name; + std::stringstream ss(argv[1]); + + // Remove leading slashes. + while (ss.peek() == '/') { + ss.get(); + } + + // Parse service and method names. Support the following patterns: + // Service + // Service Method + // Service.Method + // Service/Method + if (argc == 3) { + std::getline(ss, service_name, '/'); + method_name = argv[2]; + } else { + if (std::getline(ss, service_name, '/')) { + std::getline(ss, method_name); + } + } + + const grpc::protobuf::ServiceDescriptor* service = + desc_pool.FindServiceByName(google::protobuf::string(service_name)); + if (service != nullptr) { + if (method_name.empty()) { + output = FLAGS_l ? DescribeService(service) : SummarizeService(service); + } else { + method_name.insert(0, "."); + method_name.insert(0, service_name); + const grpc::protobuf::MethodDescriptor* method = + desc_pool.FindMethodByName(google::protobuf::string(method_name)); + if (method != nullptr) { + output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method); + } else { + fprintf(stderr, "Method %s not found in service %s.\n", + method_name.c_str(), service_name.c_str()); + return false; + } + } + } else { + if (!method_name.empty()) { + fprintf(stderr, "Service %s not found.\n", service_name.c_str()); + return false; + } else { + const grpc::protobuf::MethodDescriptor* method = + desc_pool.FindMethodByName(google::protobuf::string(service_name)); + if (method != nullptr) { + output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method); + } else { + fprintf(stderr, "Service or method %s not found.\n", + service_name.c_str()); + return false; + } + } + } + } + return callback(output); +} + +bool GrpcTool::PrintType(int /*argc*/, const char** argv, + const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Print type\n" + " grpc_cli type <address> <type>\n" + " <address> ; host:port\n" + " <type> ; Protocol buffer type name\n" + + cred.GetCredentialUsage()); + + TString server_address(argv[0]); + std::shared_ptr<grpc::Channel> channel = + CreateCliChannel(server_address, cred); + grpc::ProtoReflectionDescriptorDatabase desc_db(channel); + grpc::protobuf::DescriptorPool desc_pool(&desc_db); + + TString output; + const grpc::protobuf::Descriptor* descriptor = + desc_pool.FindMessageTypeByName(argv[1]); + if (descriptor != nullptr) { + output = descriptor->DebugString(); + } else { + fprintf(stderr, "Type %s not found.\n", argv[1]); + return false; + } + return callback(output); +} + +bool GrpcTool::CallMethod(int argc, const char** argv, + const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Call method\n" + " grpc_cli call <address> <service>[.<method>] <request>\n" + " <address> ; host:port\n" + " <service> ; Exported service name\n" + " <method> ; Method name\n" + " <request> ; Text protobuffer (overrides infile)\n" + " --protofiles ; Comma separated proto files used as a" + " fallback when parsing request/response\n" + " --proto_path ; The search path of proto files, valid" + " only when --protofiles is given\n" + " --noremotedb ; Don't attempt to use reflection service" + " at all\n" + " --metadata ; The metadata to be sent to the server\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n" + " --binary_input ; Input in binary format\n" + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + " --timeout ; Specify timeout (in seconds), used to " + "set the deadline for RPCs. The default value of -1 means no " + "deadline has been set.\n" + + cred.GetCredentialUsage()); + + std::stringstream output_ss; + TString request_text; + TString server_address(argv[0]); + TString method_name(argv[1]); + TString formatted_method_name; + std::unique_ptr<ProtoFileParser> parser; + TString serialized_request_proto; + CliArgs cli_args; + cli_args.timeout = FLAGS_timeout; + bool print_mode = false; + + std::shared_ptr<grpc::Channel> channel = + CreateCliChannel(server_address, cred); + + if (!FLAGS_binary_input || !FLAGS_binary_output) { + parser.reset( + new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr, + FLAGS_proto_path.c_str(), FLAGS_protofiles.c_str())); + if (parser->HasError()) { + fprintf( + stderr, + "Failed to find remote reflection service and local proto files.\n"); + return false; + } + } + + if (FLAGS_binary_input) { + formatted_method_name = method_name; + } else { + formatted_method_name = parser->GetFormattedMethodName(method_name); + if (parser->HasError()) { + fprintf(stderr, "Failed to find method %s in proto files.\n", + method_name.c_str()); + } + } + + if (argc == 3) { + request_text = argv[2]; + } + + if (parser->IsStreaming(method_name, true /* is_request */)) { + std::istream* input_stream; + std::ifstream input_file; + + if (FLAGS_batch) { + fprintf(stderr, "Batch mode for streaming RPC is not supported.\n"); + return false; + } + + std::multimap<TString, TString> client_metadata; + ParseMetadataFlag(&client_metadata); + PrintMetadata(client_metadata, "Sending client initial metadata:"); + + CliCall call(channel, formatted_method_name, client_metadata, cli_args); + if (FLAGS_display_peer_address) { + fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n", + formatted_method_name.c_str(), call.peer().c_str()); + } + + if (FLAGS_infile.empty()) { + if (isatty(fileno(stdin))) { + print_mode = true; + fprintf(stderr, "reading streaming request message from stdin...\n"); + } + input_stream = &std::cin; + } else { + input_file.open(FLAGS_infile, std::ios::in | std::ios::binary); + input_stream = &input_file; + } + + gpr_mu parser_mu; + gpr_mu_init(&parser_mu); + std::thread read_thread(ReadResponse, &call, method_name, callback, + parser.get(), &parser_mu, print_mode); + + std::stringstream request_ss; + std::string line; + while (!request_text.empty() || + (!input_stream->eof() && getline(*input_stream, line))) { + if (!request_text.empty()) { + if (FLAGS_binary_input) { + serialized_request_proto = request_text; + request_text.clear(); + } else { + gpr_mu_lock(&parser_mu); + serialized_request_proto = parser->GetSerializedProtoFromMethod( + method_name, request_text, true /* is_request */, + FLAGS_json_input); + request_text.clear(); + if (parser->HasError()) { + if (print_mode) { + fprintf(stderr, "Failed to parse request.\n"); + } + gpr_mu_unlock(&parser_mu); + continue; + } + gpr_mu_unlock(&parser_mu); + } + + call.WriteAndWait(serialized_request_proto); + if (print_mode) { + fprintf(stderr, "Request sent.\n"); + } + } else { + if (line.length() == 0) { + request_text = request_ss.str(); + request_ss.str(TString()); + request_ss.clear(); + } else { + request_ss << line << ' '; + } + } + } + if (input_file.is_open()) { + input_file.close(); + } + + call.WritesDoneAndWait(); + read_thread.join(); + gpr_mu_destroy(&parser_mu); + + std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata; + Status status = call.Finish(&server_trailing_metadata); + PrintMetadata(server_trailing_metadata, + "Received trailing metadata from server:"); + + if (status.ok()) { + fprintf(stderr, "Stream RPC succeeded with OK status\n"); + return true; + } else { + fprintf(stderr, "Rpc failed with status code %d, error message: %s\n", + status.error_code(), status.error_message().c_str()); + return false; + } + + } else { // parser->IsStreaming(method_name, true /* is_request */) + if (FLAGS_batch) { + if (parser->IsStreaming(method_name, false /* is_request */)) { + fprintf(stderr, "Batch mode for streaming RPC is not supported.\n"); + return false; + } + + std::istream* input_stream; + std::ifstream input_file; + + if (FLAGS_infile.empty()) { + if (isatty(fileno(stdin))) { + print_mode = true; + fprintf(stderr, "reading request messages from stdin...\n"); + } + input_stream = &std::cin; + } else { + input_file.open(FLAGS_infile, std::ios::in | std::ios::binary); + input_stream = &input_file; + } + + std::multimap<TString, TString> client_metadata; + ParseMetadataFlag(&client_metadata); + if (print_mode) { + PrintMetadata(client_metadata, "Sending client initial metadata:"); + } + + std::stringstream request_ss; + std::string line; + while (!request_text.empty() || + (!input_stream->eof() && getline(*input_stream, line))) { + if (!request_text.empty()) { + if (FLAGS_binary_input) { + serialized_request_proto = request_text; + request_text.clear(); + } else { + serialized_request_proto = parser->GetSerializedProtoFromMethod( + method_name, request_text, true /* is_request */, + FLAGS_json_input); + request_text.clear(); + if (parser->HasError()) { + if (print_mode) { + fprintf(stderr, "Failed to parse request.\n"); + } + continue; + } + } + + TString serialized_response_proto; + std::multimap<grpc::string_ref, grpc::string_ref> + server_initial_metadata, server_trailing_metadata; + CliCall call(channel, formatted_method_name, client_metadata, + cli_args); + if (FLAGS_display_peer_address) { + fprintf(stderr, + "New call for method_name:%s has peer address:|%s|\n", + formatted_method_name.c_str(), call.peer().c_str()); + } + call.Write(serialized_request_proto); + call.WritesDone(); + if (!call.Read(&serialized_response_proto, + &server_initial_metadata)) { + fprintf(stderr, "Failed to read response.\n"); + } + Status status = call.Finish(&server_trailing_metadata); + + if (status.ok()) { + if (print_mode) { + fprintf(stderr, "Rpc succeeded with OK status.\n"); + PrintMetadata(server_initial_metadata, + "Received initial metadata from server:"); + PrintMetadata(server_trailing_metadata, + "Received trailing metadata from server:"); + } + + if (FLAGS_binary_output) { + if (!callback(serialized_response_proto)) { + break; + } + } else { + TString response_text = parser->GetFormattedStringFromMethod( + method_name, serialized_response_proto, + false /* is_request */, FLAGS_json_output); + + if (parser->HasError() && print_mode) { + fprintf(stderr, "Failed to parse response.\n"); + } else { + if (!callback(response_text)) { + break; + } + } + } + } else { + if (print_mode) { + fprintf(stderr, + "Rpc failed with status code %d, error message: %s\n", + status.error_code(), status.error_message().c_str()); + } + } + } else { + if (line.length() == 0) { + request_text = request_ss.str(); + request_ss.str(TString()); + request_ss.clear(); + } else { + request_ss << line << ' '; + } + } + } + + if (input_file.is_open()) { + input_file.close(); + } + + return true; + } + + if (argc == 3) { + if (!FLAGS_infile.empty()) { + fprintf(stderr, "warning: request given in argv, ignoring --infile\n"); + } + } else { + std::stringstream input_stream; + if (FLAGS_infile.empty()) { + if (isatty(fileno(stdin))) { + fprintf(stderr, "reading request message from stdin...\n"); + } + input_stream << std::cin.rdbuf(); + } else { + std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary); + input_stream << input_file.rdbuf(); + input_file.close(); + } + request_text = input_stream.str(); + } + + if (FLAGS_binary_input) { + serialized_request_proto = request_text; + } else { + serialized_request_proto = parser->GetSerializedProtoFromMethod( + method_name, request_text, true /* is_request */, FLAGS_json_input); + if (parser->HasError()) { + fprintf(stderr, "Failed to parse request.\n"); + return false; + } + } + fprintf(stderr, "connecting to %s\n", server_address.c_str()); + + TString serialized_response_proto; + std::multimap<TString, TString> client_metadata; + std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata, + server_trailing_metadata; + ParseMetadataFlag(&client_metadata); + PrintMetadata(client_metadata, "Sending client initial metadata:"); + + CliCall call(channel, formatted_method_name, client_metadata, cli_args); + if (FLAGS_display_peer_address) { + fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n", + formatted_method_name.c_str(), call.peer().c_str()); + } + call.Write(serialized_request_proto); + call.WritesDone(); + + for (bool receive_initial_metadata = true; call.Read( + &serialized_response_proto, + receive_initial_metadata ? &server_initial_metadata : nullptr); + receive_initial_metadata = false) { + if (!FLAGS_binary_output) { + serialized_response_proto = parser->GetFormattedStringFromMethod( + method_name, serialized_response_proto, false /* is_request */, + FLAGS_json_output); + if (parser->HasError()) { + fprintf(stderr, "Failed to parse response.\n"); + return false; + } + } + + if (receive_initial_metadata) { + PrintMetadata(server_initial_metadata, + "Received initial metadata from server:"); + } + if (!callback(serialized_response_proto)) { + return false; + } + } + Status status = call.Finish(&server_trailing_metadata); + PrintMetadata(server_trailing_metadata, + "Received trailing metadata from server:"); + if (status.ok()) { + fprintf(stderr, "Rpc succeeded with OK status\n"); + return true; + } else { + fprintf(stderr, "Rpc failed with status code %d, error message: %s\n", + status.error_code(), status.error_message().c_str()); + return false; + } + } + GPR_UNREACHABLE_CODE(return false); +} + +bool GrpcTool::ParseMessage(int argc, const char** argv, + const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Parse message\n" + " grpc_cli parse <address> <type> [<message>]\n" + " <address> ; host:port\n" + " <type> ; Protocol buffer type name\n" + " <message> ; Text protobuffer (overrides --infile)\n" + " --protofiles ; Comma separated proto files used as a" + " fallback when parsing request/response\n" + " --proto_path ; The search path of proto files, valid" + " only when --protofiles is given\n" + " --noremotedb ; Don't attempt to use reflection service" + " at all\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n" + " --binary_input ; Input in binary format\n" + " --binary_output ; Output in binary format\n" + " --json_input ; Input in json format\n" + " --json_output ; Output in json format\n" + + cred.GetCredentialUsage()); + + std::stringstream output_ss; + TString message_text; + TString server_address(argv[0]); + TString type_name(argv[1]); + std::unique_ptr<grpc::testing::ProtoFileParser> parser; + TString serialized_request_proto; + + if (argc == 3) { + message_text = argv[2]; + if (!FLAGS_infile.empty()) { + fprintf(stderr, "warning: message given in argv, ignoring --infile.\n"); + } + } else { + std::stringstream input_stream; + if (FLAGS_infile.empty()) { + if (isatty(fileno(stdin))) { + fprintf(stderr, "reading request message from stdin...\n"); + } + input_stream << std::cin.rdbuf(); + } else { + std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary); + input_stream << input_file.rdbuf(); + input_file.close(); + } + message_text = input_stream.str(); + } + + if (!FLAGS_binary_input || !FLAGS_binary_output) { + std::shared_ptr<grpc::Channel> channel = + CreateCliChannel(server_address, cred); + parser.reset( + new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr, + FLAGS_proto_path.c_str(), FLAGS_protofiles.c_str())); + if (parser->HasError()) { + fprintf( + stderr, + "Failed to find remote reflection service and local proto files.\n"); + return false; + } + } + + if (FLAGS_binary_input) { + serialized_request_proto = message_text; + } else { + serialized_request_proto = parser->GetSerializedProtoFromMessageType( + type_name, message_text, FLAGS_json_input); + if (parser->HasError()) { + fprintf(stderr, "Failed to serialize the message.\n"); + return false; + } + } + + if (FLAGS_binary_output) { + output_ss << serialized_request_proto; + } else { + TString output_text; + output_text = parser->GetFormattedStringFromMessageType( + type_name, serialized_request_proto, FLAGS_json_output); + if (parser->HasError()) { + fprintf(stderr, "Failed to deserialize the message.\n"); + return false; + } + + output_ss << output_text << std::endl; + } + + return callback(output_ss.str()); +} + +bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Convert binary message to text\n" + " grpc_cli totext <protofiles> <type>\n" + " <protofiles> ; Comma separated list of proto files\n" + " <type> ; Protocol buffer type name\n" + " --proto_path ; The search path of proto files\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n"); + + FLAGS_protofiles = argv[0]; + FLAGS_remotedb = false; + FLAGS_binary_input = true; + FLAGS_binary_output = false; + return ParseMessage(argc, argv, cred, callback); +} + +bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Convert binary message to json\n" + " grpc_cli tojson <protofiles> <type>\n" + " <protofiles> ; Comma separated list of proto files\n" + " <type> ; Protocol buffer type name\n" + " --proto_path ; The search path of proto files\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n"); + + FLAGS_protofiles = argv[0]; + FLAGS_remotedb = false; + FLAGS_binary_input = true; + FLAGS_binary_output = false; + FLAGS_json_output = true; + return ParseMessage(argc, argv, cred, callback); +} + +bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback) { + CommandUsage( + "Convert text message to binary\n" + " grpc_cli tobinary <protofiles> <type> [<message>]\n" + " <protofiles> ; Comma separated list of proto files\n" + " <type> ; Protocol buffer type name\n" + " --proto_path ; The search path of proto files\n" + " --infile ; Input filename (defaults to stdin)\n" + " --outfile ; Output filename (defaults to stdout)\n"); + + FLAGS_protofiles = argv[0]; + FLAGS_remotedb = false; + FLAGS_binary_input = false; + FLAGS_binary_output = true; + return ParseMessage(argc, argv, cred, callback); +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/grpc_tool.h b/contrib/libs/grpc/test/cpp/util/grpc_tool.h new file mode 100644 index 0000000000..5bb43430d3 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/grpc_tool.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_GRPC_TOOL_H +#define GRPC_TEST_CPP_UTIL_GRPC_TOOL_H + +#include <functional> + +#include <grpcpp/support/config.h> + +#include "test/cpp/util/cli_credentials.h" + +namespace grpc { +namespace testing { + +typedef std::function<bool(const TString&)> GrpcToolOutputCallback; + +int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred, + GrpcToolOutputCallback callback); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_GRPC_TOOL_H diff --git a/contrib/libs/grpc/test/cpp/util/grpc_tool_test.cc b/contrib/libs/grpc/test/cpp/util/grpc_tool_test.cc new file mode 100644 index 0000000000..ff610daadd --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/grpc_tool_test.cc @@ -0,0 +1,1344 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/grpc_tool.h" + +#include <gflags/gflags.h> +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpcpp/channel.h> +#include <grpcpp/client_context.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/ext/proto_server_reflection_plugin.h> +#include <grpcpp/server.h> +#include <grpcpp/server_builder.h> +#include <grpcpp/server_context.h> +#include <gtest/gtest.h> + +#include <chrono> +#include <sstream> + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "src/proto/grpc/testing/echo.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/util/cli_credentials.h" +#include "test/cpp/util/string_ref_helper.h" + +#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem" +#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem" +#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key" + +using grpc::testing::EchoRequest; +using grpc::testing::EchoResponse; + +#define USAGE_REGEX "( grpc_cli .+\n){2,10}" + +#define ECHO_TEST_SERVICE_SUMMARY \ + "Echo\n" \ + "Echo1\n" \ + "Echo2\n" \ + "CheckDeadlineUpperBound\n" \ + "CheckDeadlineSet\n" \ + "CheckClientInitialMetadata\n" \ + "RequestStream\n" \ + "ResponseStream\n" \ + "BidiStream\n" \ + "Unimplemented\n" + +#define ECHO_TEST_SERVICE_DESCRIPTION \ + "filename: src/proto/grpc/testing/echo.proto\n" \ + "package: grpc.testing;\n" \ + "service EchoTestService {\n" \ + " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" \ + " rpc Echo1(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" \ + " rpc Echo2(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" \ + " rpc CheckDeadlineUpperBound(grpc.testing.SimpleRequest) returns " \ + "(grpc.testing.StringValue) {}\n" \ + " rpc CheckDeadlineSet(grpc.testing.SimpleRequest) returns " \ + "(grpc.testing.StringValue) {}\n" \ + " rpc CheckClientInitialMetadata(grpc.testing.SimpleRequest) returns " \ + "(grpc.testing.SimpleResponse) {}\n" \ + " rpc RequestStream(stream grpc.testing.EchoRequest) returns " \ + "(grpc.testing.EchoResponse) {}\n" \ + " rpc ResponseStream(grpc.testing.EchoRequest) returns (stream " \ + "grpc.testing.EchoResponse) {}\n" \ + " rpc BidiStream(stream grpc.testing.EchoRequest) returns (stream " \ + "grpc.testing.EchoResponse) {}\n" \ + " rpc Unimplemented(grpc.testing.EchoRequest) returns " \ + "(grpc.testing.EchoResponse) {}\n" \ + "}\n" \ + "\n" + +#define ECHO_METHOD_DESCRIPTION \ + " rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \ + "{}\n" + +#define ECHO_RESPONSE_MESSAGE_TEXT_FORMAT \ + "message: \"echo\"\n" \ + "param {\n" \ + " host: \"localhost\"\n" \ + " peer: \"peer\"\n" \ + "}\n\n" + +#define ECHO_RESPONSE_MESSAGE_JSON_FORMAT \ + "{\n" \ + " \"message\": \"echo\",\n" \ + " \"param\": {\n" \ + " \"host\": \"localhost\",\n" \ + " \"peer\": \"peer\"\n" \ + " }\n" \ + "}\n\n" + +DECLARE_string(channel_creds_type); +DECLARE_string(ssl_target); + +namespace grpc { +namespace testing { + +DECLARE_bool(binary_input); +DECLARE_bool(binary_output); +DECLARE_bool(json_input); +DECLARE_bool(json_output); +DECLARE_bool(l); +DECLARE_bool(batch); +DECLARE_string(metadata); +DECLARE_string(protofiles); +DECLARE_string(proto_path); +DECLARE_string(default_service_config); +DECLARE_double(timeout); + +namespace { + +const int kServerDefaultResponseStreamsToSend = 3; + +class TestCliCredentials final : public grpc::testing::CliCredentials { + public: + TestCliCredentials(bool secure = false) : secure_(secure) {} + std::shared_ptr<grpc::ChannelCredentials> GetChannelCredentials() + const override { + if (!secure_) { + return InsecureChannelCredentials(); + } + grpc_slice ca_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(CA_CERT_PATH, 1, &ca_slice))); + const char* test_root_cert = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(ca_slice); + SslCredentialsOptions ssl_opts = {test_root_cert, "", ""}; + std::shared_ptr<grpc::ChannelCredentials> credential_ptr = + grpc::SslCredentials(grpc::SslCredentialsOptions(ssl_opts)); + grpc_slice_unref(ca_slice); + return credential_ptr; + } + const TString GetCredentialUsage() const override { return ""; } + + private: + const bool secure_; +}; + +bool PrintStream(std::stringstream* ss, const TString& output) { + (*ss) << output; + return true; +} + +template <typename T> +size_t ArraySize(T& a) { + return ((sizeof(a) / sizeof(*(a))) / + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))); +} + +class TestServiceImpl : public ::grpc::testing::EchoTestService::Service { + public: + Status Echo(ServerContext* context, const EchoRequest* request, + EchoResponse* response) override { + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + response->set_message(request->message()); + return Status::OK; + } + + Status CheckDeadlineSet(ServerContext* context, const SimpleRequest* request, + StringValue* response) override { + response->set_message(context->deadline() != + std::chrono::system_clock::time_point::max() + ? "true" + : "false"); + return Status::OK; + } + + // Check if deadline - current time <= timeout + // If deadline set, timeout + current time should be an upper bound for it + Status CheckDeadlineUpperBound(ServerContext* context, + const SimpleRequest* request, + StringValue* response) override { + auto seconds = std::chrono::duration_cast<std::chrono::seconds>( + context->deadline() - std::chrono::system_clock::now()); + + // Returning string instead of bool to avoid using embedded messages in + // proto3 + response->set_message(seconds.count() <= FLAGS_timeout ? "true" : "false"); + return Status::OK; + } + + Status RequestStream(ServerContext* context, + ServerReader<EchoRequest>* reader, + EchoResponse* response) override { + EchoRequest request; + response->set_message(""); + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + while (reader->Read(&request)) { + response->mutable_message()->append(request.message()); + } + + return Status::OK; + } + + Status ResponseStream(ServerContext* context, const EchoRequest* request, + ServerWriter<EchoResponse>* writer) override { + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + + EchoResponse response; + for (int i = 0; i < kServerDefaultResponseStreamsToSend; i++) { + response.set_message(request->message() + ToString(i)); + writer->Write(response); + } + + return Status::OK; + } + + Status BidiStream( + ServerContext* context, + ServerReaderWriter<EchoResponse, EchoRequest>* stream) override { + EchoRequest request; + EchoResponse response; + if (!context->client_metadata().empty()) { + for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator + iter = context->client_metadata().begin(); + iter != context->client_metadata().end(); ++iter) { + context->AddInitialMetadata(ToString(iter->first), + ToString(iter->second)); + } + } + context->AddTrailingMetadata("trailing_key", "trailing_value"); + + while (stream->Read(&request)) { + response.set_message(request.message()); + stream->Write(response); + } + + return Status::OK; + } +}; + +} // namespace + +class GrpcToolTest : public ::testing::Test { + protected: + GrpcToolTest() {} + + // SetUpServer cannot be used with EXPECT_EXIT. grpc_pick_unused_port_or_die() + // uses atexit() to free chosen ports, and it will spawn a new thread in + // resolve_address_posix.c:192 at exit time. + const TString SetUpServer(bool secure = false) { + std::ostringstream server_address; + int port = grpc_pick_unused_port_or_die(); + server_address << "localhost:" << port; + // Setup server + ServerBuilder builder; + std::shared_ptr<grpc::ServerCredentials> creds; + grpc_slice cert_slice, key_slice; + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_CERT_PATH, 1, &cert_slice))); + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "load_file", grpc_load_file(SERVER_KEY_PATH, 1, &key_slice))); + const char* server_cert = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice); + const char* server_key = + reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice); + SslServerCredentialsOptions::PemKeyCertPair pkcp = {server_key, + server_cert}; + if (secure) { + SslServerCredentialsOptions ssl_opts; + ssl_opts.pem_root_certs = ""; + ssl_opts.pem_key_cert_pairs.push_back(pkcp); + creds = SslServerCredentials(ssl_opts); + } else { + creds = InsecureServerCredentials(); + } + builder.AddListeningPort(server_address.str(), creds); + builder.RegisterService(&service_); + server_ = builder.BuildAndStart(); + grpc_slice_unref(cert_slice); + grpc_slice_unref(key_slice); + return server_address.str(); + } + + void ShutdownServer() { server_->Shutdown(); } + + std::unique_ptr<Server> server_; + TestServiceImpl service_; + reflection::ProtoServerReflectionPlugin plugin_; +}; + +TEST_F(GrpcToolTest, NoCommand) { + // Test input "grpc_cli" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli"}; + // Exit with 1, print usage instruction in stderr + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), "No command specified\n" USAGE_REGEX); + // No output + EXPECT_TRUE(0 == output_stream.tellp()); +} + +TEST_F(GrpcToolTest, InvalidCommand) { + // Test input "grpc_cli" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli", "abc"}; + // Exit with 1, print usage instruction in stderr + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), "Invalid command 'abc'\n" USAGE_REGEX); + // No output + EXPECT_TRUE(0 == output_stream.tellp()); +} + +TEST_F(GrpcToolTest, HelpCommand) { + // Test input "grpc_cli help" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli", "help"}; + // Exit with 1, print usage instruction in stderr + EXPECT_EXIT(GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1)), + ::testing::ExitedWithCode(1), USAGE_REGEX); + // No output + EXPECT_TRUE(0 == output_stream.tellp()); +} + +TEST_F(GrpcToolTest, ListCommand) { + // Test input "grpc_cli list localhost:<port>" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str()}; + + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + "grpc.testing.EchoTestService\n" + "grpc.reflection.v1alpha.ServerReflection\n")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ListOneService) { + // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str(), + "grpc.testing.EchoTestService"}; + // without -l flag + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_TEST_SERVICE_SUMMARY + EXPECT_TRUE(0 == + strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_SUMMARY)); + + // with -l flag + output_stream.str(TString()); + output_stream.clear(); + FLAGS_l = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_TEST_SERVICE_DESCRIPTION + EXPECT_TRUE( + 0 == strcmp(output_stream.str().c_str(), ECHO_TEST_SERVICE_DESCRIPTION)); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, TypeCommand) { + // Test input "grpc_cli type localhost:<port> grpc.testing.EchoRequest" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "type", server_address.c_str(), + "grpc.testing.EchoRequest"}; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + const grpc::protobuf::Descriptor* desc = + grpc::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName( + "grpc.testing.EchoRequest"); + // Expected output: the DebugString of grpc.testing.EchoRequest + EXPECT_TRUE(0 == + strcmp(output_stream.str().c_str(), desc->DebugString().c_str())); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ListOneMethod) { + // Test input "grpc_cli list localhost:<port> grpc.testing.EchoTestService" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str(), + "grpc.testing.EchoTestService.Echo"}; + // without -l flag + FLAGS_l = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "Echo" + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), "Echo\n")); + + // with -l flag + output_stream.str(TString()); + output_stream.clear(); + FLAGS_l = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_METHOD_DESCRIPTION + EXPECT_TRUE(0 == + strcmp(output_stream.str().c_str(), ECHO_METHOD_DESCRIPTION)); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, TypeNotFound) { + // Test input "grpc_cli type localhost:<port> grpc.testing.DummyRequest" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "type", server_address.c_str(), + "grpc.testing.DummyRequest"}; + + EXPECT_TRUE(1 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommand) { + // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "message: 'Hello'"}; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello\"\n}")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandJsonInput) { + // Test input "grpc_cli call localhost:<port> Echo "{ \"message\": \"Hello\"}" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello\"}"}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello" + // } + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello\"\n}")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatch) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(TString()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInput) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{\"message\": \"Hello0\"}"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{\"message\": \"Hello1\"}\n\n{\"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + FLAGS_batch = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + // with json_output + output_stream.str(TString()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + FLAGS_json_input = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello1" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello1\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchWithBadRequest) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "message: 'Hello0'"}; + + // Mock std::cin input "message: 1\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 1\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBatchJsonInputWithBadRequest) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "{ \"message\": \"Hello0\"}"}; + + // Mock std::cin input "message: 1\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"message\": 1 }\n\n { \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: "message: "Hello0"\nmessage: "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + ss.clear(); + ss.seekg(0); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_batch = true; + FLAGS_json_input = true; + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + FLAGS_batch = false; + + // Expected output: + // { + // "message": "Hello0" + // } + // { + // "message": "Hello2" + // } + // Expected output: "message: "Hello0"\nmessage: "Hello1"\nmessage: + // "Hello2"\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "{\n \"message\": \"Hello0\"\n}\n" + "{\n \"message\": \"Hello2\"\n}\n")); + + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandRequestStream) { + // Test input: grpc_cli call localhost:<port> RequestStream "message: + // 'Hello0'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0Hello1Hello2\"" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0Hello1Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandRequestStreamJsonInput) { + // Test input: grpc_cli call localhost:<port> RequestStream "{ \"message\": + // \"Hello0\"}" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "{ \"message\": \"Hello0\" }"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"message\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello1Hello2\"" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0Hello1Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) { + // Test input: grpc_cli call localhost:<port> RequestStream "message: + // 'Hello0'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "message: 'Hello0'"}; + + // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("bad_field: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0Hello2\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequestJsonInput) { + // Test input: grpc_cli call localhost:<port> RequestStream "message: + // 'Hello0'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "RequestStream", "{ \"message\": \"Hello0\" }"}; + + // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss( + "{ \"bad_field\": \"Hello1\" }\n\n{ \"message\": \"Hello2\" }\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_input = false; + + // Expected output: "message: \"Hello0Hello2\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\"")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandWithTimeoutDeadlineSet) { + // Test input "grpc_cli call CheckDeadlineSet --timeout=5000.25" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "CheckDeadlineSet"}; + + // Set timeout to 5000.25 seconds + FLAGS_timeout = 5000.25; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: "true"", deadline set + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"true\"")); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandWithTimeoutDeadlineUpperBound) { + // Test input "grpc_cli call CheckDeadlineUpperBound --timeout=900" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "CheckDeadlineUpperBound"}; + + // Set timeout to 900 seconds + FLAGS_timeout = 900; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: "true"" + // deadline not greater than timeout + current time + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"true\"")); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandWithNegativeTimeoutValue) { + // Test input "grpc_cli call CheckDeadlineSet --timeout=-5" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "CheckDeadlineSet"}; + + // Set timeout to -5 (deadline not set) + FLAGS_timeout = -5; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: "false"", deadline not set + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"false\"")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandWithDefaultTimeoutValue) { + // Test input "grpc_cli call CheckDeadlineSet --timeout=-1" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "CheckDeadlineSet"}; + + // Set timeout to -1 (default value, deadline not set) + FLAGS_timeout = -1; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: "false"", deadline not set + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"false\"")); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandResponseStream) { + // Test input: grpc_cli call localhost:<port> ResponseStream "message: + // 'Hello'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "ResponseStream", "message: 'Hello'"}; + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello{n}\"" + for (int i = 0; i < kServerDefaultResponseStreamsToSend; i++) { + TString expected_response_text = + "message: \"Hello" + ToString(i) + "\"\n"; + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + expected_response_text.c_str())); + } + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: "{\n \"message\": \"Hello{n}\"\n}\n" + for (int i = 0; i < kServerDefaultResponseStreamsToSend; i++) { + TString expected_response_text = + "{\n \"message\": \"Hello" + ToString(i) + "\"\n}\n"; + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + expected_response_text.c_str())); + } + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBidiStream) { + // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "BidiStream", "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage: + // \"Hello2\"\n\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: " + "\"Hello1\"\nmessage: \"Hello2\"\n")); + std::cin.rdbuf(orig); + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandBidiStreamWithBadRequest) { + // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'" + std::stringstream output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), + "BidiStream", "message: 'Hello0'"}; + + // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n" + std::streambuf* orig = std::cin.rdbuf(); + std::istringstream ss("message: 1.0\n\n message: 'Hello2'\n\n"); + std::cin.rdbuf(ss.rdbuf()); + + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage: + // \"Hello2\"\n\n" + EXPECT_TRUE(nullptr != strstr(output_stream.str().c_str(), + "message: \"Hello0\"\nmessage: \"Hello2\"\n")); + std::cin.rdbuf(orig); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ParseCommand) { + // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse + // ECHO_RESPONSE_MESSAGE" + std::stringstream output_stream; + std::stringstream binary_output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), + "grpc.testing.EchoResponse", + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT}; + + FLAGS_binary_input = false; + FLAGS_binary_output = false; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); + + // Parse text message to binary message and then parse it back to text message + output_stream.str(TString()); + output_stream.clear(); + FLAGS_binary_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + TString binary_data = output_stream.str(); + output_stream.str(TString()); + output_stream.clear(); + argv[4] = binary_data.c_str(); + FLAGS_binary_input = true; + FLAGS_binary_output = false; + EXPECT_TRUE(0 == GrpcToolMainLib(5, argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: ECHO_RESPONSE_MESSAGE + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + FLAGS_binary_input = false; + FLAGS_binary_output = false; + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ParseCommandJsonFormat) { + // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse + // ECHO_RESPONSE_MESSAGE_JSON_FORMAT" + std::stringstream output_stream; + std::stringstream binary_output_stream; + + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "parse", server_address.c_str(), + "grpc.testing.EchoResponse", + ECHO_RESPONSE_MESSAGE_JSON_FORMAT}; + + FLAGS_json_input = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + + // Expected output: ECHO_RESPONSE_MESSAGE_TEXT_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_TEXT_FORMAT)); + + // with json_output + output_stream.str(TString()); + output_stream.clear(); + + FLAGS_json_output = true; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_json_output = false; + FLAGS_json_input = false; + + // Expected output: ECHO_RESPONSE_MESSAGE_JSON_FORMAT + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + ECHO_RESPONSE_MESSAGE_JSON_FORMAT)); + + ShutdownServer(); +} + +TEST_F(GrpcToolTest, TooFewArguments) { + // Test input "grpc_cli call Echo" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli", "call", "Echo"}; + + // Exit with 1 + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*"); + // No output + EXPECT_TRUE(0 == output_stream.tellp()); +} + +TEST_F(GrpcToolTest, TooManyArguments) { + // Test input "grpc_cli call localhost:<port> Echo Echo "message: 'Hello'" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli", "call", "localhost:10000", + "Echo", "Echo", "message: 'Hello'"}; + + // Exit with 1 + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), ".*Wrong number of arguments for call.*"); + // No output + EXPECT_TRUE(0 == output_stream.tellp()); +} + +TEST_F(GrpcToolTest, CallCommandWithMetadata) { + // Test input "grpc_cli call localhost:<port> Echo "message: 'Hello'" + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "call", server_address.c_str(), "Echo", + "message: 'Hello'"}; + + { + std::stringstream output_stream; + FLAGS_metadata = "key0:val0:key1:valq:key2:val2"; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, + TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + } + + { + std::stringstream output_stream; + FLAGS_metadata = "key:val\\:val"; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, + TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + } + + { + std::stringstream output_stream; + FLAGS_metadata = "key:val\\\\val"; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, + TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + // Expected output: "message: \"Hello\"" + EXPECT_TRUE(nullptr != + strstr(output_stream.str().c_str(), "message: \"Hello\"")); + } + + FLAGS_metadata = ""; + ShutdownServer(); +} + +TEST_F(GrpcToolTest, CallCommandWithBadMetadata) { + // Test input "grpc_cli call localhost:10000 Echo "message: 'Hello'" + const char* argv[] = {"grpc_cli", "call", "localhost:10000", + "grpc.testing.EchoTestService.Echo", + "message: 'Hello'"}; + FLAGS_protofiles = "src/proto/grpc/testing/echo.proto"; + char* test_srcdir = gpr_getenv("TEST_SRCDIR"); + if (test_srcdir != nullptr) { + FLAGS_proto_path = test_srcdir + TString("/com_github_grpc_grpc"); + } + + { + std::stringstream output_stream; + FLAGS_metadata = "key0:val0:key1"; + // Exit with 1 + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), ".*Failed to parse metadata flag.*"); + } + + { + std::stringstream output_stream; + FLAGS_metadata = "key:val\\val"; + // Exit with 1 + EXPECT_EXIT( + GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, std::placeholders::_1)), + ::testing::ExitedWithCode(1), ".*Failed to parse metadata flag.*"); + } + + FLAGS_metadata = ""; + FLAGS_protofiles = ""; + + gpr_free(test_srcdir); +} + +TEST_F(GrpcToolTest, ListCommand_OverrideSslHostName) { + const TString server_address = SetUpServer(true); + + // Test input "grpc_cli ls localhost:<port> --channel_creds_type=ssl + // --ssl_target=z.test.google.fr" + std::stringstream output_stream; + const char* argv[] = {"grpc_cli", "ls", server_address.c_str()}; + FLAGS_l = false; + FLAGS_channel_creds_type = "ssl"; + FLAGS_ssl_target = "z.test.google.fr"; + EXPECT_TRUE( + 0 == GrpcToolMainLib( + ArraySize(argv), argv, TestCliCredentials(true), + std::bind(PrintStream, &output_stream, std::placeholders::_1))); + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + "grpc.testing.EchoTestService\n" + "grpc.reflection.v1alpha.ServerReflection\n")); + + FLAGS_channel_creds_type = ""; + FLAGS_ssl_target = ""; + ShutdownServer(); +} + +TEST_F(GrpcToolTest, ConfiguringDefaultServiceConfig) { + // Test input "grpc_cli list localhost:<port> + // --default_service_config={\"loadBalancingConfig\":[{\"pick_first\":{}}]}" + std::stringstream output_stream; + const TString server_address = SetUpServer(); + const char* argv[] = {"grpc_cli", "ls", server_address.c_str()}; + // Just check that the tool is still operational when --default_service_config + // is configured. This particular service config is in reality redundant with + // the channel's default configuration. + FLAGS_l = false; + FLAGS_default_service_config = + "{\"loadBalancingConfig\":[{\"pick_first\":{}}]}"; + EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(), + std::bind(PrintStream, &output_stream, + std::placeholders::_1))); + FLAGS_default_service_config = ""; + EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), + "grpc.testing.EchoTestService\n" + "grpc.reflection.v1alpha.ServerReflection\n")); + ShutdownServer(); +} + +} // namespace testing +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + return RUN_ALL_TESTS(); +} diff --git a/contrib/libs/grpc/test/cpp/util/metrics_server.cc b/contrib/libs/grpc/test/cpp/util/metrics_server.cc new file mode 100644 index 0000000000..0493da053e --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/metrics_server.cc @@ -0,0 +1,117 @@ +/* + * + * 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. + *is % allowed in string + */ + +#include "test/cpp/util/metrics_server.h" + +#include <grpc/support/log.h> +#include <grpcpp/server.h> +#include <grpcpp/server_builder.h> + +#include "src/proto/grpc/testing/metrics.grpc.pb.h" +#include "src/proto/grpc/testing/metrics.pb.h" + +namespace grpc { +namespace testing { + +QpsGauge::QpsGauge() + : start_time_(gpr_now(GPR_CLOCK_REALTIME)), num_queries_(0) {} + +void QpsGauge::Reset() { + std::lock_guard<std::mutex> lock(num_queries_mu_); + num_queries_ = 0; + start_time_ = gpr_now(GPR_CLOCK_REALTIME); +} + +void QpsGauge::Incr() { + std::lock_guard<std::mutex> lock(num_queries_mu_); + num_queries_++; +} + +long QpsGauge::Get() { + std::lock_guard<std::mutex> lock(num_queries_mu_); + gpr_timespec time_diff = + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), start_time_); + long duration_secs = time_diff.tv_sec > 0 ? time_diff.tv_sec : 1; + return num_queries_ / duration_secs; +} + +grpc::Status MetricsServiceImpl::GetAllGauges( + ServerContext* /*context*/, const EmptyMessage* /*request*/, + ServerWriter<GaugeResponse>* writer) { + gpr_log(GPR_DEBUG, "GetAllGauges called"); + + std::lock_guard<std::mutex> lock(mu_); + for (auto it = qps_gauges_.begin(); it != qps_gauges_.end(); it++) { + GaugeResponse resp; + resp.set_name(it->first); // Gauge name + resp.set_long_value(it->second->Get()); // Gauge value + writer->Write(resp); + } + + return Status::OK; +} + +grpc::Status MetricsServiceImpl::GetGauge(ServerContext* /*context*/, + const GaugeRequest* request, + GaugeResponse* response) { + std::lock_guard<std::mutex> lock(mu_); + + const auto it = qps_gauges_.find(request->name()); + if (it != qps_gauges_.end()) { + response->set_name(it->first); + response->set_long_value(it->second->Get()); + } + + return Status::OK; +} + +std::shared_ptr<QpsGauge> MetricsServiceImpl::CreateQpsGauge( + const TString& name, bool* already_present) { + std::lock_guard<std::mutex> lock(mu_); + + std::shared_ptr<QpsGauge> qps_gauge(new QpsGauge()); + const auto p = qps_gauges_.insert(std::make_pair(name, qps_gauge)); + + // p.first is an iterator pointing to <name, shared_ptr<QpsGauge>> pair. + // p.second is a boolean which is set to 'true' if the QpsGauge is + // successfully inserted in the guages_ map and 'false' if it is already + // present in the map + *already_present = !p.second; + return p.first->second; +} + +// Starts the metrics server and returns the grpc::Server instance. Call Wait() +// on the returned server instance. +std::unique_ptr<grpc::Server> MetricsServiceImpl::StartServer(int port) { + gpr_log(GPR_INFO, "Building metrics server.."); + + const TString address = "0.0.0.0:" + ToString(port); + + ServerBuilder builder; + builder.AddListeningPort(address, grpc::InsecureServerCredentials()); + builder.RegisterService(this); + + std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); + gpr_log(GPR_INFO, "Metrics server %s started. Ready to receive requests..", + address.c_str()); + + return server; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/metrics_server.h b/contrib/libs/grpc/test/cpp/util/metrics_server.h new file mode 100644 index 0000000000..10ffa7b4dd --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/metrics_server.h @@ -0,0 +1,98 @@ +/* + * + * 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. + *is % allowed in string + */ +#ifndef GRPC_TEST_CPP_METRICS_SERVER_H +#define GRPC_TEST_CPP_METRICS_SERVER_H + +#include <map> +#include <mutex> + +#include <grpcpp/server.h> + +#include "src/proto/grpc/testing/metrics.grpc.pb.h" +#include "src/proto/grpc/testing/metrics.pb.h" + +/* + * This implements a Metrics server defined in + * src/proto/grpc/testing/metrics.proto. Any + * test service can use this to export Metrics (TODO (sreek): Only Gauges for + * now). + * + * Example: + * MetricsServiceImpl metricsImpl; + * .. + * // Create QpsGauge(s). Note: QpsGauges can be created even after calling + * // 'StartServer'. + * QpsGauge qps_gauge1 = metricsImpl.CreateQpsGauge("foo", is_present); + * // qps_gauge1 can now be used anywhere in the program by first making a + * // one-time call qps_gauge1.Reset() and then calling qps_gauge1.Incr() + * // every time to increment a query counter + * + * ... + * // Create the metrics server + * std::unique_ptr<grpc::Server> server = metricsImpl.StartServer(port); + * server->Wait(); // Note: This is blocking. + */ +namespace grpc { +namespace testing { + +class QpsGauge { + public: + QpsGauge(); + + // Initialize the internal timer and reset the query count to 0 + void Reset(); + + // Increment the query count by 1 + void Incr(); + + // Return the current qps (i.e query count divided by the time since this + // QpsGauge object created (or Reset() was called)) + long Get(); + + private: + gpr_timespec start_time_; + long num_queries_; + std::mutex num_queries_mu_; +}; + +class MetricsServiceImpl final : public MetricsService::Service { + public: + grpc::Status GetAllGauges(ServerContext* context, const EmptyMessage* request, + ServerWriter<GaugeResponse>* writer) override; + + grpc::Status GetGauge(ServerContext* context, const GaugeRequest* request, + GaugeResponse* response) override; + + // Create a QpsGauge with name 'name'. is_present is set to true if the Gauge + // is already present in the map. + // NOTE: CreateQpsGauge can be called anytime (i.e before or after calling + // StartServer). + std::shared_ptr<QpsGauge> CreateQpsGauge(const TString& name, + bool* already_present); + + std::unique_ptr<grpc::Server> StartServer(int port); + + private: + std::map<string, std::shared_ptr<QpsGauge>> qps_gauges_; + std::mutex mu_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_METRICS_SERVER_H diff --git a/contrib/libs/grpc/test/cpp/util/proto_file_parser.cc b/contrib/libs/grpc/test/cpp/util/proto_file_parser.cc new file mode 100644 index 0000000000..b0912a712c --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/proto_file_parser.cc @@ -0,0 +1,323 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/proto_file_parser.h" + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <unordered_set> + +#include <grpcpp/support/config.h> + +namespace grpc { +namespace testing { +namespace { + +// Match the user input method string to the full_name from method descriptor. +bool MethodNameMatch(const TString& full_name, const TString& input) { + TString clean_input = input; + std::replace(clean_input.begin(), clean_input.vend(), '/', '.'); + if (clean_input.size() > full_name.size()) { + return false; + } + return full_name.compare(full_name.size() - clean_input.size(), + clean_input.size(), clean_input) == 0; +} +} // namespace + +class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector { + public: + explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {} + + void AddError(const google::protobuf::string& filename, int line, int column, + const google::protobuf::string& message) override { + std::ostringstream oss; + oss << "error " << filename << " " << line << " " << column << " " + << message << "\n"; + parser_->LogError(oss.str()); + } + + void AddWarning(const google::protobuf::string& filename, int line, int column, + const google::protobuf::string& message) override { + std::cerr << "warning " << filename << " " << line << " " << column << " " + << message << std::endl; + } + + private: + ProtoFileParser* parser_; // not owned +}; + +ProtoFileParser::ProtoFileParser(const std::shared_ptr<grpc::Channel>& channel, + const TString& proto_path, + const TString& protofiles) + : has_error_(false), + dynamic_factory_(new protobuf::DynamicMessageFactory()) { + std::vector<TString> service_list; + if (channel) { + reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel)); + reflection_db_->GetServices(&service_list); + } + + std::unordered_set<TString> known_services; + if (!protofiles.empty()) { + source_tree_.MapPath("", google::protobuf::string(proto_path)); + error_printer_.reset(new ErrorPrinter(this)); + importer_.reset( + new protobuf::compiler::Importer(&source_tree_, error_printer_.get())); + + std::string file_name; + std::stringstream ss(protofiles); + while (std::getline(ss, file_name, ',')) { + const auto* file_desc = importer_->Import(google::protobuf::string(file_name.c_str())); + if (file_desc) { + for (int i = 0; i < file_desc->service_count(); i++) { + service_desc_list_.push_back(file_desc->service(i)); + known_services.insert(file_desc->service(i)->full_name()); + } + } else { + std::cerr << file_name << " not found" << std::endl; + } + } + + file_db_.reset(new protobuf::DescriptorPoolDatabase(*importer_->pool())); + } + + if (!reflection_db_ && !file_db_) { + LogError("No available proto database"); + return; + } + + if (!reflection_db_) { + desc_db_ = std::move(file_db_); + } else if (!file_db_) { + desc_db_ = std::move(reflection_db_); + } else { + desc_db_.reset(new protobuf::MergedDescriptorDatabase(reflection_db_.get(), + file_db_.get())); + } + + desc_pool_.reset(new protobuf::DescriptorPool(desc_db_.get())); + + for (auto it = service_list.begin(); it != service_list.end(); it++) { + if (known_services.find(*it) == known_services.end()) { + if (const protobuf::ServiceDescriptor* service_desc = + desc_pool_->FindServiceByName(google::protobuf::string(*it))) { + service_desc_list_.push_back(service_desc); + known_services.insert(*it); + } + } + } +} + +ProtoFileParser::~ProtoFileParser() {} + +TString ProtoFileParser::GetFullMethodName(const TString& method) { + has_error_ = false; + + if (known_methods_.find(method) != known_methods_.end()) { + return known_methods_[method]; + } + + const protobuf::MethodDescriptor* method_descriptor = nullptr; + for (auto it = service_desc_list_.begin(); it != service_desc_list_.end(); + it++) { + const auto* service_desc = *it; + for (int j = 0; j < service_desc->method_count(); j++) { + const auto* method_desc = service_desc->method(j); + if (MethodNameMatch(method_desc->full_name(), method)) { + if (method_descriptor) { + std::ostringstream error_stream; + error_stream << "Ambiguous method names: "; + error_stream << method_descriptor->full_name() << " "; + error_stream << method_desc->full_name(); + LogError(error_stream.str()); + } + method_descriptor = method_desc; + } + } + } + if (!method_descriptor) { + LogError("Method name not found"); + } + if (has_error_) { + return ""; + } + + known_methods_[method] = method_descriptor->full_name(); + + return method_descriptor->full_name(); +} + +TString ProtoFileParser::GetFormattedMethodName(const TString& method) { + has_error_ = false; + TString formatted_method_name = GetFullMethodName(method); + if (has_error_) { + return ""; + } + size_t last_dot = formatted_method_name.find_last_of('.'); + if (last_dot != TString::npos) { + formatted_method_name[last_dot] = '/'; + } + formatted_method_name.insert(formatted_method_name.begin(), '/'); + return formatted_method_name; +} + +TString ProtoFileParser::GetMessageTypeFromMethod(const TString& method, + bool is_request) { + has_error_ = false; + TString full_method_name = GetFullMethodName(method); + if (has_error_) { + return ""; + } + const protobuf::MethodDescriptor* method_desc = + desc_pool_->FindMethodByName(google::protobuf::string(full_method_name)); + if (!method_desc) { + LogError("Method not found"); + return ""; + } + + return is_request ? method_desc->input_type()->full_name() + : method_desc->output_type()->full_name(); +} + +bool ProtoFileParser::IsStreaming(const TString& method, bool is_request) { + has_error_ = false; + + TString full_method_name = GetFullMethodName(method); + if (has_error_) { + return false; + } + + const protobuf::MethodDescriptor* method_desc = + desc_pool_->FindMethodByName(google::protobuf::string(full_method_name)); + if (!method_desc) { + LogError("Method not found"); + return false; + } + + return is_request ? method_desc->client_streaming() + : method_desc->server_streaming(); +} + +TString ProtoFileParser::GetSerializedProtoFromMethod( + const TString& method, const TString& formatted_proto, + bool is_request, bool is_json_format) { + has_error_ = false; + TString message_type_name = GetMessageTypeFromMethod(method, is_request); + if (has_error_) { + return ""; + } + return GetSerializedProtoFromMessageType(message_type_name, formatted_proto, + is_json_format); +} + +TString ProtoFileParser::GetFormattedStringFromMethod( + const TString& method, const TString& serialized_proto, + bool is_request, bool is_json_format) { + has_error_ = false; + TString message_type_name = GetMessageTypeFromMethod(method, is_request); + if (has_error_) { + return ""; + } + return GetFormattedStringFromMessageType(message_type_name, serialized_proto, + is_json_format); +} + +TString ProtoFileParser::GetSerializedProtoFromMessageType( + const TString& message_type_name, const TString& formatted_proto, + bool is_json_format) { + has_error_ = false; + google::protobuf::string serialized; + const protobuf::Descriptor* desc = + desc_pool_->FindMessageTypeByName(google::protobuf::string(message_type_name)); + if (!desc) { + LogError("Message type not found"); + return ""; + } + std::unique_ptr<grpc::protobuf::Message> msg( + dynamic_factory_->GetPrototype(desc)->New()); + bool ok; + if (is_json_format) { + ok = grpc::protobuf::json::JsonStringToMessage(google::protobuf::string(formatted_proto), msg.get()) + .ok(); + if (!ok) { + LogError("Failed to convert json format to proto."); + return ""; + } + } else { + ok = protobuf::TextFormat::ParseFromString(google::protobuf::string(formatted_proto), msg.get()); + if (!ok) { + LogError("Failed to convert text format to proto."); + return ""; + } + } + + ok = msg->SerializeToString(&serialized); + if (!ok) { + LogError("Failed to serialize proto."); + return ""; + } + return serialized; +} + +TString ProtoFileParser::GetFormattedStringFromMessageType( + const TString& message_type_name, const TString& serialized_proto, + bool is_json_format) { + has_error_ = false; + const protobuf::Descriptor* desc = + desc_pool_->FindMessageTypeByName(google::protobuf::string(message_type_name)); + if (!desc) { + LogError("Message type not found"); + return ""; + } + std::unique_ptr<grpc::protobuf::Message> msg( + dynamic_factory_->GetPrototype(desc)->New()); + if (!msg->ParseFromString(google::protobuf::string(serialized_proto))) { + LogError("Failed to deserialize proto."); + return ""; + } + google::protobuf::string formatted_string; + + if (is_json_format) { + grpc::protobuf::json::JsonPrintOptions jsonPrintOptions; + jsonPrintOptions.add_whitespace = true; + if (!grpc::protobuf::json::MessageToJsonString( + *msg.get(), &formatted_string, jsonPrintOptions) + .ok()) { + LogError("Failed to print proto message to json format"); + return ""; + } + } else { + if (!protobuf::TextFormat::PrintToString(*msg.get(), &formatted_string)) { + LogError("Failed to print proto message to text format"); + return ""; + } + } + return formatted_string; +} + +void ProtoFileParser::LogError(const TString& error_msg) { + if (!error_msg.empty()) { + std::cerr << error_msg << std::endl; + } + has_error_ = true; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/proto_file_parser.h b/contrib/libs/grpc/test/cpp/util/proto_file_parser.h new file mode 100644 index 0000000000..c0445641c7 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/proto_file_parser.h @@ -0,0 +1,129 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H +#define GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H + +#include <memory> + +#include <grpcpp/channel.h> + +#include "test/cpp/util/config_grpc_cli.h" +#include "test/cpp/util/proto_reflection_descriptor_database.h" + +namespace grpc { +namespace testing { +class ErrorPrinter; + +// Find method and associated request/response types. +class ProtoFileParser { + public: + // The parser will search proto files using the server reflection service + // provided on the given channel. The given protofiles in a source tree rooted + // from proto_path will also be searched. + ProtoFileParser(const std::shared_ptr<grpc::Channel>& channel, + const TString& proto_path, const TString& protofiles); + + ~ProtoFileParser(); + + // The input method name in the following four functions could be a partial + // string such as Service.Method or even just Method. It will log an error if + // there is ambiguity. + // Full method name is in the form of Service.Method, it's good to be used in + // descriptor database queries. + TString GetFullMethodName(const TString& method); + + // Formatted method name is in the form of /Service/Method, it's good to be + // used as the argument of Stub::Call() + TString GetFormattedMethodName(const TString& method); + + /// Converts a text or json string to its binary proto representation for the + /// given method's input or return type. + /// \param method the name of the method (does not need to be fully qualified + /// name) + /// \param formatted_proto the text- or json-formatted proto string + /// \param is_request if \c true the resolved type is that of the input + /// parameter of the method, otherwise it is the output type + /// \param is_json_format if \c true the \c formatted_proto is treated as a + /// json-formatted proto, otherwise it is treated as a text-formatted + /// proto + /// \return the serialised binary proto representation of \c formatted_proto + TString GetSerializedProtoFromMethod(const TString& method, + const TString& formatted_proto, + bool is_request, + bool is_json_format); + + /// Converts a text or json string to its proto representation for the given + /// message type. + /// \param formatted_proto the text- or json-formatted proto string + /// \return the serialised binary proto representation of \c formatted_proto + TString GetSerializedProtoFromMessageType( + const TString& message_type_name, const TString& formatted_proto, + bool is_json_format); + + /// Converts a binary proto string to its text or json string representation + /// for the given method's input or return type. + /// \param method the name of the method (does not need to be a fully + /// qualified name) + /// \param the serialised binary proto representation of type + /// \c message_type_name + /// \return the text- or json-formatted proto string of \c serialized_proto + TString GetFormattedStringFromMethod(const TString& method, + const TString& serialized_proto, + bool is_request, + bool is_json_format); + + /// Converts a binary proto string to its text or json string representation + /// for the given message type. + /// \param the serialised binary proto representation of type + /// \c message_type_name + /// \return the text- or json-formatted proto string of \c serialized_proto + TString GetFormattedStringFromMessageType( + const TString& message_type_name, const TString& serialized_proto, + bool is_json_format); + + bool IsStreaming(const TString& method, bool is_request); + + bool HasError() const { return has_error_; } + + void LogError(const TString& error_msg); + + private: + TString GetMessageTypeFromMethod(const TString& method, + bool is_request); + + bool has_error_; + TString request_text_; + protobuf::compiler::DiskSourceTree source_tree_; + std::unique_ptr<ErrorPrinter> error_printer_; + std::unique_ptr<protobuf::compiler::Importer> importer_; + std::unique_ptr<grpc::ProtoReflectionDescriptorDatabase> reflection_db_; + std::unique_ptr<protobuf::DescriptorPoolDatabase> file_db_; + std::unique_ptr<protobuf::DescriptorDatabase> desc_db_; + std::unique_ptr<protobuf::DescriptorPool> desc_pool_; + std::unique_ptr<protobuf::DynamicMessageFactory> dynamic_factory_; + std::unique_ptr<grpc::protobuf::Message> request_prototype_; + std::unique_ptr<grpc::protobuf::Message> response_prototype_; + std::unordered_map<TString, TString> known_methods_; + std::vector<const protobuf::ServiceDescriptor*> service_desc_list_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H diff --git a/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.cc b/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.cc new file mode 100644 index 0000000000..27a4c1e4cf --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.cc @@ -0,0 +1,333 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/proto_reflection_descriptor_database.h" + +#include <vector> + +#include <grpc/support/log.h> + +using grpc::reflection::v1alpha::ErrorResponse; +using grpc::reflection::v1alpha::ListServiceResponse; +using grpc::reflection::v1alpha::ServerReflection; +using grpc::reflection::v1alpha::ServerReflectionRequest; +using grpc::reflection::v1alpha::ServerReflectionResponse; + +namespace grpc { + +ProtoReflectionDescriptorDatabase::ProtoReflectionDescriptorDatabase( + std::unique_ptr<ServerReflection::Stub> stub) + : stub_(std::move(stub)) {} + +ProtoReflectionDescriptorDatabase::ProtoReflectionDescriptorDatabase( + const std::shared_ptr<grpc::Channel>& channel) + : stub_(ServerReflection::NewStub(channel)) {} + +ProtoReflectionDescriptorDatabase::~ProtoReflectionDescriptorDatabase() { + if (stream_) { + stream_->WritesDone(); + Status status = stream_->Finish(); + if (!status.ok()) { + if (status.error_code() == StatusCode::UNIMPLEMENTED) { + fprintf(stderr, + "Reflection request not implemented; " + "is the ServerReflection service enabled?\n"); + } else { + fprintf(stderr, + "ServerReflectionInfo rpc failed. Error code: %d, message: %s, " + "debug info: %s\n", + static_cast<int>(status.error_code()), + status.error_message().c_str(), + ctx_.debug_error_string().c_str()); + } + } + } +} + +bool ProtoReflectionDescriptorDatabase::FindFileByName( + const google::protobuf::string& filename, protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileByName(filename, output)) { + return true; + } + + if (known_files_.find(filename) != known_files_.end()) { + return false; + } + + ServerReflectionRequest request; + request.set_file_by_filename(filename); + ServerReflectionResponse response; + + if (!DoOneRequest(request, response)) { + return false; + } + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse& error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + gpr_log(GPR_INFO, "NOT_FOUND from server for FindFileByName(%s)", + filename.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindFileByName(%s)\n\tError code: %d\n" + "\tError Message: %s", + filename.c_str(), error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileByName(%s) response type\n" + "\tExpecting: %d\n\tReceived: %d", + filename.c_str(), + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + + return cached_db_.FindFileByName(filename, output); +} + +bool ProtoReflectionDescriptorDatabase::FindFileContainingSymbol( + const google::protobuf::string& symbol_name, protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileContainingSymbol(symbol_name, output)) { + return true; + } + + if (missing_symbols_.find(symbol_name) != missing_symbols_.end()) { + return false; + } + + ServerReflectionRequest request; + request.set_file_containing_symbol(symbol_name); + ServerReflectionResponse response; + + if (!DoOneRequest(request, response)) { + return false; + } + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse& error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + missing_symbols_.insert(symbol_name); + gpr_log(GPR_INFO, + "NOT_FOUND from server for FindFileContainingSymbol(%s)", + symbol_name.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindFileContainingSymbol(%s)\n" + "\tError code: %d\n\tError Message: %s", + symbol_name.c_str(), error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileContainingSymbol(%s) response type\n" + "\tExpecting: %d\n\tReceived: %d", + symbol_name.c_str(), + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + return cached_db_.FindFileContainingSymbol(symbol_name, output); +} + +bool ProtoReflectionDescriptorDatabase::FindFileContainingExtension( + const google::protobuf::string& containing_type, int field_number, + protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileContainingExtension(containing_type, field_number, + output)) { + return true; + } + + if (missing_extensions_.find(containing_type) != missing_extensions_.end() && + missing_extensions_[containing_type].find(field_number) != + missing_extensions_[containing_type].end()) { + gpr_log(GPR_INFO, "nested map."); + return false; + } + + ServerReflectionRequest request; + request.mutable_file_containing_extension()->set_containing_type( + containing_type); + request.mutable_file_containing_extension()->set_extension_number( + field_number); + ServerReflectionResponse response; + + if (!DoOneRequest(request, response)) { + return false; + } + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse& error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + if (missing_extensions_.find(containing_type) == + missing_extensions_.end()) { + missing_extensions_[containing_type] = {}; + } + missing_extensions_[containing_type].insert(field_number); + gpr_log(GPR_INFO, + "NOT_FOUND from server for FindFileContainingExtension(%s, %d)", + containing_type.c_str(), field_number); + } else { + gpr_log(GPR_INFO, + "Error on FindFileContainingExtension(%s, %d)\n" + "\tError code: %d\n\tError Message: %s", + containing_type.c_str(), field_number, error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileContainingExtension(%s, %d) response type\n" + "\tExpecting: %d\n\tReceived: %d", + containing_type.c_str(), field_number, + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + + return cached_db_.FindFileContainingExtension(containing_type, field_number, + output); +} + +bool ProtoReflectionDescriptorDatabase::FindAllExtensionNumbers( + const google::protobuf::string& extendee_type, std::vector<int>* output) { + if (cached_extension_numbers_.find(extendee_type) != + cached_extension_numbers_.end()) { + *output = cached_extension_numbers_[extendee_type]; + return true; + } + + ServerReflectionRequest request; + request.set_all_extension_numbers_of_type(extendee_type); + ServerReflectionResponse response; + + if (!DoOneRequest(request, response)) { + return false; + } + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase:: + kAllExtensionNumbersResponse) { + auto number = response.all_extension_numbers_response().extension_number(); + *output = std::vector<int>(number.begin(), number.end()); + cached_extension_numbers_[extendee_type] = *output; + return true; + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse& error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + gpr_log(GPR_INFO, "NOT_FOUND from server for FindAllExtensionNumbers(%s)", + extendee_type.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindAllExtensionNumbersExtension(%s)\n" + "\tError code: %d\n\tError Message: %s", + extendee_type.c_str(), error.error_code(), + error.error_message().c_str()); + } + } + return false; +} + +bool ProtoReflectionDescriptorDatabase::GetServices( + std::vector<TString>* output) { + ServerReflectionRequest request; + request.set_list_services(""); + ServerReflectionResponse response; + + if (!DoOneRequest(request, response)) { + return false; + } + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kListServicesResponse) { + const ListServiceResponse& ls_response = response.list_services_response(); + for (int i = 0; i < ls_response.service_size(); ++i) { + (*output).push_back(ls_response.service(i).name()); + } + return true; + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse& error = response.error_response(); + gpr_log(GPR_INFO, + "Error on GetServices()\n\tError code: %d\n" + "\tError Message: %s", + error.error_code(), error.error_message().c_str()); + } else { + gpr_log( + GPR_INFO, + "Error on GetServices() response type\n\tExpecting: %d\n\tReceived: %d", + ServerReflectionResponse::MessageResponseCase::kListServicesResponse, + response.message_response_case()); + } + return false; +} + +const protobuf::FileDescriptorProto +ProtoReflectionDescriptorDatabase::ParseFileDescriptorProtoResponse( + const TString& byte_fd_proto) { + protobuf::FileDescriptorProto file_desc_proto; + file_desc_proto.ParseFromString(google::protobuf::string(byte_fd_proto)); + return file_desc_proto; +} + +void ProtoReflectionDescriptorDatabase::AddFileFromResponse( + const grpc::reflection::v1alpha::FileDescriptorResponse& response) { + for (int i = 0; i < response.file_descriptor_proto_size(); ++i) { + const protobuf::FileDescriptorProto file_proto = + ParseFileDescriptorProtoResponse(response.file_descriptor_proto(i)); + if (known_files_.find(file_proto.name()) == known_files_.end()) { + known_files_.insert(file_proto.name()); + cached_db_.Add(file_proto); + } + } +} + +const std::shared_ptr<ProtoReflectionDescriptorDatabase::ClientStream> +ProtoReflectionDescriptorDatabase::GetStream() { + if (!stream_) { + stream_ = stub_->ServerReflectionInfo(&ctx_); + } + return stream_; +} + +bool ProtoReflectionDescriptorDatabase::DoOneRequest( + const ServerReflectionRequest& request, + ServerReflectionResponse& response) { + bool success = false; + stream_mutex_.lock(); + if (GetStream()->Write(request) && GetStream()->Read(&response)) { + success = true; + } + stream_mutex_.unlock(); + return success; +} + +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.h b/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.h new file mode 100644 index 0000000000..cdd6f0cccd --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/proto_reflection_descriptor_database.h @@ -0,0 +1,111 @@ +/* + * + * Copyright 2016 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. + * + */ +#ifndef GRPC_TEST_CPP_PROTO_SERVER_REFLECTION_DATABSE_H +#define GRPC_TEST_CPP_PROTO_SERVER_REFLECTION_DATABSE_H + +#include <mutex> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include <grpcpp/grpcpp.h> +#include <grpcpp/impl/codegen/config_protobuf.h> +#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h" + +namespace grpc { + +// ProtoReflectionDescriptorDatabase takes a stub of ServerReflection and +// provides the methods defined by DescriptorDatabase interfaces. It can be used +// to feed a DescriptorPool instance. +class ProtoReflectionDescriptorDatabase : public protobuf::DescriptorDatabase { + public: + explicit ProtoReflectionDescriptorDatabase( + std::unique_ptr<reflection::v1alpha::ServerReflection::Stub> stub); + + explicit ProtoReflectionDescriptorDatabase( + const std::shared_ptr<grpc::Channel>& channel); + + virtual ~ProtoReflectionDescriptorDatabase(); + + // The following four methods implement DescriptorDatabase interfaces. + // + // Find a file by file name. Fills in *output and returns true if found. + // Otherwise, returns false, leaving the contents of *output undefined. + bool FindFileByName(const google::protobuf::string& filename, + protobuf::FileDescriptorProto* output) override; + + // Find the file that declares the given fully-qualified symbol name. + // If found, fills in *output and returns true, otherwise returns false + // and leaves *output undefined. + bool FindFileContainingSymbol(const google::protobuf::string& symbol_name, + protobuf::FileDescriptorProto* output) override; + + // Find the file which defines an extension extending the given message type + // with the given field number. If found, fills in *output and returns true, + // otherwise returns false and leaves *output undefined. containing_type + // must be a fully-qualified type name. + bool FindFileContainingExtension( + const google::protobuf::string& containing_type, int field_number, + protobuf::FileDescriptorProto* output) override; + + // Finds the tag numbers used by all known extensions of + // extendee_type, and appends them to output in an undefined + // order. This method is best-effort: it's not guaranteed that the + // database will find all extensions, and it's not guaranteed that + // FindFileContainingExtension will return true on all of the found + // numbers. Returns true if the search was successful, otherwise + // returns false and leaves output unchanged. + bool FindAllExtensionNumbers(const google::protobuf::string& extendee_type, + std::vector<int>* output) override; + + // Provide a list of full names of registered services + bool GetServices(std::vector<TString>* output); + + private: + typedef ClientReaderWriter< + grpc::reflection::v1alpha::ServerReflectionRequest, + grpc::reflection::v1alpha::ServerReflectionResponse> + ClientStream; + + const protobuf::FileDescriptorProto ParseFileDescriptorProtoResponse( + const TString& byte_fd_proto); + + void AddFileFromResponse( + const grpc::reflection::v1alpha::FileDescriptorResponse& response); + + const std::shared_ptr<ClientStream> GetStream(); + + bool DoOneRequest( + const grpc::reflection::v1alpha::ServerReflectionRequest& request, + grpc::reflection::v1alpha::ServerReflectionResponse& response); + + std::shared_ptr<ClientStream> stream_; + grpc::ClientContext ctx_; + std::unique_ptr<grpc::reflection::v1alpha::ServerReflection::Stub> stub_; + std::unordered_set<string> known_files_; + std::unordered_set<string> missing_symbols_; + std::unordered_map<string, std::unordered_set<int>> missing_extensions_; + std::unordered_map<string, std::vector<int>> cached_extension_numbers_; + std::mutex stream_mutex_; + + protobuf::SimpleDescriptorDatabase cached_db_; +}; + +} // namespace grpc + +#endif // GRPC_TEST_CPP_METRICS_SERVER_H diff --git a/contrib/libs/grpc/test/cpp/util/service_describer.cc b/contrib/libs/grpc/test/cpp/util/service_describer.cc new file mode 100644 index 0000000000..2af1104b97 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/service_describer.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/service_describer.h" + +#include <iostream> +#include <sstream> +#include <util/generic/string.h> +#include <vector> + +namespace grpc { +namespace testing { + +TString DescribeServiceList(std::vector<TString> service_list, + grpc::protobuf::DescriptorPool& desc_pool) { + std::stringstream result; + for (auto it = service_list.begin(); it != service_list.end(); it++) { + auto const& service = *it; + const grpc::protobuf::ServiceDescriptor* service_desc = + desc_pool.FindServiceByName(google::protobuf::string(service)); + if (service_desc != nullptr) { + result << DescribeService(service_desc); + } + } + return result.str(); +} + +TString DescribeService(const grpc::protobuf::ServiceDescriptor* service) { + TString result; + if (service->options().deprecated()) { + result.append("DEPRECATED\n"); + } + result.append("filename: " + service->file()->name() + "\n"); + + TString package = service->full_name(); + size_t pos = package.rfind("." + service->name()); + if (pos != TString::npos) { + package.erase(pos); + result.append("package: " + package + ";\n"); + } + result.append("service " + service->name() + " {\n"); + for (int i = 0; i < service->method_count(); ++i) { + result.append(DescribeMethod(service->method(i))); + } + result.append("}\n\n"); + return result; +} + +TString DescribeMethod(const grpc::protobuf::MethodDescriptor* method) { + std::stringstream result; + result << " rpc " << method->name() + << (method->client_streaming() ? "(stream " : "(") + << method->input_type()->full_name() << ") returns " + << (method->server_streaming() ? "(stream " : "(") + << method->output_type()->full_name() << ") {}\n"; + if (method->options().deprecated()) { + result << " DEPRECATED"; + } + return result.str(); +} + +TString SummarizeService(const grpc::protobuf::ServiceDescriptor* service) { + TString result; + for (int i = 0; i < service->method_count(); ++i) { + result.append(SummarizeMethod(service->method(i))); + } + return result; +} + +TString SummarizeMethod(const grpc::protobuf::MethodDescriptor* method) { + TString result = method->name(); + result.append("\n"); + return result; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/service_describer.h b/contrib/libs/grpc/test/cpp/util/service_describer.h new file mode 100644 index 0000000000..a473f03744 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/service_describer.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_SERVICE_DESCRIBER_H +#define GRPC_TEST_CPP_UTIL_SERVICE_DESCRIBER_H + +#include <grpcpp/support/config.h> +#include "test/cpp/util/config_grpc_cli.h" + +namespace grpc { +namespace testing { + +TString DescribeServiceList(std::vector<TString> service_list, + grpc::protobuf::DescriptorPool& desc_pool); + +TString DescribeService(const grpc::protobuf::ServiceDescriptor* service); + +TString DescribeMethod(const grpc::protobuf::MethodDescriptor* method); + +TString SummarizeService(const grpc::protobuf::ServiceDescriptor* service); + +TString SummarizeMethod(const grpc::protobuf::MethodDescriptor* method); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_SERVICE_DESCRIBER_H diff --git a/contrib/libs/grpc/test/cpp/util/slice_test.cc b/contrib/libs/grpc/test/cpp/util/slice_test.cc new file mode 100644 index 0000000000..d7e945ae38 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/slice_test.cc @@ -0,0 +1,144 @@ +/* + * + * 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. + * + */ + +#include <grpc++/support/slice.h> +#include <grpcpp/impl/grpc_library.h> + +#include <grpc/grpc.h> +#include <grpc/slice.h> +#include <gtest/gtest.h> + +#include "test/core/util/test_config.h" + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; + +namespace { + +const char* kContent = "hello xxxxxxxxxxxxxxxxxxxx world"; + +class SliceTest : public ::testing::Test { + protected: + static void SetUpTestCase() { grpc_init(); } + + static void TearDownTestCase() { grpc_shutdown(); } + + void CheckSliceSize(const Slice& s, const TString& content) { + EXPECT_EQ(content.size(), s.size()); + } + void CheckSlice(const Slice& s, const TString& content) { + EXPECT_EQ(content.size(), s.size()); + EXPECT_EQ(content, + TString(reinterpret_cast<const char*>(s.begin()), s.size())); + } +}; + +TEST_F(SliceTest, Empty) { + Slice empty_slice; + CheckSlice(empty_slice, ""); +} + +TEST_F(SliceTest, Sized) { + Slice sized_slice(strlen(kContent)); + CheckSliceSize(sized_slice, kContent); +} + +TEST_F(SliceTest, String) { + Slice spp(kContent); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, Buf) { + Slice spp(kContent, strlen(kContent)); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, StaticBuf) { + Slice spp(kContent, strlen(kContent), Slice::STATIC_SLICE); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, SliceNew) { + char* x = new char[strlen(kContent) + 1]; + strcpy(x, kContent); + Slice spp(x, strlen(x), [](void* p) { delete[] static_cast<char*>(p); }); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, SliceNewDoNothing) { + Slice spp(const_cast<char*>(kContent), strlen(kContent), [](void* /*p*/) {}); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, SliceNewWithUserData) { + struct stest { + char* x; + int y; + }; + auto* t = new stest; + t->x = new char[strlen(kContent) + 1]; + strcpy(t->x, kContent); + Slice spp(t->x, strlen(t->x), + [](void* p) { + auto* t = static_cast<stest*>(p); + delete[] t->x; + delete t; + }, + t); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, SliceNewLen) { + Slice spp(const_cast<char*>(kContent), strlen(kContent), + [](void* /*p*/, size_t l) { EXPECT_EQ(l, strlen(kContent)); }); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, Steal) { + grpc_slice s = grpc_slice_from_copied_string(kContent); + Slice spp(s, Slice::STEAL_REF); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, Add) { + grpc_slice s = grpc_slice_from_copied_string(kContent); + Slice spp(s, Slice::ADD_REF); + grpc_slice_unref(s); + CheckSlice(spp, kContent); +} + +TEST_F(SliceTest, Cslice) { + grpc_slice s = grpc_slice_from_copied_string(kContent); + Slice spp(s, Slice::STEAL_REF); + CheckSlice(spp, kContent); + grpc_slice c_slice = spp.c_slice(); + EXPECT_EQ(GRPC_SLICE_START_PTR(s), GRPC_SLICE_START_PTR(c_slice)); + EXPECT_EQ(GRPC_SLICE_END_PTR(s), GRPC_SLICE_END_PTR(c_slice)); + grpc_slice_unref(c_slice); +} + +} // namespace +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/contrib/libs/grpc/test/cpp/util/string_ref_helper.cc b/contrib/libs/grpc/test/cpp/util/string_ref_helper.cc new file mode 100644 index 0000000000..e573f5d33a --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/string_ref_helper.cc @@ -0,0 +1,29 @@ +/* + * + * 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. + * + */ + +#include "test/cpp/util/string_ref_helper.h" + +namespace grpc { +namespace testing { + +TString ToString(const grpc::string_ref& r) { + return TString(r.data(), r.size()); +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/string_ref_helper.h b/contrib/libs/grpc/test/cpp/util/string_ref_helper.h new file mode 100644 index 0000000000..e9e941f319 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/string_ref_helper.h @@ -0,0 +1,32 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_STRING_REF_HELPER_H +#define GRPC_TEST_CPP_UTIL_STRING_REF_HELPER_H + +#include <grpcpp/support/string_ref.h> + +namespace grpc { +namespace testing { + +TString ToString(const grpc::string_ref& r); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_STRING_REF_HELPER_H diff --git a/contrib/libs/grpc/test/cpp/util/string_ref_test.cc b/contrib/libs/grpc/test/cpp/util/string_ref_test.cc new file mode 100644 index 0000000000..8e3259b764 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/string_ref_test.cc @@ -0,0 +1,205 @@ +/* + * + * 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. + * + */ + +#include <grpcpp/support/string_ref.h> + +#include <string.h> + +#include <gtest/gtest.h> + +#include "test/core/util/test_config.h" + +namespace grpc { +namespace { + +const char kTestString[] = "blah"; +const char kTestStringWithEmbeddedNull[] = "blah\0foo"; +const size_t kTestStringWithEmbeddedNullLength = 8; +const char kTestUnrelatedString[] = "foo"; + +class StringRefTest : public ::testing::Test {}; + +TEST_F(StringRefTest, Empty) { + string_ref s; + EXPECT_EQ(0U, s.length()); + EXPECT_EQ(nullptr, s.data()); +} + +TEST_F(StringRefTest, FromCString) { + string_ref s(kTestString); + EXPECT_EQ(strlen(kTestString), s.length()); + EXPECT_EQ(kTestString, s.data()); +} + +TEST_F(StringRefTest, FromCStringWithLength) { + string_ref s(kTestString, 2); + EXPECT_EQ(2U, s.length()); + EXPECT_EQ(kTestString, s.data()); +} + +TEST_F(StringRefTest, FromString) { + string copy(kTestString); + string_ref s(copy); + EXPECT_EQ(copy.data(), s.data()); + EXPECT_EQ(copy.length(), s.length()); +} + +TEST_F(StringRefTest, CopyConstructor) { + string_ref s1(kTestString); + ; + const string_ref& s2(s1); + EXPECT_EQ(s1.length(), s2.length()); + EXPECT_EQ(s1.data(), s2.data()); +} + +TEST_F(StringRefTest, FromStringWithEmbeddedNull) { + string copy(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + string_ref s(copy); + EXPECT_EQ(copy.data(), s.data()); + EXPECT_EQ(copy.length(), s.length()); + EXPECT_EQ(kTestStringWithEmbeddedNullLength, s.length()); +} + +TEST_F(StringRefTest, Assignment) { + string_ref s1(kTestString); + ; + string_ref s2; + EXPECT_EQ(nullptr, s2.data()); + s2 = s1; + EXPECT_EQ(s1.length(), s2.length()); + EXPECT_EQ(s1.data(), s2.data()); +} + +TEST_F(StringRefTest, Iterator) { + string_ref s(kTestString); + size_t i = 0; + for (auto it = s.cbegin(); it != s.cend(); ++it) { + auto val = kTestString[i++]; + EXPECT_EQ(val, *it); + } + EXPECT_EQ(strlen(kTestString), i); +} + +TEST_F(StringRefTest, ReverseIterator) { + string_ref s(kTestString); + size_t i = strlen(kTestString); + for (auto rit = s.crbegin(); rit != s.crend(); ++rit) { + auto val = kTestString[--i]; + EXPECT_EQ(val, *rit); + } + EXPECT_EQ(0U, i); +} + +TEST_F(StringRefTest, Capacity) { + string_ref empty; + EXPECT_EQ(0U, empty.length()); + EXPECT_EQ(0U, empty.size()); + EXPECT_EQ(0U, empty.max_size()); + EXPECT_TRUE(empty.empty()); + + string_ref s(kTestString); + EXPECT_EQ(strlen(kTestString), s.length()); + EXPECT_EQ(s.length(), s.size()); + EXPECT_EQ(s.max_size(), s.length()); + EXPECT_FALSE(s.empty()); +} + +TEST_F(StringRefTest, Compare) { + string_ref s1(kTestString); + string s1_copy(kTestString); + string_ref s2(kTestUnrelatedString); + string_ref s3(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + EXPECT_EQ(0, s1.compare(s1_copy)); + EXPECT_NE(0, s1.compare(s2)); + EXPECT_NE(0, s1.compare(s3)); +} + +TEST_F(StringRefTest, StartsWith) { + string_ref s1(kTestString); + string_ref s2(kTestUnrelatedString); + string_ref s3(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + EXPECT_TRUE(s1.starts_with(s1)); + EXPECT_FALSE(s1.starts_with(s2)); + EXPECT_FALSE(s2.starts_with(s1)); + EXPECT_FALSE(s1.starts_with(s3)); + EXPECT_TRUE(s3.starts_with(s1)); +} + +TEST_F(StringRefTest, Endswith) { + string_ref s1(kTestString); + string_ref s2(kTestUnrelatedString); + string_ref s3(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + EXPECT_TRUE(s1.ends_with(s1)); + EXPECT_FALSE(s1.ends_with(s2)); + EXPECT_FALSE(s2.ends_with(s1)); + EXPECT_FALSE(s2.ends_with(s3)); + EXPECT_TRUE(s3.ends_with(s2)); +} + +TEST_F(StringRefTest, Find) { + string_ref s1(kTestString); + string_ref s2(kTestUnrelatedString); + string_ref s3(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + EXPECT_EQ(0U, s1.find(s1)); + EXPECT_EQ(0U, s2.find(s2)); + EXPECT_EQ(0U, s3.find(s3)); + EXPECT_EQ(string_ref::npos, s1.find(s2)); + EXPECT_EQ(string_ref::npos, s2.find(s1)); + EXPECT_EQ(string_ref::npos, s1.find(s3)); + EXPECT_EQ(0U, s3.find(s1)); + EXPECT_EQ(5U, s3.find(s2)); + EXPECT_EQ(string_ref::npos, s1.find('z')); + EXPECT_EQ(1U, s2.find('o')); +} + +TEST_F(StringRefTest, SubString) { + string_ref s(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + string_ref sub1 = s.substr(0, 4); + EXPECT_EQ(string_ref(kTestString), sub1); + string_ref sub2 = s.substr(5); + EXPECT_EQ(string_ref(kTestUnrelatedString), sub2); +} + +TEST_F(StringRefTest, ComparisonOperators) { + string_ref s1(kTestString); + string_ref s2(kTestUnrelatedString); + string_ref s3(kTestStringWithEmbeddedNull, kTestStringWithEmbeddedNullLength); + EXPECT_EQ(s1, s1); + EXPECT_EQ(s2, s2); + EXPECT_EQ(s3, s3); + EXPECT_GE(s1, s1); + EXPECT_GE(s2, s2); + EXPECT_GE(s3, s3); + EXPECT_LE(s1, s1); + EXPECT_LE(s2, s2); + EXPECT_LE(s3, s3); + EXPECT_NE(s1, s2); + EXPECT_NE(s1, s3); + EXPECT_NE(s2, s3); + EXPECT_GT(s3, s1); + EXPECT_LT(s1, s3); +} + +} // namespace +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contrib/libs/grpc/test/cpp/util/subprocess.cc b/contrib/libs/grpc/test/cpp/util/subprocess.cc new file mode 100644 index 0000000000..648bd50274 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/subprocess.cc @@ -0,0 +1,44 @@ +/* + * + * 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. + * + */ + +#include "test/cpp/util/subprocess.h" + +#include <vector> + +#include "test/core/util/subprocess.h" + +namespace grpc { + +static gpr_subprocess* MakeProcess(const std::vector<TString>& args) { + std::vector<const char*> vargs; + for (auto it = args.begin(); it != args.end(); ++it) { + vargs.push_back(it->c_str()); + } + return gpr_subprocess_create(vargs.size(), &vargs[0]); +} + +SubProcess::SubProcess(const std::vector<TString>& args) + : subprocess_(MakeProcess(args)) {} + +SubProcess::~SubProcess() { gpr_subprocess_destroy(subprocess_); } + +int SubProcess::Join() { return gpr_subprocess_join(subprocess_); } + +void SubProcess::Interrupt() { gpr_subprocess_interrupt(subprocess_); } + +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/subprocess.h b/contrib/libs/grpc/test/cpp/util/subprocess.h new file mode 100644 index 0000000000..84dda31dd1 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/subprocess.h @@ -0,0 +1,47 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_SUBPROCESS_H +#define GRPC_TEST_CPP_UTIL_SUBPROCESS_H + +#include <initializer_list> +#include <util/generic/string.h> +#include <vector> + +struct gpr_subprocess; + +namespace grpc { + +class SubProcess { + public: + SubProcess(const std::vector<TString>& args); + ~SubProcess(); + + int Join(); + void Interrupt(); + + private: + SubProcess(const SubProcess& other); + SubProcess& operator=(const SubProcess& other); + + gpr_subprocess* const subprocess_; +}; + +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_SUBPROCESS_H diff --git a/contrib/libs/grpc/test/cpp/util/test_config.h b/contrib/libs/grpc/test/cpp/util/test_config.h new file mode 100644 index 0000000000..094ed44f63 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/test_config.h @@ -0,0 +1,30 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_TEST_CONFIG_H +#define GRPC_TEST_CPP_UTIL_TEST_CONFIG_H + +namespace grpc { +namespace testing { + +void InitTest(int* argc, char*** argv, bool remove_flags); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_TEST_CONFIG_H diff --git a/contrib/libs/grpc/test/cpp/util/test_config_cc.cc b/contrib/libs/grpc/test/cpp/util/test_config_cc.cc new file mode 100644 index 0000000000..e4b6886335 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/test_config_cc.cc @@ -0,0 +1,37 @@ +/* + * + * 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. + * + */ + +#include <gflags/gflags.h> +#include "test/cpp/util/test_config.h" + +// In some distros, gflags is in the namespace google, and in some others, +// in gflags. This hack is enabling us to find both. +namespace google {} +namespace gflags {} +using namespace google; +using namespace gflags; + +namespace grpc { +namespace testing { + +void InitTest(int* argc, char*** argv, bool remove_flags) { + ParseCommandLineFlags(argc, argv, remove_flags); +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/test_credentials_provider.cc b/contrib/libs/grpc/test/cpp/util/test_credentials_provider.cc new file mode 100644 index 0000000000..f7134b773f --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/test_credentials_provider.cc @@ -0,0 +1,181 @@ + +/* + * + * Copyright 2016 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. + * + */ + +#include "test/cpp/util/test_credentials_provider.h" + +#include <cstdio> +#include <fstream> +#include <iostream> + +#include <mutex> +#include <unordered_map> + +#include <gflags/gflags.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpcpp/security/server_credentials.h> + +#include "test/core/end2end/data/ssl_test_data.h" + +DEFINE_string(tls_cert_file, "", "The TLS cert file used when --use_tls=true"); +DEFINE_string(tls_key_file, "", "The TLS key file used when --use_tls=true"); + +namespace grpc { +namespace testing { +namespace { + +TString ReadFile(const TString& src_path) { + std::ifstream src; + src.open(src_path, std::ifstream::in | std::ifstream::binary); + + TString contents; + src.seekg(0, std::ios::end); + contents.reserve(src.tellg()); + src.seekg(0, std::ios::beg); + contents.assign((std::istreambuf_iterator<char>(src)), + (std::istreambuf_iterator<char>())); + return contents; +} + +class DefaultCredentialsProvider : public CredentialsProvider { + public: + DefaultCredentialsProvider() { + if (!FLAGS_tls_key_file.empty()) { + custom_server_key_ = ReadFile(FLAGS_tls_key_file); + } + if (!FLAGS_tls_cert_file.empty()) { + custom_server_cert_ = ReadFile(FLAGS_tls_cert_file); + } + } + ~DefaultCredentialsProvider() override {} + + void AddSecureType( + const TString& type, + std::unique_ptr<CredentialTypeProvider> type_provider) override { + // This clobbers any existing entry for type, except the defaults, which + // can't be clobbered. + std::unique_lock<std::mutex> lock(mu_); + auto it = std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type); + if (it == added_secure_type_names_.end()) { + added_secure_type_names_.push_back(type); + added_secure_type_providers_.push_back(std::move(type_provider)); + } else { + added_secure_type_providers_[it - added_secure_type_names_.begin()] = + std::move(type_provider); + } + } + + std::shared_ptr<ChannelCredentials> GetChannelCredentials( + const TString& type, ChannelArguments* args) override { + if (type == grpc::testing::kInsecureCredentialsType) { + return InsecureChannelCredentials(); + } else if (type == grpc::testing::kAltsCredentialsType) { + grpc::experimental::AltsCredentialsOptions alts_opts; + return grpc::experimental::AltsCredentials(alts_opts); + } else if (type == grpc::testing::kTlsCredentialsType) { + SslCredentialsOptions ssl_opts = {test_root_cert, "", ""}; + args->SetSslTargetNameOverride("foo.test.google.fr"); + return grpc::SslCredentials(ssl_opts); + } else if (type == grpc::testing::kGoogleDefaultCredentialsType) { + return grpc::GoogleDefaultCredentials(); + } else { + std::unique_lock<std::mutex> lock(mu_); + auto it(std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type)); + if (it == added_secure_type_names_.end()) { + gpr_log(GPR_ERROR, "Unsupported credentials type %s.", type.c_str()); + return nullptr; + } + return added_secure_type_providers_[it - added_secure_type_names_.begin()] + ->GetChannelCredentials(args); + } + } + + std::shared_ptr<ServerCredentials> GetServerCredentials( + const TString& type) override { + if (type == grpc::testing::kInsecureCredentialsType) { + return InsecureServerCredentials(); + } else if (type == grpc::testing::kAltsCredentialsType) { + grpc::experimental::AltsServerCredentialsOptions alts_opts; + return grpc::experimental::AltsServerCredentials(alts_opts); + } else if (type == grpc::testing::kTlsCredentialsType) { + SslServerCredentialsOptions ssl_opts; + ssl_opts.pem_root_certs = ""; + if (!custom_server_key_.empty() && !custom_server_cert_.empty()) { + SslServerCredentialsOptions::PemKeyCertPair pkcp = { + custom_server_key_, custom_server_cert_}; + ssl_opts.pem_key_cert_pairs.push_back(pkcp); + } else { + SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key, + test_server1_cert}; + ssl_opts.pem_key_cert_pairs.push_back(pkcp); + } + return SslServerCredentials(ssl_opts); + } else { + std::unique_lock<std::mutex> lock(mu_); + auto it(std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type)); + if (it == added_secure_type_names_.end()) { + gpr_log(GPR_ERROR, "Unsupported credentials type %s.", type.c_str()); + return nullptr; + } + return added_secure_type_providers_[it - added_secure_type_names_.begin()] + ->GetServerCredentials(); + } + } + std::vector<TString> GetSecureCredentialsTypeList() override { + std::vector<TString> types; + types.push_back(grpc::testing::kTlsCredentialsType); + std::unique_lock<std::mutex> lock(mu_); + for (auto it = added_secure_type_names_.begin(); + it != added_secure_type_names_.end(); it++) { + types.push_back(*it); + } + return types; + } + + private: + std::mutex mu_; + std::vector<TString> added_secure_type_names_; + std::vector<std::unique_ptr<CredentialTypeProvider>> + added_secure_type_providers_; + TString custom_server_key_; + TString custom_server_cert_; +}; + +CredentialsProvider* g_provider = nullptr; + +} // namespace + +CredentialsProvider* GetCredentialsProvider() { + if (g_provider == nullptr) { + g_provider = new DefaultCredentialsProvider; + } + return g_provider; +} + +void SetCredentialsProvider(CredentialsProvider* provider) { + // For now, forbids overriding provider. + GPR_ASSERT(g_provider == nullptr); + g_provider = provider; +} + +} // namespace testing +} // namespace grpc diff --git a/contrib/libs/grpc/test/cpp/util/test_credentials_provider.h b/contrib/libs/grpc/test/cpp/util/test_credentials_provider.h new file mode 100644 index 0000000000..acba277ada --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/test_credentials_provider.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2016 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. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_TEST_CREDENTIALS_PROVIDER_H +#define GRPC_TEST_CPP_UTIL_TEST_CREDENTIALS_PROVIDER_H + +#include <memory> + +#include <grpcpp/security/credentials.h> +#include <grpcpp/security/server_credentials.h> +#include <grpcpp/support/channel_arguments.h> + +namespace grpc { +namespace testing { + +const char kInsecureCredentialsType[] = "INSECURE_CREDENTIALS"; +// For real credentials, like tls/ssl, this name should match the AuthContext +// property "transport_security_type". +const char kTlsCredentialsType[] = "ssl"; +const char kAltsCredentialsType[] = "alts"; +const char kGoogleDefaultCredentialsType[] = "google_default_credentials"; + +// Provide test credentials of a particular type. +class CredentialTypeProvider { + public: + virtual ~CredentialTypeProvider() {} + + virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials( + ChannelArguments* args) = 0; + virtual std::shared_ptr<ServerCredentials> GetServerCredentials() = 0; +}; + +// Provide test credentials. Thread-safe. +class CredentialsProvider { + public: + virtual ~CredentialsProvider() {} + + // Add a secure type in addition to the defaults. The default provider has + // (kInsecureCredentialsType, kTlsCredentialsType). + virtual void AddSecureType( + const TString& type, + std::unique_ptr<CredentialTypeProvider> type_provider) = 0; + + // Provide channel credentials according to the given type. Alter the channel + // arguments if needed. Return nullptr if type is not registered. + virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials( + const TString& type, ChannelArguments* args) = 0; + + // Provide server credentials according to the given type. + // Return nullptr if type is not registered. + virtual std::shared_ptr<ServerCredentials> GetServerCredentials( + const TString& type) = 0; + + // Provide a list of secure credentials type. + virtual std::vector<TString> GetSecureCredentialsTypeList() = 0; +}; + +// Get the current provider. Create a default one if not set. +// Not thread-safe. +CredentialsProvider* GetCredentialsProvider(); + +// Set the global provider. Takes ownership. The previous set provider will be +// destroyed. +// Not thread-safe. +void SetCredentialsProvider(CredentialsProvider* provider); + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_TEST_CREDENTIALS_PROVIDER_H diff --git a/contrib/libs/grpc/test/cpp/util/time_test.cc b/contrib/libs/grpc/test/cpp/util/time_test.cc new file mode 100644 index 0000000000..bcbfa14f94 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/time_test.cc @@ -0,0 +1,72 @@ +/* + * + * 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. + * + */ + +#include <grpc/support/time.h> +#include <grpcpp/support/time.h> +#include <gtest/gtest.h> + +#include "test/core/util/test_config.h" + +using std::chrono::duration_cast; +using std::chrono::microseconds; +using std::chrono::system_clock; + +namespace grpc { +namespace { + +class TimeTest : public ::testing::Test {}; + +TEST_F(TimeTest, AbsolutePointTest) { + int64_t us = 10000000L; + gpr_timespec ts = gpr_time_from_micros(us, GPR_TIMESPAN); + ts.clock_type = GPR_CLOCK_REALTIME; + system_clock::time_point tp{microseconds(us)}; + system_clock::time_point tp_converted = Timespec2Timepoint(ts); + gpr_timespec ts_converted; + Timepoint2Timespec(tp_converted, &ts_converted); + EXPECT_TRUE(ts.tv_sec == ts_converted.tv_sec); + EXPECT_TRUE(ts.tv_nsec == ts_converted.tv_nsec); + system_clock::time_point tp_converted_2 = Timespec2Timepoint(ts_converted); + EXPECT_TRUE(tp == tp_converted); + EXPECT_TRUE(tp == tp_converted_2); +} + +// gpr_inf_future is treated specially and mapped to/from time_point::max() +TEST_F(TimeTest, InfFuture) { + EXPECT_EQ(system_clock::time_point::max(), + Timespec2Timepoint(gpr_inf_future(GPR_CLOCK_REALTIME))); + gpr_timespec from_time_point_max; + Timepoint2Timespec(system_clock::time_point::max(), &from_time_point_max); + EXPECT_EQ( + 0, gpr_time_cmp(gpr_inf_future(GPR_CLOCK_REALTIME), from_time_point_max)); + // This will cause an overflow + Timepoint2Timespec( + std::chrono::time_point<system_clock, std::chrono::seconds>::max(), + &from_time_point_max); + EXPECT_EQ( + 0, gpr_time_cmp(gpr_inf_future(GPR_CLOCK_REALTIME), from_time_point_max)); +} + +} // namespace +} // namespace grpc + +int main(int argc, char** argv) { + grpc::testing::TestEnvironment env(argc, argv); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contrib/libs/grpc/test/cpp/util/ya.make b/contrib/libs/grpc/test/cpp/util/ya.make new file mode 100644 index 0000000000..f043cc5b14 --- /dev/null +++ b/contrib/libs/grpc/test/cpp/util/ya.make @@ -0,0 +1,39 @@ +LIBRARY() + +LICENSE(Apache-2.0) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +OWNER(orivej) + +PEERDIR( + contrib/libs/gflags + contrib/libs/protoc + contrib/libs/grpc/src/proto/grpc/reflection/v1alpha + contrib/restricted/googletest/googlemock + contrib/restricted/googletest/googletest +) + +ADDINCL( + ${ARCADIA_BUILD_ROOT}/contrib/libs/grpc + contrib/libs/grpc +) + +NO_COMPILER_WARNINGS() + +SRCS( + byte_buffer_proto_helper.cc + # grpc_cli_libs: + cli_call.cc + cli_credentials.cc + grpc_tool.cc + proto_file_parser.cc + service_describer.cc + string_ref_helper.cc + # grpc++_proto_reflection_desc_db: + proto_reflection_descriptor_database.cc + # grpc++_test_config: + test_config_cc.cc +) + +END() |