1
#include "source/common/grpc/google_grpc_utils.h"
2

            
3
#include <atomic>
4
#include <cstdint>
5
#include <cstring>
6
#include <string>
7

            
8
#include "envoy/grpc/google_grpc_creds.h"
9
#include "envoy/registry/registry.h"
10

            
11
#include "source/common/buffer/buffer_impl.h"
12
#include "source/common/common/assert.h"
13
#include "source/common/common/empty_string.h"
14
#include "source/common/common/enum_to_int.h"
15
#include "source/common/common/fmt.h"
16
#include "source/common/common/macros.h"
17
#include "source/common/common/utility.h"
18

            
19
#include "absl/container/fixed_array.h"
20
#include "absl/strings/match.h"
21

            
22
namespace Envoy {
23
namespace Grpc {
24

            
25
namespace {
26

            
27
std::shared_ptr<grpc::ChannelCredentials>
28
getGoogleGrpcChannelCredentials(const envoy::config::core::v3::GrpcService& grpc_service,
29
1089
                                Server::Configuration::CommonFactoryContext& context) {
30
1089
  GoogleGrpcCredentialsFactory* credentials_factory = nullptr;
31
1089
  const std::string& google_grpc_credentials_factory_name =
32
1089
      grpc_service.google_grpc().credentials_factory_name();
33
1089
  if (google_grpc_credentials_factory_name.empty()) {
34
1079
    credentials_factory = Registry::FactoryRegistry<GoogleGrpcCredentialsFactory>::getFactory(
35
1079
        "envoy.grpc_credentials.default");
36
1083
  } else {
37
10
    credentials_factory = Registry::FactoryRegistry<GoogleGrpcCredentialsFactory>::getFactory(
38
10
        google_grpc_credentials_factory_name);
39
10
  }
40
1089
  if (credentials_factory == nullptr) {
41
1
    throw EnvoyException(absl::StrCat("Unknown google grpc credentials factory: ",
42
1
                                      google_grpc_credentials_factory_name));
43
1
  }
44
1088
  return credentials_factory->getChannelCredentials(grpc_service, context);
45
1089
}
46

            
47
} // namespace
48

            
49
struct BufferInstanceContainer {
50
  BufferInstanceContainer(int ref_count, Buffer::InstancePtr&& buffer)
51
65290
      : ref_count_(ref_count), buffer_(std::move(buffer)) {}
52
  std::atomic<uint32_t> ref_count_; // In case gPRC dereferences in a different threads.
53
  Buffer::InstancePtr buffer_;
54

            
55
65292
  static void derefBufferInstanceContainer(void* container_ptr) {
56
65292
    auto container = static_cast<BufferInstanceContainer*>(container_ptr);
57
65292
    container->ref_count_--;
58
    // This is safe because the ref_count_ is never incremented.
59
65292
    if (container->ref_count_ <= 0) {
60
65290
      delete container;
61
65290
    }
62
65292
  }
63
};
64

            
65
65294
grpc::ByteBuffer GoogleGrpcUtils::makeByteBuffer(Buffer::InstancePtr&& buffer_instance) {
66
65294
  if (!buffer_instance) {
67
1
    return {};
68
1
  }
69
65293
  Buffer::RawSliceVector raw_slices = buffer_instance->getRawSlices();
70
65293
  if (raw_slices.empty()) {
71
3
    return {};
72
3
  }
73

            
74
65290
  auto* container =
75
65290
      new BufferInstanceContainer{static_cast<int>(raw_slices.size()), std::move(buffer_instance)};
76
65290
  std::vector<grpc::Slice> slices;
77
65290
  slices.reserve(raw_slices.size());
78
65292
  for (Buffer::RawSlice& raw_slice : raw_slices) {
79
65292
    slices.emplace_back(raw_slice.mem_, raw_slice.len_,
80
65292
                        &BufferInstanceContainer::derefBufferInstanceContainer, container);
81
65292
  }
82
65290
  return {&slices[0], slices.size()}; // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
83
65293
}
84

            
85
class GrpcSliceBufferFragmentImpl : public Buffer::BufferFragment {
86
public:
87
15594
  explicit GrpcSliceBufferFragmentImpl(grpc::Slice&& slice) : slice_(std::move(slice)) {}
88

            
89
  // Buffer::BufferFragment
90
15594
  const void* data() const override { return slice_.begin(); }
91
46782
  size_t size() const override { return slice_.size(); }
92
15594
  void done() override { delete this; }
93

            
94
private:
95
  const grpc::Slice slice_;
96
};
97

            
98
3862
Buffer::InstancePtr GoogleGrpcUtils::makeBufferInstance(const grpc::ByteBuffer& byte_buffer) {
99
3862
  auto buffer = std::make_unique<Buffer::OwnedImpl>();
100
3862
  if (byte_buffer.Length() == 0) {
101
10
    return buffer;
102
10
  }
103
  // NB: ByteBuffer::Dump moves the data out of the ByteBuffer so we need to ensure that the
104
  // lifetime of the Slice(s) exceeds our Buffer::Instance.
105
3852
  std::vector<grpc::Slice> slices;
106
3852
  if (!byte_buffer.Dump(&slices).ok()) {
107
    return nullptr;
108
  }
109

            
110
15594
  for (auto& slice : slices) {
111
15594
    buffer->addBufferFragment(*new GrpcSliceBufferFragmentImpl(std::move(slice)));
112
15594
  }
113
3852
  return buffer;
114
3852
}
115

            
116
grpc::ChannelArguments
117
1089
GoogleGrpcUtils::channelArgsFromConfig(const envoy::config::core::v3::GrpcService& config) {
118
1089
  grpc::ChannelArguments args;
119
1092
  for (const auto& channel_arg : config.google_grpc().channel_args().args()) {
120
5
    switch (channel_arg.second.value_specifier_case()) {
121
3
    case envoy::config::core::v3::GrpcService::GoogleGrpc::ChannelArgs::Value::kStringValue:
122
3
      args.SetString(channel_arg.first, channel_arg.second.string_value());
123
3
      break;
124
2
    case envoy::config::core::v3::GrpcService::GoogleGrpc::ChannelArgs::Value::kIntValue:
125
2
      args.SetInt(channel_arg.first, channel_arg.second.int_value());
126
2
      break;
127
    case envoy::config::core::v3::GrpcService::GoogleGrpc::ChannelArgs::Value::
128
        VALUE_SPECIFIER_NOT_SET:
129
      PANIC_DUE_TO_PROTO_UNSET;
130
5
    }
131
5
  }
132
1089
  return args;
133
1089
}
134

            
135
std::shared_ptr<grpc::Channel>
136
GoogleGrpcUtils::createChannel(const envoy::config::core::v3::GrpcService& config,
137
1089
                               Server::Configuration::CommonFactoryContext& context) {
138
1089
  std::shared_ptr<grpc::ChannelCredentials> creds =
139
1089
      getGoogleGrpcChannelCredentials(config, context);
140
1089
  const grpc::ChannelArguments args = channelArgsFromConfig(config);
141
1089
  return CreateCustomChannel(config.google_grpc().target_uri(), creds, args);
142
1089
}
143

            
144
} // namespace Grpc
145
} // namespace Envoy