grpc_tool.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2016 gRPC authors.
4  *
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
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18 
20 
22 
23 #include <cstdio>
24 #include <fstream>
25 #include <iostream>
26 #include <memory>
27 #include <sstream>
28 #include <string>
29 #include <thread>
30 
31 #include "absl/flags/flag.h"
32 #include "absl/memory/memory.h"
33 
34 #include <grpc/grpc.h>
35 #include <grpcpp/channel.h>
36 #include <grpcpp/create_channel.h>
37 #include <grpcpp/grpcpp.h>
40 
41 #include "test/cpp/util/cli_call.h"
45 
46 #if GPR_WINDOWS
47 #include <io.h>
48 #else
49 #include <unistd.h>
50 #endif
51 
52 ABSL_FLAG(bool, l, false, "Use a long listing format");
53 ABSL_FLAG(bool, remotedb, true,
54  "Use server types to parse and format messages");
56  "Metadata to send to server, in the form of key1:val1:key2:val2");
57 ABSL_FLAG(std::string, proto_path, ".",
58  "Path to look for the proto file. "
59  "Multiple paths can be separated by " GRPC_CLI_PATH_SEPARATOR);
60 ABSL_FLAG(std::string, protofiles, "", "Name of 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");
63 ABSL_FLAG(std::string, default_service_config, "",
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 "
66  "config.");
67 ABSL_FLAG(bool, display_peer_address, false,
68  "Log the peer socket address of the connection that each RPC is made "
69  "on to stderr.");
70 ABSL_FLAG(bool, json_input, false, "Input in json format");
71 ABSL_FLAG(bool, json_output, false, "Output in json format");
72 ABSL_FLAG(std::string, infile, "", "Input file (default is stdin)");
73 ABSL_FLAG(bool, batch, false,
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.");
78 // TODO(Capstan): Consider using absl::Duration
79 ABSL_FLAG(double, timeout, -1,
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.");
82 
83 namespace grpc {
84 namespace testing {
85 namespace {
86 
87 class GrpcTool {
88  public:
89  explicit GrpcTool();
90  virtual ~GrpcTool() {}
91 
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,
100  // TODO(zyc): implement the following methods
101  // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback
102  // callback);
103  // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback
104  // callback);
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,
113 
114  void SetPrintCommandMode(int exit_status) {
115  print_command_usage_ = true;
116  usage_exit_status_ = exit_status;
117  }
118 
119  private:
120  void CommandUsage(const std::string& usage) const;
124 };
125 
126 template <typename T>
127 std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
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);
133 }
134 
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)))));
139 }
140 
141 void ParseMetadataFlag(
142  std::multimap<std::string, std::string>* client_metadata) {
143  if (absl::GetFlag(FLAGS_metadata).empty()) {
144  return;
145  }
146  std::vector<std::string> fields;
147  const char delim = ':';
148  const char escape = '\\';
149  size_t cur = -1;
150  std::stringstream ss;
151  while (++cur < absl::GetFlag(FLAGS_metadata).length()) {
152  switch (absl::GetFlag(FLAGS_metadata).at(cur)) {
153  case escape:
154  if (cur < absl::GetFlag(FLAGS_metadata).length() - 1) {
155  char c = absl::GetFlag(FLAGS_metadata).at(++cur);
156  if (c == delim || c == escape) {
157  ss << c;
158  continue;
159  }
160  }
161  fprintf(stderr, "Failed to parse metadata flag.\n");
162  exit(1);
163  case delim:
164  fields.push_back(ss.str());
165  ss.str("");
166  ss.clear();
167  break;
168  default:
169  ss << absl::GetFlag(FLAGS_metadata).at(cur);
170  }
171  }
172  fields.push_back(ss.str());
173  if (fields.size() % 2) {
174  fprintf(stderr, "Failed to parse metadata flag.\n");
175  exit(1);
176  }
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]));
180  }
181 }
182 
183 template <typename T>
184 void PrintMetadata(const T& m, const std::string& message) {
185  if (m.empty()) {
186  return;
187  }
188  fprintf(stderr, "%s\n", message.c_str());
190  for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
191  pair.clear();
192  pair.append(iter->first.data(), iter->first.size());
193  pair.append(" : ");
194  pair.append(iter->second.data(), iter->second.size());
195  fprintf(stderr, "%s\n", pair.c_str());
196  }
197 }
198 
199 void ReadResponse(CliCall* call, const std::string& method_name,
201  ProtoFileParser* parser, gpr_mu* parser_mu, bool print_mode) {
202  std::string serialized_response_proto;
203  std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata;
204 
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");
210  if (!absl::GetFlag(FLAGS_binary_output)) {
211  gpr_mu_lock(parser_mu);
212  serialized_response_proto = parser->GetFormattedStringFromMethod(
213  method_name, serialized_response_proto, false /* is_request */,
214  absl::GetFlag(FLAGS_json_output));
215  if (parser->HasError() && print_mode) {
216  fprintf(stderr, "Failed to parse response.\n");
217  }
218  gpr_mu_unlock(parser_mu);
219  }
220  if (receive_initial_metadata) {
221  PrintMetadata(server_initial_metadata,
222  "Received initial metadata from server:");
223  }
224  if (!callback(serialized_response_proto) && print_mode) {
225  fprintf(stderr, "Failed to output response.\n");
226  }
227  }
228 }
229 
230 std::shared_ptr<grpc::Channel> CreateCliChannel(
231  const std::string& server_address, const CliCredentials& cred) {
233  if (!cred.GetSslTargetNameOverride().empty()) {
234  args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride());
235  }
236  if (!absl::GetFlag(FLAGS_default_service_config).empty()) {
237  args.SetString(GRPC_ARG_SERVICE_CONFIG,
238  absl::GetFlag(FLAGS_default_service_config).c_str());
239  }
240  // See |GRPC_ARG_MAX_METADATA_SIZE| in |grpc_types.h|.
241  // Set to large enough size (10M) that should work for most use cases.
242  args.SetInt(GRPC_ARG_MAX_METADATA_SIZE, 10 * 1024 * 1024);
243  return grpc::CreateCustomChannel(server_address, cred.GetCredentials(), args);
244 }
245 
246 struct Command {
247  const char* command;
248  std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
250  function;
251  int min_args;
252  int max_args;
253 };
254 
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},
261  {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
262  {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
263  {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
264  {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3},
265 };
266 
267 void Usage(const std::string& msg) {
268  fprintf(
269  stderr,
270  "%s\n"
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"
279  "\n",
280  msg.c_str());
281 
282  exit(1);
283 }
284 
285 const Command* FindCommand(const std::string& name) {
286  for (int i = 0; i < static_cast<int>(ArraySize(ops)); i++) {
287  if (name == ops[i].command) {
288  return &ops[i];
289  }
290  }
291  return nullptr;
292 }
293 } // namespace
294 
295 int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred,
297  if (argc < 2) {
298  Usage("No command specified");
299  }
300 
301  std::string command = argv[1];
302  argc -= 2;
303  argv += 2;
304 
305  const Command* cmd = FindCommand(command);
306  if (cmd != nullptr) {
307  GrpcTool grpc_tool;
308  if (argc < cmd->min_args || argc > cmd->max_args) {
309  // Force the command to print its usage message
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);
313  }
314  const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback);
315  return ok ? 0 : 1;
316  } else {
317  Usage("Invalid command '" + std::string(command.c_str()) + "'");
318  }
319  return 1;
320 }
321 
322 GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {}
323 
324 void GrpcTool::CommandUsage(const std::string& usage) const {
325  if (print_command_usage_) {
326  fprintf(stderr, "\n%s%s\n", usage.c_str(),
327  (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : "");
328  exit(usage_exit_status_);
329  }
330 }
331 
332 bool GrpcTool::Help(int argc, const char** argv, const CliCredentials& cred,
334  CommandUsage(
335  "Print help\n"
336  " grpc_cli help [subcommand]\n");
337 
338  if (argc == 0) {
339  Usage("");
340  } else {
341  const Command* cmd = FindCommand(argv[0]);
342  if (cmd == nullptr) {
343  Usage("Unknown command '" + std::string(argv[0]) + "'");
344  }
345  SetPrintCommandMode(0);
346  cmd->function(this, -1, nullptr, cred, callback);
347  }
348  return true;
349 }
350 
351 bool GrpcTool::ListServices(int argc, const char** argv,
352  const CliCredentials& cred,
354  CommandUsage(
355  "List services\n"
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());
363 
364  std::string server_address(argv[0]);
365  std::shared_ptr<grpc::Channel> channel =
366  CreateCliChannel(server_address, cred);
368  grpc::protobuf::DescriptorPool desc_pool(&desc_db);
369 
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");
373  return false;
374  }
375 
376  // If no service is specified, dump the list of services.
378  if (argc < 2) {
379  // List all services, if --l is passed, then include full description,
380  // otherwise include a summarized list only.
381  if (absl::GetFlag(FLAGS_l)) {
382  output = DescribeServiceList(service_list, desc_pool);
383  } else {
384  for (auto it = service_list.begin(); it != service_list.end(); it++) {
385  auto const& service = *it;
386  output.append(service);
387  output.append("\n");
388  }
389  }
390  } else {
391  std::string service_name;
393  std::stringstream ss(argv[1]);
394 
395  // Remove leading slashes.
396  while (ss.peek() == '/') {
397  ss.get();
398  }
399 
400  // Parse service and method names. Support the following patterns:
401  // Service
402  // Service Method
403  // Service.Method
404  // Service/Method
405  if (argc == 3) {
406  std::getline(ss, service_name, '/');
407  method_name = argv[2];
408  } else {
409  if (std::getline(ss, service_name, '/')) {
410  std::getline(ss, method_name);
411  }
412  }
413 
415  desc_pool.FindServiceByName(service_name);
416  if (service != nullptr) {
417  if (method_name.empty()) {
420  } else {
421  method_name.insert(0, 1, '.');
422  method_name.insert(0, service_name);
424  desc_pool.FindMethodByName(method_name);
425  if (method != nullptr) {
428  } else {
429  fprintf(stderr, "Method %s not found in service %s.\n",
430  method_name.c_str(), service_name.c_str());
431  return false;
432  }
433  }
434  } else {
435  if (!method_name.empty()) {
436  fprintf(stderr, "Service %s not found.\n", service_name.c_str());
437  return false;
438  } else {
440  desc_pool.FindMethodByName(service_name);
441  if (method != nullptr) {
444  } else {
445  fprintf(stderr, "Service or method %s not found.\n",
446  service_name.c_str());
447  return false;
448  }
449  }
450  }
451  }
452  return callback(output);
453 }
454 
455 bool GrpcTool::PrintType(int /*argc*/, const char** argv,
456  const CliCredentials& cred,
458  CommandUsage(
459  "Print type\n"
460  " grpc_cli type <address> <type>\n"
461  " <address> ; host:port\n"
462  " <type> ; Protocol buffer type name\n" +
463  cred.GetCredentialUsage());
464 
465  std::string server_address(argv[0]);
466  std::shared_ptr<grpc::Channel> channel =
467  CreateCliChannel(server_address, cred);
469  grpc::protobuf::DescriptorPool desc_pool(&desc_db);
470 
473  desc_pool.FindMessageTypeByName(argv[1]);
474  if (descriptor != nullptr) {
475  output = descriptor->DebugString();
476  } else {
477  fprintf(stderr, "Type %s not found.\n", argv[1]);
478  return false;
479  }
480  return callback(output);
481 }
482 
483 bool GrpcTool::CallMethod(int argc, const char** argv,
484  const CliCredentials& cred,
486  CommandUsage(
487  "Call method\n"
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"
499  " at all\n"
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());
511 
512  std::stringstream output_ss;
513  std::string request_text;
514  std::string server_address(argv[0]);
515  std::string method_name(argv[1]);
516  std::string formatted_method_name;
517  std::unique_ptr<ProtoFileParser> parser;
518  std::string serialized_request_proto;
519  CliArgs cli_args;
520  cli_args.timeout = absl::GetFlag(FLAGS_timeout);
521  bool print_mode = false;
522 
523  std::shared_ptr<grpc::Channel> channel =
524  CreateCliChannel(server_address, cred);
525 
526  if (!absl::GetFlag(FLAGS_binary_input) ||
527  !absl::GetFlag(FLAGS_binary_output)) {
528  parser = absl::make_unique<grpc::testing::ProtoFileParser>(
529  absl::GetFlag(FLAGS_remotedb) ? channel : nullptr,
530  absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles));
531  if (parser->HasError()) {
532  fprintf(
533  stderr,
534  "Failed to find remote reflection service and local proto files.\n");
535  return false;
536  }
537  }
538 
539  if (absl::GetFlag(FLAGS_binary_input)) {
540  formatted_method_name = method_name;
541  } else {
542  formatted_method_name = parser->GetFormattedMethodName(method_name);
543  if (parser->HasError()) {
544  fprintf(stderr, "Failed to find method %s in proto files.\n",
545  method_name.c_str());
546  }
547  }
548 
549  if (argc == 3) {
550  request_text = argv[2];
551  }
552 
553  if (parser->IsStreaming(method_name, true /* is_request */)) {
554  std::istream* input_stream;
555  std::ifstream input_file;
556 
557  if (absl::GetFlag(FLAGS_batch)) {
558  fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
559  return false;
560  }
561 
562  std::multimap<std::string, std::string> client_metadata;
563  ParseMetadataFlag(&client_metadata);
564  PrintMetadata(client_metadata, "Sending client initial metadata:");
565 
566  CliCall call(channel, formatted_method_name, client_metadata, cli_args);
567  if (absl::GetFlag(FLAGS_display_peer_address)) {
568  fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n",
569  formatted_method_name.c_str(), call.peer().c_str());
570  }
571 
572  if (absl::GetFlag(FLAGS_infile).empty()) {
573  if (isatty(fileno(stdin))) {
574  print_mode = true;
575  fprintf(stderr, "reading streaming request message from stdin...\n");
576  }
577  input_stream = &std::cin;
578  } else {
579  input_file.open(absl::GetFlag(FLAGS_infile),
580  std::ios::in | std::ios::binary);
581  input_stream = &input_file;
582  }
583 
584  gpr_mu parser_mu;
585  gpr_mu_init(&parser_mu);
586  std::thread read_thread(ReadResponse, &call, method_name, callback,
587  parser.get(), &parser_mu, print_mode);
588 
589  std::stringstream request_ss;
591  while (!request_text.empty() ||
592  (!input_stream->eof() && getline(*input_stream, line))) {
593  if (!request_text.empty()) {
594  if (absl::GetFlag(FLAGS_binary_input)) {
595  serialized_request_proto = request_text;
596  request_text.clear();
597  } else {
598  gpr_mu_lock(&parser_mu);
599  serialized_request_proto = parser->GetSerializedProtoFromMethod(
600  method_name, request_text, true /* is_request */,
601  absl::GetFlag(FLAGS_json_input));
602  request_text.clear();
603  if (parser->HasError()) {
604  if (print_mode) {
605  fprintf(stderr, "Failed to parse request.\n");
606  }
607  gpr_mu_unlock(&parser_mu);
608  continue;
609  }
610  gpr_mu_unlock(&parser_mu);
611  }
612 
613  call.WriteAndWait(serialized_request_proto);
614  if (print_mode) {
615  fprintf(stderr, "Request sent.\n");
616  }
617  } else {
618  if (line.length() == 0) {
619  request_text = request_ss.str();
620  request_ss.str(std::string());
621  request_ss.clear();
622  } else {
623  request_ss << line << ' ';
624  }
625  }
626  }
627  if (input_file.is_open()) {
628  input_file.close();
629  }
630 
631  call.WritesDoneAndWait();
632  read_thread.join();
633  gpr_mu_destroy(&parser_mu);
634 
635  std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata;
636  Status status = call.Finish(&server_trailing_metadata);
637  PrintMetadata(server_trailing_metadata,
638  "Received trailing metadata from server:");
639 
640  if (status.ok()) {
641  fprintf(stderr, "Stream RPC succeeded with OK status\n");
642  return true;
643  } else {
644  fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
645  status.error_code(), status.error_message().c_str());
646  return false;
647  }
648 
649  } else { // parser->IsStreaming(method_name, true /* is_request */)
650  if (absl::GetFlag(FLAGS_batch)) {
651  if (parser->IsStreaming(method_name, false /* is_request */)) {
652  fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
653  return false;
654  }
655 
656  std::istream* input_stream;
657  std::ifstream input_file;
658 
659  if (absl::GetFlag(FLAGS_infile).empty()) {
660  if (isatty(fileno(stdin))) {
661  print_mode = true;
662  fprintf(stderr, "reading request messages from stdin...\n");
663  }
664  input_stream = &std::cin;
665  } else {
666  input_file.open(absl::GetFlag(FLAGS_infile),
667  std::ios::in | std::ios::binary);
668  input_stream = &input_file;
669  }
670 
671  std::multimap<std::string, std::string> client_metadata;
672  ParseMetadataFlag(&client_metadata);
673  if (print_mode) {
674  PrintMetadata(client_metadata, "Sending client initial metadata:");
675  }
676 
677  std::stringstream request_ss;
679  while (!request_text.empty() ||
680  (!input_stream->eof() && getline(*input_stream, line))) {
681  if (!request_text.empty()) {
682  if (absl::GetFlag(FLAGS_binary_input)) {
683  serialized_request_proto = request_text;
684  request_text.clear();
685  } else {
686  serialized_request_proto = parser->GetSerializedProtoFromMethod(
687  method_name, request_text, true /* is_request */,
688  absl::GetFlag(FLAGS_json_input));
689  request_text.clear();
690  if (parser->HasError()) {
691  if (print_mode) {
692  fprintf(stderr, "Failed to parse request.\n");
693  }
694  continue;
695  }
696  }
697 
698  std::string serialized_response_proto;
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,
702  cli_args);
703  if (absl::GetFlag(FLAGS_display_peer_address)) {
704  fprintf(stderr,
705  "New call for method_name:%s has peer address:|%s|\n",
706  formatted_method_name.c_str(), call.peer().c_str());
707  }
708  call.Write(serialized_request_proto);
709  call.WritesDone();
710  if (!call.Read(&serialized_response_proto,
711  &server_initial_metadata)) {
712  fprintf(stderr, "Failed to read response.\n");
713  }
714  Status status = call.Finish(&server_trailing_metadata);
715 
716  if (status.ok()) {
717  if (print_mode) {
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:");
723  }
724 
725  if (absl::GetFlag(FLAGS_binary_output)) {
726  if (!callback(serialized_response_proto)) {
727  break;
728  }
729  } else {
730  std::string response_text = parser->GetFormattedStringFromMethod(
731  method_name, serialized_response_proto,
732  false /* is_request */, absl::GetFlag(FLAGS_json_output));
733 
734  if (parser->HasError() && print_mode) {
735  fprintf(stderr, "Failed to parse response.\n");
736  } else {
737  if (!callback(response_text)) {
738  break;
739  }
740  }
741  }
742  } else {
743  if (print_mode) {
744  fprintf(stderr,
745  "Rpc failed with status code %d, error message: %s\n",
746  status.error_code(), status.error_message().c_str());
747  }
748  }
749  } else {
750  if (line.length() == 0) {
751  request_text = request_ss.str();
752  request_ss.str(std::string());
753  request_ss.clear();
754  } else {
755  request_ss << line << ' ';
756  }
757  }
758  }
759 
760  if (input_file.is_open()) {
761  input_file.close();
762  }
763 
764  return true;
765  }
766 
767  if (argc == 3) {
768  if (!absl::GetFlag(FLAGS_infile).empty()) {
769  fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
770  }
771  } else {
772  std::stringstream input_stream;
773  if (absl::GetFlag(FLAGS_infile).empty()) {
774  if (isatty(fileno(stdin))) {
775  fprintf(stderr, "reading request message from stdin...\n");
776  }
777  input_stream << std::cin.rdbuf();
778  } else {
779  std::ifstream input_file(absl::GetFlag(FLAGS_infile),
780  std::ios::in | std::ios::binary);
781  input_stream << input_file.rdbuf();
782  input_file.close();
783  }
784  request_text = input_stream.str();
785  }
786 
787  if (absl::GetFlag(FLAGS_binary_input)) {
788  serialized_request_proto = request_text;
789  } else {
790  serialized_request_proto = parser->GetSerializedProtoFromMethod(
791  method_name, request_text, true /* is_request */,
792  absl::GetFlag(FLAGS_json_input));
793  if (parser->HasError()) {
794  fprintf(stderr, "Failed to parse request.\n");
795  return false;
796  }
797  }
798  fprintf(stderr, "connecting to %s\n", server_address.c_str());
799 
800  std::string serialized_response_proto;
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:");
806 
807  CliCall call(channel, formatted_method_name, client_metadata, cli_args);
808  if (absl::GetFlag(FLAGS_display_peer_address)) {
809  fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n",
810  formatted_method_name.c_str(), call.peer().c_str());
811  }
812  call.Write(serialized_request_proto);
813  call.WritesDone();
814 
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) {
819  if (!absl::GetFlag(FLAGS_binary_output)) {
820  serialized_response_proto = parser->GetFormattedStringFromMethod(
821  method_name, serialized_response_proto, false /* is_request */,
822  absl::GetFlag(FLAGS_json_output));
823  if (parser->HasError()) {
824  fprintf(stderr, "Failed to parse response.\n");
825  return false;
826  }
827  }
828 
829  if (receive_initial_metadata) {
830  PrintMetadata(server_initial_metadata,
831  "Received initial metadata from server:");
832  }
833  if (!callback(serialized_response_proto)) {
834  return false;
835  }
836  }
837  Status status = call.Finish(&server_trailing_metadata);
838  PrintMetadata(server_trailing_metadata,
839  "Received trailing metadata from server:");
840  if (status.ok()) {
841  fprintf(stderr, "Rpc succeeded with OK status\n");
842  return true;
843  } else {
844  fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
845  status.error_code(), status.error_message().c_str());
846  return false;
847  }
848  }
849  GPR_UNREACHABLE_CODE(return false);
850 }
851 
852 bool GrpcTool::ParseMessage(int argc, const char** argv,
853  const CliCredentials& cred,
855  CommandUsage(
856  "Parse message\n"
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"
867  " at all\n"
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());
875 
876  std::stringstream output_ss;
877  std::string message_text;
878  std::string server_address(argv[0]);
879  std::string type_name(argv[1]);
880  std::unique_ptr<grpc::testing::ProtoFileParser> parser;
881  std::string serialized_request_proto;
882 
883  if (argc == 3) {
884  message_text = argv[2];
885  if (!absl::GetFlag(FLAGS_infile).empty()) {
886  fprintf(stderr, "warning: message given in argv, ignoring --infile.\n");
887  }
888  } else {
889  std::stringstream input_stream;
890  if (absl::GetFlag(FLAGS_infile).empty()) {
891  if (isatty(fileno(stdin))) {
892  fprintf(stderr, "reading request message from stdin...\n");
893  }
894  input_stream << std::cin.rdbuf();
895  } else {
896  std::ifstream input_file(absl::GetFlag(FLAGS_infile),
897  std::ios::in | std::ios::binary);
898  input_stream << input_file.rdbuf();
899  input_file.close();
900  }
901  message_text = input_stream.str();
902  }
903 
904  if (!absl::GetFlag(FLAGS_binary_input) ||
905  !absl::GetFlag(FLAGS_binary_output)) {
906  std::shared_ptr<grpc::Channel> channel =
907  CreateCliChannel(server_address, cred);
908  parser = absl::make_unique<grpc::testing::ProtoFileParser>(
909  absl::GetFlag(FLAGS_remotedb) ? channel : nullptr,
910  absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles));
911  if (parser->HasError()) {
912  fprintf(
913  stderr,
914  "Failed to find remote reflection service and local proto files.\n");
915  return false;
916  }
917  }
918 
919  if (absl::GetFlag(FLAGS_binary_input)) {
920  serialized_request_proto = message_text;
921  } else {
922  serialized_request_proto = parser->GetSerializedProtoFromMessageType(
923  type_name, message_text, absl::GetFlag(FLAGS_json_input));
924  if (parser->HasError()) {
925  fprintf(stderr, "Failed to serialize the message.\n");
926  return false;
927  }
928  }
929 
930  if (absl::GetFlag(FLAGS_binary_output)) {
931  output_ss << serialized_request_proto;
932  } else {
933  std::string output_text;
934  output_text = parser->GetFormattedStringFromMessageType(
935  type_name, serialized_request_proto, absl::GetFlag(FLAGS_json_output));
936  if (parser->HasError()) {
937  fprintf(stderr, "Failed to deserialize the message.\n");
938  return false;
939  }
940 
941  output_ss << output_text << std::endl;
942  }
943 
944  return callback(output_ss.str());
945 }
946 
947 bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred,
949  CommandUsage(
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"
956  " separated)\n"
957  " --infile ; Input filename (defaults to stdin)\n"
958  " --outfile ; Output filename (defaults to stdout)\n");
959 
960  absl::SetFlag(&FLAGS_protofiles, argv[0]);
961  absl::SetFlag(&FLAGS_remotedb, false);
962  absl::SetFlag(&FLAGS_binary_input, true);
963  absl::SetFlag(&FLAGS_binary_output, false);
964  return ParseMessage(argc, argv, cred, callback);
965 }
966 
967 bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred,
969  CommandUsage(
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"
976  " separated)\n"
977  " --infile ; Input filename (defaults to stdin)\n"
978  " --outfile ; Output filename (defaults to stdout)\n");
979 
980  absl::SetFlag(&FLAGS_protofiles, argv[0]);
981  absl::SetFlag(&FLAGS_remotedb, false);
982  absl::SetFlag(&FLAGS_binary_input, true);
983  absl::SetFlag(&FLAGS_binary_output, false);
984  absl::SetFlag(&FLAGS_json_output, true);
985  return ParseMessage(argc, argv, cred, callback);
986 }
987 
988 bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred,
990  CommandUsage(
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"
997  " separated)\n"
998  " --infile ; Input filename (defaults to stdin)\n"
999  " --outfile ; Output filename (defaults to stdout)\n");
1000 
1001  absl::SetFlag(&FLAGS_protofiles, argv[0]);
1002  absl::SetFlag(&FLAGS_remotedb, false);
1003  absl::SetFlag(&FLAGS_binary_input, false);
1004  absl::SetFlag(&FLAGS_binary_output, true);
1005  return ParseMessage(argc, argv, cred, callback);
1006 }
1007 
1008 } // namespace testing
1009 } // namespace grpc
testing
Definition: aws_request_signer_test.cc:25
grpc::ProtoReflectionDescriptorDatabase
Definition: proto_reflection_descriptor_database.h:36
grpc::status
auto status
Definition: cpp/client/credentials_test.cc:200
gpr_mu_unlock
GPRAPI void gpr_mu_unlock(gpr_mu *mu)
absl::SetFlag
void SetFlag(absl::Flag< T > *flag, const T &v)
Definition: abseil-cpp/absl/flags/flag.h:110
regen-readme.it
it
Definition: regen-readme.py:15
bool
bool
Definition: setup_once.h:312
grpc::testing::GrpcToolMainLib
int GrpcToolMainLib(int argc, const char **argv, const CliCredentials &cred, const GrpcToolOutputCallback &callback)
Definition: grpc_tool.cc:295
metadata
Definition: cq_verifier.cc:48
grpc::testing::DescribeService
std::string DescribeService(const grpc::protobuf::ServiceDescriptor *service)
Definition: service_describer.cc:43
grpc
Definition: grpcpp/alarm.h:33
fix_build_deps.c
list c
Definition: fix_build_deps.py:490
false
#define false
Definition: setup_once.h:323
grpc::testing::SummarizeMethod
std::string SummarizeMethod(const grpc::protobuf::MethodDescriptor *method)
Definition: service_describer.cc:85
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
grpc::testing::SummarizeService
std::string SummarizeService(const grpc::protobuf::ServiceDescriptor *service)
Definition: service_describer.cc:77
cli_call.h
setup.name
name
Definition: setup.py:542
grpc::protobuf::DescriptorPool
GRPC_CUSTOM_DESCRIPTORPOOL DescriptorPool
Definition: include/grpcpp/impl/codegen/config_protobuf.h:82
grpc::testing::GrpcToolOutputCallback
std::function< bool(const std::string &)> GrpcToolOutputCallback
Definition: grpc_tool.h:31
grpc_tool.h
server_address
std::string server_address("0.0.0.0:10000")
message
char * message
Definition: libuv/docs/code/tty-gravity/main.c:12
T
#define T(upbtypeconst, upbtype, ctype, default_value)
demumble_test.stdin
stdin
Definition: demumble_test.py:37
proto_reflection_descriptor_database.h
call
FilterStackCall * call
Definition: call.cc:750
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
GRPC_CLI_PATH_SEPARATOR
#define GRPC_CLI_PATH_SEPARATOR
Definition: proto_file_parser.h:32
gpr_mu_destroy
GPRAPI void gpr_mu_destroy(gpr_mu *mu)
in
const char * in
Definition: third_party/abseil-cpp/absl/strings/internal/str_format/parser_test.cc:391
channel
wrapped_grpc_channel * channel
Definition: src/php/ext/grpc/call.h:33
asyncio_get_stats.parser
parser
Definition: asyncio_get_stats.py:34
service_describer.h
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
min_args
int min_args
Definition: grpc_tool.cc:251
gen_stats_data.c_str
def c_str(s, encoding='ascii')
Definition: gen_stats_data.py:38
gmock_output_test.output
output
Definition: bloaty/third_party/googletest/googlemock/test/gmock_output_test.py:175
cred_usage_
const std::string cred_usage_
Definition: grpc_tool.cc:123
usage_exit_status_
int usage_exit_status_
Definition: grpc_tool.cc:122
gpr_mu_init
GPRAPI void gpr_mu_init(gpr_mu *mu)
grpc::testing::CliCredentials
Definition: cli_credentials.h:28
grpc.h
grpc::protobuf::MethodDescriptor
GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor
Definition: include/grpcpp/impl/codegen/config_protobuf.h:87
regen-readme.cmd
cmd
Definition: regen-readme.py:21
google::protobuf.reflection.ParseMessage
def ParseMessage(descriptor, byte_str)
Definition: third_party/bloaty/third_party/protobuf/python/google/protobuf/reflection.py:62
grpcpp.h
channel.h
gpr_mu_lock
GPRAPI void gpr_mu_lock(gpr_mu *mu)
absl::GetFlag
ABSL_MUST_USE_RESULT T GetFlag(const absl::Flag< T > &flag)
Definition: abseil-cpp/absl/flags/flag.h:98
Usage
static int Usage(const char *program)
Definition: bssl_shim.cc:84
callback
static void callback(void *arg, int status, int timeouts, struct hostent *host)
Definition: acountry.c:224
batch
grpc_transport_stream_op_batch * batch
Definition: retry_filter.cc:243
GPR_UNREACHABLE_CODE
#define GPR_UNREACHABLE_CODE(STATEMENT)
Definition: impl/codegen/port_platform.h:652
ABSL_FLAG
ABSL_FLAG(bool, l, false, "Use a long listing format")
msg
std::string msg
Definition: client_interceptors_end2end_test.cc:372
google_benchmark.example.empty
def empty(state)
Definition: example.py:31
grpc::testing::DescribeServiceList
std::string DescribeServiceList(std::vector< std::string > service_list, grpc::protobuf::DescriptorPool &desc_pool)
Definition: service_describer.cc:29
memory_diff.cur
def cur
Definition: memory_diff.py:83
grpc::ChannelArguments
Definition: grpcpp/support/channel_arguments.h:39
credentials.h
func
const EVP_CIPHER *(* func)(void)
Definition: cipher_extra.c:73
bloaty::usage
const char usage[]
Definition: bloaty.cc:1843
gpr_mu
pthread_mutex_t gpr_mu
Definition: impl/codegen/sync_posix.h:47
GRPC_ARG_MAX_METADATA_SIZE
#define GRPC_ARG_MAX_METADATA_SIZE
Definition: grpc_types.h:293
GRPC_ARG_SERVICE_CONFIG
#define GRPC_ARG_SERVICE_CONFIG
Definition: grpc_types.h:304
profile_analyzer.fields
list fields
Definition: profile_analyzer.py:266
print_command_usage_
bool print_command_usage_
Definition: grpc_tool.cc:121
grpc::CreateCustomChannel
std::shared_ptr< Channel > CreateCustomChannel(const grpc::string &target, const std::shared_ptr< ChannelCredentials > &creds, const ChannelArguments &args)
grpc::protobuf::util::Status
GRPC_CUSTOM_UTIL_STATUS Status
Definition: include/grpcpp/impl/codegen/config_protobuf.h:93
regen-readme.line
line
Definition: regen-readme.py:30
ok
bool ok
Definition: async_end2end_test.cc:197
absl::string_view::empty
constexpr bool empty() const noexcept
Definition: abseil-cpp/absl/strings/string_view.h:292
grpc::testing::DescribeMethod
std::string DescribeMethod(const grpc::protobuf::MethodDescriptor *method)
Definition: service_describer.cc:64
iter
Definition: test_winkernel.cpp:47
service
__attribute__((deprecated("Please use GRPCProtoMethod."))) @interface ProtoMethod NSString * service
Definition: ProtoMethod.h:25
grpc::protobuf::Descriptor
GRPC_CUSTOM_DESCRIPTOR Descriptor
Definition: include/grpcpp/impl/codegen/config_protobuf.h:81
function
std::function< bool(GrpcTool *, int, const char **, const CliCredentials &, GrpcToolOutputCallback)> function
Definition: grpc_tool.cc:250
run_grpclb_interop_tests.l
dictionary l
Definition: run_grpclb_interop_tests.py:410
length
std::size_t length
Definition: abseil-cpp/absl/time/internal/test_util.cc:57
regress.m
m
Definition: regress/regress.py:25
method
NSString * method
Definition: ProtoMethod.h:28
proto_file_parser.h
escape
static bool escape(upb_json_parser *p, const char *ptr)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:9283
descriptor
static const char descriptor[1336]
Definition: certs.upbdefs.c:16
ops
static grpc_op ops[6]
Definition: test/core/fling/client.cc:39
method_name
absl::string_view method_name
Definition: call_creds_util.cc:40
pair
std::pair< std::string, std::string > pair
Definition: abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc:78
max_args
int max_args
Definition: grpc_tool.cc:252
grpc::protobuf::ServiceDescriptor
GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor
Definition: include/grpcpp/impl/codegen/config_protobuf.h:88
thread
static uv_thread_t thread
Definition: test-async-null-cb.c:29
timeout
uv_timer_t timeout
Definition: libuv/docs/code/uvwget/main.c:9
command
const char * command
Definition: grpc_tool.cc:247
string_ref.h
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
create_channel.h
type_name
static const char * type_name(int type)
Definition: adig.c:889
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:45