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
1078
                                Server::Configuration::CommonFactoryContext& context) {
30
1078
  GoogleGrpcCredentialsFactory* credentials_factory = nullptr;
31
1078
  const std::string& google_grpc_credentials_factory_name =
32
1078
      grpc_service.google_grpc().credentials_factory_name();
33
1078
  if (google_grpc_credentials_factory_name.empty()) {
34
1068
    credentials_factory = Registry::FactoryRegistry<GoogleGrpcCredentialsFactory>::getFactory(
35
1068
        "envoy.grpc_credentials.default");
36
1072
  } else {
37
10
    credentials_factory = Registry::FactoryRegistry<GoogleGrpcCredentialsFactory>::getFactory(
38
10
        google_grpc_credentials_factory_name);
39
10
  }
40
1078
  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
1077
  return credentials_factory->getChannelCredentials(grpc_service, context);
45
1078
}
46

            
47
} // namespace
48

            
49
struct BufferInstanceContainer {
50
  BufferInstanceContainer(int ref_count, Buffer::InstancePtr&& buffer)
51
65261
      : 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
65263
  static void derefBufferInstanceContainer(void* container_ptr) {
56
65263
    auto container = static_cast<BufferInstanceContainer*>(container_ptr);
57
65263
    container->ref_count_--;
58
    // This is safe because the ref_count_ is never incremented.
59
65263
    if (container->ref_count_ <= 0) {
60
65261
      delete container;
61
65261
    }
62
65263
  }
63
};
64

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

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

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

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

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

            
98
3836
Buffer::InstancePtr GoogleGrpcUtils::makeBufferInstance(const grpc::ByteBuffer& byte_buffer) {
99
3836
  auto buffer = std::make_unique<Buffer::OwnedImpl>();
100
3836
  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
3826
  std::vector<grpc::Slice> slices;
106
3826
  if (!byte_buffer.Dump(&slices).ok()) {
107
    return nullptr;
108
  }
109

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

            
116
grpc::ChannelArguments
117
1078
GoogleGrpcUtils::channelArgsFromConfig(const envoy::config::core::v3::GrpcService& config) {
118
1078
  grpc::ChannelArguments args;
119
1081
  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
1078
  return args;
133
1078
}
134

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

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