31 #include "absl/flags/flag.h"
32 #include "absl/memory/memory.h"
52 ABSL_FLAG(
bool,
l,
false,
"Use a long listing format");
54 "Use server types to parse and format messages");
56 "Metadata to send to server, in the form of key1:val1:key2:val2");
58 "Path to look for the proto file. "
61 ABSL_FLAG(
bool, binary_input,
false,
"Input in binary format");
62 ABSL_FLAG(
bool, binary_output,
false,
"Output in binary format");
64 "Default service config to use on the channel, if non-empty. Note "
65 "that this will be ignored if the name resolver returns a service "
67 ABSL_FLAG(
bool, display_peer_address,
false,
68 "Log the peer socket address of the connection that each RPC is made "
70 ABSL_FLAG(
bool, json_input,
false,
"Input in json format");
71 ABSL_FLAG(
bool, json_output,
false,
"Output in json format");
74 "Input contains multiple requests. Please do not use this to send "
75 "more than a few RPCs. gRPC CLI has very different performance "
76 "characteristics compared with normal RPC calls which make it "
77 "unsuitable for loadtesting or significant production traffic.");
80 "Specify timeout in seconds, used to set the deadline for all "
81 "RPCs. The default value of -1 means no deadline has been set.");
90 virtual ~GrpcTool() {}
92 bool Help(
int argc,
const char** argv,
const CliCredentials& cred,
94 bool CallMethod(
int argc,
const char** argv,
const CliCredentials& cred,
96 bool ListServices(
int argc,
const char** argv,
const CliCredentials& cred,
98 bool PrintType(
int argc,
const char** argv,
const CliCredentials& cred,
105 bool ParseMessage(
int argc,
const char** argv,
const CliCredentials& cred,
107 bool ToText(
int argc,
const char** argv,
const CliCredentials& cred,
109 bool ToJson(
int argc,
const char** argv,
const CliCredentials& cred,
111 bool ToBinary(
int argc,
const char** argv,
const CliCredentials& cred,
114 void SetPrintCommandMode(
int exit_status) {
126 template <
typename T>
129 BindWith5Args(
T&&
func) {
130 return std::bind(std::forward<T>(
func), std::placeholders::_1,
131 std::placeholders::_2, std::placeholders::_3,
132 std::placeholders::_4, std::placeholders::_5);
135 template <
typename T>
136 size_t ArraySize(
T& a) {
137 return ((
sizeof(a) /
sizeof(*(a))) /
138 static_cast<size_t>(!(
sizeof(a) %
sizeof(*(a)))));
141 void ParseMetadataFlag(
142 std::multimap<std::string, std::string>* client_metadata) {
146 std::vector<std::string>
fields;
147 const char delim =
':';
150 std::stringstream ss;
156 if (c == delim || c ==
escape) {
161 fprintf(
stderr,
"Failed to parse metadata flag.\n");
164 fields.push_back(ss.str());
172 fields.push_back(ss.str());
174 fprintf(
stderr,
"Failed to parse metadata flag.\n");
177 for (
size_t i = 0;
i <
fields.size();
i += 2) {
178 client_metadata->insert(
179 std::pair<std::string, std::string>(
fields[i],
fields[i + 1]));
183 template <
typename T>
190 for (
typename T::const_iterator
iter =
m.begin();
iter !=
m.end(); ++
iter) {
201 ProtoFileParser*
parser,
gpr_mu* parser_mu,
bool print_mode) {
203 std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata;
205 for (
bool receive_initial_metadata =
true;
call->ReadAndMaybeNotifyWrite(
206 &serialized_response_proto,
207 receive_initial_metadata ? &server_initial_metadata :
nullptr);
208 receive_initial_metadata =
false) {
209 fprintf(
stderr,
"got response.\n");
212 serialized_response_proto =
parser->GetFormattedStringFromMethod(
215 if (
parser->HasError() && print_mode) {
216 fprintf(
stderr,
"Failed to parse response.\n");
220 if (receive_initial_metadata) {
221 PrintMetadata(server_initial_metadata,
222 "Received initial metadata from server:");
224 if (!
callback(serialized_response_proto) && print_mode) {
225 fprintf(
stderr,
"Failed to output response.\n");
230 std::shared_ptr<grpc::Channel> CreateCliChannel(
233 if (!cred.GetSslTargetNameOverride().empty()) {
234 args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride());
255 const Command
ops[] = {
256 {
"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX},
257 {
"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3},
258 {
"list", BindWith5Args(&GrpcTool::ListServices), 1, 3},
259 {
"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3},
260 {
"type", BindWith5Args(&GrpcTool::PrintType), 2, 2},
262 {
"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
263 {
"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
264 {
"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3},
271 " grpc_cli ls ... ; List services\n"
272 " grpc_cli call ... ; Call method\n"
273 " grpc_cli type ... ; Print type\n"
274 " grpc_cli parse ... ; Parse message\n"
275 " grpc_cli totext ... ; Convert binary message to text\n"
276 " grpc_cli tojson ... ; Convert binary message to json\n"
277 " grpc_cli tobinary ... ; Convert text message to binary\n"
278 " grpc_cli help ... ; Print this message, or per-command usage\n"
286 for (
int i = 0; i < static_cast<int>(ArraySize(
ops));
i++) {
298 Usage(
"No command specified");
306 if (
cmd !=
nullptr) {
308 if (argc < cmd->
min_args || argc >
cmd->max_args) {
310 fprintf(
stderr,
"\nWrong number of arguments for %s\n",
command.c_str());
311 grpc_tool.SetPrintCommandMode(1);
312 return cmd->function(&grpc_tool, -1,
nullptr, cred,
callback);
314 const bool ok =
cmd->function(&grpc_tool, argc, argv, cred,
callback);
332 bool GrpcTool::Help(
int argc,
const char** argv,
const CliCredentials& cred,
336 " grpc_cli help [subcommand]\n");
341 const Command*
cmd = FindCommand(argv[0]);
342 if (
cmd ==
nullptr) {
345 SetPrintCommandMode(0);
351 bool GrpcTool::ListServices(
int argc,
const char** argv,
352 const CliCredentials& cred,
356 " grpc_cli ls <address> [<service>[/<method>]]\n"
357 " <address> ; host:port\n"
358 " <service> ; Exported service name\n"
359 " <method> ; Method name\n"
360 " --l ; Use a long listing format\n"
361 " --outfile ; Output filename (defaults to stdout)\n" +
362 cred.GetCredentialUsage());
365 std::shared_ptr<grpc::Channel>
channel =
370 std::vector<std::string> service_list;
371 if (!desc_db.GetServices(&service_list)) {
372 fprintf(
stderr,
"Received an error when querying services endpoint.\n");
384 for (
auto it = service_list.begin();
it != service_list.end();
it++) {
393 std::stringstream ss(argv[1]);
396 while (ss.peek() ==
'/') {
406 std::getline(ss, service_name,
'/');
409 if (std::getline(ss, service_name,
'/')) {
415 desc_pool.FindServiceByName(service_name);
429 fprintf(
stderr,
"Method %s not found in service %s.\n",
436 fprintf(
stderr,
"Service %s not found.\n", service_name.c_str());
440 desc_pool.FindMethodByName(service_name);
445 fprintf(
stderr,
"Service or method %s not found.\n",
446 service_name.c_str());
455 bool GrpcTool::PrintType(
int ,
const char** argv,
456 const CliCredentials& cred,
460 " grpc_cli type <address> <type>\n"
461 " <address> ; host:port\n"
462 " <type> ; Protocol buffer type name\n" +
463 cred.GetCredentialUsage());
466 std::shared_ptr<grpc::Channel>
channel =
473 desc_pool.FindMessageTypeByName(argv[1]);
477 fprintf(
stderr,
"Type %s not found.\n", argv[1]);
483 bool GrpcTool::CallMethod(
int argc,
const char** argv,
484 const CliCredentials& cred,
488 " grpc_cli call <address> <service>[.<method>] <request>\n"
489 " <address> ; host:port\n"
490 " <service> ; Exported service name\n"
491 " <method> ; Method name\n"
492 " <request> ; Text protobuffer (overrides infile)\n"
493 " --protofiles ; Comma separated proto files used as a"
494 " fallback when parsing request/response\n"
495 " --proto_path ; The search paths of proto files"
497 " separated), valid only when --protofiles is given\n"
498 " --noremotedb ; Don't attempt to use reflection service"
500 " --metadata ; The metadata to be sent to the server\n"
501 " --infile ; Input filename (defaults to stdin)\n"
502 " --outfile ; Output filename (defaults to stdout)\n"
503 " --binary_input ; Input in binary format\n"
504 " --binary_output ; Output in binary format\n"
505 " --json_input ; Input in json format\n"
506 " --json_output ; Output in json format\n"
507 " --timeout ; Specify timeout (in seconds), used to "
508 "set the deadline for RPCs. The default value of -1 means no "
509 "deadline has been set.\n" +
510 cred.GetCredentialUsage());
512 std::stringstream output_ss;
517 std::unique_ptr<ProtoFileParser>
parser;
521 bool print_mode =
false;
523 std::shared_ptr<grpc::Channel>
channel =
528 parser = absl::make_unique<grpc::testing::ProtoFileParser>(
534 "Failed to find remote reflection service and local proto files.\n");
544 fprintf(
stderr,
"Failed to find method %s in proto files.\n",
550 request_text = argv[2];
554 std::istream* input_stream;
555 std::ifstream input_file;
558 fprintf(
stderr,
"Batch mode for streaming RPC is not supported.\n");
562 std::multimap<std::string, std::string> client_metadata;
563 ParseMetadataFlag(&client_metadata);
564 PrintMetadata(client_metadata,
"Sending client initial metadata:");
566 CliCall
call(
channel, formatted_method_name, client_metadata, cli_args);
568 fprintf(
stderr,
"New call for method_name:%s has peer address:|%s|\n",
569 formatted_method_name.c_str(),
call.peer().c_str());
573 if (isatty(fileno(
stdin))) {
575 fprintf(
stderr,
"reading streaming request message from stdin...\n");
577 input_stream = &std::cin;
581 input_stream = &input_file;
587 parser.get(), &parser_mu, print_mode);
589 std::stringstream request_ss;
591 while (!request_text.empty() ||
592 (!input_stream->eof() && getline(*input_stream,
line))) {
593 if (!request_text.empty()) {
595 serialized_request_proto = request_text;
596 request_text.clear();
599 serialized_request_proto =
parser->GetSerializedProtoFromMethod(
602 request_text.clear();
605 fprintf(
stderr,
"Failed to parse request.\n");
613 call.WriteAndWait(serialized_request_proto);
615 fprintf(
stderr,
"Request sent.\n");
618 if (
line.length() == 0) {
619 request_text = request_ss.str();
623 request_ss <<
line <<
' ';
627 if (input_file.is_open()) {
631 call.WritesDoneAndWait();
635 std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata;
637 PrintMetadata(server_trailing_metadata,
638 "Received trailing metadata from server:");
641 fprintf(
stderr,
"Stream RPC succeeded with OK status\n");
644 fprintf(
stderr,
"Rpc failed with status code %d, error message: %s\n",
652 fprintf(
stderr,
"Batch mode for streaming RPC is not supported.\n");
656 std::istream* input_stream;
657 std::ifstream input_file;
660 if (isatty(fileno(
stdin))) {
662 fprintf(
stderr,
"reading request messages from stdin...\n");
664 input_stream = &std::cin;
668 input_stream = &input_file;
671 std::multimap<std::string, std::string> client_metadata;
672 ParseMetadataFlag(&client_metadata);
674 PrintMetadata(client_metadata,
"Sending client initial metadata:");
677 std::stringstream request_ss;
679 while (!request_text.empty() ||
680 (!input_stream->eof() && getline(*input_stream,
line))) {
681 if (!request_text.empty()) {
683 serialized_request_proto = request_text;
684 request_text.clear();
686 serialized_request_proto =
parser->GetSerializedProtoFromMethod(
689 request_text.clear();
692 fprintf(
stderr,
"Failed to parse request.\n");
699 std::multimap<grpc::string_ref, grpc::string_ref>
700 server_initial_metadata, server_trailing_metadata;
701 CliCall
call(
channel, formatted_method_name, client_metadata,
705 "New call for method_name:%s has peer address:|%s|\n",
706 formatted_method_name.c_str(),
call.peer().c_str());
708 call.Write(serialized_request_proto);
710 if (!
call.Read(&serialized_response_proto,
711 &server_initial_metadata)) {
712 fprintf(
stderr,
"Failed to read response.\n");
718 fprintf(
stderr,
"Rpc succeeded with OK status.\n");
719 PrintMetadata(server_initial_metadata,
720 "Received initial metadata from server:");
721 PrintMetadata(server_trailing_metadata,
722 "Received trailing metadata from server:");
726 if (!
callback(serialized_response_proto)) {
734 if (
parser->HasError() && print_mode) {
735 fprintf(
stderr,
"Failed to parse response.\n");
745 "Rpc failed with status code %d, error message: %s\n",
750 if (
line.length() == 0) {
751 request_text = request_ss.str();
755 request_ss <<
line <<
' ';
760 if (input_file.is_open()) {
769 fprintf(
stderr,
"warning: request given in argv, ignoring --infile\n");
772 std::stringstream input_stream;
774 if (isatty(fileno(
stdin))) {
775 fprintf(
stderr,
"reading request message from stdin...\n");
777 input_stream << std::cin.rdbuf();
781 input_stream << input_file.rdbuf();
784 request_text = input_stream.str();
788 serialized_request_proto = request_text;
790 serialized_request_proto =
parser->GetSerializedProtoFromMethod(
794 fprintf(
stderr,
"Failed to parse request.\n");
801 std::multimap<std::string, std::string> client_metadata;
802 std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
803 server_trailing_metadata;
804 ParseMetadataFlag(&client_metadata);
805 PrintMetadata(client_metadata,
"Sending client initial metadata:");
807 CliCall
call(
channel, formatted_method_name, client_metadata, cli_args);
809 fprintf(
stderr,
"New call for method_name:%s has peer address:|%s|\n",
810 formatted_method_name.c_str(),
call.peer().c_str());
812 call.Write(serialized_request_proto);
815 for (
bool receive_initial_metadata =
true;
call.Read(
816 &serialized_response_proto,
817 receive_initial_metadata ? &server_initial_metadata :
nullptr);
818 receive_initial_metadata =
false) {
820 serialized_response_proto =
parser->GetFormattedStringFromMethod(
824 fprintf(
stderr,
"Failed to parse response.\n");
829 if (receive_initial_metadata) {
830 PrintMetadata(server_initial_metadata,
831 "Received initial metadata from server:");
833 if (!
callback(serialized_response_proto)) {
838 PrintMetadata(server_trailing_metadata,
839 "Received trailing metadata from server:");
841 fprintf(
stderr,
"Rpc succeeded with OK status\n");
844 fprintf(
stderr,
"Rpc failed with status code %d, error message: %s\n",
853 const CliCredentials& cred,
857 " grpc_cli parse <address> <type> [<message>]\n"
858 " <address> ; host:port\n"
859 " <type> ; Protocol buffer type name\n"
860 " <message> ; Text protobuffer (overrides --infile)\n"
861 " --protofiles ; Comma separated proto files used as a"
862 " fallback when parsing request/response\n"
863 " --proto_path ; The search paths of proto files"
865 " separated), valid only when --protofiles is given\n"
866 " --noremotedb ; Don't attempt to use reflection service"
868 " --infile ; Input filename (defaults to stdin)\n"
869 " --outfile ; Output filename (defaults to stdout)\n"
870 " --binary_input ; Input in binary format\n"
871 " --binary_output ; Output in binary format\n"
872 " --json_input ; Input in json format\n"
873 " --json_output ; Output in json format\n" +
874 cred.GetCredentialUsage());
876 std::stringstream output_ss;
880 std::unique_ptr<grpc::testing::ProtoFileParser>
parser;
884 message_text = argv[2];
886 fprintf(
stderr,
"warning: message given in argv, ignoring --infile.\n");
889 std::stringstream input_stream;
891 if (isatty(fileno(
stdin))) {
892 fprintf(
stderr,
"reading request message from stdin...\n");
894 input_stream << std::cin.rdbuf();
898 input_stream << input_file.rdbuf();
901 message_text = input_stream.str();
906 std::shared_ptr<grpc::Channel>
channel =
908 parser = absl::make_unique<grpc::testing::ProtoFileParser>(
914 "Failed to find remote reflection service and local proto files.\n");
920 serialized_request_proto = message_text;
922 serialized_request_proto =
parser->GetSerializedProtoFromMessageType(
925 fprintf(
stderr,
"Failed to serialize the message.\n");
931 output_ss << serialized_request_proto;
934 output_text =
parser->GetFormattedStringFromMessageType(
937 fprintf(
stderr,
"Failed to deserialize the message.\n");
941 output_ss << output_text << std::endl;
947 bool GrpcTool::ToText(
int argc,
const char** argv,
const CliCredentials& cred,
950 "Convert binary message to text\n"
951 " grpc_cli totext <protofiles> <type>\n"
952 " <protofiles> ; Comma separated list of proto files\n"
953 " <type> ; Protocol buffer type name\n"
954 " --proto_path ; The search paths of proto files"
957 " --infile ; Input filename (defaults to stdin)\n"
958 " --outfile ; Output filename (defaults to stdout)\n");
967 bool GrpcTool::ToJson(
int argc,
const char** argv,
const CliCredentials& cred,
970 "Convert binary message to json\n"
971 " grpc_cli tojson <protofiles> <type>\n"
972 " <protofiles> ; Comma separated list of proto files\n"
973 " <type> ; Protocol buffer type name\n"
974 " --proto_path ; The search paths of proto files"
977 " --infile ; Input filename (defaults to stdin)\n"
978 " --outfile ; Output filename (defaults to stdout)\n");
988 bool GrpcTool::ToBinary(
int argc,
const char** argv,
const CliCredentials& cred,
991 "Convert text message to binary\n"
992 " grpc_cli tobinary <protofiles> <type> [<message>]\n"
993 " <protofiles> ; Comma separated list of proto files\n"
994 " <type> ; Protocol buffer type name\n"
995 " --proto_path ; The search paths of proto files"
998 " --infile ; Input filename (defaults to stdin)\n"
999 " --outfile ; Output filename (defaults to stdout)\n");