1
// NOLINT(namespace-envoy)
2

            
3
// This file provides host-side implementations for ABI callbacks specific to bootstrap extensions.
4

            
5
#include "envoy/server/admin.h"
6

            
7
#include "source/common/buffer/buffer_impl.h"
8
#include "source/common/stats/symbol_table.h"
9
#include "source/common/stats/utility.h"
10
#include "source/extensions/bootstrap/dynamic_modules/extension.h"
11
#include "source/extensions/bootstrap/dynamic_modules/extension_config.h"
12
#include "source/extensions/dynamic_modules/abi/abi.h"
13

            
14
using Envoy::Extensions::Bootstrap::DynamicModules::DynamicModuleBootstrapExtension;
15
using Envoy::Extensions::Bootstrap::DynamicModules::DynamicModuleBootstrapExtensionConfig;
16
using Envoy::Extensions::Bootstrap::DynamicModules::DynamicModuleBootstrapExtensionConfigScheduler;
17
using Envoy::Extensions::Bootstrap::DynamicModules::DynamicModuleBootstrapExtensionTimer;
18

            
19
extern "C" {
20

            
21
envoy_dynamic_module_type_bootstrap_extension_config_scheduler_module_ptr
22
envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_new(
23
7
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr) {
24
7
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
25
7
  return new DynamicModuleBootstrapExtensionConfigScheduler(config->weak_from_this(),
26
7
                                                            config->main_thread_dispatcher_);
27
7
}
28

            
29
void envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_delete(
30
    envoy_dynamic_module_type_bootstrap_extension_config_scheduler_module_ptr
31
7
        scheduler_module_ptr) {
32
7
  delete static_cast<DynamicModuleBootstrapExtensionConfigScheduler*>(scheduler_module_ptr);
33
7
}
34

            
35
void envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_commit(
36
    envoy_dynamic_module_type_bootstrap_extension_config_scheduler_module_ptr scheduler_module_ptr,
37
6
    uint64_t event_id) {
38
6
  auto* scheduler =
39
6
      static_cast<DynamicModuleBootstrapExtensionConfigScheduler*>(scheduler_module_ptr);
40
6
  scheduler->commit(event_id);
41
6
}
42

            
43
// -------------------- Init Manager Callbacks --------------------
44

            
45
void envoy_dynamic_module_callback_bootstrap_extension_config_signal_init_complete(
46
69
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr) {
47
69
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
48
69
  config->signalInitComplete();
49
69
}
50

            
51
// -------------------- HTTP Callout Callbacks --------------------
52

            
53
envoy_dynamic_module_type_http_callout_init_result
54
envoy_dynamic_module_callback_bootstrap_extension_http_callout(
55
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr,
56
    uint64_t* callout_id_out, envoy_dynamic_module_type_module_buffer cluster_name,
57
    envoy_dynamic_module_type_module_http_header* headers, size_t headers_size,
58
9
    envoy_dynamic_module_type_module_buffer body, uint64_t timeout_milliseconds) {
59
9
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
60

            
61
  // Build the HTTP request message.
62
9
  Envoy::Http::RequestHeaderMapPtr header_map = Envoy::Http::RequestHeaderMapImpl::create();
63
35
  for (size_t i = 0; i < headers_size; ++i) {
64
26
    header_map->addCopy(
65
26
        Envoy::Http::LowerCaseString(std::string(headers[i].key_ptr, headers[i].key_length)),
66
26
        std::string(headers[i].value_ptr, headers[i].value_length));
67
26
  }
68

            
69
  // Check required headers.
70
9
  if (header_map->Path() == nullptr || header_map->Method() == nullptr ||
71
9
      header_map->Host() == nullptr) {
72
1
    return envoy_dynamic_module_type_http_callout_init_result_MissingRequiredHeaders;
73
1
  }
74

            
75
8
  Envoy::Http::RequestMessagePtr message =
76
8
      std::make_unique<Envoy::Http::RequestMessageImpl>(std::move(header_map));
77

            
78
8
  if (body.length > 0 && body.ptr != nullptr) {
79
1
    message->body().add(absl::string_view(body.ptr, body.length));
80
1
  }
81

            
82
8
  return config->sendHttpCallout(callout_id_out,
83
8
                                 absl::string_view(cluster_name.ptr, cluster_name.length),
84
8
                                 std::move(message), timeout_milliseconds);
85
9
}
86

            
87
// -------------------- Stats Access Callbacks --------------------
88

            
89
bool envoy_dynamic_module_callback_bootstrap_extension_get_counter_value(
90
    envoy_dynamic_module_type_bootstrap_extension_envoy_ptr extension_envoy_ptr,
91
3
    envoy_dynamic_module_type_module_buffer name, uint64_t* value_ptr) {
92
3
  auto* extension = static_cast<DynamicModuleBootstrapExtension*>(extension_envoy_ptr);
93
3
  Envoy::Stats::Store& stats_store = extension->statsStore();
94
3
  const absl::string_view name_view(name.ptr, name.length);
95

            
96
  // Use iterate() instead of forEachCounter() to enable early exit once the stat is found.
97
3
  bool found = false;
98
3
  Envoy::Stats::IterateFn<Envoy::Stats::Counter> counter_callback =
99
184
      [&name_view, &found, value_ptr](const Envoy::Stats::CounterSharedPtr& counter) -> bool {
100
183
    if (counter->name() == name_view) {
101
1
      *value_ptr = counter->value();
102
1
      found = true;
103
1
      return false; // Stop iteration.
104
1
    }
105
182
    return true; // Continue iteration.
106
183
  };
107
3
  stats_store.iterate(counter_callback);
108
3
  return found;
109
3
}
110

            
111
bool envoy_dynamic_module_callback_bootstrap_extension_get_gauge_value(
112
    envoy_dynamic_module_type_bootstrap_extension_envoy_ptr extension_envoy_ptr,
113
4
    envoy_dynamic_module_type_module_buffer name, uint64_t* value_ptr) {
114
4
  auto* extension = static_cast<DynamicModuleBootstrapExtension*>(extension_envoy_ptr);
115
4
  Envoy::Stats::Store& stats_store = extension->statsStore();
116
4
  const absl::string_view name_view(name.ptr, name.length);
117

            
118
  // Use iterate() instead of forEachGauge() to enable early exit once the stat is found.
119
4
  bool found = false;
120
4
  Envoy::Stats::IterateFn<Envoy::Stats::Gauge> gauge_callback =
121
107
      [&name_view, &found, value_ptr](const Envoy::Stats::GaugeSharedPtr& gauge) -> bool {
122
106
    if (gauge->name() == name_view) {
123
2
      *value_ptr = gauge->value();
124
2
      found = true;
125
2
      return false; // Stop iteration.
126
2
    }
127
104
    return true; // Continue iteration.
128
106
  };
129
4
  stats_store.iterate(gauge_callback);
130
4
  return found;
131
4
}
132

            
133
bool envoy_dynamic_module_callback_bootstrap_extension_get_histogram_summary(
134
    envoy_dynamic_module_type_bootstrap_extension_envoy_ptr extension_envoy_ptr,
135
    envoy_dynamic_module_type_module_buffer name, uint64_t* sample_count_ptr,
136
1
    double* sample_sum_ptr) {
137
1
  auto* extension = static_cast<DynamicModuleBootstrapExtension*>(extension_envoy_ptr);
138
1
  Envoy::Stats::Store& stats_store = extension->statsStore();
139
1
  const absl::string_view name_view(name.ptr, name.length);
140

            
141
1
  bool found = false;
142
1
  stats_store.forEachHistogram(
143
1
      [](size_t) {},
144
12
      [&name_view, &found, sample_count_ptr, sample_sum_ptr](Envoy::Stats::ParentHistogram& hist) {
145
12
        if (!found && hist.name() == name_view) {
146
          const auto& stats = hist.cumulativeStatistics();
147
          *sample_count_ptr = stats.sampleCount();
148
          *sample_sum_ptr = stats.sampleSum();
149
          found = true;
150
        }
151
12
      });
152
1
  return found;
153
1
}
154

            
155
void envoy_dynamic_module_callback_bootstrap_extension_iterate_counters(
156
    envoy_dynamic_module_type_bootstrap_extension_envoy_ptr extension_envoy_ptr,
157
2
    envoy_dynamic_module_type_counter_iterator_fn iterator_fn, void* user_data) {
158
2
  auto* extension = static_cast<DynamicModuleBootstrapExtension*>(extension_envoy_ptr);
159
2
  Envoy::Stats::Store& stats_store = extension->statsStore();
160

            
161
2
  stats_store.forEachCounter([](size_t) {},
162
185
                             [iterator_fn, user_data](Envoy::Stats::Counter& counter) {
163
185
                               std::string name = counter.name();
164
185
                               envoy_dynamic_module_type_envoy_buffer name_buffer{name.data(),
165
185
                                                                                  name.size()};
166
185
                               auto action = iterator_fn(name_buffer, counter.value(), user_data);
167
                               // Note: forEachCounter doesn't support early exit, so we ignore Stop
168
                               // action. The module should handle this by setting a flag in
169
                               // user_data.
170
185
                               (void)action;
171
185
                             });
172
2
}
173

            
174
void envoy_dynamic_module_callback_bootstrap_extension_iterate_gauges(
175
    envoy_dynamic_module_type_bootstrap_extension_envoy_ptr extension_envoy_ptr,
176
2
    envoy_dynamic_module_type_gauge_iterator_fn iterator_fn, void* user_data) {
177
2
  auto* extension = static_cast<DynamicModuleBootstrapExtension*>(extension_envoy_ptr);
178
2
  Envoy::Stats::Store& stats_store = extension->statsStore();
179

            
180
2
  stats_store.forEachGauge([](size_t) {},
181
70
                           [iterator_fn, user_data](Envoy::Stats::Gauge& gauge) {
182
70
                             std::string name = gauge.name();
183
70
                             envoy_dynamic_module_type_envoy_buffer name_buffer{name.data(),
184
70
                                                                                name.size()};
185
70
                             auto action = iterator_fn(name_buffer, gauge.value(), user_data);
186
                             // Note: forEachGauge doesn't support early exit, so we ignore Stop
187
                             // action. The module should handle this by setting a flag in
188
                             // user_data.
189
70
                             (void)action;
190
70
                           });
191
2
}
192

            
193
// -------------------- Stats Definition and Update Callbacks --------------------
194

            
195
} // extern "C"
196

            
197
namespace {
198

            
199
// Helper to build a StatNameTagVector from label names and label values.
200
Envoy::Stats::StatNameTagVector buildTagsForBootstrapMetric(
201
    DynamicModuleBootstrapExtensionConfig& config, const Envoy::Stats::StatNameVec& label_names,
202
10
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length) {
203
10
  ASSERT(label_values_length == label_names.size());
204
10
  Envoy::Stats::StatNameTagVector tags;
205
10
  tags.reserve(label_values_length);
206
22
  for (size_t i = 0; i < label_values_length; i++) {
207
12
    absl::string_view label_value_view(label_values[i].ptr, label_values[i].length);
208
12
    auto label_value = config.stat_name_pool_.add(label_value_view);
209
12
    tags.push_back(Envoy::Stats::StatNameTag(label_names[i], label_value));
210
12
  }
211
10
  return tags;
212
10
}
213

            
214
} // namespace
215

            
216
extern "C" {
217

            
218
envoy_dynamic_module_type_metrics_result
219
envoy_dynamic_module_callback_bootstrap_extension_config_define_counter(
220
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr,
221
    envoy_dynamic_module_type_module_buffer name,
222
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
223
8
    size_t* counter_id_ptr) {
224
8
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
225
8
  absl::string_view name_view(name.ptr, name.length);
226
8
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
227

            
228
  // Handle the special case where the labels size is zero.
229
8
  if (label_names_length == 0) {
230
4
    Envoy::Stats::Counter& c =
231
4
        Envoy::Stats::Utility::counterFromStatNames(*config->stats_scope_, {main_stat_name});
232
4
    *counter_id_ptr = config->addCounter({c});
233
4
    return envoy_dynamic_module_type_metrics_result_Success;
234
4
  }
235

            
236
4
  Envoy::Stats::StatNameVec label_names_vec;
237
11
  for (size_t i = 0; i < label_names_length; i++) {
238
7
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
239
7
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
240
7
  }
241
4
  *counter_id_ptr = config->addCounterVec({main_stat_name, label_names_vec});
242
4
  return envoy_dynamic_module_type_metrics_result_Success;
243
8
}
244

            
245
envoy_dynamic_module_type_metrics_result
246
envoy_dynamic_module_callback_bootstrap_extension_config_increment_counter(
247
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr, size_t id,
248
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
249
12
    uint64_t value) {
250
12
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
251

            
252
  // Handle the special case where the labels size is zero.
253
12
  if (label_values_length == 0) {
254
7
    auto counter = config->getCounterById(id);
255
7
    if (!counter.has_value()) {
256
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
257
1
    }
258
6
    counter->add(value);
259
6
    return envoy_dynamic_module_type_metrics_result_Success;
260
7
  }
261

            
262
5
  auto counter = config->getCounterVecById(id);
263
5
  if (!counter.has_value()) {
264
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
265
1
  }
266
4
  if (label_values_length != counter->getLabelNames().size()) {
267
2
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
268
2
  }
269
2
  auto tags = buildTagsForBootstrapMetric(*config, counter->getLabelNames(), label_values,
270
2
                                          label_values_length);
271
2
  counter->add(*config->stats_scope_, tags, value);
272
2
  return envoy_dynamic_module_type_metrics_result_Success;
273
4
}
274

            
275
envoy_dynamic_module_type_metrics_result
276
envoy_dynamic_module_callback_bootstrap_extension_config_define_gauge(
277
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr,
278
    envoy_dynamic_module_type_module_buffer name,
279
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
280
7
    size_t* gauge_id_ptr) {
281
7
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
282
7
  absl::string_view name_view(name.ptr, name.length);
283
7
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
284
7
  Envoy::Stats::Gauge::ImportMode import_mode = Envoy::Stats::Gauge::ImportMode::Accumulate;
285

            
286
  // Handle the special case where the labels size is zero.
287
7
  if (label_names_length == 0) {
288
4
    Envoy::Stats::Gauge& g = Envoy::Stats::Utility::gaugeFromStatNames(
289
4
        *config->stats_scope_, {main_stat_name}, import_mode);
290
4
    *gauge_id_ptr = config->addGauge({g});
291
4
    return envoy_dynamic_module_type_metrics_result_Success;
292
4
  }
293

            
294
3
  Envoy::Stats::StatNameVec label_names_vec;
295
6
  for (size_t i = 0; i < label_names_length; i++) {
296
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
297
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
298
3
  }
299
3
  *gauge_id_ptr = config->addGaugeVec({main_stat_name, label_names_vec, import_mode});
300
3
  return envoy_dynamic_module_type_metrics_result_Success;
301
7
}
302

            
303
envoy_dynamic_module_type_metrics_result
304
envoy_dynamic_module_callback_bootstrap_extension_config_set_gauge(
305
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr, size_t id,
306
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
307
7
    uint64_t value) {
308
7
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
309
  // Handle the special case where the labels size is zero.
310
7
  if (label_values_length == 0) {
311
3
    auto gauge = config->getGaugeById(id);
312
3
    if (!gauge.has_value()) {
313
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
314
1
    }
315
2
    gauge->set(value);
316
2
    return envoy_dynamic_module_type_metrics_result_Success;
317
3
  }
318
4
  auto gauge = config->getGaugeVecById(id);
319
4
  if (!gauge.has_value()) {
320
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
321
1
  }
322
3
  if (label_values_length != gauge->getLabelNames().size()) {
323
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
324
1
  }
325
2
  auto tags = buildTagsForBootstrapMetric(*config, gauge->getLabelNames(), label_values,
326
2
                                          label_values_length);
327
2
  gauge->set(*config->stats_scope_, tags, value);
328
2
  return envoy_dynamic_module_type_metrics_result_Success;
329
3
}
330

            
331
envoy_dynamic_module_type_metrics_result
332
envoy_dynamic_module_callback_bootstrap_extension_config_increment_gauge(
333
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr, size_t id,
334
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
335
7
    uint64_t value) {
336
7
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
337
  // Handle the special case where the labels size is zero.
338
7
  if (label_values_length == 0) {
339
3
    auto gauge = config->getGaugeById(id);
340
3
    if (!gauge.has_value()) {
341
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
342
1
    }
343
2
    gauge->add(value);
344
2
    return envoy_dynamic_module_type_metrics_result_Success;
345
3
  }
346
4
  auto gauge = config->getGaugeVecById(id);
347
4
  if (!gauge.has_value()) {
348
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
349
1
  }
350
3
  if (label_values_length != gauge->getLabelNames().size()) {
351
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
352
1
  }
353
2
  auto tags = buildTagsForBootstrapMetric(*config, gauge->getLabelNames(), label_values,
354
2
                                          label_values_length);
355
2
  gauge->add(*config->stats_scope_, tags, value);
356
2
  return envoy_dynamic_module_type_metrics_result_Success;
357
3
}
358

            
359
envoy_dynamic_module_type_metrics_result
360
envoy_dynamic_module_callback_bootstrap_extension_config_decrement_gauge(
361
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr, size_t id,
362
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
363
7
    uint64_t value) {
364
7
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
365
  // Handle the special case where the labels size is zero.
366
7
  if (label_values_length == 0) {
367
3
    auto gauge = config->getGaugeById(id);
368
3
    if (!gauge.has_value()) {
369
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
370
1
    }
371
2
    gauge->sub(value);
372
2
    return envoy_dynamic_module_type_metrics_result_Success;
373
3
  }
374
4
  auto gauge = config->getGaugeVecById(id);
375
4
  if (!gauge.has_value()) {
376
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
377
1
  }
378
3
  if (label_values_length != gauge->getLabelNames().size()) {
379
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
380
1
  }
381
2
  auto tags = buildTagsForBootstrapMetric(*config, gauge->getLabelNames(), label_values,
382
2
                                          label_values_length);
383
2
  gauge->sub(*config->stats_scope_, tags, value);
384
2
  return envoy_dynamic_module_type_metrics_result_Success;
385
3
}
386

            
387
envoy_dynamic_module_type_metrics_result
388
envoy_dynamic_module_callback_bootstrap_extension_config_define_histogram(
389
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr,
390
    envoy_dynamic_module_type_module_buffer name,
391
    envoy_dynamic_module_type_module_buffer* label_names, size_t label_names_length,
392
5
    size_t* histogram_id_ptr) {
393
5
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
394
5
  absl::string_view name_view(name.ptr, name.length);
395
5
  Envoy::Stats::StatName main_stat_name = config->stat_name_pool_.add(name_view);
396
5
  Envoy::Stats::Histogram::Unit unit = Envoy::Stats::Histogram::Unit::Unspecified;
397

            
398
  // Handle the special case where the labels size is zero.
399
5
  if (label_names_length == 0) {
400
2
    Envoy::Stats::Histogram& h = Envoy::Stats::Utility::histogramFromStatNames(
401
2
        *config->stats_scope_, {main_stat_name}, unit);
402
2
    *histogram_id_ptr = config->addHistogram({h});
403
2
    return envoy_dynamic_module_type_metrics_result_Success;
404
2
  }
405

            
406
3
  Envoy::Stats::StatNameVec label_names_vec;
407
6
  for (size_t i = 0; i < label_names_length; i++) {
408
3
    absl::string_view label_name_view(label_names[i].ptr, label_names[i].length);
409
3
    label_names_vec.push_back(config->stat_name_pool_.add(label_name_view));
410
3
  }
411
3
  *histogram_id_ptr = config->addHistogramVec({main_stat_name, label_names_vec, unit});
412
3
  return envoy_dynamic_module_type_metrics_result_Success;
413
5
}
414

            
415
envoy_dynamic_module_type_metrics_result
416
envoy_dynamic_module_callback_bootstrap_extension_config_record_histogram_value(
417
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr config_envoy_ptr, size_t id,
418
    envoy_dynamic_module_type_module_buffer* label_values, size_t label_values_length,
419
9
    uint64_t value) {
420
9
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(config_envoy_ptr);
421
  // Handle the special case where the labels size is zero.
422
9
  if (label_values_length == 0) {
423
5
    auto histogram = config->getHistogramById(id);
424
5
    if (!histogram.has_value()) {
425
1
      return envoy_dynamic_module_type_metrics_result_MetricNotFound;
426
1
    }
427
4
    histogram->recordValue(value);
428
4
    return envoy_dynamic_module_type_metrics_result_Success;
429
5
  }
430
4
  auto histogram = config->getHistogramVecById(id);
431
4
  if (!histogram.has_value()) {
432
1
    return envoy_dynamic_module_type_metrics_result_MetricNotFound;
433
1
  }
434
3
  if (label_values_length != histogram->getLabelNames().size()) {
435
1
    return envoy_dynamic_module_type_metrics_result_InvalidLabels;
436
1
  }
437
2
  auto tags = buildTagsForBootstrapMetric(*config, histogram->getLabelNames(), label_values,
438
2
                                          label_values_length);
439
2
  histogram->recordValue(*config->stats_scope_, tags, value);
440
2
  return envoy_dynamic_module_type_metrics_result_Success;
441
3
}
442

            
443
// -------------------- Timer Callbacks --------------------
444

            
445
envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr
446
envoy_dynamic_module_callback_bootstrap_extension_timer_new(
447
6
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr) {
448
6
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
449

            
450
  // Allocate the timer wrapper first so we can capture a stable heap pointer in the callback.
451
6
  auto* timer_wrapper = new DynamicModuleBootstrapExtensionTimer(config->weak_from_this());
452

            
453
  // Create the timer on the main thread dispatcher. The callback captures a weak_ptr to the config
454
  // to safely handle the case where the config is destroyed before the timer fires. The
455
  // timer_wrapper raw pointer is captured by value (copied) and is stable since it is
456
  // heap-allocated and its lifetime is managed by the module via timer_new/timer_delete.
457
6
  auto envoy_timer = config->main_thread_dispatcher_.createTimer(
458
6
      [weak_config = config->weak_from_this(), timer_wrapper]() {
459
4
        if (auto config_shared = weak_config.lock()) {
460
3
          if (config_shared->in_module_config_ != nullptr &&
461
3
              config_shared->on_bootstrap_extension_timer_fired_ != nullptr) {
462
3
            config_shared->on_bootstrap_extension_timer_fired_(config_shared->thisAsVoidPtr(),
463
3
                                                               config_shared->in_module_config_,
464
3
                                                               static_cast<void*>(timer_wrapper));
465
3
          }
466
3
        }
467
4
      });
468

            
469
6
  timer_wrapper->setTimer(std::move(envoy_timer));
470
6
  return static_cast<void*>(timer_wrapper);
471
6
}
472

            
473
void envoy_dynamic_module_callback_bootstrap_extension_timer_enable(
474
    envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr timer_ptr,
475
6
    uint64_t delay_milliseconds) {
476
6
  auto* timer = static_cast<DynamicModuleBootstrapExtensionTimer*>(timer_ptr);
477
6
  timer->timer().enableTimer(std::chrono::milliseconds(delay_milliseconds));
478
6
}
479

            
480
void envoy_dynamic_module_callback_bootstrap_extension_timer_disable(
481
1
    envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr timer_ptr) {
482
1
  auto* timer = static_cast<DynamicModuleBootstrapExtensionTimer*>(timer_ptr);
483
1
  timer->timer().disableTimer();
484
1
}
485

            
486
bool envoy_dynamic_module_callback_bootstrap_extension_timer_enabled(
487
5
    envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr timer_ptr) {
488
5
  auto* timer = static_cast<DynamicModuleBootstrapExtensionTimer*>(timer_ptr);
489
5
  return timer->timer().enabled();
490
5
}
491

            
492
void envoy_dynamic_module_callback_bootstrap_extension_timer_delete(
493
6
    envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr timer_ptr) {
494
6
  delete static_cast<DynamicModuleBootstrapExtensionTimer*>(timer_ptr);
495
6
}
496

            
497
// -------------------- Admin Handler Callbacks --------------------
498

            
499
void envoy_dynamic_module_callback_bootstrap_extension_admin_set_response(
500
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr,
501
2
    envoy_dynamic_module_type_module_buffer response_body) {
502
2
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
503
2
  if (response_body.ptr != nullptr && response_body.length > 0) {
504
2
    config->admin_response_body_.assign(response_body.ptr, response_body.length);
505
2
  }
506
2
}
507

            
508
bool envoy_dynamic_module_callback_bootstrap_extension_register_admin_handler(
509
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr,
510
    envoy_dynamic_module_type_module_buffer path_prefix,
511
5
    envoy_dynamic_module_type_module_buffer help_text, bool removable, bool mutates_server_state) {
512
5
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
513
5
  Envoy::OptRef<Envoy::Server::Admin> admin = config->context_.admin();
514
5
  if (!admin.has_value()) {
515
1
    return false;
516
1
  }
517

            
518
4
  const std::string prefix_str(path_prefix.ptr, path_prefix.length);
519
4
  const std::string help_str(help_text.ptr, help_text.length);
520

            
521
  // Capture a shared_ptr to the config to ensure it stays alive during admin handler callbacks.
522
4
  auto config_shared = config->shared_from_this();
523

            
524
4
  return admin->addHandler(
525
4
      prefix_str, help_str,
526
4
      [config_shared](Envoy::Http::ResponseHeaderMap& response_headers,
527
4
                      Envoy::Buffer::Instance& response,
528
5
                      Envoy::Server::AdminStream& admin_stream) -> Envoy::Http::Code {
529
3
        const auto& request_headers = admin_stream.getRequestHeaders();
530
3
        const auto method_entry = request_headers.getMethodValue();
531
3
        const auto path_entry = request_headers.getPathValue();
532
3
        const std::string method_str(method_entry.data(), method_entry.size());
533
3
        const std::string path_str(path_entry.data(), path_entry.size());
534

            
535
3
        std::string body_str;
536
3
        const Envoy::Buffer::Instance* request_body = admin_stream.getRequestBody();
537
3
        if (request_body != nullptr && request_body->length() > 0) {
538
          body_str = request_body->toString();
539
        }
540

            
541
3
        envoy_dynamic_module_type_envoy_buffer method_buf{method_str.data(), method_str.size()};
542
3
        envoy_dynamic_module_type_envoy_buffer path_buf{path_str.data(), path_str.size()};
543
3
        envoy_dynamic_module_type_envoy_buffer body_buf{body_str.data(), body_str.size()};
544

            
545
        // Clear any previous response body before calling the event hook.
546
3
        config_shared->admin_response_body_.clear();
547

            
548
3
        uint32_t status_code = config_shared->on_bootstrap_extension_admin_request_(
549
3
            config_shared->thisAsVoidPtr(), config_shared->in_module_config_, method_buf, path_buf,
550
3
            body_buf);
551

            
552
3
        if (!config_shared->admin_response_body_.empty()) {
553
2
          response.add(config_shared->admin_response_body_);
554
2
        }
555

            
556
        // Set content-type to text/plain by default.
557
3
        response_headers.setReferenceContentType(
558
3
            Envoy::Http::Headers::get().ContentTypeValues.Text);
559

            
560
3
        return static_cast<Envoy::Http::Code>(status_code);
561
3
      },
562
4
      removable, mutates_server_state);
563
5
}
564

            
565
bool envoy_dynamic_module_callback_bootstrap_extension_remove_admin_handler(
566
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr,
567
3
    envoy_dynamic_module_type_module_buffer path_prefix) {
568
3
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
569
3
  Envoy::OptRef<Envoy::Server::Admin> admin = config->context_.admin();
570
3
  if (!admin.has_value()) {
571
1
    return false;
572
1
  }
573

            
574
2
  const std::string prefix_str(path_prefix.ptr, path_prefix.length);
575
2
  return admin->removeHandler(prefix_str);
576
3
}
577

            
578
// -------------------- Cluster Lifecycle Callbacks --------------------
579

            
580
bool envoy_dynamic_module_callback_bootstrap_extension_enable_cluster_lifecycle(
581
3
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr) {
582
3
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
583
3
  return config->enableClusterLifecycle();
584
3
}
585

            
586
// -------------------- Listener Lifecycle Callbacks --------------------
587

            
588
bool envoy_dynamic_module_callback_bootstrap_extension_enable_listener_lifecycle(
589
4
    envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr extension_config_envoy_ptr) {
590
4
  auto* config = static_cast<DynamicModuleBootstrapExtensionConfig*>(extension_config_envoy_ptr);
591
4
  return config->enableListenerLifecycle();
592
4
}
593

            
594
} // extern "C"