1
#include "source/extensions/dynamic_modules/abi/abi.h"
2
#include "source/extensions/load_balancing_policies/dynamic_modules/load_balancer.h"
3

            
4
namespace Envoy {
5
namespace Extensions {
6
namespace LoadBalancingPolicies {
7
namespace DynamicModules {
8
namespace {
9

            
10
191
DynamicModuleLoadBalancer* getLb(envoy_dynamic_module_type_lb_envoy_ptr ptr) {
11
191
  return static_cast<DynamicModuleLoadBalancer*>(ptr);
12
191
}
13

            
14
32
Upstream::LoadBalancerContext* getContext(envoy_dynamic_module_type_lb_context_envoy_ptr ptr) {
15
32
  return static_cast<Upstream::LoadBalancerContext*>(ptr);
16
32
}
17

            
18
// Helper to look up a metadata value by filter name and key for a host.
19
const Protobuf::Value* getHostMetadataValue(envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr,
20
                                            uint32_t priority, size_t index,
21
                                            envoy_dynamic_module_type_module_buffer filter_name,
22
19
                                            envoy_dynamic_module_type_module_buffer key) {
23
19
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
24
19
  if (priority >= host_sets.size()) {
25
1
    return nullptr;
26
1
  }
27
18
  const auto& hosts = host_sets[priority]->hosts();
28
18
  if (index >= hosts.size()) {
29
1
    return nullptr;
30
1
  }
31
17
  const auto& metadata = hosts[index]->metadata();
32
17
  if (metadata == nullptr) {
33
7
    return nullptr;
34
7
  }
35
10
  const auto& filter_metadata = metadata->filter_metadata();
36
10
  absl::string_view filter_name_view(filter_name.ptr, filter_name.length);
37
10
  auto filter_it = filter_metadata.find(filter_name_view);
38
10
  if (filter_it == filter_metadata.end()) {
39
1
    return nullptr;
40
1
  }
41
9
  absl::string_view key_view(key.ptr, key.length);
42
9
  auto field_it = filter_it->second.fields().find(key_view);
43
9
  if (field_it == filter_it->second.fields().end()) {
44
3
    return nullptr;
45
3
  }
46
6
  return &field_it->second;
47
9
}
48

            
49
} // namespace
50
} // namespace DynamicModules
51
} // namespace LoadBalancingPolicies
52
} // namespace Extensions
53
} // namespace Envoy
54

            
55
using namespace Envoy::Extensions::LoadBalancingPolicies::DynamicModules;
56

            
57
extern "C" {
58

            
59
void envoy_dynamic_module_callback_lb_get_cluster_name(
60
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr,
61
5
    envoy_dynamic_module_type_envoy_buffer* result) {
62
5
  if (lb_envoy_ptr == nullptr || result == nullptr) {
63
1
    if (result != nullptr) {
64
1
      result->ptr = nullptr;
65
1
      result->length = 0;
66
1
    }
67
1
    return;
68
1
  }
69
4
  const auto& name = getLb(lb_envoy_ptr)->clusterName();
70
4
  result->ptr = name.data();
71
4
  result->length = name.size();
72
4
}
73

            
74
size_t envoy_dynamic_module_callback_lb_get_hosts_count(
75
6
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
76
6
  if (lb_envoy_ptr == nullptr) {
77
1
    return 0;
78
1
  }
79
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
80
5
  if (priority >= host_sets.size()) {
81
1
    return 0;
82
1
  }
83
4
  return host_sets[priority]->hosts().size();
84
5
}
85

            
86
size_t envoy_dynamic_module_callback_lb_get_healthy_hosts_count(
87
12
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
88
12
  if (lb_envoy_ptr == nullptr) {
89
1
    return 0;
90
1
  }
91
11
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
92
11
  if (priority >= host_sets.size()) {
93
2
    return 0;
94
2
  }
95
9
  return host_sets[priority]->healthyHosts().size();
96
11
}
97

            
98
size_t envoy_dynamic_module_callback_lb_get_degraded_hosts_count(
99
6
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
100
6
  if (lb_envoy_ptr == nullptr) {
101
1
    return 0;
102
1
  }
103
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
104
5
  if (priority >= host_sets.size()) {
105
1
    return 0;
106
1
  }
107
4
  return host_sets[priority]->degradedHosts().size();
108
5
}
109

            
110
size_t envoy_dynamic_module_callback_lb_get_priority_set_size(
111
5
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr) {
112
5
  if (lb_envoy_ptr == nullptr) {
113
1
    return 0;
114
1
  }
115
4
  return getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority().size();
116
5
}
117

            
118
bool envoy_dynamic_module_callback_lb_get_healthy_host_address(
119
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
120
7
    envoy_dynamic_module_type_envoy_buffer* result) {
121
7
  if (lb_envoy_ptr == nullptr || result == nullptr) {
122
1
    if (result != nullptr) {
123
1
      result->ptr = nullptr;
124
1
      result->length = 0;
125
1
    }
126
1
    return false;
127
1
  }
128
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
129
6
  if (priority >= host_sets.size()) {
130
1
    result->ptr = nullptr;
131
1
    result->length = 0;
132
1
    return false;
133
1
  }
134
5
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
135
5
  if (index >= healthy_hosts.size()) {
136
1
    result->ptr = nullptr;
137
1
    result->length = 0;
138
1
    return false;
139
1
  }
140
4
  const auto& address_str = healthy_hosts[index]->address()->asStringView();
141
4
  result->ptr = address_str.data();
142
4
  result->length = address_str.size();
143
4
  return true;
144
5
}
145

            
146
uint32_t envoy_dynamic_module_callback_lb_get_healthy_host_weight(
147
8
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
148
8
  if (lb_envoy_ptr == nullptr) {
149
1
    return 0;
150
1
  }
151
7
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
152
7
  if (priority >= host_sets.size()) {
153
1
    return 0;
154
1
  }
155
6
  const auto& healthy_hosts = host_sets[priority]->healthyHosts();
156
6
  if (index >= healthy_hosts.size()) {
157
1
    return 0;
158
1
  }
159
5
  return healthy_hosts[index]->weight();
160
6
}
161

            
162
envoy_dynamic_module_type_host_health envoy_dynamic_module_callback_lb_get_host_health(
163
9
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
164
9
  if (lb_envoy_ptr == nullptr) {
165
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
166
1
  }
167
8
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
168
8
  if (priority >= host_sets.size()) {
169
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
170
1
  }
171
7
  const auto& hosts = host_sets[priority]->hosts();
172
7
  if (index >= hosts.size()) {
173
1
    return envoy_dynamic_module_type_host_health_Unhealthy;
174
1
  }
175
6
  switch (hosts[index]->coarseHealth()) {
176
  case Envoy::Upstream::Host::Health::Unhealthy:
177
    return envoy_dynamic_module_type_host_health_Unhealthy;
178
1
  case Envoy::Upstream::Host::Health::Degraded:
179
1
    return envoy_dynamic_module_type_host_health_Degraded;
180
5
  case Envoy::Upstream::Host::Health::Healthy:
181
5
    return envoy_dynamic_module_type_host_health_Healthy;
182
6
  }
183
  return envoy_dynamic_module_type_host_health_Unhealthy;
184
6
}
185

            
186
bool envoy_dynamic_module_callback_lb_get_host_health_by_address(
187
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr,
188
    envoy_dynamic_module_type_module_buffer address,
189
14
    envoy_dynamic_module_type_host_health* result) {
190
14
  if (result == nullptr) {
191
1
    return false;
192
1
  }
193
13
  *result = envoy_dynamic_module_type_host_health_Unhealthy;
194

            
195
13
  if (lb_envoy_ptr == nullptr || address.ptr == nullptr) {
196
2
    return false;
197
2
  }
198
11
  const auto host_map = getLb(lb_envoy_ptr)->prioritySet().crossPriorityHostMap();
199
11
  if (host_map == nullptr) {
200
    return false;
201
  }
202
11
  std::string address_str(address.ptr, address.length);
203
11
  const auto it = host_map->find(address_str);
204
11
  if (it == host_map->end()) {
205
8
    return false;
206
8
  }
207
3
  switch (it->second->coarseHealth()) {
208
  case Envoy::Upstream::Host::Health::Unhealthy:
209
    *result = envoy_dynamic_module_type_host_health_Unhealthy;
210
    break;
211
1
  case Envoy::Upstream::Host::Health::Degraded:
212
1
    *result = envoy_dynamic_module_type_host_health_Degraded;
213
1
    break;
214
2
  case Envoy::Upstream::Host::Health::Healthy:
215
2
    *result = envoy_dynamic_module_type_host_health_Healthy;
216
2
    break;
217
3
  }
218
3
  return true;
219
3
}
220

            
221
bool envoy_dynamic_module_callback_lb_get_host_address(
222
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
223
10
    envoy_dynamic_module_type_envoy_buffer* result) {
224
10
  if (lb_envoy_ptr == nullptr || result == nullptr) {
225
1
    if (result != nullptr) {
226
1
      result->ptr = nullptr;
227
1
      result->length = 0;
228
1
    }
229
1
    return false;
230
1
  }
231
9
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
232
9
  if (priority >= host_sets.size()) {
233
1
    result->ptr = nullptr;
234
1
    result->length = 0;
235
1
    return false;
236
1
  }
237
8
  const auto& hosts = host_sets[priority]->hosts();
238
8
  if (index >= hosts.size()) {
239
1
    result->ptr = nullptr;
240
1
    result->length = 0;
241
1
    return false;
242
1
  }
243
7
  const auto& address_str = hosts[index]->address()->asStringView();
244
7
  result->ptr = address_str.data();
245
7
  result->length = address_str.size();
246
7
  return true;
247
8
}
248

            
249
uint32_t envoy_dynamic_module_callback_lb_get_host_weight(
250
9
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index) {
251
9
  if (lb_envoy_ptr == nullptr) {
252
1
    return 0;
253
1
  }
254
8
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
255
8
  if (priority >= host_sets.size()) {
256
1
    return 0;
257
1
  }
258
7
  const auto& hosts = host_sets[priority]->hosts();
259
7
  if (index >= hosts.size()) {
260
1
    return 0;
261
1
  }
262
6
  return hosts[index]->weight();
263
7
}
264

            
265
bool envoy_dynamic_module_callback_lb_get_host_locality(
266
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
267
    envoy_dynamic_module_type_envoy_buffer* region, envoy_dynamic_module_type_envoy_buffer* zone,
268
9
    envoy_dynamic_module_type_envoy_buffer* sub_zone) {
269
9
  if (lb_envoy_ptr == nullptr) {
270
1
    return false;
271
1
  }
272
8
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
273
8
  if (priority >= host_sets.size()) {
274
1
    return false;
275
1
  }
276
7
  const auto& hosts = host_sets[priority]->hosts();
277
7
  if (index >= hosts.size()) {
278
1
    return false;
279
1
  }
280
6
  const auto& locality = hosts[index]->locality();
281
6
  if (region != nullptr) {
282
6
    region->ptr = locality.region().data();
283
6
    region->length = locality.region().size();
284
6
  }
285
6
  if (zone != nullptr) {
286
5
    zone->ptr = locality.zone().data();
287
5
    zone->length = locality.zone().size();
288
5
  }
289
6
  if (sub_zone != nullptr) {
290
5
    sub_zone->ptr = locality.sub_zone().data();
291
5
    sub_zone->length = locality.sub_zone().size();
292
5
  }
293
6
  return true;
294
7
}
295

            
296
bool envoy_dynamic_module_callback_lb_context_compute_hash_key(
297
5
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr, uint64_t* hash_out) {
298
5
  if (context_envoy_ptr == nullptr || hash_out == nullptr) {
299
1
    return false;
300
1
  }
301
4
  auto hash = getContext(context_envoy_ptr)->computeHashKey();
302
4
  if (hash.has_value()) {
303
3
    *hash_out = hash.value();
304
3
    return true;
305
3
  }
306
1
  return false;
307
4
}
308

            
309
size_t envoy_dynamic_module_callback_lb_context_get_downstream_headers_size(
310
5
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr) {
311
5
  if (context_envoy_ptr == nullptr) {
312
1
    return 0;
313
1
  }
314
4
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
315
4
  if (headers == nullptr) {
316
1
    return 0;
317
1
  }
318
3
  return headers->size();
319
4
}
320

            
321
bool envoy_dynamic_module_callback_lb_context_get_downstream_headers(
322
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr,
323
5
    envoy_dynamic_module_type_envoy_http_header* result_headers) {
324
5
  if (context_envoy_ptr == nullptr || result_headers == nullptr) {
325
2
    return false;
326
2
  }
327
3
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
328
3
  if (headers == nullptr) {
329
    return false;
330
  }
331
3
  size_t i = 0;
332
3
  headers->iterate([&i, &result_headers](
333
7
                       const Envoy::Http::HeaderEntry& header) -> Envoy::Http::HeaderMap::Iterate {
334
7
    auto& key = header.key();
335
7
    result_headers[i].key_ptr = const_cast<char*>(key.getStringView().data());
336
7
    result_headers[i].key_length = key.size();
337
7
    auto& value = header.value();
338
7
    result_headers[i].value_ptr = const_cast<char*>(value.getStringView().data());
339
7
    result_headers[i].value_length = value.size();
340
7
    i++;
341
7
    return Envoy::Http::HeaderMap::Iterate::Continue;
342
7
  });
343
3
  return true;
344
3
}
345

            
346
bool envoy_dynamic_module_callback_lb_context_get_downstream_header(
347
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr,
348
    envoy_dynamic_module_type_module_buffer key,
349
9
    envoy_dynamic_module_type_envoy_buffer* result_buffer, size_t index, size_t* optional_size) {
350
9
  if (context_envoy_ptr == nullptr || result_buffer == nullptr) {
351
1
    if (result_buffer != nullptr) {
352
1
      *result_buffer = {.ptr = nullptr, .length = 0};
353
1
    }
354
1
    if (optional_size != nullptr) {
355
      *optional_size = 0;
356
    }
357
1
    return false;
358
1
  }
359
8
  const auto* headers = getContext(context_envoy_ptr)->downstreamHeaders();
360
8
  if (headers == nullptr) {
361
1
    *result_buffer = {.ptr = nullptr, .length = 0};
362
1
    if (optional_size != nullptr) {
363
1
      *optional_size = 0;
364
1
    }
365
1
    return false;
366
1
  }
367
7
  absl::string_view key_view(key.ptr, key.length);
368
7
  const auto values = headers->get(Envoy::Http::LowerCaseString(key_view));
369
7
  if (optional_size != nullptr) {
370
4
    *optional_size = values.size();
371
4
  }
372
7
  if (index >= values.size()) {
373
3
    *result_buffer = {.ptr = nullptr, .length = 0};
374
3
    return false;
375
3
  }
376
4
  const auto value = values[index]->value().getStringView();
377
4
  *result_buffer = {.ptr = const_cast<char*>(value.data()), .length = value.size()};
378
4
  return true;
379
7
}
380

            
381
uint32_t envoy_dynamic_module_callback_lb_context_get_host_selection_retry_count(
382
5
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr) {
383
5
  if (context_envoy_ptr == nullptr) {
384
1
    return 0;
385
1
  }
386
4
  return getContext(context_envoy_ptr)->hostSelectionRetryCount();
387
5
}
388

            
389
bool envoy_dynamic_module_callback_lb_context_should_select_another_host(
390
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr,
391
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr, uint32_t priority,
392
7
    size_t index) {
393
7
  if (lb_envoy_ptr == nullptr || context_envoy_ptr == nullptr) {
394
1
    return false;
395
1
  }
396
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
397
6
  if (priority >= host_sets.size()) {
398
1
    return false;
399
1
  }
400
5
  const auto& hosts = host_sets[priority]->hosts();
401
5
  if (index >= hosts.size()) {
402
1
    return false;
403
1
  }
404
4
  return getContext(context_envoy_ptr)->shouldSelectAnotherHost(*hosts[index]);
405
5
}
406

            
407
bool envoy_dynamic_module_callback_lb_context_get_override_host(
408
    envoy_dynamic_module_type_lb_context_envoy_ptr context_envoy_ptr,
409
8
    envoy_dynamic_module_type_envoy_buffer* address, bool* strict) {
410
8
  if (context_envoy_ptr == nullptr || address == nullptr || strict == nullptr) {
411
3
    return false;
412
3
  }
413
5
  auto override_host = getContext(context_envoy_ptr)->overrideHostToSelect();
414
5
  if (!override_host.has_value()) {
415
3
    return false;
416
3
  }
417
2
  auto host_address = override_host.value().first;
418
2
  address->ptr = const_cast<char*>(host_address.data());
419
2
  address->length = host_address.size();
420
2
  *strict = override_host.value().second;
421
2
  return true;
422
5
}
423

            
424
bool envoy_dynamic_module_callback_lb_set_host_data(
425
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
426
8
    uintptr_t data) {
427
8
  if (lb_envoy_ptr == nullptr) {
428
1
    return false;
429
1
  }
430
7
  return getLb(lb_envoy_ptr)->setHostData(priority, index, data);
431
8
}
432

            
433
bool envoy_dynamic_module_callback_lb_get_host_data(
434
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
435
10
    uintptr_t* data) {
436
10
  if (lb_envoy_ptr == nullptr || data == nullptr) {
437
2
    if (data != nullptr) {
438
1
      *data = 0;
439
1
    }
440
2
    return false;
441
2
  }
442
8
  return getLb(lb_envoy_ptr)->getHostData(priority, index, data);
443
10
}
444

            
445
bool envoy_dynamic_module_callback_lb_get_host_metadata_string(
446
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
447
    envoy_dynamic_module_type_module_buffer filter_name,
448
11
    envoy_dynamic_module_type_module_buffer key, envoy_dynamic_module_type_envoy_buffer* result) {
449
11
  if (lb_envoy_ptr == nullptr || result == nullptr) {
450
1
    if (result != nullptr) {
451
1
      result->ptr = nullptr;
452
1
      result->length = 0;
453
1
    }
454
1
    return false;
455
1
  }
456
10
  const auto* value = getHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
457
10
  if (value == nullptr || !value->has_string_value()) {
458
8
    result->ptr = nullptr;
459
8
    result->length = 0;
460
8
    return false;
461
8
  }
462
2
  const auto& str = value->string_value();
463
2
  result->ptr = str.data();
464
2
  result->length = str.size();
465
2
  return true;
466
10
}
467

            
468
bool envoy_dynamic_module_callback_lb_get_host_metadata_number(
469
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
470
    envoy_dynamic_module_type_module_buffer filter_name,
471
6
    envoy_dynamic_module_type_module_buffer key, double* result) {
472
6
  if (lb_envoy_ptr == nullptr || result == nullptr) {
473
1
    return false;
474
1
  }
475
5
  const auto* value = getHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
476
5
  if (value == nullptr || !value->has_number_value()) {
477
4
    return false;
478
4
  }
479
1
  *result = value->number_value();
480
1
  return true;
481
5
}
482

            
483
bool envoy_dynamic_module_callback_lb_get_host_metadata_bool(
484
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t index,
485
    envoy_dynamic_module_type_module_buffer filter_name,
486
5
    envoy_dynamic_module_type_module_buffer key, bool* result) {
487
5
  if (lb_envoy_ptr == nullptr || result == nullptr) {
488
1
    return false;
489
1
  }
490
4
  const auto* value = getHostMetadataValue(lb_envoy_ptr, priority, index, filter_name, key);
491
4
  if (value == nullptr || !value->has_bool_value()) {
492
3
    return false;
493
3
  }
494
1
  *result = value->bool_value();
495
1
  return true;
496
4
}
497

            
498
size_t envoy_dynamic_module_callback_lb_get_locality_count(
499
6
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority) {
500
6
  if (lb_envoy_ptr == nullptr) {
501
1
    return 0;
502
1
  }
503
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
504
5
  if (priority >= host_sets.size()) {
505
1
    return 0;
506
1
  }
507
4
  return host_sets[priority]->healthyHostsPerLocality().get().size();
508
5
}
509

            
510
size_t envoy_dynamic_module_callback_lb_get_locality_host_count(
511
6
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t locality_index) {
512
6
  if (lb_envoy_ptr == nullptr) {
513
1
    return 0;
514
1
  }
515
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
516
5
  if (priority >= host_sets.size()) {
517
1
    return 0;
518
1
  }
519
4
  const auto& localities = host_sets[priority]->healthyHostsPerLocality().get();
520
4
  if (locality_index >= localities.size()) {
521
1
    return 0;
522
1
  }
523
3
  return localities[locality_index].size();
524
4
}
525

            
526
bool envoy_dynamic_module_callback_lb_get_locality_host_address(
527
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t locality_index,
528
6
    size_t host_index, envoy_dynamic_module_type_envoy_buffer* result) {
529
6
  if (lb_envoy_ptr == nullptr || result == nullptr) {
530
1
    if (result != nullptr) {
531
1
      result->ptr = nullptr;
532
1
      result->length = 0;
533
1
    }
534
1
    return false;
535
1
  }
536
5
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
537
5
  if (priority >= host_sets.size()) {
538
1
    result->ptr = nullptr;
539
1
    result->length = 0;
540
1
    return false;
541
1
  }
542
4
  const auto& localities = host_sets[priority]->healthyHostsPerLocality().get();
543
4
  if (locality_index >= localities.size()) {
544
1
    result->ptr = nullptr;
545
1
    result->length = 0;
546
1
    return false;
547
1
  }
548
3
  const auto& hosts_in_locality = localities[locality_index];
549
3
  if (host_index >= hosts_in_locality.size()) {
550
1
    result->ptr = nullptr;
551
1
    result->length = 0;
552
1
    return false;
553
1
  }
554
2
  const auto& address_str = hosts_in_locality[host_index]->address()->asStringView();
555
2
  result->ptr = address_str.data();
556
2
  result->length = address_str.size();
557
2
  return true;
558
3
}
559

            
560
uint32_t envoy_dynamic_module_callback_lb_get_locality_weight(
561
7
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, uint32_t priority, size_t locality_index) {
562
7
  if (lb_envoy_ptr == nullptr) {
563
1
    return 0;
564
1
  }
565
6
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
566
6
  if (priority >= host_sets.size()) {
567
1
    return 0;
568
1
  }
569
5
  const auto weights = host_sets[priority]->localityWeights();
570
5
  if (weights == nullptr || locality_index >= weights->size()) {
571
2
    return 0;
572
2
  }
573
3
  return (*weights)[locality_index];
574
5
}
575

            
576
bool envoy_dynamic_module_callback_lb_get_member_update_host_address(
577
    envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr, size_t index, bool is_added,
578
8
    envoy_dynamic_module_type_envoy_buffer* result) {
579
8
  if (lb_envoy_ptr == nullptr || result == nullptr) {
580
3
    if (result != nullptr) {
581
2
      result->ptr = nullptr;
582
2
      result->length = 0;
583
2
    }
584
3
    return false;
585
3
  }
586
5
  const auto* hosts =
587
5
      is_added ? getLb(lb_envoy_ptr)->hostsAdded() : getLb(lb_envoy_ptr)->hostsRemoved();
588
5
  if (hosts == nullptr || index >= hosts->size()) {
589
3
    result->ptr = nullptr;
590
3
    result->length = 0;
591
3
    return false;
592
3
  }
593
2
  const auto& address_str = (*hosts)[index]->address()->asStringView();
594
2
  result->ptr = address_str.data();
595
2
  result->length = address_str.size();
596
2
  return true;
597
5
}
598

            
599
uint64_t
600
envoy_dynamic_module_callback_lb_get_host_stat(envoy_dynamic_module_type_lb_envoy_ptr lb_envoy_ptr,
601
                                               uint32_t priority, size_t index,
602
40
                                               envoy_dynamic_module_type_host_stat stat) {
603
40
  if (lb_envoy_ptr == nullptr) {
604
1
    return 0;
605
1
  }
606
39
  const auto& host_sets = getLb(lb_envoy_ptr)->prioritySet().hostSetsPerPriority();
607
39
  if (priority >= host_sets.size()) {
608
1
    return 0;
609
1
  }
610
38
  const auto& hosts = host_sets[priority]->hosts();
611
38
  if (index >= hosts.size()) {
612
1
    return 0;
613
1
  }
614
37
  const auto& host_stats = hosts[index]->stats();
615
37
  switch (stat) {
616
4
  case envoy_dynamic_module_type_host_stat_CxConnectFail:
617
4
    return host_stats.cx_connect_fail_.value();
618
4
  case envoy_dynamic_module_type_host_stat_CxTotal:
619
4
    return host_stats.cx_total_.value();
620
4
  case envoy_dynamic_module_type_host_stat_RqError:
621
4
    return host_stats.rq_error_.value();
622
4
  case envoy_dynamic_module_type_host_stat_RqSuccess:
623
4
    return host_stats.rq_success_.value();
624
4
  case envoy_dynamic_module_type_host_stat_RqTimeout:
625
4
    return host_stats.rq_timeout_.value();
626
5
  case envoy_dynamic_module_type_host_stat_RqTotal:
627
5
    return host_stats.rq_total_.value();
628
6
  case envoy_dynamic_module_type_host_stat_CxActive:
629
6
    return host_stats.cx_active_.value();
630
6
  case envoy_dynamic_module_type_host_stat_RqActive:
631
6
    return host_stats.rq_active_.value();
632
37
  }
633
  return 0;
634
37
}
635

            
636
} // extern "C"
637

            
638
// =============================================================================
639
// Metrics Callbacks
640
// =============================================================================
641

            
642
namespace {
643

            
644
Envoy::Stats::StatNameTagVector
645
buildTagsForLbMetric(DynamicModuleLbConfig& config, const Envoy::Stats::StatNameVec& label_names,
646
                     envoy_dynamic_module_type_module_buffer* label_values,
647
6
                     size_t label_values_length) {
648
6
  ASSERT(label_values_length == label_names.size());
649
6
  Envoy::Stats::StatNameTagVector tags;
650
6
  tags.reserve(label_values_length);
651
14
  for (size_t i = 0; i < label_values_length; i++) {
652
8
    absl::string_view label_value_view(label_values[i].ptr, label_values[i].length);
653
8
    auto label_value = config.stat_name_pool_.add(label_value_view);
654
8
    tags.push_back(Envoy::Stats::StatNameTag(label_names[i], label_value));
655
8
  }
656
6
  return tags;
657
6
}
658

            
659
} // namespace
660

            
661
extern "C" {
662

            
663
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_define_counter(
664
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr,
665
    envoy_dynamic_module_type_module_buffer name,
666
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
667
5
    size_t* counter_id_ptr) {
668
5
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
669
5
  absl::string_view name_view(name.ptr, name.length);
670
5
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
671

            
672
  // Handle the special case where the labels size is zero.
673
5
  if (label_names_length == 0) {
674
3
    Envoy::Stats::Counter& c =
675
3
        Envoy::Stats::Utility::counterFromStatNames(*config->stats_scope_, {main_stat_name});
676
3
    *counter_id_ptr = config->addCounter({c});
677
3
    return envoy_dynamic_module_type_metrics_result_Success;
678
3
  }
679

            
680
2
  Envoy::Stats::StatNameVec label_names_vec;
681
5
  for (size_t i = 0; i < label_names_length; i++) {
682
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
683
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
684
3
  }
685
2
  *counter_id_ptr = config->addCounterVec({main_stat_name, label_names_vec});
686
2
  return envoy_dynamic_module_type_metrics_result_Success;
687
5
}
688

            
689
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_increment_counter(
690
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr, size_t id,
691
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
692
11
    uint64_t value) {
693
11
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
694

            
695
11
  if (label_values_length == 0) {
696
7
    auto counter = config->getCounterById(id);
697
7
    if (!counter.has_value()) {
698
      // A vec metric with this ID may exist; 0 labels is invalid for it.
699
3
      if (config->getCounterVecById(id).has_value()) {
700
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
701
1
      }
702
2
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
703
3
    }
704
4
    counter->add(value);
705
4
    return envoy_dynamic_module_type_metrics_result_Success;
706
7
  }
707

            
708
4
  auto counter = config->getCounterVecById(id);
709
4
  if (!counter.has_value()) {
710
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
711
1
  }
712
3
  if (label_values_length != counter->getLabelNames().size()) {
713
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
714
1
  }
715
2
  auto tags =
716
2
      buildTagsForLbMetric(*config, counter->getLabelNames(), label_values, label_values_length);
717
2
  counter->add(*config->stats_scope_, tags, value);
718
2
  return envoy_dynamic_module_type_metrics_result_Success;
719
3
}
720

            
721
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_define_gauge(
722
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr,
723
    envoy_dynamic_module_type_module_buffer name,
724
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
725
4
    size_t* gauge_id_ptr) {
726
4
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
727
4
  absl::string_view name_view(name.ptr, name.length);
728
4
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
729
4
  Envoy::Stats::Gauge::ImportMode import_mode = Envoy::Stats::Gauge::ImportMode::Accumulate;
730

            
731
  // Handle the special case where the labels size is zero.
732
4
  if (label_names_length == 0) {
733
1
    Envoy::Stats::Gauge& g = Envoy::Stats::Utility::gaugeFromStatNames(
734
1
        *config->stats_scope_, {main_stat_name}, import_mode);
735
1
    *gauge_id_ptr = config->addGauge({g});
736
1
    return envoy_dynamic_module_type_metrics_result_Success;
737
1
  }
738

            
739
3
  Envoy::Stats::StatNameVec label_names_vec;
740
6
  for (size_t i = 0; i < label_names_length; i++) {
741
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
742
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
743
3
  }
744
3
  *gauge_id_ptr = config->addGaugeVec({main_stat_name, label_names_vec, import_mode});
745
3
  return envoy_dynamic_module_type_metrics_result_Success;
746
4
}
747

            
748
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_set_gauge(
749
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr, size_t id,
750
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
751
8
    uint64_t value) {
752
8
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
753

            
754
8
  if (label_values_length == 0) {
755
5
    auto gauge = config->getGaugeById(id);
756
5
    if (!gauge.has_value()) {
757
4
      if (config->getGaugeVecById(id).has_value()) {
758
2
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
759
2
      }
760
2
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
761
4
    }
762
1
    gauge->set(value);
763
1
    return envoy_dynamic_module_type_metrics_result_Success;
764
5
  }
765

            
766
3
  auto gauge = config->getGaugeVecById(id);
767
3
  if (!gauge.has_value()) {
768
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
769
1
  }
770
2
  if (label_values_length != gauge->getLabelNames().size()) {
771
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
772
1
  }
773
1
  auto tags =
774
1
      buildTagsForLbMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
775
1
  gauge->set(*config->stats_scope_, tags, value);
776
1
  return envoy_dynamic_module_type_metrics_result_Success;
777
2
}
778

            
779
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_increment_gauge(
780
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr, size_t id,
781
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
782
6
    uint64_t value) {
783
6
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
784

            
785
6
  if (label_values_length == 0) {
786
3
    auto gauge = config->getGaugeById(id);
787
3
    if (!gauge.has_value()) {
788
2
      if (config->getGaugeVecById(id).has_value()) {
789
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
790
1
      }
791
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
792
2
    }
793
1
    gauge->add(value);
794
1
    return envoy_dynamic_module_type_metrics_result_Success;
795
3
  }
796

            
797
3
  auto gauge = config->getGaugeVecById(id);
798
3
  if (!gauge.has_value()) {
799
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
800
1
  }
801
2
  if (label_values_length != gauge->getLabelNames().size()) {
802
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
803
1
  }
804
1
  auto tags =
805
1
      buildTagsForLbMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
806
1
  gauge->add(*config->stats_scope_, tags, value);
807
1
  return envoy_dynamic_module_type_metrics_result_Success;
808
2
}
809

            
810
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_decrement_gauge(
811
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr, size_t id,
812
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
813
6
    uint64_t value) {
814
6
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
815

            
816
6
  if (label_values_length == 0) {
817
3
    auto gauge = config->getGaugeById(id);
818
3
    if (!gauge.has_value()) {
819
2
      if (config->getGaugeVecById(id).has_value()) {
820
1
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
821
1
      }
822
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
823
2
    }
824
1
    gauge->sub(value);
825
1
    return envoy_dynamic_module_type_metrics_result_Success;
826
3
  }
827

            
828
3
  auto gauge = config->getGaugeVecById(id);
829
3
  if (!gauge.has_value()) {
830
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
831
1
  }
832
2
  if (label_values_length != gauge->getLabelNames().size()) {
833
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
834
1
  }
835
1
  auto tags =
836
1
      buildTagsForLbMetric(*config, gauge->getLabelNames(), label_values, label_values_length);
837
1
  gauge->sub(*config->stats_scope_, tags, value);
838
1
  return envoy_dynamic_module_type_metrics_result_Success;
839
2
}
840

            
841
envoy_dynamic_module_type_metrics_result envoy_dynamic_module_callback_lb_config_define_histogram(
842
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr,
843
    envoy_dynamic_module_type_module_buffer name,
844
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
845
3
    size_t* histogram_id_ptr) {
846
3
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
847
3
  absl::string_view name_view(name.ptr, name.length);
848
3
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
849
3
  Envoy::Stats::Histogram::Unit unit = Envoy::Stats::Histogram::Unit::Unspecified;
850

            
851
  // Handle the special case where the labels size is zero.
852
3
  if (label_names_length == 0) {
853
1
    Envoy::Stats::Histogram& h = Envoy::Stats::Utility::histogramFromStatNames(
854
1
        *config->stats_scope_, {main_stat_name}, unit);
855
1
    *histogram_id_ptr = config->addHistogram({h});
856
1
    return envoy_dynamic_module_type_metrics_result_Success;
857
1
  }
858

            
859
2
  Envoy::Stats::StatNameVec label_names_vec;
860
4
  for (size_t i = 0; i < label_names_length; i++) {
861
2
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
862
2
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
863
2
  }
864
2
  *histogram_id_ptr = config->addHistogramVec({main_stat_name, label_names_vec, unit});
865
2
  return envoy_dynamic_module_type_metrics_result_Success;
866
3
}
867

            
868
envoy_dynamic_module_type_metrics_result
869
envoy_dynamic_module_callback_lb_config_record_histogram_value(
870
    envoy_dynamic_module_type_lb_config_envoy_ptr lb_config_envoy_ptr, size_t id,
871
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
872
8
    uint64_t value) {
873
8
  auto* config = static_cast<DynamicModuleLbConfig*>(lb_config_envoy_ptr);
874

            
875
8
  if (label_values_length == 0) {
876
5
    auto histogram = config->getHistogramById(id);
877
5
    if (!histogram.has_value()) {
878
4
      if (config->getHistogramVecById(id).has_value()) {
879
2
        return envoy_dynamic_module_type_metrics_result_InvalidLabels;
880
2
      }
881
2
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
882
4
    }
883
1
    histogram->recordValue(value);
884
1
    return envoy_dynamic_module_type_metrics_result_Success;
885
5
  }
886

            
887
3
  auto histogram = config->getHistogramVecById(id);
888
3
  if (!histogram.has_value()) {
889
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
890
1
  }
891
2
  if (label_values_length != histogram->getLabelNames().size()) {
892
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
893
1
  }
894
1
  auto tags =
895
1
      buildTagsForLbMetric(*config, histogram->getLabelNames(), label_values, label_values_length);
896
1
  histogram->recordValue(*config->stats_scope_, tags, value);
897
1
  return envoy_dynamic_module_type_metrics_result_Success;
898
2
}
899

            
900
} // extern "C"