| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 | /* * * Copyright 2016 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */#include "test/cpp/util/proto_file_parser.h"#include <algorithm>#include <iostream>#include <sstream>#include <unordered_set>#include <grpcpp/support/config.h>namespace grpc {namespace testing {namespace {// Match the user input method string to the full_name from method descriptor.bool MethodNameMatch(const grpc::string& full_name, const grpc::string& input) {  grpc::string clean_input = input;  std::replace(clean_input.begin(), clean_input.end(), '/', '.');  if (clean_input.size() > full_name.size()) {    return false;  }  return full_name.compare(full_name.size() - clean_input.size(),                           clean_input.size(), clean_input) == 0;}}  // namespaceclass ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector { public:  explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {}  void AddError(const grpc::string& filename, int line, int column,                const grpc::string& message) override {    std::ostringstream oss;    oss << "error " << filename << " " << line << " " << column << " "        << message << "\n";    parser_->LogError(oss.str());  }  void AddWarning(const grpc::string& filename, int line, int column,                  const grpc::string& message) override {    std::cerr << "warning " << filename << " " << line << " " << column << " "              << message << std::endl;  } private:  ProtoFileParser* parser_;  // not owned};ProtoFileParser::ProtoFileParser(const std::shared_ptr<grpc::Channel>& channel,                                 const grpc::string& proto_path,                                 const grpc::string& protofiles)    : has_error_(false),      dynamic_factory_(new protobuf::DynamicMessageFactory()) {  std::vector<grpc::string> service_list;  if (channel) {    reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel));    reflection_db_->GetServices(&service_list);  }  std::unordered_set<grpc::string> known_services;  if (!protofiles.empty()) {    source_tree_.MapPath("", proto_path);    error_printer_.reset(new ErrorPrinter(this));    importer_.reset(        new protobuf::compiler::Importer(&source_tree_, error_printer_.get()));    grpc::string file_name;    std::stringstream ss(protofiles);    while (std::getline(ss, file_name, ',')) {      const auto* file_desc = importer_->Import(file_name);      if (file_desc) {        for (int i = 0; i < file_desc->service_count(); i++) {          service_desc_list_.push_back(file_desc->service(i));          known_services.insert(file_desc->service(i)->full_name());        }      } else {        std::cerr << file_name << " not found" << std::endl;      }    }    file_db_.reset(new protobuf::DescriptorPoolDatabase(*importer_->pool()));  }  if (!reflection_db_ && !file_db_) {    LogError("No available proto database");    return;  }  if (!reflection_db_) {    desc_db_ = std::move(file_db_);  } else if (!file_db_) {    desc_db_ = std::move(reflection_db_);  } else {    desc_db_.reset(new protobuf::MergedDescriptorDatabase(reflection_db_.get(),                                                          file_db_.get()));  }  desc_pool_.reset(new protobuf::DescriptorPool(desc_db_.get()));  for (auto it = service_list.begin(); it != service_list.end(); it++) {    if (known_services.find(*it) == known_services.end()) {      if (const protobuf::ServiceDescriptor* service_desc =              desc_pool_->FindServiceByName(*it)) {        service_desc_list_.push_back(service_desc);        known_services.insert(*it);      }    }  }}ProtoFileParser::~ProtoFileParser() {}grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {  has_error_ = false;  if (known_methods_.find(method) != known_methods_.end()) {    return known_methods_[method];  }  const protobuf::MethodDescriptor* method_descriptor = nullptr;  for (auto it = service_desc_list_.begin(); it != service_desc_list_.end();       it++) {    const auto* service_desc = *it;    for (int j = 0; j < service_desc->method_count(); j++) {      const auto* method_desc = service_desc->method(j);      if (MethodNameMatch(method_desc->full_name(), method)) {        if (method_descriptor) {          std::ostringstream error_stream;          error_stream << "Ambiguous method names: ";          error_stream << method_descriptor->full_name() << " ";          error_stream << method_desc->full_name();          LogError(error_stream.str());        }        method_descriptor = method_desc;      }    }  }  if (!method_descriptor) {    LogError("Method name not found");  }  if (has_error_) {    return "";  }  known_methods_[method] = method_descriptor->full_name();  return method_descriptor->full_name();}grpc::string ProtoFileParser::GetFormattedMethodName(    const grpc::string& method) {  has_error_ = false;  grpc::string formatted_method_name = GetFullMethodName(method);  if (has_error_) {    return "";  }  size_t last_dot = formatted_method_name.find_last_of('.');  if (last_dot != grpc::string::npos) {    formatted_method_name[last_dot] = '/';  }  formatted_method_name.insert(formatted_method_name.begin(), '/');  return formatted_method_name;}grpc::string ProtoFileParser::GetMessageTypeFromMethod(    const grpc::string& method, bool is_request) {  has_error_ = false;  grpc::string full_method_name = GetFullMethodName(method);  if (has_error_) {    return "";  }  const protobuf::MethodDescriptor* method_desc =      desc_pool_->FindMethodByName(full_method_name);  if (!method_desc) {    LogError("Method not found");    return "";  }  return is_request ? method_desc->input_type()->full_name()                    : method_desc->output_type()->full_name();}bool ProtoFileParser::IsStreaming(const grpc::string& method, bool is_request) {  has_error_ = false;  grpc::string full_method_name = GetFullMethodName(method);  if (has_error_) {    return false;  }  const protobuf::MethodDescriptor* method_desc =      desc_pool_->FindMethodByName(full_method_name);  if (!method_desc) {    LogError("Method not found");    return false;  }  return is_request ? method_desc->client_streaming()                    : method_desc->server_streaming();}grpc::string ProtoFileParser::GetSerializedProtoFromMethod(    const grpc::string& method, const grpc::string& formatted_proto,    bool is_request, bool is_json_format) {  has_error_ = false;  grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);  if (has_error_) {    return "";  }  return GetSerializedProtoFromMessageType(message_type_name, formatted_proto,                                           is_json_format);}grpc::string ProtoFileParser::GetFormattedStringFromMethod(    const grpc::string& method, const grpc::string& serialized_proto,    bool is_request, bool is_json_format) {  has_error_ = false;  grpc::string message_type_name = GetMessageTypeFromMethod(method, is_request);  if (has_error_) {    return "";  }  return GetFormattedStringFromMessageType(message_type_name, serialized_proto,                                           is_json_format);}grpc::string ProtoFileParser::GetSerializedProtoFromMessageType(    const grpc::string& message_type_name, const grpc::string& formatted_proto,    bool is_json_format) {  has_error_ = false;  grpc::string serialized;  const protobuf::Descriptor* desc =      desc_pool_->FindMessageTypeByName(message_type_name);  if (!desc) {    LogError("Message type not found");    return "";  }  std::unique_ptr<grpc::protobuf::Message> msg(      dynamic_factory_->GetPrototype(desc)->New());  bool ok;  if (is_json_format) {    ok = grpc::protobuf::json::JsonStringToMessage(formatted_proto, msg.get())             .ok();    if (!ok) {      LogError("Failed to convert json format to proto.");      return "";    }  } else {    ok = protobuf::TextFormat::ParseFromString(formatted_proto, msg.get());    if (!ok) {      LogError("Failed to convert text format to proto.");      return "";    }  }  ok = msg->SerializeToString(&serialized);  if (!ok) {    LogError("Failed to serialize proto.");    return "";  }  return serialized;}grpc::string ProtoFileParser::GetFormattedStringFromMessageType(    const grpc::string& message_type_name, const grpc::string& serialized_proto,    bool is_json_format) {  has_error_ = false;  const protobuf::Descriptor* desc =      desc_pool_->FindMessageTypeByName(message_type_name);  if (!desc) {    LogError("Message type not found");    return "";  }  std::unique_ptr<grpc::protobuf::Message> msg(      dynamic_factory_->GetPrototype(desc)->New());  if (!msg->ParseFromString(serialized_proto)) {    LogError("Failed to deserialize proto.");    return "";  }  grpc::string formatted_string;  if (is_json_format) {    grpc::protobuf::json::JsonPrintOptions jsonPrintOptions;    jsonPrintOptions.add_whitespace = true;    if (!grpc::protobuf::json::MessageToJsonString(             *msg.get(), &formatted_string, jsonPrintOptions)             .ok()) {      LogError("Failed to print proto message to json format");      return "";    }  } else {    if (!protobuf::TextFormat::PrintToString(*msg.get(), &formatted_string)) {      LogError("Failed to print proto message to text format");      return "";    }  }  return formatted_string;}void ProtoFileParser::LogError(const grpc::string& error_msg) {  if (!error_msg.empty()) {    std::cerr << error_msg << std::endl;  }  has_error_ = true;}}  // namespace testing}  // namespace grpc
 |