grpc_tool.cc 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  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. #include <grpc/support/port_platform.h>
  19. #include "test/cpp/util/grpc_tool.h"
  20. #include <cstdio>
  21. #include <fstream>
  22. #include <iostream>
  23. #include <memory>
  24. #include <sstream>
  25. #include <string>
  26. #include <thread>
  27. #include "absl/flags/flag.h"
  28. #include "absl/memory/memory.h"
  29. #include <grpc/grpc.h>
  30. #include <grpcpp/channel.h>
  31. #include <grpcpp/create_channel.h>
  32. #include <grpcpp/grpcpp.h>
  33. #include <grpcpp/security/credentials.h>
  34. #include <grpcpp/support/string_ref.h>
  35. #include "test/cpp/util/cli_call.h"
  36. #include "test/cpp/util/proto_file_parser.h"
  37. #include "test/cpp/util/proto_reflection_descriptor_database.h"
  38. #include "test/cpp/util/service_describer.h"
  39. #if GPR_WINDOWS
  40. #include <io.h>
  41. #else
  42. #include <unistd.h>
  43. #endif
  44. ABSL_FLAG(bool, l, false, "Use a long listing format");
  45. ABSL_FLAG(bool, remotedb, true,
  46. "Use server types to parse and format messages");
  47. ABSL_FLAG(std::string, metadata, "",
  48. "Metadata to send to server, in the form of key1:val1:key2:val2");
  49. ABSL_FLAG(std::string, proto_path, ".",
  50. "Path to look for the proto file. "
  51. "Multiple paths can be separated by " GRPC_CLI_PATH_SEPARATOR);
  52. ABSL_FLAG(std::string, protofiles, "", "Name of the proto file.");
  53. ABSL_FLAG(bool, binary_input, false, "Input in binary format");
  54. ABSL_FLAG(bool, binary_output, false, "Output in binary format");
  55. ABSL_FLAG(std::string, default_service_config, "",
  56. "Default service config to use on the channel, if non-empty. Note "
  57. "that this will be ignored if the name resolver returns a service "
  58. "config.");
  59. ABSL_FLAG(bool, display_peer_address, false,
  60. "Log the peer socket address of the connection that each RPC is made "
  61. "on to stderr.");
  62. ABSL_FLAG(bool, json_input, false, "Input in json format");
  63. ABSL_FLAG(bool, json_output, false, "Output in json format");
  64. ABSL_FLAG(std::string, infile, "", "Input file (default is stdin)");
  65. ABSL_FLAG(bool, batch, false,
  66. "Input contains multiple requests. Please do not use this to send "
  67. "more than a few RPCs. gRPC CLI has very different performance "
  68. "characteristics compared with normal RPC calls which make it "
  69. "unsuitable for loadtesting or significant production traffic.");
  70. // TODO(Capstan): Consider using absl::Duration
  71. ABSL_FLAG(double, timeout, -1,
  72. "Specify timeout in seconds, used to set the deadline for all "
  73. "RPCs. The default value of -1 means no deadline has been set.");
  74. namespace grpc {
  75. namespace testing {
  76. namespace {
  77. class GrpcTool {
  78. public:
  79. explicit GrpcTool();
  80. virtual ~GrpcTool() {}
  81. bool Help(int argc, const char** argv, const CliCredentials& cred,
  82. const GrpcToolOutputCallback& callback);
  83. bool CallMethod(int argc, const char** argv, const CliCredentials& cred,
  84. const GrpcToolOutputCallback& callback);
  85. bool ListServices(int argc, const char** argv, const CliCredentials& cred,
  86. const GrpcToolOutputCallback& callback);
  87. bool PrintType(int argc, const char** argv, const CliCredentials& cred,
  88. const GrpcToolOutputCallback& callback);
  89. // TODO(zyc): implement the following methods
  90. // bool ListServices(int argc, const char** argv, GrpcToolOutputCallback
  91. // callback);
  92. // bool PrintTypeId(int argc, const char** argv, GrpcToolOutputCallback
  93. // callback);
  94. bool ParseMessage(int argc, const char** argv, const CliCredentials& cred,
  95. const GrpcToolOutputCallback& callback);
  96. bool ToText(int argc, const char** argv, const CliCredentials& cred,
  97. const GrpcToolOutputCallback& callback);
  98. bool ToJson(int argc, const char** argv, const CliCredentials& cred,
  99. const GrpcToolOutputCallback& callback);
  100. bool ToBinary(int argc, const char** argv, const CliCredentials& cred,
  101. const GrpcToolOutputCallback& callback);
  102. void SetPrintCommandMode(int exit_status) {
  103. print_command_usage_ = true;
  104. usage_exit_status_ = exit_status;
  105. }
  106. private:
  107. void CommandUsage(const std::string& usage) const;
  108. bool print_command_usage_;
  109. int usage_exit_status_;
  110. const std::string cred_usage_;
  111. };
  112. template <typename T>
  113. std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
  114. GrpcToolOutputCallback)>
  115. BindWith5Args(T&& func) {
  116. return std::bind(std::forward<T>(func), std::placeholders::_1,
  117. std::placeholders::_2, std::placeholders::_3,
  118. std::placeholders::_4, std::placeholders::_5);
  119. }
  120. template <typename T>
  121. size_t ArraySize(T& a) {
  122. return ((sizeof(a) / sizeof(*(a))) /
  123. static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
  124. }
  125. void ParseMetadataFlag(
  126. std::multimap<std::string, std::string>* client_metadata) {
  127. if (absl::GetFlag(FLAGS_metadata).empty()) {
  128. return;
  129. }
  130. std::vector<std::string> fields;
  131. const char delim = ':';
  132. const char escape = '\\';
  133. size_t cur = -1;
  134. std::stringstream ss;
  135. while (++cur < absl::GetFlag(FLAGS_metadata).length()) {
  136. switch (absl::GetFlag(FLAGS_metadata).at(cur)) {
  137. case escape:
  138. if (cur < absl::GetFlag(FLAGS_metadata).length() - 1) {
  139. char c = absl::GetFlag(FLAGS_metadata).at(++cur);
  140. if (c == delim || c == escape) {
  141. ss << c;
  142. continue;
  143. }
  144. }
  145. fprintf(stderr, "Failed to parse metadata flag.\n");
  146. exit(1);
  147. case delim:
  148. fields.push_back(ss.str());
  149. ss.str("");
  150. ss.clear();
  151. break;
  152. default:
  153. ss << absl::GetFlag(FLAGS_metadata).at(cur);
  154. }
  155. }
  156. fields.push_back(ss.str());
  157. if (fields.size() % 2) {
  158. fprintf(stderr, "Failed to parse metadata flag.\n");
  159. exit(1);
  160. }
  161. for (size_t i = 0; i < fields.size(); i += 2) {
  162. client_metadata->insert(
  163. std::pair<std::string, std::string>(fields[i], fields[i + 1]));
  164. }
  165. }
  166. template <typename T>
  167. void PrintMetadata(const T& m, const std::string& message) {
  168. if (m.empty()) {
  169. return;
  170. }
  171. fprintf(stderr, "%s\n", message.c_str());
  172. std::string pair;
  173. for (typename T::const_iterator iter = m.begin(); iter != m.end(); ++iter) {
  174. pair.clear();
  175. pair.append(iter->first.data(), iter->first.size());
  176. pair.append(" : ");
  177. pair.append(iter->second.data(), iter->second.size());
  178. fprintf(stderr, "%s\n", pair.c_str());
  179. }
  180. }
  181. void ReadResponse(CliCall* call, const std::string& method_name,
  182. const GrpcToolOutputCallback& callback,
  183. ProtoFileParser* parser, gpr_mu* parser_mu, bool print_mode) {
  184. std::string serialized_response_proto;
  185. std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata;
  186. for (bool receive_initial_metadata = true; call->ReadAndMaybeNotifyWrite(
  187. &serialized_response_proto,
  188. receive_initial_metadata ? &server_initial_metadata : nullptr);
  189. receive_initial_metadata = false) {
  190. fprintf(stderr, "got response.\n");
  191. if (!absl::GetFlag(FLAGS_binary_output)) {
  192. gpr_mu_lock(parser_mu);
  193. serialized_response_proto = parser->GetFormattedStringFromMethod(
  194. method_name, serialized_response_proto, false /* is_request */,
  195. absl::GetFlag(FLAGS_json_output));
  196. if (parser->HasError() && print_mode) {
  197. fprintf(stderr, "Failed to parse response.\n");
  198. }
  199. gpr_mu_unlock(parser_mu);
  200. }
  201. if (receive_initial_metadata) {
  202. PrintMetadata(server_initial_metadata,
  203. "Received initial metadata from server:");
  204. }
  205. if (!callback(serialized_response_proto) && print_mode) {
  206. fprintf(stderr, "Failed to output response.\n");
  207. }
  208. }
  209. }
  210. std::shared_ptr<grpc::Channel> CreateCliChannel(
  211. const std::string& server_address, const CliCredentials& cred) {
  212. grpc::ChannelArguments args;
  213. if (!cred.GetSslTargetNameOverride().empty()) {
  214. args.SetSslTargetNameOverride(cred.GetSslTargetNameOverride());
  215. }
  216. if (!absl::GetFlag(FLAGS_default_service_config).empty()) {
  217. args.SetString(GRPC_ARG_SERVICE_CONFIG,
  218. absl::GetFlag(FLAGS_default_service_config).c_str());
  219. }
  220. // See |GRPC_ARG_MAX_METADATA_SIZE| in |grpc_types.h|.
  221. // Set to large enough size (10M) that should work for most use cases.
  222. args.SetInt(GRPC_ARG_MAX_METADATA_SIZE, 10 * 1024 * 1024);
  223. return grpc::CreateCustomChannel(server_address, cred.GetCredentials(), args);
  224. }
  225. struct Command {
  226. const char* command;
  227. std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
  228. GrpcToolOutputCallback)>
  229. function;
  230. int min_args;
  231. int max_args;
  232. };
  233. const Command ops[] = {
  234. {"help", BindWith5Args(&GrpcTool::Help), 0, INT_MAX},
  235. {"ls", BindWith5Args(&GrpcTool::ListServices), 1, 3},
  236. {"list", BindWith5Args(&GrpcTool::ListServices), 1, 3},
  237. {"call", BindWith5Args(&GrpcTool::CallMethod), 2, 3},
  238. {"type", BindWith5Args(&GrpcTool::PrintType), 2, 2},
  239. {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
  240. {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
  241. {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
  242. {"tojson", BindWith5Args(&GrpcTool::ToJson), 2, 3},
  243. };
  244. void Usage(const std::string& msg) {
  245. fprintf(
  246. stderr,
  247. "%s\n"
  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"
  256. "\n",
  257. msg.c_str());
  258. exit(1);
  259. }
  260. const Command* FindCommand(const std::string& name) {
  261. for (int i = 0; i < static_cast<int>(ArraySize(ops)); i++) {
  262. if (name == ops[i].command) {
  263. return &ops[i];
  264. }
  265. }
  266. return nullptr;
  267. }
  268. } // namespace
  269. int GrpcToolMainLib(int argc, const char** argv, const CliCredentials& cred,
  270. const GrpcToolOutputCallback& callback) {
  271. if (argc < 2) {
  272. Usage("No command specified");
  273. }
  274. std::string command = argv[1];
  275. argc -= 2;
  276. argv += 2;
  277. const Command* cmd = FindCommand(command);
  278. if (cmd != nullptr) {
  279. GrpcTool grpc_tool;
  280. if (argc < cmd->min_args || argc > cmd->max_args) {
  281. // Force the command to print its usage message
  282. fprintf(stderr, "\nWrong number of arguments for %s\n", command.c_str());
  283. grpc_tool.SetPrintCommandMode(1);
  284. return cmd->function(&grpc_tool, -1, nullptr, cred, callback);
  285. }
  286. const bool ok = cmd->function(&grpc_tool, argc, argv, cred, callback);
  287. return ok ? 0 : 1;
  288. } else {
  289. Usage("Invalid command '" + std::string(command.c_str()) + "'");
  290. }
  291. return 1;
  292. }
  293. GrpcTool::GrpcTool() : print_command_usage_(false), usage_exit_status_(0) {}
  294. void GrpcTool::CommandUsage(const std::string& usage) const {
  295. if (print_command_usage_) {
  296. fprintf(stderr, "\n%s%s\n", usage.c_str(),
  297. (usage.empty() || usage[usage.size() - 1] != '\n') ? "\n" : "");
  298. exit(usage_exit_status_);
  299. }
  300. }
  301. bool GrpcTool::Help(int argc, const char** argv, const CliCredentials& cred,
  302. const GrpcToolOutputCallback& callback) {
  303. CommandUsage(
  304. "Print help\n"
  305. " grpc_cli help [subcommand]\n");
  306. if (argc == 0) {
  307. Usage("");
  308. } else {
  309. const Command* cmd = FindCommand(argv[0]);
  310. if (cmd == nullptr) {
  311. Usage("Unknown command '" + std::string(argv[0]) + "'");
  312. }
  313. SetPrintCommandMode(0);
  314. cmd->function(this, -1, nullptr, cred, callback);
  315. }
  316. return true;
  317. }
  318. bool GrpcTool::ListServices(int argc, const char** argv,
  319. const CliCredentials& cred,
  320. const GrpcToolOutputCallback& callback) {
  321. CommandUsage(
  322. "List services\n"
  323. " grpc_cli ls <address> [<service>[/<method>]]\n"
  324. " <address> ; host:port\n"
  325. " <service> ; Exported service name\n"
  326. " <method> ; Method name\n"
  327. " --l ; Use a long listing format\n"
  328. " --outfile ; Output filename (defaults to stdout)\n" +
  329. cred.GetCredentialUsage());
  330. std::string server_address(argv[0]);
  331. std::shared_ptr<grpc::Channel> channel =
  332. CreateCliChannel(server_address, cred);
  333. grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
  334. grpc::protobuf::DescriptorPool desc_pool(&desc_db);
  335. std::vector<std::string> service_list;
  336. if (!desc_db.GetServices(&service_list)) {
  337. fprintf(stderr, "Received an error when querying services endpoint.\n");
  338. return false;
  339. }
  340. // If no service is specified, dump the list of services.
  341. std::string output;
  342. if (argc < 2) {
  343. // List all services, if --l is passed, then include full description,
  344. // otherwise include a summarized list only.
  345. if (absl::GetFlag(FLAGS_l)) {
  346. output = DescribeServiceList(service_list, desc_pool);
  347. } else {
  348. for (auto it = service_list.begin(); it != service_list.end(); it++) {
  349. auto const& service = *it;
  350. output.append(service);
  351. output.append("\n");
  352. }
  353. }
  354. } else {
  355. std::string service_name;
  356. std::string method_name;
  357. std::stringstream ss(argv[1]);
  358. // Remove leading slashes.
  359. while (ss.peek() == '/') {
  360. ss.get();
  361. }
  362. // Parse service and method names. Support the following patterns:
  363. // Service
  364. // Service Method
  365. // Service.Method
  366. // Service/Method
  367. if (argc == 3) {
  368. std::getline(ss, service_name, '/');
  369. method_name = argv[2];
  370. } else {
  371. if (std::getline(ss, service_name, '/')) {
  372. std::getline(ss, method_name);
  373. }
  374. }
  375. const grpc::protobuf::ServiceDescriptor* service =
  376. desc_pool.FindServiceByName(service_name);
  377. if (service != nullptr) {
  378. if (method_name.empty()) {
  379. output = absl::GetFlag(FLAGS_l) ? DescribeService(service)
  380. : SummarizeService(service);
  381. } else {
  382. method_name.insert(0, 1, '.');
  383. method_name.insert(0, service_name);
  384. const grpc::protobuf::MethodDescriptor* method =
  385. desc_pool.FindMethodByName(method_name);
  386. if (method != nullptr) {
  387. output = absl::GetFlag(FLAGS_l) ? DescribeMethod(method)
  388. : SummarizeMethod(method);
  389. } else {
  390. fprintf(stderr, "Method %s not found in service %s.\n",
  391. method_name.c_str(), service_name.c_str());
  392. return false;
  393. }
  394. }
  395. } else {
  396. if (!method_name.empty()) {
  397. fprintf(stderr, "Service %s not found.\n", service_name.c_str());
  398. return false;
  399. } else {
  400. const grpc::protobuf::MethodDescriptor* method =
  401. desc_pool.FindMethodByName(service_name);
  402. if (method != nullptr) {
  403. output = absl::GetFlag(FLAGS_l) ? DescribeMethod(method)
  404. : SummarizeMethod(method);
  405. } else {
  406. fprintf(stderr, "Service or method %s not found.\n",
  407. service_name.c_str());
  408. return false;
  409. }
  410. }
  411. }
  412. }
  413. return callback(output);
  414. }
  415. bool GrpcTool::PrintType(int /*argc*/, const char** argv,
  416. const CliCredentials& cred,
  417. const GrpcToolOutputCallback& callback) {
  418. CommandUsage(
  419. "Print type\n"
  420. " grpc_cli type <address> <type>\n"
  421. " <address> ; host:port\n"
  422. " <type> ; Protocol buffer type name\n" +
  423. cred.GetCredentialUsage());
  424. std::string server_address(argv[0]);
  425. std::shared_ptr<grpc::Channel> channel =
  426. CreateCliChannel(server_address, cred);
  427. grpc::ProtoReflectionDescriptorDatabase desc_db(channel);
  428. grpc::protobuf::DescriptorPool desc_pool(&desc_db);
  429. std::string output;
  430. const grpc::protobuf::Descriptor* descriptor =
  431. desc_pool.FindMessageTypeByName(argv[1]);
  432. if (descriptor != nullptr) {
  433. output = descriptor->DebugString();
  434. } else {
  435. fprintf(stderr, "Type %s not found.\n", argv[1]);
  436. return false;
  437. }
  438. return callback(output);
  439. }
  440. bool GrpcTool::CallMethod(int argc, const char** argv,
  441. const CliCredentials& cred,
  442. const GrpcToolOutputCallback& callback) {
  443. CommandUsage(
  444. "Call method\n"
  445. " grpc_cli call <address> <service>[.<method>] <request>\n"
  446. " <address> ; host:port\n"
  447. " <service> ; Exported service name\n"
  448. " <method> ; Method name\n"
  449. " <request> ; Text protobuffer (overrides infile)\n"
  450. " --protofiles ; Comma separated proto files used as a"
  451. " fallback when parsing request/response\n"
  452. " --proto_path ; The search paths of proto files"
  453. " (" GRPC_CLI_PATH_SEPARATOR
  454. " separated), valid only when --protofiles is given\n"
  455. " --noremotedb ; Don't attempt to use reflection service"
  456. " at all\n"
  457. " --metadata ; The metadata to be sent to the server\n"
  458. " --infile ; Input filename (defaults to stdin)\n"
  459. " --outfile ; Output filename (defaults to stdout)\n"
  460. " --binary_input ; Input in binary format\n"
  461. " --binary_output ; Output in binary format\n"
  462. " --json_input ; Input in json format\n"
  463. " --json_output ; Output in json format\n"
  464. " --timeout ; Specify timeout (in seconds), used to "
  465. "set the deadline for RPCs. The default value of -1 means no "
  466. "deadline has been set.\n" +
  467. cred.GetCredentialUsage());
  468. std::stringstream output_ss;
  469. std::string request_text;
  470. std::string server_address(argv[0]);
  471. std::string method_name(argv[1]);
  472. std::string formatted_method_name;
  473. std::unique_ptr<ProtoFileParser> parser;
  474. std::string serialized_request_proto;
  475. CliArgs cli_args;
  476. cli_args.timeout = absl::GetFlag(FLAGS_timeout);
  477. bool print_mode = false;
  478. std::shared_ptr<grpc::Channel> channel =
  479. CreateCliChannel(server_address, cred);
  480. if (!absl::GetFlag(FLAGS_binary_input) ||
  481. !absl::GetFlag(FLAGS_binary_output)) {
  482. parser = absl::make_unique<grpc::testing::ProtoFileParser>(
  483. absl::GetFlag(FLAGS_remotedb) ? channel : nullptr,
  484. absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles));
  485. if (parser->HasError()) {
  486. fprintf(
  487. stderr,
  488. "Failed to find remote reflection service and local proto files.\n");
  489. return false;
  490. }
  491. }
  492. if (absl::GetFlag(FLAGS_binary_input)) {
  493. formatted_method_name = method_name;
  494. } else {
  495. formatted_method_name = parser->GetFormattedMethodName(method_name);
  496. if (parser->HasError()) {
  497. fprintf(stderr, "Failed to find method %s in proto files.\n",
  498. method_name.c_str());
  499. }
  500. }
  501. if (argc == 3) {
  502. request_text = argv[2];
  503. }
  504. if (parser->IsStreaming(method_name, true /* is_request */)) {
  505. std::istream* input_stream;
  506. std::ifstream input_file;
  507. if (absl::GetFlag(FLAGS_batch)) {
  508. fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
  509. return false;
  510. }
  511. std::multimap<std::string, std::string> client_metadata;
  512. ParseMetadataFlag(&client_metadata);
  513. PrintMetadata(client_metadata, "Sending client initial metadata:");
  514. CliCall call(channel, formatted_method_name, client_metadata, cli_args);
  515. if (absl::GetFlag(FLAGS_display_peer_address)) {
  516. fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n",
  517. formatted_method_name.c_str(), call.peer().c_str());
  518. }
  519. if (absl::GetFlag(FLAGS_infile).empty()) {
  520. if (isatty(fileno(stdin))) {
  521. print_mode = true;
  522. fprintf(stderr, "reading streaming request message from stdin...\n");
  523. }
  524. input_stream = &std::cin;
  525. } else {
  526. input_file.open(absl::GetFlag(FLAGS_infile),
  527. std::ios::in | std::ios::binary);
  528. input_stream = &input_file;
  529. }
  530. gpr_mu parser_mu;
  531. gpr_mu_init(&parser_mu);
  532. std::thread read_thread(ReadResponse, &call, method_name, callback,
  533. parser.get(), &parser_mu, print_mode);
  534. std::stringstream request_ss;
  535. std::string line;
  536. while (!request_text.empty() ||
  537. (!input_stream->eof() && getline(*input_stream, line))) {
  538. if (!request_text.empty()) {
  539. if (absl::GetFlag(FLAGS_binary_input)) {
  540. serialized_request_proto = request_text;
  541. request_text.clear();
  542. } else {
  543. gpr_mu_lock(&parser_mu);
  544. serialized_request_proto = parser->GetSerializedProtoFromMethod(
  545. method_name, request_text, true /* is_request */,
  546. absl::GetFlag(FLAGS_json_input));
  547. request_text.clear();
  548. if (parser->HasError()) {
  549. if (print_mode) {
  550. fprintf(stderr, "Failed to parse request.\n");
  551. }
  552. gpr_mu_unlock(&parser_mu);
  553. continue;
  554. }
  555. gpr_mu_unlock(&parser_mu);
  556. }
  557. call.WriteAndWait(serialized_request_proto);
  558. if (print_mode) {
  559. fprintf(stderr, "Request sent.\n");
  560. }
  561. } else {
  562. if (line.length() == 0) {
  563. request_text = request_ss.str();
  564. request_ss.str(std::string());
  565. request_ss.clear();
  566. } else {
  567. request_ss << line << ' ';
  568. }
  569. }
  570. }
  571. if (input_file.is_open()) {
  572. input_file.close();
  573. }
  574. call.WritesDoneAndWait();
  575. read_thread.join();
  576. gpr_mu_destroy(&parser_mu);
  577. std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata;
  578. Status status = call.Finish(&server_trailing_metadata);
  579. PrintMetadata(server_trailing_metadata,
  580. "Received trailing metadata from server:");
  581. if (status.ok()) {
  582. fprintf(stderr, "Stream RPC succeeded with OK status\n");
  583. return true;
  584. } else {
  585. fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
  586. status.error_code(), status.error_message().c_str());
  587. return false;
  588. }
  589. } else { // parser->IsStreaming(method_name, true /* is_request */)
  590. if (absl::GetFlag(FLAGS_batch)) {
  591. if (parser->IsStreaming(method_name, false /* is_request */)) {
  592. fprintf(stderr, "Batch mode for streaming RPC is not supported.\n");
  593. return false;
  594. }
  595. std::istream* input_stream;
  596. std::ifstream input_file;
  597. if (absl::GetFlag(FLAGS_infile).empty()) {
  598. if (isatty(fileno(stdin))) {
  599. print_mode = true;
  600. fprintf(stderr, "reading request messages from stdin...\n");
  601. }
  602. input_stream = &std::cin;
  603. } else {
  604. input_file.open(absl::GetFlag(FLAGS_infile),
  605. std::ios::in | std::ios::binary);
  606. input_stream = &input_file;
  607. }
  608. std::multimap<std::string, std::string> client_metadata;
  609. ParseMetadataFlag(&client_metadata);
  610. if (print_mode) {
  611. PrintMetadata(client_metadata, "Sending client initial metadata:");
  612. }
  613. std::stringstream request_ss;
  614. std::string line;
  615. while (!request_text.empty() ||
  616. (!input_stream->eof() && getline(*input_stream, line))) {
  617. if (!request_text.empty()) {
  618. if (absl::GetFlag(FLAGS_binary_input)) {
  619. serialized_request_proto = request_text;
  620. request_text.clear();
  621. } else {
  622. serialized_request_proto = parser->GetSerializedProtoFromMethod(
  623. method_name, request_text, true /* is_request */,
  624. absl::GetFlag(FLAGS_json_input));
  625. request_text.clear();
  626. if (parser->HasError()) {
  627. if (print_mode) {
  628. fprintf(stderr, "Failed to parse request.\n");
  629. }
  630. continue;
  631. }
  632. }
  633. std::string serialized_response_proto;
  634. std::multimap<grpc::string_ref, grpc::string_ref>
  635. server_initial_metadata, server_trailing_metadata;
  636. CliCall call(channel, formatted_method_name, client_metadata,
  637. cli_args);
  638. if (absl::GetFlag(FLAGS_display_peer_address)) {
  639. fprintf(stderr,
  640. "New call for method_name:%s has peer address:|%s|\n",
  641. formatted_method_name.c_str(), call.peer().c_str());
  642. }
  643. call.Write(serialized_request_proto);
  644. call.WritesDone();
  645. if (!call.Read(&serialized_response_proto,
  646. &server_initial_metadata)) {
  647. fprintf(stderr, "Failed to read response.\n");
  648. }
  649. Status status = call.Finish(&server_trailing_metadata);
  650. if (status.ok()) {
  651. if (print_mode) {
  652. fprintf(stderr, "Rpc succeeded with OK status.\n");
  653. PrintMetadata(server_initial_metadata,
  654. "Received initial metadata from server:");
  655. PrintMetadata(server_trailing_metadata,
  656. "Received trailing metadata from server:");
  657. }
  658. if (absl::GetFlag(FLAGS_binary_output)) {
  659. if (!callback(serialized_response_proto)) {
  660. break;
  661. }
  662. } else {
  663. std::string response_text = parser->GetFormattedStringFromMethod(
  664. method_name, serialized_response_proto,
  665. false /* is_request */, absl::GetFlag(FLAGS_json_output));
  666. if (parser->HasError() && print_mode) {
  667. fprintf(stderr, "Failed to parse response.\n");
  668. } else {
  669. if (!callback(response_text)) {
  670. break;
  671. }
  672. }
  673. }
  674. } else {
  675. if (print_mode) {
  676. fprintf(stderr,
  677. "Rpc failed with status code %d, error message: %s\n",
  678. status.error_code(), status.error_message().c_str());
  679. }
  680. }
  681. } else {
  682. if (line.length() == 0) {
  683. request_text = request_ss.str();
  684. request_ss.str(std::string());
  685. request_ss.clear();
  686. } else {
  687. request_ss << line << ' ';
  688. }
  689. }
  690. }
  691. if (input_file.is_open()) {
  692. input_file.close();
  693. }
  694. return true;
  695. }
  696. if (argc == 3) {
  697. if (!absl::GetFlag(FLAGS_infile).empty()) {
  698. fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
  699. }
  700. } else {
  701. std::stringstream input_stream;
  702. if (absl::GetFlag(FLAGS_infile).empty()) {
  703. if (isatty(fileno(stdin))) {
  704. fprintf(stderr, "reading request message from stdin...\n");
  705. }
  706. input_stream << std::cin.rdbuf();
  707. } else {
  708. std::ifstream input_file(absl::GetFlag(FLAGS_infile),
  709. std::ios::in | std::ios::binary);
  710. input_stream << input_file.rdbuf();
  711. input_file.close();
  712. }
  713. request_text = input_stream.str();
  714. }
  715. if (absl::GetFlag(FLAGS_binary_input)) {
  716. serialized_request_proto = request_text;
  717. } else {
  718. serialized_request_proto = parser->GetSerializedProtoFromMethod(
  719. method_name, request_text, true /* is_request */,
  720. absl::GetFlag(FLAGS_json_input));
  721. if (parser->HasError()) {
  722. fprintf(stderr, "Failed to parse request.\n");
  723. return false;
  724. }
  725. }
  726. fprintf(stderr, "connecting to %s\n", server_address.c_str());
  727. std::string serialized_response_proto;
  728. std::multimap<std::string, std::string> client_metadata;
  729. std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata,
  730. server_trailing_metadata;
  731. ParseMetadataFlag(&client_metadata);
  732. PrintMetadata(client_metadata, "Sending client initial metadata:");
  733. CliCall call(channel, formatted_method_name, client_metadata, cli_args);
  734. if (absl::GetFlag(FLAGS_display_peer_address)) {
  735. fprintf(stderr, "New call for method_name:%s has peer address:|%s|\n",
  736. formatted_method_name.c_str(), call.peer().c_str());
  737. }
  738. call.Write(serialized_request_proto);
  739. call.WritesDone();
  740. for (bool receive_initial_metadata = true; call.Read(
  741. &serialized_response_proto,
  742. receive_initial_metadata ? &server_initial_metadata : nullptr);
  743. receive_initial_metadata = false) {
  744. if (!absl::GetFlag(FLAGS_binary_output)) {
  745. serialized_response_proto = parser->GetFormattedStringFromMethod(
  746. method_name, serialized_response_proto, false /* is_request */,
  747. absl::GetFlag(FLAGS_json_output));
  748. if (parser->HasError()) {
  749. fprintf(stderr, "Failed to parse response.\n");
  750. return false;
  751. }
  752. }
  753. if (receive_initial_metadata) {
  754. PrintMetadata(server_initial_metadata,
  755. "Received initial metadata from server:");
  756. }
  757. if (!callback(serialized_response_proto)) {
  758. return false;
  759. }
  760. }
  761. Status status = call.Finish(&server_trailing_metadata);
  762. PrintMetadata(server_trailing_metadata,
  763. "Received trailing metadata from server:");
  764. if (status.ok()) {
  765. fprintf(stderr, "Rpc succeeded with OK status\n");
  766. return true;
  767. } else {
  768. fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
  769. status.error_code(), status.error_message().c_str());
  770. return false;
  771. }
  772. }
  773. GPR_UNREACHABLE_CODE(return false);
  774. }
  775. bool GrpcTool::ParseMessage(int argc, const char** argv,
  776. const CliCredentials& cred,
  777. const GrpcToolOutputCallback& callback) {
  778. CommandUsage(
  779. "Parse message\n"
  780. " grpc_cli parse <address> <type> [<message>]\n"
  781. " <address> ; host:port\n"
  782. " <type> ; Protocol buffer type name\n"
  783. " <message> ; Text protobuffer (overrides --infile)\n"
  784. " --protofiles ; Comma separated proto files used as a"
  785. " fallback when parsing request/response\n"
  786. " --proto_path ; The search paths of proto files"
  787. " (" GRPC_CLI_PATH_SEPARATOR
  788. " separated), valid only when --protofiles is given\n"
  789. " --noremotedb ; Don't attempt to use reflection service"
  790. " at all\n"
  791. " --infile ; Input filename (defaults to stdin)\n"
  792. " --outfile ; Output filename (defaults to stdout)\n"
  793. " --binary_input ; Input in binary format\n"
  794. " --binary_output ; Output in binary format\n"
  795. " --json_input ; Input in json format\n"
  796. " --json_output ; Output in json format\n" +
  797. cred.GetCredentialUsage());
  798. std::stringstream output_ss;
  799. std::string message_text;
  800. std::string server_address(argv[0]);
  801. std::string type_name(argv[1]);
  802. std::unique_ptr<grpc::testing::ProtoFileParser> parser;
  803. std::string serialized_request_proto;
  804. if (argc == 3) {
  805. message_text = argv[2];
  806. if (!absl::GetFlag(FLAGS_infile).empty()) {
  807. fprintf(stderr, "warning: message given in argv, ignoring --infile.\n");
  808. }
  809. } else {
  810. std::stringstream input_stream;
  811. if (absl::GetFlag(FLAGS_infile).empty()) {
  812. if (isatty(fileno(stdin))) {
  813. fprintf(stderr, "reading request message from stdin...\n");
  814. }
  815. input_stream << std::cin.rdbuf();
  816. } else {
  817. std::ifstream input_file(absl::GetFlag(FLAGS_infile),
  818. std::ios::in | std::ios::binary);
  819. input_stream << input_file.rdbuf();
  820. input_file.close();
  821. }
  822. message_text = input_stream.str();
  823. }
  824. if (!absl::GetFlag(FLAGS_binary_input) ||
  825. !absl::GetFlag(FLAGS_binary_output)) {
  826. std::shared_ptr<grpc::Channel> channel =
  827. CreateCliChannel(server_address, cred);
  828. parser = absl::make_unique<grpc::testing::ProtoFileParser>(
  829. absl::GetFlag(FLAGS_remotedb) ? channel : nullptr,
  830. absl::GetFlag(FLAGS_proto_path), absl::GetFlag(FLAGS_protofiles));
  831. if (parser->HasError()) {
  832. fprintf(
  833. stderr,
  834. "Failed to find remote reflection service and local proto files.\n");
  835. return false;
  836. }
  837. }
  838. if (absl::GetFlag(FLAGS_binary_input)) {
  839. serialized_request_proto = message_text;
  840. } else {
  841. serialized_request_proto = parser->GetSerializedProtoFromMessageType(
  842. type_name, message_text, absl::GetFlag(FLAGS_json_input));
  843. if (parser->HasError()) {
  844. fprintf(stderr, "Failed to serialize the message.\n");
  845. return false;
  846. }
  847. }
  848. if (absl::GetFlag(FLAGS_binary_output)) {
  849. output_ss << serialized_request_proto;
  850. } else {
  851. std::string output_text;
  852. output_text = parser->GetFormattedStringFromMessageType(
  853. type_name, serialized_request_proto, absl::GetFlag(FLAGS_json_output));
  854. if (parser->HasError()) {
  855. fprintf(stderr, "Failed to deserialize the message.\n");
  856. return false;
  857. }
  858. output_ss << output_text << std::endl;
  859. }
  860. return callback(output_ss.str());
  861. }
  862. bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred,
  863. const GrpcToolOutputCallback& callback) {
  864. CommandUsage(
  865. "Convert binary message to text\n"
  866. " grpc_cli totext <protofiles> <type>\n"
  867. " <protofiles> ; Comma separated list of proto files\n"
  868. " <type> ; Protocol buffer type name\n"
  869. " --proto_path ; The search paths of proto files"
  870. " (" GRPC_CLI_PATH_SEPARATOR
  871. " separated)\n"
  872. " --infile ; Input filename (defaults to stdin)\n"
  873. " --outfile ; Output filename (defaults to stdout)\n");
  874. absl::SetFlag(&FLAGS_protofiles, argv[0]);
  875. absl::SetFlag(&FLAGS_remotedb, false);
  876. absl::SetFlag(&FLAGS_binary_input, true);
  877. absl::SetFlag(&FLAGS_binary_output, false);
  878. return ParseMessage(argc, argv, cred, callback);
  879. }
  880. bool GrpcTool::ToJson(int argc, const char** argv, const CliCredentials& cred,
  881. const GrpcToolOutputCallback& callback) {
  882. CommandUsage(
  883. "Convert binary message to json\n"
  884. " grpc_cli tojson <protofiles> <type>\n"
  885. " <protofiles> ; Comma separated list of proto files\n"
  886. " <type> ; Protocol buffer type name\n"
  887. " --proto_path ; The search paths of proto files"
  888. " (" GRPC_CLI_PATH_SEPARATOR
  889. " separated)\n"
  890. " --infile ; Input filename (defaults to stdin)\n"
  891. " --outfile ; Output filename (defaults to stdout)\n");
  892. absl::SetFlag(&FLAGS_protofiles, argv[0]);
  893. absl::SetFlag(&FLAGS_remotedb, false);
  894. absl::SetFlag(&FLAGS_binary_input, true);
  895. absl::SetFlag(&FLAGS_binary_output, false);
  896. absl::SetFlag(&FLAGS_json_output, true);
  897. return ParseMessage(argc, argv, cred, callback);
  898. }
  899. bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred,
  900. const GrpcToolOutputCallback& callback) {
  901. CommandUsage(
  902. "Convert text message to binary\n"
  903. " grpc_cli tobinary <protofiles> <type> [<message>]\n"
  904. " <protofiles> ; Comma separated list of proto files\n"
  905. " <type> ; Protocol buffer type name\n"
  906. " --proto_path ; The search paths of proto files"
  907. " (" GRPC_CLI_PATH_SEPARATOR
  908. " separated)\n"
  909. " --infile ; Input filename (defaults to stdin)\n"
  910. " --outfile ; Output filename (defaults to stdout)\n");
  911. absl::SetFlag(&FLAGS_protofiles, argv[0]);
  912. absl::SetFlag(&FLAGS_remotedb, false);
  913. absl::SetFlag(&FLAGS_binary_input, false);
  914. absl::SetFlag(&FLAGS_binary_output, true);
  915. return ParseMessage(argc, argv, cred, callback);
  916. }
  917. } // namespace testing
  918. } // namespace grpc