/* * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "absl/flags/flag.h" #include "absl/memory/memory.h" #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 #else #include #endif ABSL_FLAG(bool, l, false, "Use a long listing format"); ABSL_FLAG(bool, remotedb, true, "Use server types to parse and format messages"); ABSL_FLAG(std::string, metadata, "", "Metadata to send to server, in the form of key1:val1:key2:val2"); ABSL_FLAG(std::string, proto_path, ".", "Path to look for the proto file."); ABSL_FLAG(std::string, protofiles, "", "Name of the proto file."); ABSL_FLAG(bool, binary_input, false, "Input in binary format"); ABSL_FLAG(bool, binary_output, false, "Output in binary format"); ABSL_FLAG(std::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."); ABSL_FLAG(bool, display_peer_address, false, "Log the peer socket address of the connection that each RPC is made " "on to stderr."); ABSL_FLAG(bool, json_input, false, "Input in json format"); ABSL_FLAG(bool, json_output, false, "Output in json format"); ABSL_FLAG(std::string, infile, "", "Input file (default is stdin)"); ABSL_FLAG(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."); // TODO(Capstan): Consider using absl::Duration ABSL_FLAG(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 grpc { namespace testing { namespace { class GrpcTool { public: explicit GrpcTool(); virtual ~GrpcTool() {} bool Help(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); bool CallMethod(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); bool ListServices(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); bool PrintType(int argc, const char** argv, const CliCredentials& cred, const 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, const GrpcToolOutputCallback& callback); bool ToText(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); bool ToJson(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); bool ToBinary(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback); void SetPrintCommandMode(int exit_status) { print_command_usage_ = true; usage_exit_status_ = exit_status; } private: void CommandUsage(const std::string& usage) const; bool print_command_usage_; int usage_exit_status_; const std::string cred_usage_; }; template std::function BindWith5Args(T&& func) { return std::bind(std::forward(func), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); } template size_t ArraySize(T& a) { return ((sizeof(a) / sizeof(*(a))) / static_cast(!(sizeof(a) % sizeof(*(a))))); } void ParseMetadataFlag( std::multimap* client_metadata) { if (absl::GetFlag(FLAGS_metadata).empty()) { return; } std::vector fields; const char delim = ':'; const char escape = '\\'; size_t cur = -1; std::stringstream ss; while (++cur < absl::GetFlag(FLAGS_metadata).length()) { switch (absl::GetFlag(FLAGS_metadata).at(cur)) { case escape: if (cur < absl::GetFlag(FLAGS_metadata).length() - 1) { char c = absl::GetFlag(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 << absl::GetFlag(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(fields[i], fields[i + 1])); } } template void PrintMetadata(const T& m, const std::string& message) { if (m.empty()) { return; } fprintf(stderr, "%s\n", message.c_str()); std::string 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 std::string& method_name, const GrpcToolOutputCallback& callback, ProtoFileParser* parser, gpr_mu* parser_mu, bool print_mode) { std::string serialized_response_proto; std::multimap 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 (!absl::GetFlag(FLAGS_binary_output)) { gpr_mu_lock(parser_mu); serialized_response_proto = parser->GetFormattedStringFromMethod( method_name, serialized_response_proto, false /* is_request */, absl::GetFlag(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 CreateCliChannel( const std::string& server_address, const CliCredentials& cred) { grpc::ChannelArguments args; if (!cred.GetSslTargetNameOverride().empty()) { args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride()); } if (!absl::GetFlag(FLAGS_default_service_config).empty()) { args.SetString(GRPC_ARG_SERVICE_CONFIG, absl::GetFlag(FLAGS_default_service_config).c_str()); } // See |GRPC_ARG_MAX_METADATA_SIZE| in |grpc_types.h|. // Set to large enough size (10M) that should work for most use cases. args.SetInt(GRPC_ARG_MAX_METADATA_SIZE, 10 * 1024 * 1024); return ::grpc::CreateCustomChannel(server_address, cred.GetCredentials(), args); } struct Command { const char* command; std::function 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 std::string& 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 std::string& name) { for (int i = 0; i < static_cast(ArraySize(ops)); i++) { if (name == ops[i].command) { return &ops[i]; } } return nullptr; } } // namespace int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback) { if (argc < 2) { Usage("No command specified"); } std::string 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 '" + std::string(command.c_str()) + "'"); } return 1; } GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {} void GrpcTool::CommandUsage(const std::string& 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, const 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 '" + std::string(argv[0]) + "'"); } SetPrintCommandMode(0); cmd->function(this, -1, nullptr, cred, callback); } return true; } bool GrpcTool::ListServices(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback) { CommandUsage( "List services\n" " grpc_cli ls
[[/]]\n" "
; host:port\n" " ; Exported service name\n" " ; Method name\n" " --l ; Use a long listing format\n" " --outfile ; Output filename (defaults to stdout)\n" + cred.GetCredentialUsage()); std::string server_address(argv[0]); std::shared_ptr channel = CreateCliChannel(server_address, cred); grpc::ProtoReflectionDescriptorDatabase desc_db(channel); grpc::protobuf::DescriptorPool desc_pool(&desc_db); std::vector 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. std::string output; if (argc < 2) { // List all services, if --l is passed, then include full description, // otherwise include a summarized list only. if (absl::GetFlag(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(service_name); if (service != nullptr) { if (method_name.empty()) { output = absl::GetFlag(FLAGS_l) ? DescribeService(service) : SummarizeService(service); } else { method_name.insert(0, 1, '.'); method_name.insert(0, service_name); const grpc::protobuf::MethodDescriptor* method = desc_pool.FindMethodByName(method_name); if (method != nullptr) { output = absl::GetFlag(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(service_name); if (method != nullptr) { output = absl::GetFlag(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, const GrpcToolOutputCallback& callback) { CommandUsage( "Print type\n" " grpc_cli type
\n" "
; host:port\n" " ; Protocol buffer type name\n" + cred.GetCredentialUsage()); std::string server_address(argv[0]); std::shared_ptr channel = CreateCliChannel(server_address, cred); grpc::ProtoReflectionDescriptorDatabase desc_db(channel); grpc::protobuf::DescriptorPool desc_pool(&desc_db); std::string 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, const GrpcToolOutputCallback& callback) { CommandUsage( "Call method\n" " grpc_cli call
[.] \n" "
; host:port\n" " ; Exported service name\n" " ; Method name\n" " ; 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; std::string request_text; std::string server_address(argv[0]); std::string method_name(argv[1]); std::string formatted_method_name; std::unique_ptr parser; std::string serialized_request_proto; CliArgs cli_args; cli_args.timeout = absl::GetFlag(FLAGS_timeout); bool print_mode = false; std::shared_ptr channel = CreateCliChannel(server_address, cred); if (!absl::GetFlag(FLAGS_binary_input) || !absl::GetFlag(FLAGS_binary_output)) { parser = absl::make_unique( absl::GetFlag(FLAGS_remotedb) ? channel : nullptr, absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles)); if (parser->HasError()) { fprintf( stderr, "Failed to find remote reflection service and local proto files.\n"); return false; } } if (absl::GetFlag(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 (absl::GetFlag(FLAGS_batch)) { fprintf(stderr, "Batch mode for streaming RPC is not supported.\n"); return false; } std::multimap client_metadata; ParseMetadataFlag(&client_metadata); PrintMetadata(client_metadata, "Sending client initial metadata:"); CliCall call(channel, formatted_method_name, client_metadata, cli_args); if (absl::GetFlag(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 (absl::GetFlag(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(absl::GetFlag(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 (absl::GetFlag(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 */, absl::GetFlag(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(std::string()); 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 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 (absl::GetFlag(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 (absl::GetFlag(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(absl::GetFlag(FLAGS_infile), std::ios::in | std::ios::binary); input_stream = &input_file; } std::multimap 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 (absl::GetFlag(FLAGS_binary_input)) { serialized_request_proto = request_text; request_text.clear(); } else { serialized_request_proto = parser->GetSerializedProtoFromMethod( method_name, request_text, true /* is_request */, absl::GetFlag(FLAGS_json_input)); request_text.clear(); if (parser->HasError()) { if (print_mode) { fprintf(stderr, "Failed to parse request.\n"); } continue; } } std::string serialized_response_proto; std::multimap server_initial_metadata, server_trailing_metadata; CliCall call(channel, formatted_method_name, client_metadata, cli_args); if (absl::GetFlag(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 (absl::GetFlag(FLAGS_binary_output)) { if (!callback(serialized_response_proto)) { break; } } else { std::string response_text = parser->GetFormattedStringFromMethod( method_name, serialized_response_proto, false /* is_request */, absl::GetFlag(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(std::string()); request_ss.clear(); } else { request_ss << line << ' '; } } } if (input_file.is_open()) { input_file.close(); } return true; } if (argc == 3) { if (!absl::GetFlag(FLAGS_infile).empty()) { fprintf(stderr, "warning: request given in argv, ignoring --infile\n"); } } else { std::stringstream input_stream; if (absl::GetFlag(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(absl::GetFlag(FLAGS_infile), std::ios::in | std::ios::binary); input_stream << input_file.rdbuf(); input_file.close(); } request_text = input_stream.str(); } if (absl::GetFlag(FLAGS_binary_input)) { serialized_request_proto = request_text; } else { serialized_request_proto = parser->GetSerializedProtoFromMethod( method_name, request_text, true /* is_request */, absl::GetFlag(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()); std::string serialized_response_proto; std::multimap client_metadata; std::multimap 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 (absl::GetFlag(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 (!absl::GetFlag(FLAGS_binary_output)) { serialized_response_proto = parser->GetFormattedStringFromMethod( method_name, serialized_response_proto, false /* is_request */, absl::GetFlag(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, const GrpcToolOutputCallback& callback) { CommandUsage( "Parse message\n" " grpc_cli parse
[]\n" "
; host:port\n" " ; Protocol buffer type name\n" " ; 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; std::string message_text; std::string server_address(argv[0]); std::string type_name(argv[1]); std::unique_ptr parser; std::string serialized_request_proto; if (argc == 3) { message_text = argv[2]; if (!absl::GetFlag(FLAGS_infile).empty()) { fprintf(stderr, "warning: message given in argv, ignoring --infile.\n"); } } else { std::stringstream input_stream; if (absl::GetFlag(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(absl::GetFlag(FLAGS_infile), std::ios::in | std::ios::binary); input_stream << input_file.rdbuf(); input_file.close(); } message_text = input_stream.str(); } if (!absl::GetFlag(FLAGS_binary_input) || !absl::GetFlag(FLAGS_binary_output)) { std::shared_ptr channel = CreateCliChannel(server_address, cred); parser = absl::make_unique( absl::GetFlag(FLAGS_remotedb) ? channel : nullptr, absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles)); if (parser->HasError()) { fprintf( stderr, "Failed to find remote reflection service and local proto files.\n"); return false; } } if (absl::GetFlag(FLAGS_binary_input)) { serialized_request_proto = message_text; } else { serialized_request_proto = parser->GetSerializedProtoFromMessageType( type_name, message_text, absl::GetFlag(FLAGS_json_input)); if (parser->HasError()) { fprintf(stderr, "Failed to serialize the message.\n"); return false; } } if (absl::GetFlag(FLAGS_binary_output)) { output_ss << serialized_request_proto; } else { std::string output_text; output_text = parser->GetFormattedStringFromMessageType( type_name, serialized_request_proto, absl::GetFlag(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, const GrpcToolOutputCallback& callback) { CommandUsage( "Convert binary message to text\n" " grpc_cli totext \n" " ; Comma separated list of proto files\n" " ; 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"); absl::SetFlag(&FLAGS_protofiles, argv[0]); absl::SetFlag(&FLAGS_remotedb, false); absl::SetFlag(&FLAGS_binary_input, true); absl::SetFlag(&FLAGS_binary_output, false); return ParseMessage(argc, argv, cred, callback); } bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback) { CommandUsage( "Convert binary message to json\n" " grpc_cli tojson \n" " ; Comma separated list of proto files\n" " ; 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"); absl::SetFlag(&FLAGS_protofiles, argv[0]); absl::SetFlag(&FLAGS_remotedb, false); absl::SetFlag(&FLAGS_binary_input, true); absl::SetFlag(&FLAGS_binary_output, false); absl::SetFlag(&FLAGS_json_output, true); return ParseMessage(argc, argv, cred, callback); } bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred, const GrpcToolOutputCallback& callback) { CommandUsage( "Convert text message to binary\n" " grpc_cli tobinary []\n" " ; Comma separated list of proto files\n" " ; 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"); absl::SetFlag(&FLAGS_protofiles, argv[0]); absl::SetFlag(&FLAGS_remotedb, false); absl::SetFlag(&FLAGS_binary_input, false); absl::SetFlag(&FLAGS_binary_output, true); return ParseMessage(argc, argv, cred, callback); } } // namespace testing } // namespace grpc