1
// NOLINT(namespace-envoy)
2

            
3
// This file provides host-side implementations for the cluster dynamic module ABI callbacks.
4

            
5
#include "source/common/common/assert.h"
6
#include "source/common/http/message_impl.h"
7
#include "source/extensions/clusters/dynamic_modules/cluster.h"
8
#include "source/extensions/dynamic_modules/abi/abi.h"
9

            
10
namespace {
11

            
12
Envoy::Extensions::Clusters::DynamicModules::DynamicModuleCluster*
13
42
getCluster(envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr) {
14
42
  return static_cast<Envoy::Extensions::Clusters::DynamicModules::DynamicModuleCluster*>(
15
42
      cluster_envoy_ptr);
16
42
}
17

            
18
Envoy::Extensions::Clusters::DynamicModules::DynamicModuleLoadBalancer*
19
154
getLb(envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr) {
20
154
  return static_cast<Envoy::Extensions::Clusters::DynamicModules::DynamicModuleLoadBalancer*>(
21
154
      lb_envoy_ptr);
22
154
}
23

            
24
Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterConfig*
25
49
getConfig(envoy_dynamic_module_type_cluster_config_envoy_ptr config_envoy_ptr) {
26
49
  return static_cast<Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterConfig*>(
27
49
      config_envoy_ptr);
28
49
}
29

            
30
Envoy::Upstream::LoadBalancerContext*
31
24
getContext(envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr) {
32
24
  return static_cast<Envoy::Upstream::LoadBalancerContext*>(context_envoy_ptr);
33
24
}
34

            
35
// Helper to look up a metadata value by filter name and key for a host in the cluster priority set.
36
const Envoy::Protobuf::Value*
37
getClusterHostMetadataValue(envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
38
                            uint32_t priority, size_t index,
39
                            envoy_dynamic_module_type_module_buffer filter_name,
40
13
                            envoy_dynamic_module_type_module_buffer key) {
41
13
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
42
13
  if (priority >= host_sets.size()) {
43
1
    return nullptr;
44
1
  }
45
12
  const auto& hosts = host_sets[priority]->hosts();
46
12
  if (index >= hosts.size()) {
47
1
    return nullptr;
48
1
  }
49
11
  const auto& metadata = hosts[index]->metadata();
50
11
  if (metadata == nullptr) {
51
3
    return nullptr;
52
3
  }
53
8
  const auto& filter_metadata = metadata->filter_metadata();
54
8
  absl::string_view filter_name_view(filter_name.ptr, filter_name.length);
55
8
  auto filter_it = filter_metadata.find(filter_name_view);
56
8
  if (filter_it == filter_metadata.end()) {
57
1
    return nullptr;
58
1
  }
59
7
  absl::string_view key_view(key.ptr, key.length);
60
7
  auto field_it = filter_it->second.fields().find(key_view);
61
7
  if (field_it == filter_it->second.fields().end()) {
62
1
    return nullptr;
63
1
  }
64
6
  return &field_it->second;
65
7
}
66

            
67
Envoy::Stats::StatNameTagVector buildTagsForClusterMetric(
68
    Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterConfig& config,
69
    const Envoy::Stats::StatNameVec& label_names,
70
5
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length) {
71
5
  ASSERT(label_values_length == label_names.size());
72
5
  Envoy::Stats::StatNameTagVector tags;
73
5
  tags.reserve(label_values_length);
74
11
  for (size_t i = 0; i < label_values_length; i++) {
75
6
    absl::string_view label_value_view(label_values[i].ptr, label_values[i].length);
76
6
    auto label_value = config.stat_name_pool_.add(label_value_view);
77
6
    tags.push_back(Envoy::Stats::StatNameTag(label_names[i], label_value));
78
6
  }
79
5
  return tags;
80
5
}
81

            
82
} // namespace
83

            
84
extern "C" {
85

            
86
bool envoy_dynamic_module_callback_cluster_add_hosts(
87
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr, uint32_t priority,
88
    const envoy_dynamic_module_type_module_buffer* addresses, const uint32_t* weights,
89
    const envoy_dynamic_module_type_module_buffer* regions,
90
    const envoy_dynamic_module_type_module_buffer* zones,
91
    const envoy_dynamic_module_type_module_buffer* sub_zones,
92
    const envoy_dynamic_module_type_module_buffer* metadata_pairs, size_t metadata_pairs_per_host,
93
15
    size_t count, envoy_dynamic_module_type_cluster_host_envoy_ptr* result_host_ptrs) {
94
15
  auto* cluster = getCluster(cluster_envoy_ptr);
95
15
  std::vector<std::string> address_strings;
96
15
  address_strings.reserve(count);
97
15
  std::vector<uint32_t> weight_vec(weights, weights + count);
98
15
  std::vector<std::string> region_strings;
99
15
  region_strings.reserve(count);
100
15
  std::vector<std::string> zone_strings;
101
15
  zone_strings.reserve(count);
102
15
  std::vector<std::string> sub_zone_strings;
103
15
  sub_zone_strings.reserve(count);
104
32
  for (size_t i = 0; i < count; ++i) {
105
17
    address_strings.emplace_back(addresses[i].ptr, addresses[i].length);
106
17
    region_strings.emplace_back(regions[i].ptr, regions[i].length);
107
17
    zone_strings.emplace_back(zones[i].ptr, zones[i].length);
108
17
    sub_zone_strings.emplace_back(sub_zones[i].ptr, sub_zones[i].length);
109
17
  }
110

            
111
  // Parse metadata triples: each host has metadata_pairs_per_host triples of
112
  // (filter_name, key, value), laid out contiguously.
113
15
  std::vector<std::vector<std::tuple<std::string, std::string, std::string>>> metadata_vec;
114
15
  if (metadata_pairs != nullptr && metadata_pairs_per_host > 0) {
115
1
    metadata_vec.resize(count);
116
2
    for (size_t i = 0; i < count; ++i) {
117
1
      metadata_vec[i].reserve(metadata_pairs_per_host);
118
2
      for (size_t j = 0; j < metadata_pairs_per_host; ++j) {
119
1
        size_t base = (i * metadata_pairs_per_host + j) * 3;
120
1
        std::string filter_name(metadata_pairs[base].ptr, metadata_pairs[base].length);
121
1
        std::string key(metadata_pairs[base + 1].ptr, metadata_pairs[base + 1].length);
122
1
        std::string value(metadata_pairs[base + 2].ptr, metadata_pairs[base + 2].length);
123
1
        metadata_vec[i].emplace_back(std::move(filter_name), std::move(key), std::move(value));
124
1
      }
125
1
    }
126
1
  }
127

            
128
15
  std::vector<Envoy::Upstream::HostSharedPtr> result_hosts;
129
15
  if (!cluster->addHosts(address_strings, weight_vec, region_strings, zone_strings,
130
15
                         sub_zone_strings, metadata_vec, result_hosts, priority)) {
131
2
    return false;
132
2
  }
133
28
  for (size_t i = 0; i < result_hosts.size(); ++i) {
134
15
    result_host_ptrs[i] = const_cast<Envoy::Upstream::Host*>(result_hosts[i].get());
135
15
  }
136
13
  return true;
137
15
}
138

            
139
size_t envoy_dynamic_module_callback_cluster_remove_hosts(
140
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr,
141
2
    const envoy_dynamic_module_type_cluster_host_envoy_ptr* host_envoy_ptrs, size_t count) {
142
2
  auto* cluster = getCluster(cluster_envoy_ptr);
143
2
  std::vector<Envoy::Upstream::HostSharedPtr> hosts;
144
2
  hosts.reserve(count);
145
6
  for (size_t i = 0; i < count; ++i) {
146
4
    hosts.emplace_back(cluster->findHost(host_envoy_ptrs[i]));
147
4
  }
148
2
  return cluster->removeHosts(hosts);
149
2
}
150

            
151
bool envoy_dynamic_module_callback_cluster_update_host_health(
152
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr,
153
    envoy_dynamic_module_type_cluster_host_envoy_ptr host_envoy_ptr,
154
3
    envoy_dynamic_module_type_host_health health_status) {
155
3
  auto* cluster = getCluster(cluster_envoy_ptr);
156
3
  auto host = cluster->findHost(host_envoy_ptr);
157
3
  return cluster->updateHostHealth(std::move(host), health_status);
158
3
}
159

            
160
envoy_dynamic_module_type_cluster_host_envoy_ptr
161
envoy_dynamic_module_callback_cluster_find_host_by_address(
162
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr,
163
2
    envoy_dynamic_module_type_module_buffer address) {
164
2
  auto* cluster = getCluster(cluster_envoy_ptr);
165
2
  std::string address_str(address.ptr, address.length);
166
2
  auto host = cluster->findHostByAddress(address_str);
167
2
  if (host == nullptr) {
168
1
    return nullptr;
169
1
  }
170
1
  return const_cast<Envoy::Upstream::Host*>(host.get());
171
2
}
172

            
173
void envoy_dynamic_module_callback_cluster_pre_init_complete(
174
6
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr) {
175
6
  getCluster(cluster_envoy_ptr)->preInitComplete();
176
6
}
177

            
178
size_t envoy_dynamic_module_callback_cluster_lb_get_healthy_host_count(
179
12
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
180
12
  if (lb_envoy_ptr == nullptr) {
181
    return 0;
182
  }
183
12
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
184
12
  if (priority >= host_sets.size()) {
185
1
    return 0;
186
1
  }
187
11
  return host_sets[priority]->healthyHosts().size();
188
12
}
189

            
190
envoy_dynamic_module_type_cluster_host_envoy_ptr
191
envoy_dynamic_module_callback_cluster_lb_get_healthy_host(
192
6
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
193
6
  if (lb_envoy_ptr == nullptr) {
194
    return nullptr;
195
  }
196
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
197
6
  if (priority >= host_sets.size()) {
198
1
    return nullptr;
199
1
  }
200
5
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
201
5
  if (index >= healthy_hosts.size()) {
202
2
    return nullptr;
203
2
  }
204
3
  return const_cast<Envoy::Upstream::Host*>(healthy_hosts[index].get());
205
5
}
206

            
207
// =============================================================================
208
// Cluster LB Host Information Callbacks
209
// =============================================================================
210

            
211
void envoy_dynamic_module_callback_cluster_lb_get_cluster_name(
212
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
213
2
    envoy_dynamic_module_type_envoy_buffer* result) {
214
2
  if (lb_envoy_ptr == nullptr || result == nullptr) {
215
1
    if (result != nullptr) {
216
1
      result->ptr = nullptr;
217
1
      result->length = 0;
218
1
    }
219
1
    return;
220
1
  }
221
1
  const auto& name = getLb(lb_envoy_ptr)->handle()->cluster()->info()->observabilityName();
222
1
  result->ptr = name.data();
223
1
  result->length = name.size();
224
1
}
225

            
226
size_t envoy_dynamic_module_callback_cluster_lb_get_hosts_count(
227
13
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
228
13
  if (lb_envoy_ptr == nullptr) {
229
1
    return 0;
230
1
  }
231
12
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
232
12
  if (priority >= host_sets.size()) {
233
1
    return 0;
234
1
  }
235
11
  return host_sets[priority]->hosts().size();
236
12
}
237

            
238
size_t envoy_dynamic_module_callback_cluster_lb_get_degraded_hosts_count(
239
3
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
240
3
  if (lb_envoy_ptr == nullptr) {
241
1
    return 0;
242
1
  }
243
2
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
244
2
  if (priority >= host_sets.size()) {
245
1
    return 0;
246
1
  }
247
1
  return host_sets[priority]->degradedHosts().size();
248
2
}
249

            
250
size_t envoy_dynamic_module_callback_cluster_lb_get_priority_set_size(
251
4
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr) {
252
4
  if (lb_envoy_ptr == nullptr) {
253
1
    return 0;
254
1
  }
255
3
  return getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority().size();
256
4
}
257

            
258
bool envoy_dynamic_module_callback_cluster_lb_get_healthy_host_address(
259
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
260
4
    envoy_dynamic_module_type_envoy_buffer* result) {
261
4
  if (lb_envoy_ptr == nullptr || result == nullptr) {
262
1
    if (result != nullptr) {
263
1
      result->ptr = nullptr;
264
1
      result->length = 0;
265
1
    }
266
1
    return false;
267
1
  }
268
3
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
269
3
  if (priority >= host_sets.size()) {
270
1
    result->ptr = nullptr;
271
1
    result->length = 0;
272
1
    return false;
273
1
  }
274
2
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
275
2
  if (index >= healthy_hosts.size()) {
276
1
    result->ptr = nullptr;
277
1
    result->length = 0;
278
1
    return false;
279
1
  }
280
1
  const auto& address_str = healthy_hosts[index]->address()->asStringView();
281
1
  result->ptr = address_str.data();
282
1
  result->length = address_str.size();
283
1
  return true;
284
2
}
285

            
286
uint32_t envoy_dynamic_module_callback_cluster_lb_get_healthy_host_weight(
287
6
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
288
6
  if (lb_envoy_ptr == nullptr) {
289
1
    return 0;
290
1
  }
291
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
292
5
  if (priority >= host_sets.size()) {
293
1
    return 0;
294
1
  }
295
4
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
296
4
  if (index >= healthy_hosts.size()) {
297
1
    return 0;
298
1
  }
299
3
  return healthy_hosts[index]->weight();
300
4
}
301

            
302
envoy_dynamic_module_type_host_health envoy_dynamic_module_callback_cluster_lb_get_host_health(
303
7
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
304
7
  if (lb_envoy_ptr == nullptr) {
305
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
306
1
  }
307
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
308
6
  if (priority >= host_sets.size()) {
309
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
310
1
  }
311
5
  const auto& hosts = host_sets[priority]->hosts();
312
5
  if (index >= hosts.size()) {
313
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
314
1
  }
315
4
  switch (hosts[index]->coarseHealth()) {
316
2
  case Envoy::Upstream::Host::Health::Unhealthy:
317
2
    return envoy_dynamic_module_type_host_health_Unhealthy;
318
1
  case Envoy::Upstream::Host::Health::Degraded:
319
1
    return envoy_dynamic_module_type_host_health_Degraded;
320
1
  case Envoy::Upstream::Host::Health::Healthy:
321
1
    return envoy_dynamic_module_type_host_health_Healthy;
322
4
  }
323
  return envoy_dynamic_module_type_host_health_Unhealthy;
324
4
}
325

            
326
bool envoy_dynamic_module_callback_cluster_lb_get_host_health_by_address(
327
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
328
    envoy_dynamic_module_type_module_buffer address,
329
7
    envoy_dynamic_module_type_host_health* result) {
330
7
  if (result == nullptr) {
331
1
    return false;
332
1
  }
333
6
  *result = envoy_dynamic_module_type_host_health_Unhealthy;
334

            
335
6
  if (lb_envoy_ptr == nullptr || address.ptr == nullptr) {
336
2
    return false;
337
2
  }
338
4
  const auto host_map = getLb(lb_envoy_ptr)->prioritySet().crossPriorityHostMap();
339
4
  if (host_map == nullptr) {
340
    return false;
341
  }
342
4
  std::string address_str(address.ptr, address.length);
343
4
  const auto it = host_map->find(address_str);
344
4
  if (it == host_map->end()) {
345
1
    return false;
346
1
  }
347
3
  switch (it->second->coarseHealth()) {
348
1
  case Envoy::Upstream::Host::Health::Unhealthy:
349
1
    *result = envoy_dynamic_module_type_host_health_Unhealthy;
350
1
    break;
351
1
  case Envoy::Upstream::Host::Health::Degraded:
352
1
    *result = envoy_dynamic_module_type_host_health_Degraded;
353
1
    break;
354
1
  case Envoy::Upstream::Host::Health::Healthy:
355
1
    *result = envoy_dynamic_module_type_host_health_Healthy;
356
1
    break;
357
3
  }
358
3
  return true;
359
3
}
360

            
361
envoy_dynamic_module_type_cluster_host_envoy_ptr
362
envoy_dynamic_module_callback_cluster_lb_find_host_by_address(
363
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
364
7
    envoy_dynamic_module_type_module_buffer address) {
365
7
  if (lb_envoy_ptr == nullptr || address.ptr == nullptr) {
366
2
    return nullptr;
367
2
  }
368
5
  const auto host_map = getLb(lb_envoy_ptr)->prioritySet().crossPriorityHostMap();
369
5
  if (host_map == nullptr) {
370
    return nullptr;
371
  }
372
5
  std::string address_str(address.ptr, address.length);
373
5
  const auto it = host_map->find(address_str);
374
5
  if (it == host_map->end()) {
375
1
    return nullptr;
376
1
  }
377
4
  return const_cast<Envoy::Upstream::Host*>(it->second.get());
378
5
}
379

            
380
envoy_dynamic_module_type_cluster_host_envoy_ptr envoy_dynamic_module_callback_cluster_lb_get_host(
381
7
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
382
7
  if (lb_envoy_ptr == nullptr) {
383
1
    return nullptr;
384
1
  }
385
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
386
6
  if (priority >= host_sets.size()) {
387
1
    return nullptr;
388
1
  }
389
5
  const auto& hosts = host_sets[priority]->hosts();
390
5
  if (index >= hosts.size()) {
391
1
    return nullptr;
392
1
  }
393
4
  return const_cast<Envoy::Upstream::Host*>(hosts[index].get());
394
5
}
395

            
396
bool envoy_dynamic_module_callback_cluster_lb_get_host_address(
397
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
398
4
    envoy_dynamic_module_type_envoy_buffer* result) {
399
4
  if (lb_envoy_ptr == nullptr || result == nullptr) {
400
1
    if (result != nullptr) {
401
1
      result->ptr = nullptr;
402
1
      result->length = 0;
403
1
    }
404
1
    return false;
405
1
  }
406
3
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
407
3
  if (priority >= host_sets.size()) {
408
1
    result->ptr = nullptr;
409
1
    result->length = 0;
410
1
    return false;
411
1
  }
412
2
  const auto& hosts = host_sets[priority]->hosts();
413
2
  if (index >= hosts.size()) {
414
1
    result->ptr = nullptr;
415
1
    result->length = 0;
416
1
    return false;
417
1
  }
418
1
  const auto& address_str = hosts[index]->address()->asStringView();
419
1
  result->ptr = address_str.data();
420
1
  result->length = address_str.size();
421
1
  return true;
422
2
}
423

            
424
uint32_t envoy_dynamic_module_callback_cluster_lb_get_host_weight(
425
6
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
426
6
  if (lb_envoy_ptr == nullptr) {
427
1
    return 0;
428
1
  }
429
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
430
5
  if (priority >= host_sets.size()) {
431
1
    return 0;
432
1
  }
433
4
  const auto& hosts = host_sets[priority]->hosts();
434
4
  if (index >= hosts.size()) {
435
1
    return 0;
436
1
  }
437
3
  return hosts[index]->weight();
438
4
}
439

            
440
uint64_t envoy_dynamic_module_callback_cluster_lb_get_host_stat(
441
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
442
11
    envoy_dynamic_module_type_host_stat stat) {
443
11
  if (lb_envoy_ptr == nullptr) {
444
1
    return 0;
445
1
  }
446
10
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
447
10
  if (priority >= host_sets.size()) {
448
1
    return 0;
449
1
  }
450
9
  const auto& hosts = host_sets[priority]->hosts();
451
9
  if (index >= hosts.size()) {
452
1
    return 0;
453
1
  }
454
8
  const auto& host_stats = hosts[index]->stats();
455
8
  switch (stat) {
456
1
  case envoy_dynamic_module_type_host_stat_CxConnectFail:
457
1
    return host_stats.cx_connect_fail_.value();
458
1
  case envoy_dynamic_module_type_host_stat_CxTotal:
459
1
    return host_stats.cx_total_.value();
460
1
  case envoy_dynamic_module_type_host_stat_RqError:
461
1
    return host_stats.rq_error_.value();
462
1
  case envoy_dynamic_module_type_host_stat_RqSuccess:
463
1
    return host_stats.rq_success_.value();
464
1
  case envoy_dynamic_module_type_host_stat_RqTimeout:
465
1
    return host_stats.rq_timeout_.value();
466
1
  case envoy_dynamic_module_type_host_stat_RqTotal:
467
1
    return host_stats.rq_total_.value();
468
1
  case envoy_dynamic_module_type_host_stat_CxActive:
469
1
    return host_stats.cx_active_.value();
470
1
  case envoy_dynamic_module_type_host_stat_RqActive:
471
1
    return host_stats.rq_active_.value();
472
8
  }
473
  return 0;
474
8
}
475

            
476
bool envoy_dynamic_module_callback_cluster_lb_get_host_locality(
477
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
478
    envoy_dynamic_module_type_envoy_buffer* region, envoy_dynamic_module_type_envoy_buffer* zone,
479
8
    envoy_dynamic_module_type_envoy_buffer* sub_zone) {
480
8
  if (lb_envoy_ptr == nullptr) {
481
1
    return false;
482
1
  }
483
7
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
484
7
  if (priority >= host_sets.size()) {
485
1
    return false;
486
1
  }
487
6
  const auto& hosts = host_sets[priority]->hosts();
488
6
  if (index >= hosts.size()) {
489
1
    return false;
490
1
  }
491
5
  const auto& locality = hosts[index]->locality();
492
5
  if (region != nullptr) {
493
4
    region->ptr = locality.region().data();
494
4
    region->length = locality.region().size();
495
4
  }
496
5
  if (zone != nullptr) {
497
4
    zone->ptr = locality.zone().data();
498
4
    zone->length = locality.zone().size();
499
4
  }
500
5
  if (sub_zone != nullptr) {
501
4
    sub_zone->ptr = locality.sub_zone().data();
502
4
    sub_zone->length = locality.sub_zone().size();
503
4
  }
504
5
  return true;
505
6
}
506

            
507
bool envoy_dynamic_module_callback_cluster_lb_set_host_data(
508
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
509
4
    uintptr_t data) {
510
4
  if (lb_envoy_ptr == nullptr) {
511
1
    return false;
512
1
  }
513
3
  return getLb(lb_envoy_ptr)->setHostData(priority, index, data);
514
4
}
515

            
516
bool envoy_dynamic_module_callback_cluster_lb_get_host_data(
517
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
518
5
    uintptr_t* data) {
519
5
  if (lb_envoy_ptr == nullptr || data == nullptr) {
520
1
    if (data != nullptr) {
521
1
      *data = 0;
522
1
    }
523
1
    return false;
524
1
  }
525
4
  return getLb(lb_envoy_ptr)->getHostData(priority, index, data);
526
5
}
527

            
528
bool envoy_dynamic_module_callback_cluster_lb_get_host_metadata_string(
529
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
530
    envoy_dynamic_module_type_module_buffer filter_name,
531
8
    envoy_dynamic_module_type_module_buffer key, envoy_dynamic_module_type_envoy_buffer* result) {
532
8
  if (lb_envoy_ptr == nullptr || result == nullptr) {
533
1
    if (result != nullptr) {
534
1
      result->ptr = nullptr;
535
1
      result->length = 0;
536
1
    }
537
1
    return false;
538
1
  }
539
7
  const auto* value = getClusterHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
540
7
  if (value == nullptr || !value->has_string_value()) {
541
5
    result->ptr = nullptr;
542
5
    result->length = 0;
543
5
    return false;
544
5
  }
545
2
  const auto& str = value->string_value();
546
2
  result->ptr = str.data();
547
2
  result->length = str.size();
548
2
  return true;
549
7
}
550

            
551
bool envoy_dynamic_module_callback_cluster_lb_get_host_metadata_number(
552
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
553
    envoy_dynamic_module_type_module_buffer filter_name,
554
4
    envoy_dynamic_module_type_module_buffer key, double* result) {
555
4
  if (lb_envoy_ptr == nullptr || result == nullptr) {
556
1
    return false;
557
1
  }
558
3
  const auto* value = getClusterHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
559
3
  if (value == nullptr || !value->has_number_value()) {
560
2
    return false;
561
2
  }
562
1
  *result = value->number_value();
563
1
  return true;
564
3
}
565

            
566
bool envoy_dynamic_module_callback_cluster_lb_get_host_metadata_bool(
567
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
568
    envoy_dynamic_module_type_module_buffer filter_name,
569
4
    envoy_dynamic_module_type_module_buffer key, bool* result) {
570
4
  if (lb_envoy_ptr == nullptr || result == nullptr) {
571
1
    return false;
572
1
  }
573
3
  const auto* value = getClusterHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
574
3
  if (value == nullptr || !value->has_bool_value()) {
575
2
    return false;
576
2
  }
577
1
  *result = value->bool_value();
578
1
  return true;
579
3
}
580

            
581
size_t envoy_dynamic_module_callback_cluster_lb_get_locality_count(
582
5
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
583
5
  if (lb_envoy_ptr == nullptr) {
584
1
    return 0;
585
1
  }
586
4
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
587
4
  if (priority >= host_sets.size()) {
588
1
    return 0;
589
1
  }
590
3
  return host_sets[priority]->healthyHostsPerLocality().get().size();
591
4
}
592

            
593
size_t envoy_dynamic_module_callback_cluster_lb_get_locality_host_count(
594
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority,
595
6
    size_t locality_index) {
596
6
  if (lb_envoy_ptr == nullptr) {
597
1
    return 0;
598
1
  }
599
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
600
5
  if (priority >= host_sets.size()) {
601
1
    return 0;
602
1
  }
603
4
  const auto& localities = host_sets[priority]->healthyHostsPerLocality().get();
604
4
  if (locality_index >= localities.size()) {
605
2
    return 0;
606
2
  }
607
2
  return localities[locality_index].size();
608
4
}
609

            
610
bool envoy_dynamic_module_callback_cluster_lb_get_locality_host_address(
611
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority,
612
6
    size_t locality_index, size_t host_index, envoy_dynamic_module_type_envoy_buffer* result) {
613
6
  if (lb_envoy_ptr == nullptr || result == nullptr) {
614
1
    if (result != nullptr) {
615
1
      result->ptr = nullptr;
616
1
      result->length = 0;
617
1
    }
618
1
    return false;
619
1
  }
620
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
621
5
  if (priority >= host_sets.size()) {
622
1
    result->ptr = nullptr;
623
1
    result->length = 0;
624
1
    return false;
625
1
  }
626
4
  const auto& localities = host_sets[priority]->healthyHostsPerLocality().get();
627
4
  if (locality_index >= localities.size()) {
628
1
    result->ptr = nullptr;
629
1
    result->length = 0;
630
1
    return false;
631
1
  }
632
3
  const auto& hosts = localities[locality_index];
633
3
  if (host_index >= hosts.size()) {
634
1
    result->ptr = nullptr;
635
1
    result->length = 0;
636
1
    return false;
637
1
  }
638
2
  const auto& address_str = hosts[host_index]->address()->asStringView();
639
2
  result->ptr = address_str.data();
640
2
  result->length = address_str.size();
641
2
  return true;
642
3
}
643

            
644
uint32_t envoy_dynamic_module_callback_cluster_lb_get_locality_weight(
645
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, uint32_t priority,
646
5
    size_t locality_index) {
647
5
  if (lb_envoy_ptr == nullptr) {
648
1
    return 0;
649
1
  }
650
4
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
651
4
  if (priority >= host_sets.size()) {
652
1
    return 0;
653
1
  }
654
3
  const auto weights = host_sets[priority]->localityWeights();
655
3
  if (weights == nullptr || locality_index >= weights->size()) {
656
1
    return 0;
657
1
  }
658
2
  return (*weights)[locality_index];
659
3
}
660

            
661
bool envoy_dynamic_module_callback_cluster_lb_context_compute_hash_key(
662
4
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr, uint64_t* hash_out) {
663
4
  if (context_envoy_ptr == nullptr || hash_out == nullptr) {
664
2
    return false;
665
2
  }
666
2
  auto hash = getContext(context_envoy_ptr)->computeHashKey();
667
2
  if (hash.has_value()) {
668
1
    *hash_out = hash.value();
669
1
    return true;
670
1
  }
671
1
  return false;
672
2
}
673

            
674
size_t envoy_dynamic_module_callback_cluster_lb_context_get_downstream_headers_size(
675
4
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr) {
676
4
  if (context_envoy_ptr == nullptr) {
677
1
    return 0;
678
1
  }
679
3
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
680
3
  if (headers == nullptr) {
681
1
    return 0;
682
1
  }
683
2
  return headers->size();
684
3
}
685

            
686
bool envoy_dynamic_module_callback_cluster_lb_context_get_downstream_headers(
687
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr,
688
4
    envoy_dynamic_module_type_envoy_http_header* result_headers) {
689
4
  if (context_envoy_ptr == nullptr || result_headers == nullptr) {
690
2
    return false;
691
2
  }
692
2
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
693
2
  if (headers == nullptr) {
694
1
    return false;
695
1
  }
696
1
  size_t i = 0;
697
1
  headers->iterate([&i, &result_headers](
698
2
                       const Envoy::Http::HeaderEntry& header) -> Envoy::Http::HeaderMap::Iterate {
699
2
    auto& key = header.key();
700
2
    result_headers[i].key_ptr = const_cast<char*>(key.getStringView().data());
701
2
    result_headers[i].key_length = key.size();
702
2
    auto& value = header.value();
703
2
    result_headers[i].value_ptr = const_cast<char*>(value.getStringView().data());
704
2
    result_headers[i].value_length = value.size();
705
2
    i++;
706
2
    return Envoy::Http::HeaderMap::Iterate::Continue;
707
2
  });
708
1
  return true;
709
2
}
710

            
711
bool envoy_dynamic_module_callback_cluster_lb_context_get_downstream_header(
712
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr,
713
    envoy_dynamic_module_type_module_buffer key,
714
5
    envoy_dynamic_module_type_envoy_buffer* result_buffer, size_t index, size_t* optional_size) {
715
5
  if (context_envoy_ptr == nullptr || result_buffer == nullptr) {
716
1
    if (result_buffer != nullptr) {
717
1
      *result_buffer = {.ptr = nullptr, .length = 0};
718
1
    }
719
1
    if (optional_size != nullptr) {
720
1
      *optional_size = 0;
721
1
    }
722
1
    return false;
723
1
  }
724
4
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
725
4
  if (headers == nullptr) {
726
1
    *result_buffer = {.ptr = nullptr, .length = 0};
727
1
    if (optional_size != nullptr) {
728
1
      *optional_size = 0;
729
1
    }
730
1
    return false;
731
1
  }
732
3
  absl::string_view key_view(key.ptr, key.length);
733
3
  const auto values = headers->get(Envoy::Http::LowerCaseString(key_view));
734
3
  if (optional_size != nullptr) {
735
2
    *optional_size = values.size();
736
2
  }
737
3
  if (index >= values.size()) {
738
2
    *result_buffer = {.ptr = nullptr, .length = 0};
739
2
    return false;
740
2
  }
741
1
  const auto value = values[index]->value().getStringView();
742
1
  *result_buffer = {.ptr = const_cast<char*>(value.data()), .length = value.size()};
743
1
  return true;
744
3
}
745

            
746
uint32_t envoy_dynamic_module_callback_cluster_lb_context_get_host_selection_retry_count(
747
2
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr) {
748
2
  if (context_envoy_ptr == nullptr) {
749
1
    return 0;
750
1
  }
751
1
  return getContext(context_envoy_ptr)->hostSelectionRetryCount();
752
2
}
753

            
754
bool envoy_dynamic_module_callback_cluster_lb_context_should_select_another_host(
755
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
756
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr, uint32_t priority,
757
6
    size_t index) {
758
6
  if (lb_envoy_ptr == nullptr || context_envoy_ptr == nullptr) {
759
2
    return false;
760
2
  }
761
4
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
762
4
  if (priority >= host_sets.size()) {
763
1
    return false;
764
1
  }
765
3
  const auto& hosts = host_sets[priority]->healthyHosts();
766
3
  if (index >= hosts.size()) {
767
1
    return false;
768
1
  }
769
2
  return getContext(context_envoy_ptr)->shouldSelectAnotherHost(*hosts[index]);
770
3
}
771

            
772
bool envoy_dynamic_module_callback_cluster_lb_context_get_override_host(
773
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr,
774
6
    envoy_dynamic_module_type_envoy_buffer* address, bool* strict) {
775
6
  if (context_envoy_ptr == nullptr || address == nullptr || strict == nullptr) {
776
3
    return false;
777
3
  }
778
3
  auto override_host = getContext(context_envoy_ptr)->overrideHostToSelect();
779
3
  if (!override_host.has_value()) {
780
1
    return false;
781
1
  }
782
2
  auto host_address = override_host.value().first;
783
2
  address->ptr = const_cast<char*>(host_address.data());
784
2
  address->length = host_address.size();
785
2
  *strict = override_host.value().second;
786
2
  return true;
787
3
}
788

            
789
bool envoy_dynamic_module_callback_cluster_lb_context_get_downstream_connection_sni(
790
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr,
791
5
    envoy_dynamic_module_type_envoy_buffer* result_buffer) {
792
5
  if (context_envoy_ptr == nullptr || result_buffer == nullptr) {
793
2
    return false;
794
2
  }
795
3
  const auto* connection = getContext(context_envoy_ptr)->downstreamConnection();
796
3
  if (connection == nullptr) {
797
1
    return false;
798
1
  }
799
2
  auto sni = connection->requestedServerName();
800
2
  if (sni.empty()) {
801
1
    return false;
802
1
  }
803
1
  result_buffer->ptr = const_cast<char*>(sni.data());
804
1
  result_buffer->length = sni.size();
805
1
  return true;
806
2
}
807

            
808
envoy_dynamic_module_type_cluster_scheduler_module_ptr
809
envoy_dynamic_module_callback_cluster_scheduler_new(
810
5
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr) {
811
5
  return Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterScheduler::create(
812
5
      getCluster(cluster_envoy_ptr));
813
5
}
814

            
815
void envoy_dynamic_module_callback_cluster_scheduler_delete(
816
5
    envoy_dynamic_module_type_cluster_scheduler_module_ptr scheduler_module_ptr) {
817
5
  delete static_cast<Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterScheduler*>(
818
5
      scheduler_module_ptr);
819
5
}
820

            
821
void envoy_dynamic_module_callback_cluster_scheduler_commit(
822
    envoy_dynamic_module_type_cluster_scheduler_module_ptr scheduler_module_ptr,
823
4
    uint64_t event_id) {
824
4
  auto* scheduler =
825
4
      static_cast<Envoy::Extensions::Clusters::DynamicModules::DynamicModuleClusterScheduler*>(
826
4
          scheduler_module_ptr);
827
4
  scheduler->commit(event_id);
828
4
}
829

            
830
// =============================================================================
831
// Metrics Callbacks
832
// =============================================================================
833

            
834
envoy_dynamic_module_type_metrics_result
835
envoy_dynamic_module_callback_cluster_config_define_counter(
836
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr,
837
    envoy_dynamic_module_type_module_buffer name,
838
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
839
5
    size_t* counter_id_ptr) {
840
5
  auto* config = getConfig(cluster_config_envoy_ptr);
841
5
  absl::string_view name_view(name.ptr, name.length);
842
5
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
843

            
844
  // Handle the special case where the labels size is zero.
845
5
  if (label_names_length == 0) {
846
3
    Envoy::Stats::Counter& c =
847
3
        Envoy::Stats::Utility::counterFromStatNames(*config->stats_scope_, {main_stat_name});
848
3
    *counter_id_ptr = config->addCounter({c});
849
3
    return envoy_dynamic_module_type_metrics_result_Success;
850
3
  }
851

            
852
2
  Envoy::Stats::StatNameVec label_names_vec;
853
5
  for (size_t i = 0; i < label_names_length; i++) {
854
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
855
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
856
3
  }
857
2
  *counter_id_ptr = config->addCounterVec({main_stat_name, label_names_vec});
858
2
  return envoy_dynamic_module_type_metrics_result_Success;
859
5
}
860

            
861
envoy_dynamic_module_type_metrics_result
862
envoy_dynamic_module_callback_cluster_config_increment_counter(
863
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr, size_t id,
864
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
865
11
    uint64_t value) {
866
11
  auto* config = getConfig(cluster_config_envoy_ptr);
867

            
868
11
  if (label_values_length == 0) {
869
8
    auto counter = config->getCounterById(id);
870
8
    if (!counter.has_value()) {
871
3
      if (config->getCounterVecById(id).has_value()) {
872
2
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
873
2
      }
874
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
875
3
    }
876
5
    counter->add(value);
877
5
    return envoy_dynamic_module_type_metrics_result_Success;
878
8
  }
879

            
880
3
  auto counter = config->getCounterVecById(id);
881
3
  if (!counter.has_value()) {
882
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
883
1
  }
884
2
  if (label_values_length != counter->getLabelNames().size()) {
885
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
886
1
  }
887
1
  auto tags = buildTagsForClusterMetric(*config, counter->getLabelNames(), label_values,
888
1
                                        label_values_length);
889
1
  counter->add(*config->stats_scope_, tags, value);
890
1
  return envoy_dynamic_module_type_metrics_result_Success;
891
2
}
892

            
893
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_cluster_config_define_gauge(
894
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr,
895
    envoy_dynamic_module_type_module_buffer name,
896
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
897
4
    size_t* gauge_id_ptr) {
898
4
  auto* config = getConfig(cluster_config_envoy_ptr);
899
4
  absl::string_view name_view(name.ptr, name.length);
900
4
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
901
4
  Envoy::Stats::Gauge::ImportMode import_mode = Envoy::Stats::Gauge::ImportMode::Accumulate;
902

            
903
  // Handle the special case where the labels size is zero.
904
4
  if (label_names_length == 0) {
905
1
    Envoy::Stats::Gauge& g = Envoy::Stats::Utility::gaugeFromStatNames(
906
1
        *config->stats_scope_, {main_stat_name}, import_mode);
907
1
    *gauge_id_ptr = config->addGauge({g});
908
1
    return envoy_dynamic_module_type_metrics_result_Success;
909
1
  }
910

            
911
3
  Envoy::Stats::StatNameVec label_names_vec;
912
6
  for (size_t i = 0; i < label_names_length; i++) {
913
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
914
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
915
3
  }
916
3
  *gauge_id_ptr = config->addGaugeVec({main_stat_name, label_names_vec, import_mode});
917
3
  return envoy_dynamic_module_type_metrics_result_Success;
918
4
}
919

            
920
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_cluster_config_set_gauge(
921
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr, size_t id,
922
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
923
6
    uint64_t value) {
924
6
  auto* config = getConfig(cluster_config_envoy_ptr);
925

            
926
6
  if (label_values_length == 0) {
927
3
    auto gauge = config->getGaugeById(id);
928
3
    if (!gauge.has_value()) {
929
2
      if (config->getGaugeVecById(id).has_value()) {
930
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
931
1
      }
932
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
933
2
    }
934
1
    gauge->set(value);
935
1
    return envoy_dynamic_module_type_metrics_result_Success;
936
3
  }
937

            
938
3
  auto gauge = config->getGaugeVecById(id);
939
3
  if (!gauge.has_value()) {
940
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
941
1
  }
942
2
  if (label_values_length != gauge->getLabelNames().size()) {
943
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
944
1
  }
945
1
  auto tags =
946
1
      buildTagsForClusterMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
947
1
  gauge->set(*config->stats_scope_, tags, value);
948
1
  return envoy_dynamic_module_type_metrics_result_Success;
949
2
}
950

            
951
envoy_dynamic_module_type_metrics_result
952
envoy_dynamic_module_callback_cluster_config_increment_gauge(
953
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr, size_t id,
954
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
955
6
    uint64_t value) {
956
6
  auto* config = getConfig(cluster_config_envoy_ptr);
957

            
958
6
  if (label_values_length == 0) {
959
3
    auto gauge = config->getGaugeById(id);
960
3
    if (!gauge.has_value()) {
961
2
      if (config->getGaugeVecById(id).has_value()) {
962
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
963
1
      }
964
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
965
2
    }
966
1
    gauge->add(value);
967
1
    return envoy_dynamic_module_type_metrics_result_Success;
968
3
  }
969

            
970
3
  auto gauge = config->getGaugeVecById(id);
971
3
  if (!gauge.has_value()) {
972
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
973
1
  }
974
2
  if (label_values_length != gauge->getLabelNames().size()) {
975
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
976
1
  }
977
1
  auto tags =
978
1
      buildTagsForClusterMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
979
1
  gauge->add(*config->stats_scope_, tags, value);
980
1
  return envoy_dynamic_module_type_metrics_result_Success;
981
2
}
982

            
983
envoy_dynamic_module_type_metrics_result
984
envoy_dynamic_module_callback_cluster_config_decrement_gauge(
985
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr, size_t id,
986
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
987
6
    uint64_t value) {
988
6
  auto* config = getConfig(cluster_config_envoy_ptr);
989

            
990
6
  if (label_values_length == 0) {
991
3
    auto gauge = config->getGaugeById(id);
992
3
    if (!gauge.has_value()) {
993
2
      if (config->getGaugeVecById(id).has_value()) {
994
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
995
1
      }
996
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
997
2
    }
998
1
    gauge->sub(value);
999
1
    return envoy_dynamic_module_type_metrics_result_Success;
3
  }
3
  auto gauge = config->getGaugeVecById(id);
3
  if (!gauge.has_value()) {
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
1
  }
2
  if (label_values_length != gauge->getLabelNames().size()) {
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
1
  }
1
  auto tags =
1
      buildTagsForClusterMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
1
  gauge->sub(*config->stats_scope_, tags, value);
1
  return envoy_dynamic_module_type_metrics_result_Success;
2
}
envoy_dynamic_module_type_metrics_result
envoy_dynamic_module_callback_cluster_config_define_histogram(
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr,
    envoy_dynamic_module_type_module_buffer name,
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
4
    size_t* histogram_id_ptr) {
4
  auto* config = getConfig(cluster_config_envoy_ptr);
4
  absl::string_view name_view(name.ptr, name.length);
4
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
4
  Envoy::Stats::Histogram::Unit unit = Envoy::Stats::Histogram::Unit::Unspecified;
  // Handle the special case where the labels size is zero.
4
  if (label_names_length == 0) {
1
    Envoy::Stats::Histogram& h = Envoy::Stats::Utility::histogramFromStatNames(
1
        *config->stats_scope_, {main_stat_name}, unit);
1
    *histogram_id_ptr = config->addHistogram({h});
1
    return envoy_dynamic_module_type_metrics_result_Success;
1
  }
3
  Envoy::Stats::StatNameVec label_names_vec;
6
  for (size_t i = 0; i < label_names_length; i++) {
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
3
  }
3
  *histogram_id_ptr = config->addHistogramVec({main_stat_name, label_names_vec, unit});
3
  return envoy_dynamic_module_type_metrics_result_Success;
4
}
envoy_dynamic_module_type_metrics_result
envoy_dynamic_module_callback_cluster_config_record_histogram_value(
    envoy_dynamic_module_type_cluster_config_envoy_ptr cluster_config_envoy_ptr, size_t id,
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
7
    uint64_t value) {
7
  auto* config = getConfig(cluster_config_envoy_ptr);
7
  if (label_values_length == 0) {
4
    auto histogram = config->getHistogramById(id);
4
    if (!histogram.has_value()) {
3
      if (config->getHistogramVecById(id).has_value()) {
2
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
2
      }
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
3
    }
1
    histogram->recordValue(value);
1
    return envoy_dynamic_module_type_metrics_result_Success;
4
  }
3
  auto histogram = config->getHistogramVecById(id);
3
  if (!histogram.has_value()) {
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
1
  }
2
  if (label_values_length != histogram->getLabelNames().size()) {
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
1
  }
1
  auto tags = buildTagsForClusterMetric(*config, histogram->getLabelNames(), label_values,
1
                                        label_values_length);
1
  histogram->recordValue(*config->stats_scope_, tags, value);
1
  return envoy_dynamic_module_type_metrics_result_Success;
2
}
void envoy_dynamic_module_callback_cluster_lb_async_host_selection_complete(
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr,
    envoy_dynamic_module_type_cluster_lb_context_envoy_ptr context_envoy_ptr,
    envoy_dynamic_module_type_cluster_host_envoy_ptr host,
4
    envoy_dynamic_module_type_module_buffer details) {
4
  auto* lb = getLb(lb_envoy_ptr);
  // Copy the details string on the calling thread. The pointer is not valid after we return.
4
  std::string details_str;
4
  if (details.ptr != nullptr && details.length > 0) {
3
    details_str.assign(details.ptr, details.length);
3
  }
4
  auto cancelled = lb->activeAsyncCancelled();
4
  auto* dispatcher = lb->activeAsyncDispatcher();
4
  if (dispatcher != nullptr) {
    // Post all work to the worker thread. The host lookup and context access must happen
    // on the worker thread because the module may call this from a background thread.
    // Keep the cluster alive via the handle's shared_ptr until the callback fires.
1
    auto handle = lb->handle();
1
    dispatcher->post([context_envoy_ptr, host, details_str = std::move(details_str),
1
                      cancelled = std::move(cancelled), handle = std::move(handle)]() {
1
      if (cancelled != nullptr && cancelled->load(std::memory_order_acquire)) {
        return;
      }
1
      auto* context = getContext(context_envoy_ptr);
1
      Envoy::Upstream::HostConstSharedPtr host_shared;
1
      if (host != nullptr) {
1
        host_shared = handle->cluster()->findHost(host);
1
      }
1
      context->onAsyncHostSelection(std::move(host_shared), std::string(details_str));
1
    });
4
  } else {
    // No worker dispatcher. Complete inline on the calling thread.
3
    auto* context = getContext(context_envoy_ptr);
3
    Envoy::Upstream::HostConstSharedPtr host_shared;
3
    if (host != nullptr) {
1
      host_shared = lb->handle()->cluster()->findHost(host);
1
    }
3
    context->onAsyncHostSelection(std::move(host_shared), std::move(details_str));
3
  }
4
}
// =============================================================================
// HTTP Callout Callback
// =============================================================================
envoy_dynamic_module_type_http_callout_init_result
envoy_dynamic_module_callback_cluster_http_callout(
    envoy_dynamic_module_type_cluster_envoy_ptr cluster_envoy_ptr, uint64_t* callout_id_out,
    envoy_dynamic_module_type_module_buffer cluster_name,
    envoy_dynamic_module_type_module_http_header* headers, size_t headers_size,
9
    envoy_dynamic_module_type_module_buffer body, uint64_t timeout_milliseconds) {
9
  auto* cluster = getCluster(cluster_envoy_ptr);
9
  Envoy::Http::RequestHeaderMapPtr header_map = Envoy::Http::RequestHeaderMapImpl::create();
34
  for (size_t i = 0; i < headers_size; ++i) {
25
    header_map->addCopy(
25
        Envoy::Http::LowerCaseString(std::string(headers[i].key_ptr, headers[i].key_length)),
25
        std::string(headers[i].value_ptr, headers[i].value_length));
25
  }
9
  if (header_map->Path() == nullptr || header_map->Method() == nullptr ||
9
      header_map->Host() == nullptr) {
1
    return envoy_dynamic_module_type_http_callout_init_result_MissingRequiredHeaders;
1
  }
8
  Envoy::Http::RequestMessagePtr message =
8
      std::make_unique<Envoy::Http::RequestMessageImpl>(std::move(header_map));
8
  if (body.length > 0 && body.ptr != nullptr) {
1
    message->body().add(absl::string_view(body.ptr, body.length));
1
  }
8
  return cluster->sendHttpCallout(callout_id_out,
8
                                  absl::string_view(cluster_name.ptr, cluster_name.length),
8
                                  std::move(message), timeout_milliseconds);
9
}
bool envoy_dynamic_module_callback_cluster_lb_get_member_update_host_address(
    envoy_dynamic_module_type_cluster_lb_envoy_ptr lb_envoy_ptr, size_t index, bool is_added,
20
    envoy_dynamic_module_type_envoy_buffer* result) {
20
  if (lb_envoy_ptr == nullptr || result == nullptr) {
2
    if (result != nullptr) {
1
      result->ptr = nullptr;
1
      result->length = 0;
1
    }
2
    return false;
2
  }
18
  const auto* hosts =
18
      is_added ? getLb(lb_envoy_ptr)->hostsAdded() : getLb(lb_envoy_ptr)->hostsRemoved();
18
  if (hosts == nullptr || index >= hosts->size()) {
11
    result->ptr = nullptr;
11
    result->length = 0;
11
    return false;
11
  }
7
  const auto& address_str = (*hosts)[index]->address()->asStringView();
7
  result->ptr = address_str.data();
7
  result->length = address_str.size();
7
  return true;
18
}
} // extern "C"