3 * Copyright 2016 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include "test/cpp/util/grpc_tool.h"
29 #include <gflags/gflags.h>
30 #include <grpc/grpc.h>
31 #include <grpc/support/port_platform.h>
32 #include <grpcpp/channel.h>
33 #include <grpcpp/create_channel.h>
34 #include <grpcpp/grpcpp.h>
35 #include <grpcpp/security/credentials.h>
36 #include <grpcpp/support/string_ref.h>
38 #include "test/cpp/util/cli_call.h"
39 #include "test/cpp/util/proto_file_parser.h"
40 #include "test/cpp/util/proto_reflection_descriptor_database.h"
41 #include "test/cpp/util/service_describer.h"
52 DEFINE_bool(l, false, "Use a long listing format");
53 DEFINE_bool(remotedb, true, "Use server types to parse and format messages");
54 DEFINE_string(metadata, "",
55 "Metadata to send to server, in the form of key1:val1:key2:val2");
56 DEFINE_string(proto_path, ".", "Path to look for the proto file.");
57 DEFINE_string(protofiles, "", "Name of the proto file.");
58 DEFINE_bool(binary_input, false, "Input in binary format");
59 DEFINE_bool(binary_output, false, "Output in binary format");
60 DEFINE_bool(json_input, false, "Input in json format");
61 DEFINE_bool(json_output, false, "Output in json format");
62 DEFINE_string(infile, "", "Input file (default is stdin)");
63 DEFINE_bool(batch, false,
64 "Input contains multiple requests. Please do not use this to send "
65 "more than a few RPCs. gRPC CLI has very different performance "
66 "characteristics compared with normal RPC calls which make it "
67 "unsuitable for loadtesting or significant production traffic.");
74 virtual ~GrpcTool() {}
76 bool Help(int argc, const char** argv, const CliCredentials& cred,
77 GrpcToolOutputCallback callback);
78 bool CallMethod(int argc, const char** argv, const CliCredentials& cred,
79 GrpcToolOutputCallback callback);
80 bool ListServices(int argc, const char** argv, const CliCredentials& cred,
81 GrpcToolOutputCallback callback);
82 bool PrintType(int argc, const char** argv, const CliCredentials& cred,
83 GrpcToolOutputCallback callback);
84 // TODO(zyc): implement the following methods
85 // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback
87 // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback
89 bool ParseMessage(int argc, const char** argv, const CliCredentials& cred,
90 GrpcToolOutputCallback callback);
91 bool ToText(int argc, const char** argv, const CliCredentials& cred,
92 GrpcToolOutputCallback callback);
93 bool ToJson(int argc, const char** argv, const CliCredentials& cred,
94 GrpcToolOutputCallback callback);
95 bool ToBinary(int argc, const char** argv, const CliCredentials& cred,
96 GrpcToolOutputCallback callback);
98 void SetPrintCommandMode(int exit_status) {
99 print_command_usage_ = true;
100 usage_exit_status_ = exit_status;
104 void CommandUsage(const grpc::string& usage) const;
105 bool print_command_usage_;
106 int usage_exit_status_;
107 const grpc::string cred_usage_;
110 template <typename T>
111 std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
112 GrpcToolOutputCallback)>
113 BindWith5Args(T&& func) {
114 return std::bind(std::forward<T>(func), std::placeholders::_1,
115 std::placeholders::_2, std::placeholders::_3,
116 std::placeholders::_4, std::placeholders::_5);
119 template <typename T>
120 size_t ArraySize(T& a) {
121 return ((sizeof(a) / sizeof(*(a))) /
122 static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
125 void ParseMetadataFlag(
126 std::multimap<grpc::string, grpc::string>* client_metadata) {
127 if (FLAGS_metadata.empty()) {
130 std::vector<grpc::string> fields;
131 const char delim = ':';
132 const char escape = '\\';
134 std::stringstream ss;
135 while (++cur < FLAGS_metadata.length()) {
136 switch (FLAGS_metadata.at(cur)) {
138 if (cur < FLAGS_metadata.length() - 1) {
139 char c = FLAGS_metadata.at(++cur);
140 if (c == delim || c == escape) {
145 fprintf(stderr, "Failed to parse metadata flag.\n");
148 fields.push_back(ss.str());
153 ss << FLAGS_metadata.at(cur);
156 fields.push_back(ss.str());
157 if (fields.size() % 2) {
158 fprintf(stderr, "Failed to parse metadata flag.\n");
161 for (size_t i = 0; i < fields.size(); i += 2) {
162 client_metadata->insert(
163 std::pair<grpc::string, grpc::string>(fields[i], fields[i + 1]));
167 template <typename T>
168 void PrintMetadata(const T& m, const grpc::string& message) {
172 fprintf(stderr, "%s\n", message.c_str());
174 for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
176 pair.append(iter->first.data(), iter->first.size());
178 pair.append(iter->second.data(), iter->second.size());
179 fprintf(stderr, "%s\n", pair.c_str());
183 void ReadResponse(CliCall* call, const grpc::string& method_name,
184 GrpcToolOutputCallback callback, ProtoFileParser* parser,
185 gpr_mu* parser_mu, bool print_mode) {
186 grpc::string serialized_response_proto;
187 std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata;
189 for (bool receive_initial_metadata = true; call->ReadAndMaybeNotifyWrite(
190 &serialized_response_proto,
191 receive_initial_metadata ? &server_initial_metadata : nullptr);
192 receive_initial_metadata = false) {
193 fprintf(stderr, "got response.\n");
194 if (!FLAGS_binary_output) {
195 gpr_mu_lock(parser_mu);
196 serialized_response_proto = parser->GetFormattedStringFromMethod(
197 method_name, serialized_response_proto, false /* is_request */,
199 if (parser->HasError() && print_mode) {
200 fprintf(stderr, "Failed to parse response.\n");
202 gpr_mu_unlock(parser_mu);
204 if (receive_initial_metadata) {
205 PrintMetadata(server_initial_metadata,
206 "Received initial metadata from server:");
208 if (!callback(serialized_response_proto) && print_mode) {
209 fprintf(stderr, "Failed to output response.\n");
214 std::shared_ptr<grpc::Channel> CreateCliChannel(
215 const grpc::string& server_address, const CliCredentials& cred) {
216 grpc::ChannelArguments args;
217 if (!cred.GetSslTargetNameOverride().empty()) {
218 args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride());
220 return grpc::CreateCustomChannel(server_address, cred.GetCredentials(), args);
225 std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
226 GrpcToolOutputCallback)>
232 const Command ops[] = {
233 {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX},
234 {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3},
235 {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3},
236 {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3},
237 {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2},
238 {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
239 {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
240 {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
241 {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3},
244 void Usage(const grpc::string& msg) {
248 " grpc_cli ls ... ; List services\n"
249 " grpc_cli call ... ; Call method\n"
250 " grpc_cli type ... ; Print type\n"
251 " grpc_cli parse ... ; Parse message\n"
252 " grpc_cli totext ... ; Convert binary message to text\n"
253 " grpc_cli tojson ... ; Convert binary message to json\n"
254 " grpc_cli tobinary ... ; Convert text message to binary\n"
255 " grpc_cli help ... ; Print this message, or per-command usage\n"
262 const Command* FindCommand(const grpc::string& name) {
263 for (int i = 0; i < (int)ArraySize(ops); i++) {
264 if (name == ops[i].command) {
272 int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred,
273 GrpcToolOutputCallback callback) {
275 Usage("No command specified");
278 grpc::string command = argv[1];
282 const Command* cmd = FindCommand(command);
283 if (cmd != nullptr) {
285 if (argc < cmd->min_args || argc > cmd->max_args) {
286 // Force the command to print its usage message
287 fprintf(stderr, "\nWrong number of arguments for %s\n", command.c_str());
288 grpc_tool.SetPrintCommandMode(1);
289 return cmd->function(&grpc_tool, -1, nullptr, cred, callback);
291 const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback);
294 Usage("Invalid command '" + grpc::string(command.c_str()) + "'");
299 GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {}
301 void GrpcTool::CommandUsage(const grpc::string& usage) const {
302 if (print_command_usage_) {
303 fprintf(stderr, "\n%s%s\n", usage.c_str(),
304 (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : "");
305 exit(usage_exit_status_);
309 bool GrpcTool::Help(int argc, const char** argv, const CliCredentials& cred,
310 GrpcToolOutputCallback callback) {
313 " grpc_cli help [subcommand]\n");
318 const Command* cmd = FindCommand(argv[0]);
319 if (cmd == nullptr) {
320 Usage("Unknown command '" + grpc::string(argv[0]) + "'");
322 SetPrintCommandMode(0);
323 cmd->function(this, -1, nullptr, cred, callback);
328 bool GrpcTool::ListServices(int argc, const char** argv,
329 const CliCredentials& cred,
330 GrpcToolOutputCallback callback) {
333 " grpc_cli ls <address> [<service>[/<method>]]\n"
334 " <address> ; host:port\n"
335 " <service> ; Exported service name\n"
336 " <method> ; Method name\n"
337 " --l ; Use a long listing format\n"
338 " --outfile ; Output filename (defaults to stdout)\n" +
339 cred.GetCredentialUsage());
341 grpc::string server_address(argv[0]);
342 std::shared_ptr<grpc::Channel> channel =
343 CreateCliChannel(server_address, cred);
344 grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
345 grpc::protobuf::DescriptorPool desc_pool(&desc_db);
347 std::vector<grpc::string> service_list;
348 if (!desc_db.GetServices(&service_list)) {
349 fprintf(stderr, "Received an error when querying services endpoint.\n");
353 // If no service is specified, dump the list of services.
356 // List all services, if --l is passed, then include full description,
357 // otherwise include a summarized list only.
359 output = DescribeServiceList(service_list, desc_pool);
361 for (auto it = service_list.begin(); it != service_list.end(); it++) {
362 auto const& service = *it;
363 output.append(service);
368 grpc::string service_name;
369 grpc::string method_name;
370 std::stringstream ss(argv[1]);
372 // Remove leading slashes.
373 while (ss.peek() == '/') {
377 // Parse service and method names. Support the following patterns:
383 std::getline(ss, service_name, '/');
384 method_name = argv[2];
386 if (std::getline(ss, service_name, '/')) {
387 std::getline(ss, method_name);
391 const grpc::protobuf::ServiceDescriptor* service =
392 desc_pool.FindServiceByName(service_name);
393 if (service != nullptr) {
394 if (method_name.empty()) {
395 output = FLAGS_l ? DescribeService(service) : SummarizeService(service);
397 method_name.insert(0, 1, '.');
398 method_name.insert(0, service_name);
399 const grpc::protobuf::MethodDescriptor* method =
400 desc_pool.FindMethodByName(method_name);
401 if (method != nullptr) {
402 output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method);
404 fprintf(stderr, "Method %s not found in service %s.\n",
405 method_name.c_str(), service_name.c_str());
410 if (!method_name.empty()) {
411 fprintf(stderr, "Service %s not found.\n", service_name.c_str());
414 const grpc::protobuf::MethodDescriptor* method =
415 desc_pool.FindMethodByName(service_name);
416 if (method != nullptr) {
417 output = FLAGS_l ? DescribeMethod(method) : SummarizeMethod(method);
419 fprintf(stderr, "Service or method %s not found.\n",
420 service_name.c_str());
426 return callback(output);
429 bool GrpcTool::PrintType(int argc, const char** argv,
430 const CliCredentials& cred,
431 GrpcToolOutputCallback callback) {
434 " grpc_cli type <address> <type>\n"
435 " <address> ; host:port\n"
436 " <type> ; Protocol buffer type name\n" +
437 cred.GetCredentialUsage());
439 grpc::string server_address(argv[0]);
440 std::shared_ptr<grpc::Channel> channel =
441 CreateCliChannel(server_address, cred);
442 grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
443 grpc::protobuf::DescriptorPool desc_pool(&desc_db);
446 const grpc::protobuf::Descriptor* descriptor =
447 desc_pool.FindMessageTypeByName(argv[1]);
448 if (descriptor != nullptr) {
449 output = descriptor->DebugString();
451 fprintf(stderr, "Type %s not found.\n", argv[1]);
454 return callback(output);
457 bool GrpcTool::CallMethod(int argc, const char** argv,
458 const CliCredentials& cred,
459 GrpcToolOutputCallback callback) {
462 " grpc_cli call <address> <service>[.<method>] <request>\n"
463 " <address> ; host:port\n"
464 " <service> ; Exported service name\n"
465 " <method> ; Method name\n"
466 " <request> ; Text protobuffer (overrides infile)\n"
467 " --protofiles ; Comma separated proto files used as a"
468 " fallback when parsing request/response\n"
469 " --proto_path ; The search path of proto files, valid"
470 " only when --protofiles is given\n"
471 " --metadata ; The metadata to be sent to the server\n"
472 " --infile ; Input filename (defaults to stdin)\n"
473 " --outfile ; Output filename (defaults to stdout)\n"
474 " --binary_input ; Input in binary format\n"
475 " --binary_output ; Output in binary format\n"
476 " --json_input ; Input in json format\n"
477 " --json_output ; Output in json format\n" +
478 cred.GetCredentialUsage());
480 std::stringstream output_ss;
481 grpc::string request_text;
482 grpc::string server_address(argv[0]);
483 grpc::string method_name(argv[1]);
484 grpc::string formatted_method_name;
485 std::unique_ptr<ProtoFileParser> parser;
486 grpc::string serialized_request_proto;
487 bool print_mode = false;
489 std::shared_ptr<grpc::Channel> channel =
490 CreateCliChannel(server_address, cred);
492 if (!FLAGS_binary_input || !FLAGS_binary_output) {
494 new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
495 FLAGS_proto_path, FLAGS_protofiles));
496 if (parser->HasError()) {
499 "Failed to find remote reflection service and local proto files.\n");
504 if (FLAGS_binary_input) {
505 formatted_method_name = method_name;
507 formatted_method_name = parser->GetFormattedMethodName(method_name);
508 if (parser->HasError()) {
509 fprintf(stderr, "Failed to find method %s in proto files.\n",
510 method_name.c_str());
515 request_text = argv[2];
518 if (parser->IsStreaming(method_name, true /* is_request */)) {
519 std::istream* input_stream;
520 std::ifstream input_file;
523 fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
527 std::multimap<grpc::string, grpc::string> client_metadata;
528 ParseMetadataFlag(&client_metadata);
529 PrintMetadata(client_metadata, "Sending client initial metadata:");
531 CliCall call(channel, formatted_method_name, client_metadata);
533 if (FLAGS_infile.empty()) {
534 if (isatty(fileno(stdin))) {
536 fprintf(stderr, "reading streaming request message from stdin...\n");
538 input_stream = &std::cin;
540 input_file.open(FLAGS_infile, std::ios::in | std::ios::binary);
541 input_stream = &input_file;
545 gpr_mu_init(&parser_mu);
546 std::thread read_thread(ReadResponse, &call, method_name, callback,
547 parser.get(), &parser_mu, print_mode);
549 std::stringstream request_ss;
551 while (!request_text.empty() ||
552 (!input_stream->eof() && getline(*input_stream, line))) {
553 if (!request_text.empty()) {
554 if (FLAGS_binary_input) {
555 serialized_request_proto = request_text;
556 request_text.clear();
558 gpr_mu_lock(&parser_mu);
559 serialized_request_proto = parser->GetSerializedProtoFromMethod(
560 method_name, request_text, true /* is_request */,
562 request_text.clear();
563 if (parser->HasError()) {
565 fprintf(stderr, "Failed to parse request.\n");
567 gpr_mu_unlock(&parser_mu);
570 gpr_mu_unlock(&parser_mu);
573 call.WriteAndWait(serialized_request_proto);
575 fprintf(stderr, "Request sent.\n");
578 if (line.length() == 0) {
579 request_text = request_ss.str();
580 request_ss.str(grpc::string());
583 request_ss << line << ' ';
587 if (input_file.is_open()) {
591 call.WritesDoneAndWait();
593 gpr_mu_destroy(&parser_mu);
595 std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata;
596 Status status = call.Finish(&server_trailing_metadata);
597 PrintMetadata(server_trailing_metadata,
598 "Received trailing metadata from server:");
601 fprintf(stderr, "Stream RPC succeeded with OK status\n");
604 fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
605 status.error_code(), status.error_message().c_str());
609 } else { // parser->IsStreaming(method_name, true /* is_request */)
611 if (parser->IsStreaming(method_name, false /* is_request */)) {
612 fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
616 std::istream* input_stream;
617 std::ifstream input_file;
619 if (FLAGS_infile.empty()) {
620 if (isatty(fileno(stdin))) {
622 fprintf(stderr, "reading request messages from stdin...\n");
624 input_stream = &std::cin;
626 input_file.open(FLAGS_infile, std::ios::in | std::ios::binary);
627 input_stream = &input_file;
630 std::multimap<grpc::string, grpc::string> client_metadata;
631 ParseMetadataFlag(&client_metadata);
633 PrintMetadata(client_metadata, "Sending client initial metadata:");
636 std::stringstream request_ss;
638 while (!request_text.empty() ||
639 (!input_stream->eof() && getline(*input_stream, line))) {
640 if (!request_text.empty()) {
641 if (FLAGS_binary_input) {
642 serialized_request_proto = request_text;
643 request_text.clear();
645 serialized_request_proto = parser->GetSerializedProtoFromMethod(
646 method_name, request_text, true /* is_request */,
648 request_text.clear();
649 if (parser->HasError()) {
651 fprintf(stderr, "Failed to parse request.\n");
657 grpc::string serialized_response_proto;
658 std::multimap<grpc::string_ref, grpc::string_ref>
659 server_initial_metadata, server_trailing_metadata;
660 CliCall call(channel, formatted_method_name, client_metadata);
661 call.Write(serialized_request_proto);
663 if (!call.Read(&serialized_response_proto,
664 &server_initial_metadata)) {
665 fprintf(stderr, "Failed to read response.\n");
667 Status status = call.Finish(&server_trailing_metadata);
671 fprintf(stderr, "Rpc succeeded with OK status.\n");
672 PrintMetadata(server_initial_metadata,
673 "Received initial metadata from server:");
674 PrintMetadata(server_trailing_metadata,
675 "Received trailing metadata from server:");
678 if (FLAGS_binary_output) {
679 if (!callback(serialized_response_proto)) {
683 grpc::string response_text = parser->GetFormattedStringFromMethod(
684 method_name, serialized_response_proto,
685 false /* is_request */, FLAGS_json_output);
687 if (parser->HasError() && print_mode) {
688 fprintf(stderr, "Failed to parse response.\n");
690 if (!callback(response_text)) {
698 "Rpc failed with status code %d, error message: %s\n",
699 status.error_code(), status.error_message().c_str());
703 if (line.length() == 0) {
704 request_text = request_ss.str();
705 request_ss.str(grpc::string());
708 request_ss << line << ' ';
713 if (input_file.is_open()) {
721 if (!FLAGS_infile.empty()) {
722 fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
725 std::stringstream input_stream;
726 if (FLAGS_infile.empty()) {
727 if (isatty(fileno(stdin))) {
728 fprintf(stderr, "reading request message from stdin...\n");
730 input_stream << std::cin.rdbuf();
732 std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary);
733 input_stream << input_file.rdbuf();
736 request_text = input_stream.str();
739 if (FLAGS_binary_input) {
740 serialized_request_proto = request_text;
742 serialized_request_proto = parser->GetSerializedProtoFromMethod(
743 method_name, request_text, true /* is_request */, FLAGS_json_input);
744 if (parser->HasError()) {
745 fprintf(stderr, "Failed to parse request.\n");
749 fprintf(stderr, "connecting to %s\n", server_address.c_str());
751 grpc::string serialized_response_proto;
752 std::multimap<grpc::string, grpc::string> client_metadata;
753 std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
754 server_trailing_metadata;
755 ParseMetadataFlag(&client_metadata);
756 PrintMetadata(client_metadata, "Sending client initial metadata:");
758 CliCall call(channel, formatted_method_name, client_metadata);
759 call.Write(serialized_request_proto);
762 for (bool receive_initial_metadata = true; call.Read(
763 &serialized_response_proto,
764 receive_initial_metadata ? &server_initial_metadata : nullptr);
765 receive_initial_metadata = false) {
766 if (!FLAGS_binary_output) {
767 serialized_response_proto = parser->GetFormattedStringFromMethod(
768 method_name, serialized_response_proto, false /* is_request */,
770 if (parser->HasError()) {
771 fprintf(stderr, "Failed to parse response.\n");
776 if (receive_initial_metadata) {
777 PrintMetadata(server_initial_metadata,
778 "Received initial metadata from server:");
780 if (!callback(serialized_response_proto)) {
784 Status status = call.Finish(&server_trailing_metadata);
785 PrintMetadata(server_trailing_metadata,
786 "Received trailing metadata from server:");
788 fprintf(stderr, "Rpc succeeded with OK status\n");
791 fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
792 status.error_code(), status.error_message().c_str());
796 GPR_UNREACHABLE_CODE(return false);
799 bool GrpcTool::ParseMessage(int argc, const char** argv,
800 const CliCredentials& cred,
801 GrpcToolOutputCallback callback) {
804 " grpc_cli parse <address> <type> [<message>]\n"
805 " <address> ; host:port\n"
806 " <type> ; Protocol buffer type name\n"
807 " <message> ; Text protobuffer (overrides --infile)\n"
808 " --protofiles ; Comma separated proto files used as a"
809 " fallback when parsing request/response\n"
810 " --proto_path ; The search path of proto files, valid"
811 " only when --protofiles is given\n"
812 " --infile ; Input filename (defaults to stdin)\n"
813 " --outfile ; Output filename (defaults to stdout)\n"
814 " --binary_input ; Input in binary format\n"
815 " --binary_output ; Output in binary format\n"
816 " --json_input ; Input in json format\n"
817 " --json_output ; Output in json format\n" +
818 cred.GetCredentialUsage());
820 std::stringstream output_ss;
821 grpc::string message_text;
822 grpc::string server_address(argv[0]);
823 grpc::string type_name(argv[1]);
824 std::unique_ptr<grpc::testing::ProtoFileParser> parser;
825 grpc::string serialized_request_proto;
828 message_text = argv[2];
829 if (!FLAGS_infile.empty()) {
830 fprintf(stderr, "warning: message given in argv, ignoring --infile.\n");
833 std::stringstream input_stream;
834 if (FLAGS_infile.empty()) {
835 if (isatty(fileno(stdin))) {
836 fprintf(stderr, "reading request message from stdin...\n");
838 input_stream << std::cin.rdbuf();
840 std::ifstream input_file(FLAGS_infile, std::ios::in | std::ios::binary);
841 input_stream << input_file.rdbuf();
844 message_text = input_stream.str();
847 if (!FLAGS_binary_input || !FLAGS_binary_output) {
848 std::shared_ptr<grpc::Channel> channel =
849 CreateCliChannel(server_address, cred);
851 new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
852 FLAGS_proto_path, FLAGS_protofiles));
853 if (parser->HasError()) {
856 "Failed to find remote reflection service and local proto files.\n");
861 if (FLAGS_binary_input) {
862 serialized_request_proto = message_text;
864 serialized_request_proto = parser->GetSerializedProtoFromMessageType(
865 type_name, message_text, FLAGS_json_input);
866 if (parser->HasError()) {
867 fprintf(stderr, "Failed to serialize the message.\n");
872 if (FLAGS_binary_output) {
873 output_ss << serialized_request_proto;
875 grpc::string output_text;
876 output_text = parser->GetFormattedStringFromMessageType(
877 type_name, serialized_request_proto, FLAGS_json_output);
878 if (parser->HasError()) {
879 fprintf(stderr, "Failed to deserialize the message.\n");
883 output_ss << output_text << std::endl;
886 return callback(output_ss.str());
889 bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred,
890 GrpcToolOutputCallback callback) {
892 "Convert binary message to text\n"
893 " grpc_cli totext <protofiles> <type>\n"
894 " <protofiles> ; Comma separated list of proto files\n"
895 " <type> ; Protocol buffer type name\n"
896 " --proto_path ; The search path of proto files\n"
897 " --infile ; Input filename (defaults to stdin)\n"
898 " --outfile ; Output filename (defaults to stdout)\n");
900 FLAGS_protofiles = argv[0];
901 FLAGS_remotedb = false;
902 FLAGS_binary_input = true;
903 FLAGS_binary_output = false;
904 return ParseMessage(argc, argv, cred, callback);
907 bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred,
908 GrpcToolOutputCallback callback) {
910 "Convert binary message to json\n"
911 " grpc_cli tojson <protofiles> <type>\n"
912 " <protofiles> ; Comma separated list of proto files\n"
913 " <type> ; Protocol buffer type name\n"
914 " --proto_path ; The search path of proto files\n"
915 " --infile ; Input filename (defaults to stdin)\n"
916 " --outfile ; Output filename (defaults to stdout)\n");
918 FLAGS_protofiles = argv[0];
919 FLAGS_remotedb = false;
920 FLAGS_binary_input = true;
921 FLAGS_binary_output = false;
922 FLAGS_json_output = true;
923 return ParseMessage(argc, argv, cred, callback);
926 bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred,
927 GrpcToolOutputCallback callback) {
929 "Convert text message to binary\n"
930 " grpc_cli tobinary <protofiles> <type> [<message>]\n"
931 " <protofiles> ; Comma separated list of proto files\n"
932 " <type> ; Protocol buffer type name\n"
933 " --proto_path ; The search path of proto files\n"
934 " --infile ; Input filename (defaults to stdin)\n"
935 " --outfile ; Output filename (defaults to stdout)\n");
937 FLAGS_protofiles = argv[0];
938 FLAGS_remotedb = false;
939 FLAGS_binary_input = false;
940 FLAGS_binary_output = true;
941 return ParseMessage(argc, argv, cred, callback);
944 } // namespace testing