1
use crate::abi::envoy_dynamic_module_type_metrics_result;
2
use crate::buffer::EnvoyBuffer;
3
use crate::{
4
  abi,
5
  drop_wrapped_c_void_ptr,
6
  str_to_module_buffer,
7
  wrap_into_c_void_ptr,
8
  EnvoyCounterId,
9
  EnvoyCounterVecId,
10
  EnvoyGaugeId,
11
  EnvoyGaugeVecId,
12
  EnvoyHistogramId,
13
  EnvoyHistogramVecId,
14
  NewBootstrapExtensionConfigFunction,
15
  NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION,
16
};
17
use mockall::*;
18

            
19
/// EnvoyBootstrapExtensionConfig is the Envoy-side bootstrap extension configuration.
20
/// This is a handle to the Envoy configuration object.
21
#[automock]
22
#[allow(clippy::needless_lifetimes)] // Explicit lifetime specifiers are needed for mockall.
23
pub trait EnvoyBootstrapExtensionConfig {
24
  /// Create a new implementation of the [`EnvoyBootstrapExtensionConfigScheduler`] trait.
25
  ///
26
  /// This can be used to schedule an event to the main thread where the config is running.
27
  fn new_scheduler(&self) -> Box<dyn EnvoyBootstrapExtensionConfigScheduler>;
28

            
29
  /// Send an HTTP callout to the given cluster with the given headers and optional body.
30
  ///
31
  /// Headers must contain the `:method`, `:path`, and `host` headers.
32
  ///
33
  /// This returns a tuple of (status, callout_id):
34
  ///   * Success + valid callout_id: The callout was started successfully. The callout ID can be
35
  ///     used to correlate the response in [`BootstrapExtensionConfig::on_http_callout_done`].
36
  ///   * ClusterNotFound: The cluster does not exist.
37
  ///   * MissingRequiredHeaders: The headers are missing required headers.
38
  ///   * CannotCreateRequest: The request could not be created, e.g., there's no healthy upstream
39
  ///     host in the cluster.
40
  ///
41
  /// The callout result will be delivered to the [`BootstrapExtensionConfig::on_http_callout_done`]
42
  /// method.
43
  ///
44
  /// This must be called on the main thread. To call from other threads, use the scheduler
45
  /// mechanism to post an event to the main thread first.
46
  fn send_http_callout<'a>(
47
    &mut self,
48
    _cluster_name: &'a str,
49
    _headers: Vec<(&'a str, &'a [u8])>,
50
    _body: Option<&'a [u8]>,
51
    _timeout_milliseconds: u64,
52
  ) -> (
53
    abi::envoy_dynamic_module_type_http_callout_init_result,
54
    u64, // callout id
55
  );
56

            
57
  /// Signal that the module's initialization is complete. Envoy automatically registers an init
58
  /// target for every bootstrap extension, blocking traffic until this is called.
59
  ///
60
  /// The module must call this exactly once during or after `new_bootstrap_extension_config` to
61
  /// unblock Envoy. If the module does not require asynchronous initialization, it should call
62
  /// this immediately during config creation.
63
  ///
64
  /// This must be called on the main thread. To call from other threads, use the scheduler
65
  /// mechanism to post an event to the main thread first.
66
  fn signal_init_complete(&self);
67

            
68
  /// Define a new counter scoped to this bootstrap extension config with the given name.
69
  fn define_counter(
70
    &mut self,
71
    name: &str,
72
  ) -> Result<EnvoyCounterId, envoy_dynamic_module_type_metrics_result>;
73

            
74
  /// Define a new counter vec scoped to this bootstrap extension config with the given name.
75
  fn define_counter_vec<'a>(
76
    &mut self,
77
    name: &str,
78
    labels: &[&'a str],
79
  ) -> Result<EnvoyCounterVecId, envoy_dynamic_module_type_metrics_result>;
80

            
81
  /// Define a new gauge scoped to this bootstrap extension config with the given name.
82
  fn define_gauge(
83
    &mut self,
84
    name: &str,
85
  ) -> Result<EnvoyGaugeId, envoy_dynamic_module_type_metrics_result>;
86

            
87
  /// Define a new gauge vec scoped to this bootstrap extension config with the given name.
88
  fn define_gauge_vec<'a>(
89
    &mut self,
90
    name: &str,
91
    labels: &[&'a str],
92
  ) -> Result<EnvoyGaugeVecId, envoy_dynamic_module_type_metrics_result>;
93

            
94
  /// Define a new histogram scoped to this bootstrap extension config with the given name.
95
  fn define_histogram(
96
    &mut self,
97
    name: &str,
98
  ) -> Result<EnvoyHistogramId, envoy_dynamic_module_type_metrics_result>;
99

            
100
  /// Define a new histogram vec scoped to this bootstrap extension config with the given name.
101
  fn define_histogram_vec<'a>(
102
    &mut self,
103
    name: &str,
104
    labels: &[&'a str],
105
  ) -> Result<EnvoyHistogramVecId, envoy_dynamic_module_type_metrics_result>;
106

            
107
  /// Increment the counter with the given id.
108
  fn increment_counter(
109
    &self,
110
    id: EnvoyCounterId,
111
    value: u64,
112
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
113

            
114
  /// Increment the counter vec with the given id.
115
  fn increment_counter_vec<'a>(
116
    &self,
117
    id: EnvoyCounterVecId,
118
    labels: &[&'a str],
119
    value: u64,
120
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
121

            
122
  /// Set the value of the gauge with the given id.
123
  fn set_gauge(
124
    &self,
125
    id: EnvoyGaugeId,
126
    value: u64,
127
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
128

            
129
  /// Set the value of the gauge vec with the given id.
130
  fn set_gauge_vec<'a>(
131
    &self,
132
    id: EnvoyGaugeVecId,
133
    labels: &[&'a str],
134
    value: u64,
135
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
136

            
137
  /// Increase the gauge with the given id.
138
  fn increase_gauge(
139
    &self,
140
    id: EnvoyGaugeId,
141
    value: u64,
142
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
143

            
144
  /// Increase the gauge vec with the given id.
145
  fn increase_gauge_vec<'a>(
146
    &self,
147
    id: EnvoyGaugeVecId,
148
    labels: &[&'a str],
149
    value: u64,
150
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
151

            
152
  /// Decrease the gauge with the given id.
153
  fn decrease_gauge(
154
    &self,
155
    id: EnvoyGaugeId,
156
    value: u64,
157
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
158

            
159
  /// Decrease the gauge vec with the given id.
160
  fn decrease_gauge_vec<'a>(
161
    &self,
162
    id: EnvoyGaugeVecId,
163
    labels: &[&'a str],
164
    value: u64,
165
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
166

            
167
  /// Record a value in the histogram with the given id.
168
  fn record_histogram_value(
169
    &self,
170
    id: EnvoyHistogramId,
171
    value: u64,
172
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
173

            
174
  /// Record a value in the histogram vec with the given id.
175
  fn record_histogram_value_vec<'a>(
176
    &self,
177
    id: EnvoyHistogramVecId,
178
    labels: &[&'a str],
179
    value: u64,
180
  ) -> Result<(), envoy_dynamic_module_type_metrics_result>;
181

            
182
  /// Create a new timer on the main thread dispatcher.
183
  ///
184
  /// The timer is not armed upon creation. Call [`EnvoyBootstrapExtensionTimer::enable`] to arm it.
185
  /// When the timer fires, [`BootstrapExtensionConfig::on_timer_fired`] is called on the main
186
  /// thread.
187
  ///
188
  /// The returned timer handle owns the underlying Envoy timer and will destroy it when dropped.
189
  ///
190
  /// This must be called on the main thread.
191
  fn new_timer(&self) -> Box<dyn EnvoyBootstrapExtensionTimer>;
192

            
193
  /// Register a custom admin HTTP endpoint.
194
  ///
195
  /// When the endpoint is requested, [`BootstrapExtensionConfig::on_admin_request`] is called.
196
  ///
197
  /// * `path_prefix` is the URL prefix to handle (e.g. "/my_module/status").
198
  /// * `help_text` is the help text displayed in the admin console.
199
  /// * `removable` if true, allows the handler to be removed later via
200
  ///   [`EnvoyBootstrapExtensionConfig::remove_admin_handler`].
201
  /// * `mutates_server_state` if true, indicates the handler mutates server state.
202
  ///
203
  /// Returns `true` if the handler was successfully registered, `false` otherwise.
204
  ///
205
  /// This must be called on the main thread.
206
  fn register_admin_handler(
207
    &self,
208
    path_prefix: &str,
209
    help_text: &str,
210
    removable: bool,
211
    mutates_server_state: bool,
212
  ) -> bool;
213

            
214
  /// Remove a previously registered admin HTTP endpoint.
215
  ///
216
  /// * `path_prefix` is the URL prefix of the handler to remove.
217
  ///
218
  /// Returns `true` if the handler was successfully removed, `false` otherwise.
219
  ///
220
  /// This must be called on the main thread.
221
  fn remove_admin_handler(&self, path_prefix: &str) -> bool;
222

            
223
  /// Enable cluster lifecycle event notifications. When enabled, the module will receive
224
  /// [`BootstrapExtensionConfig::on_cluster_add_or_update`] and
225
  /// [`BootstrapExtensionConfig::on_cluster_removal`] callbacks when clusters are added, updated,
226
  /// or removed from the ClusterManager.
227
  ///
228
  /// This must be called on the main thread, typically during or after `on_server_initialized`,
229
  /// since the ClusterManager is not available until that point.
230
  ///
231
  /// This should be called at most once. Subsequent calls are no-ops and return `false`.
232
  fn enable_cluster_lifecycle(&self) -> bool;
233

            
234
  /// Enable listener lifecycle event notifications. When enabled, the module will receive
235
  /// [`BootstrapExtensionConfig::on_listener_add_or_update`] and
236
  /// [`BootstrapExtensionConfig::on_listener_removal`] callbacks when listeners are added, updated,
237
  /// or removed from the ListenerManager.
238
  ///
239
  /// This must be called on the main thread, typically during or after `on_server_initialized`,
240
  /// since the ListenerManager is not available until that point.
241
  ///
242
  /// This should be called at most once. Subsequent calls are no-ops and return `false`.
243
  fn enable_listener_lifecycle(&self) -> bool;
244
}
245

            
246
/// EnvoyBootstrapExtension is the Envoy-side bootstrap extension.
247
/// This is a handle to the Envoy extension object.
248
pub trait EnvoyBootstrapExtension {
249
  /// Get the current value of a counter by name.
250
  ///
251
  /// Returns `Some(value)` if the counter exists, `None` otherwise.
252
  fn get_counter_value(&self, name: &str) -> Option<u64>;
253

            
254
  /// Get the current value of a gauge by name.
255
  ///
256
  /// Returns `Some(value)` if the gauge exists, `None` otherwise.
257
  fn get_gauge_value(&self, name: &str) -> Option<u64>;
258

            
259
  /// Get the summary statistics of a histogram by name.
260
  ///
261
  /// Returns `Some((sample_count, sample_sum))` if the histogram exists, `None` otherwise.
262
  /// These are cumulative statistics since the server started.
263
  fn get_histogram_summary(&self, name: &str) -> Option<(u64, f64)>;
264

            
265
  /// Iterate over all counters in the stats store.
266
  ///
267
  /// The callback receives the counter name and its current value.
268
  /// Return `true` to continue iteration, `false` to stop.
269
  fn iterate_counters(&self, callback: &mut dyn FnMut(&str, u64) -> bool);
270

            
271
  /// Iterate over all gauges in the stats store.
272
  ///
273
  /// The callback receives the gauge name and its current value.
274
  /// Return `true` to continue iteration, `false` to stop.
275
  fn iterate_gauges(&self, callback: &mut dyn FnMut(&str, u64) -> bool);
276
}
277

            
278
/// BootstrapExtensionConfig is the module-side bootstrap extension configuration.
279
///
280
/// This trait must be implemented by the module to handle bootstrap extension configuration.
281
/// The object is created when the corresponding Envoy bootstrap extension config is created, and
282
/// it is dropped when the corresponding Envoy bootstrap extension config is destroyed. Therefore,
283
/// the implementation is recommended to implement the [`Drop`] trait to handle the necessary
284
/// cleanup.
285
///
286
/// Implementations must also be `Send + Sync` since they may be accessed from multiple threads.
287
pub trait BootstrapExtensionConfig: Send + Sync {
288
  /// Create a new bootstrap extension instance.
289
  ///
290
  /// This is called when a new bootstrap extension is created.
291
  fn new_bootstrap_extension(
292
    &self,
293
    envoy_extension: &mut dyn EnvoyBootstrapExtension,
294
  ) -> Box<dyn BootstrapExtension>;
295

            
296
  /// This is called when a new event is scheduled via the
297
  /// [`EnvoyBootstrapExtensionConfigScheduler::commit`] for this [`BootstrapExtensionConfig`].
298
  ///
299
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
300
  /// * `event_id` is the ID of the event that was scheduled with
301
  ///   [`EnvoyBootstrapExtensionConfigScheduler::commit`] to distinguish multiple scheduled events.
302
  fn on_scheduled(
303
    &self,
304
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
305
    _event_id: u64,
306
  ) {
307
  }
308

            
309
  /// This is called when an HTTP callout response is received.
310
  ///
311
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
312
  /// * `callout_id` is the ID of the callout returned by
313
  ///   [`EnvoyBootstrapExtensionConfig::send_http_callout`].
314
  /// * `result` is the result of the callout.
315
  /// * `response_headers` is a list of key-value pairs of the response headers. This is optional.
316
  /// * `response_body` is the response body. This is optional.
317
  fn on_http_callout_done(
318
    &self,
319
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
320
    _callout_id: u64,
321
    _result: abi::envoy_dynamic_module_type_http_callout_result,
322
    _response_headers: Option<&[(EnvoyBuffer, EnvoyBuffer)]>,
323
    _response_body: Option<&[EnvoyBuffer]>,
324
  ) {
325
  }
326

            
327
  /// This is called when a timer created via [`EnvoyBootstrapExtensionConfig::new_timer`] fires.
328
  ///
329
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
330
  /// * `timer` is a non-owning reference to the timer that fired. The module can re-arm the timer
331
  ///   by calling [`EnvoyBootstrapExtensionTimer::enable`] on it.
332
  fn on_timer_fired(
333
    &self,
334
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
335
    _timer: &dyn EnvoyBootstrapExtensionTimer,
336
  ) {
337
  }
338

            
339
  /// This is called when an admin endpoint registered via
340
  /// [`EnvoyBootstrapExtensionConfig::register_admin_handler`] is requested.
341
  ///
342
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
343
  /// * `method` is the HTTP method of the request (e.g. "GET", "POST").
344
  /// * `path` is the full path and query string of the request.
345
  /// * `body` is the request body. May be empty.
346
  ///
347
  /// Returns a tuple of (HTTP status code, response body string).
348
  fn on_admin_request(
349
    &self,
350
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
351
    _method: &str,
352
    _path: &str,
353
    _body: &[u8],
354
  ) -> (u32, String) {
355
    (404, String::new())
356
  }
357

            
358
  /// This is called when a cluster is added to or updated in the ClusterManager.
359
  ///
360
  /// This is only called if the module has opted in via
361
  /// [`EnvoyBootstrapExtensionConfig::enable_cluster_lifecycle`]. The callback is invoked on the
362
  /// main thread.
363
  ///
364
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
365
  /// * `cluster_name` is the name of the cluster that was added or updated.
366
  fn on_cluster_add_or_update(
367
    &self,
368
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
369
    _cluster_name: &str,
370
  ) {
371
  }
372

            
373
  /// This is called when a cluster is removed from the ClusterManager.
374
  ///
375
  /// This is only called if the module has opted in via
376
  /// [`EnvoyBootstrapExtensionConfig::enable_cluster_lifecycle`]. The callback is invoked on the
377
  /// main thread.
378
  ///
379
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
380
  /// * `cluster_name` is the name of the cluster that was removed.
381
  fn on_cluster_removal(
382
    &self,
383
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
384
    _cluster_name: &str,
385
  ) {
386
  }
387

            
388
  /// This is called when a listener is added to or updated in the ListenerManager.
389
  ///
390
  /// This is only called if the module has opted in via
391
  /// [`EnvoyBootstrapExtensionConfig::enable_listener_lifecycle`]. The callback is invoked on the
392
  /// main thread.
393
  ///
394
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
395
  /// * `listener_name` is the name of the listener that was added or updated.
396
  fn on_listener_add_or_update(
397
    &self,
398
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
399
    _listener_name: &str,
400
  ) {
401
  }
402

            
403
  /// This is called when a listener is removed from the ListenerManager.
404
  ///
405
  /// This is only called if the module has opted in via
406
  /// [`EnvoyBootstrapExtensionConfig::enable_listener_lifecycle`]. The callback is invoked on the
407
  /// main thread.
408
  ///
409
  /// * `envoy_extension_config` can be used to interact with the underlying Envoy config object.
410
  /// * `listener_name` is the name of the listener that was removed.
411
  fn on_listener_removal(
412
    &self,
413
    _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
414
    _listener_name: &str,
415
  ) {
416
  }
417
}
418

            
419
/// A completion callback that must be invoked exactly once to signal that an asynchronous
420
/// operation has finished. Envoy will wait for this callback before proceeding.
421
///
422
/// The callback is invoked by calling [`CompletionCallback::done`].
423
pub struct CompletionCallback {
424
  callback: abi::envoy_dynamic_module_type_event_cb,
425
  context: *mut std::os::raw::c_void,
426
}
427

            
428
// Safety: The completion callback is provided by Envoy and is safe to send across threads.
429
unsafe impl Send for CompletionCallback {}
430
// Safety: The completion callback function pointer is thread-safe when invoked exactly once.
431
unsafe impl Sync for CompletionCallback {}
432

            
433
impl CompletionCallback {
434
  pub(crate) fn new(
435
    callback: abi::envoy_dynamic_module_type_event_cb,
436
    context: *mut std::os::raw::c_void,
437
  ) -> Self {
438
    Self { callback, context }
439
  }
440

            
441
  /// Signal that the asynchronous operation is complete. This must be called exactly once.
442
  pub fn done(self) {
443
    unsafe {
444
      if let Some(cb) = self.callback {
445
        cb(self.context);
446
      }
447
    }
448
    // Prevent Drop from running since we've consumed the callback.
449
    std::mem::forget(self);
450
  }
451
}
452

            
453
impl Drop for CompletionCallback {
454
  fn drop(&mut self) {
455
    // If the callback is dropped without being called, invoke it to prevent Envoy from hanging.
456
    unsafe {
457
      if let Some(cb) = self.callback {
458
        cb(self.context);
459
      }
460
    }
461
  }
462
}
463

            
464
/// BootstrapExtension is the module-side bootstrap extension.
465
///
466
/// This trait must be implemented by the module to handle bootstrap extension lifecycle events.
467
///
468
/// All the event hooks are called on the main thread unless otherwise noted.
469
pub trait BootstrapExtension: Send + Sync {
470
  /// Called when the server is initialized.
471
  ///
472
  /// This is called on the main thread after the ServerFactoryContext is fully initialized.
473
  /// This is where you can perform initialization tasks like fetching configuration from
474
  /// external services, initializing global state, or registering singleton resources.
475
  fn on_server_initialized(&mut self, _envoy_extension: &mut dyn EnvoyBootstrapExtension) {}
476

            
477
  /// Called when a worker thread is initialized.
478
  ///
479
  /// This is called once per worker thread when it starts. You can use this to perform
480
  /// per-worker-thread initialization like setting up thread-local storage.
481
  fn on_worker_thread_initialized(&mut self, _envoy_extension: &mut dyn EnvoyBootstrapExtension) {}
482

            
483
  /// Called when Envoy begins draining.
484
  ///
485
  /// This is called on the main thread before workers are stopped. The module can still make HTTP
486
  /// callouts and use timers during drain. This is the appropriate place to close persistent
487
  /// connections, stop background tasks, or de-register from service discovery.
488
  fn on_drain_started(&mut self, _envoy_extension: &mut dyn EnvoyBootstrapExtension) {}
489

            
490
  /// Called when Envoy is about to exit.
491
  ///
492
  /// This is called on the main thread during the ShutdownExit lifecycle stage. The module MUST
493
  /// signal completion by calling [`CompletionCallback::done`] when it has finished cleanup. Envoy
494
  /// will wait for the callback before terminating.
495
  ///
496
  /// If the [`CompletionCallback`] is dropped without calling `done`, it will automatically signal
497
  /// completion to prevent Envoy from hanging.
498
  fn on_shutdown(
499
    &mut self,
500
    _envoy_extension: &mut dyn EnvoyBootstrapExtension,
501
    completion: CompletionCallback,
502
  ) {
503
    completion.done();
504
  }
505
}
506

            
507
/// This represents a thread-safe object that can be used to schedule a generic event to the
508
/// Envoy bootstrap extension config on the main thread.
509
#[automock]
510
pub trait EnvoyBootstrapExtensionConfigScheduler: Send + Sync {
511
  /// Commit the scheduled event to the main thread.
512
  fn commit(&self, event_id: u64);
513
}
514

            
515
struct EnvoyBootstrapExtensionConfigSchedulerImpl {
516
  raw_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_scheduler_module_ptr,
517
}
518

            
519
unsafe impl Send for EnvoyBootstrapExtensionConfigSchedulerImpl {}
520
unsafe impl Sync for EnvoyBootstrapExtensionConfigSchedulerImpl {}
521

            
522
impl Drop for EnvoyBootstrapExtensionConfigSchedulerImpl {
523
  fn drop(&mut self) {
524
    unsafe {
525
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_delete(self.raw_ptr);
526
    }
527
  }
528
}
529

            
530
impl EnvoyBootstrapExtensionConfigScheduler for EnvoyBootstrapExtensionConfigSchedulerImpl {
531
  fn commit(&self, event_id: u64) {
532
    unsafe {
533
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_commit(
534
        self.raw_ptr,
535
        event_id,
536
      );
537
    }
538
  }
539
}
540

            
541
impl EnvoyBootstrapExtensionConfigScheduler for Box<dyn EnvoyBootstrapExtensionConfigScheduler> {
542
  fn commit(&self, event_id: u64) {
543
    (**self).commit(event_id);
544
  }
545
}
546

            
547
/// A timer handle for bootstrap extensions on the main thread event loop.
548
///
549
/// The timer is created via [`EnvoyBootstrapExtensionConfig::new_timer`] and fires by calling
550
/// [`BootstrapExtensionConfig::on_timer_fired`]. All methods must be called on the main thread.
551
///
552
/// The owning handle (returned by `new_timer`) will automatically destroy the underlying Envoy
553
/// timer when dropped.
554
///
555
/// Each timer has a unique [`id`](EnvoyBootstrapExtensionTimer::id) that is stable for its
556
/// lifetime. This allows modules with multiple timers to identify which timer fired in the
557
/// [`BootstrapExtensionConfig::on_timer_fired`] callback by comparing the id of the fired timer
558
/// reference against the ids of their stored timer handles.
559
#[automock]
560
pub trait EnvoyBootstrapExtensionTimer: Send + Sync {
561
  /// Returns a unique opaque identifier for this timer. The identifier is stable for the
562
  /// lifetime of the timer and can be used to distinguish between multiple timers in the
563
  /// [`BootstrapExtensionConfig::on_timer_fired`] callback.
564
  fn id(&self) -> usize;
565

            
566
  /// Enable the timer with the given delay. If the timer is already enabled, it is reset.
567
  fn enable(&self, delay: std::time::Duration);
568

            
569
  /// Disable the timer without destroying it. The timer can be re-enabled later.
570
  fn disable(&self);
571

            
572
  /// Check whether the timer is currently armed.
573
  fn enabled(&self) -> bool;
574
}
575

            
576
/// Owning implementation of [`EnvoyBootstrapExtensionTimer`]. Calls `timer_delete` on drop.
577
struct EnvoyBootstrapExtensionTimerImpl {
578
  raw_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr,
579
}
580

            
581
unsafe impl Send for EnvoyBootstrapExtensionTimerImpl {}
582
unsafe impl Sync for EnvoyBootstrapExtensionTimerImpl {}
583

            
584
impl Drop for EnvoyBootstrapExtensionTimerImpl {
585
  fn drop(&mut self) {
586
    unsafe {
587
      abi::envoy_dynamic_module_callback_bootstrap_extension_timer_delete(self.raw_ptr);
588
    }
589
  }
590
}
591

            
592
impl EnvoyBootstrapExtensionTimer for EnvoyBootstrapExtensionTimerImpl {
593
  fn id(&self) -> usize {
594
    self.raw_ptr as usize
595
  }
596

            
597
  fn enable(&self, delay: std::time::Duration) {
598
    unsafe {
599
      abi::envoy_dynamic_module_callback_bootstrap_extension_timer_enable(
600
        self.raw_ptr,
601
        delay.as_millis() as u64,
602
      );
603
    }
604
  }
605

            
606
  fn disable(&self) {
607
    unsafe {
608
      abi::envoy_dynamic_module_callback_bootstrap_extension_timer_disable(self.raw_ptr);
609
    }
610
  }
611

            
612
  fn enabled(&self) -> bool {
613
    unsafe { abi::envoy_dynamic_module_callback_bootstrap_extension_timer_enabled(self.raw_ptr) }
614
  }
615
}
616

            
617
/// Non-owning reference to a timer, used in the [`BootstrapExtensionConfig::on_timer_fired`]
618
/// callback. Does NOT call `timer_delete` on drop.
619
struct EnvoyBootstrapExtensionTimerRef {
620
  raw_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr,
621
}
622

            
623
// SAFETY: The raw pointer is only used on the main thread, matching Envoy's threading model.
624
unsafe impl Send for EnvoyBootstrapExtensionTimerRef {}
625
unsafe impl Sync for EnvoyBootstrapExtensionTimerRef {}
626

            
627
impl EnvoyBootstrapExtensionTimer for EnvoyBootstrapExtensionTimerRef {
628
  fn id(&self) -> usize {
629
    self.raw_ptr as usize
630
  }
631

            
632
  fn enable(&self, delay: std::time::Duration) {
633
    unsafe {
634
      abi::envoy_dynamic_module_callback_bootstrap_extension_timer_enable(
635
        self.raw_ptr,
636
        delay.as_millis() as u64,
637
      );
638
    }
639
  }
640

            
641
  fn disable(&self) {
642
    unsafe {
643
      abi::envoy_dynamic_module_callback_bootstrap_extension_timer_disable(self.raw_ptr);
644
    }
645
  }
646

            
647
  fn enabled(&self) -> bool {
648
    unsafe { abi::envoy_dynamic_module_callback_bootstrap_extension_timer_enabled(self.raw_ptr) }
649
  }
650
}
651

            
652
impl EnvoyBootstrapExtensionTimer for Box<dyn EnvoyBootstrapExtensionTimer> {
653
  fn id(&self) -> usize {
654
    (**self).id()
655
  }
656

            
657
  fn enable(&self, delay: std::time::Duration) {
658
    (**self).enable(delay);
659
  }
660

            
661
  fn disable(&self) {
662
    (**self).disable();
663
  }
664

            
665
  fn enabled(&self) -> bool {
666
    (**self).enabled()
667
  }
668
}
669

            
670
// Implementation of EnvoyBootstrapExtensionConfig
671

            
672
pub(crate) struct EnvoyBootstrapExtensionConfigImpl {
673
  raw: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
674
}
675

            
676
impl EnvoyBootstrapExtensionConfigImpl {
677
  pub(crate) fn new(
678
    raw: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
679
  ) -> Self {
680
    Self { raw }
681
  }
682
}
683

            
684
impl EnvoyBootstrapExtensionConfig for EnvoyBootstrapExtensionConfigImpl {
685
  fn new_scheduler(&self) -> Box<dyn EnvoyBootstrapExtensionConfigScheduler> {
686
    unsafe {
687
      let scheduler_ptr =
688
        abi::envoy_dynamic_module_callback_bootstrap_extension_config_scheduler_new(self.raw);
689
      Box::new(EnvoyBootstrapExtensionConfigSchedulerImpl {
690
        raw_ptr: scheduler_ptr,
691
      })
692
    }
693
  }
694

            
695
  fn send_http_callout<'a>(
696
    &mut self,
697
    cluster_name: &'a str,
698
    headers: Vec<(&'a str, &'a [u8])>,
699
    body: Option<&'a [u8]>,
700
    timeout_milliseconds: u64,
701
  ) -> (abi::envoy_dynamic_module_type_http_callout_init_result, u64) {
702
    let body_ptr = body.map(|s| s.as_ptr()).unwrap_or(std::ptr::null());
703
    let body_length = body.map(|s| s.len()).unwrap_or(0);
704

            
705
    // Convert headers to module HTTP headers.
706
    let module_headers: Vec<abi::envoy_dynamic_module_type_module_http_header> = headers
707
      .iter()
708
      .map(|(k, v)| abi::envoy_dynamic_module_type_module_http_header {
709
        key_ptr: k.as_ptr() as *const _,
710
        key_length: k.len(),
711
        value_ptr: v.as_ptr() as *const _,
712
        value_length: v.len(),
713
      })
714
      .collect();
715

            
716
    let mut callout_id: u64 = 0;
717

            
718
    let result = unsafe {
719
      abi::envoy_dynamic_module_callback_bootstrap_extension_http_callout(
720
        self.raw,
721
        &mut callout_id as *mut _ as *mut _,
722
        str_to_module_buffer(cluster_name),
723
        module_headers.as_ptr() as *mut _,
724
        module_headers.len(),
725
        abi::envoy_dynamic_module_type_module_buffer {
726
          ptr: body_ptr as *mut _,
727
          length: body_length,
728
        },
729
        timeout_milliseconds,
730
      )
731
    };
732

            
733
    (result, callout_id)
734
  }
735

            
736
  fn signal_init_complete(&self) {
737
    unsafe {
738
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_signal_init_complete(self.raw);
739
    }
740
  }
741

            
742
  fn define_counter(
743
    &mut self,
744
    name: &str,
745
  ) -> Result<EnvoyCounterId, envoy_dynamic_module_type_metrics_result> {
746
    let mut id: usize = 0;
747
    Result::from(unsafe {
748
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_counter(
749
        self.raw,
750
        str_to_module_buffer(name),
751
        std::ptr::null_mut(),
752
        0,
753
        &mut id,
754
      )
755
    })?;
756
    Ok(EnvoyCounterId(id))
757
  }
758

            
759
  fn define_counter_vec(
760
    &mut self,
761
    name: &str,
762
    labels: &[&str],
763
  ) -> Result<EnvoyCounterVecId, envoy_dynamic_module_type_metrics_result> {
764
    let labels_ptr = labels.as_ptr();
765
    let labels_size = labels.len();
766
    let mut id: usize = 0;
767
    Result::from(unsafe {
768
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_counter(
769
        self.raw,
770
        str_to_module_buffer(name),
771
        labels_ptr as *const _ as *mut _,
772
        labels_size,
773
        &mut id,
774
      )
775
    })?;
776
    Ok(EnvoyCounterVecId(id))
777
  }
778

            
779
  fn define_gauge(
780
    &mut self,
781
    name: &str,
782
  ) -> Result<EnvoyGaugeId, envoy_dynamic_module_type_metrics_result> {
783
    let mut id: usize = 0;
784
    Result::from(unsafe {
785
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_gauge(
786
        self.raw,
787
        str_to_module_buffer(name),
788
        std::ptr::null_mut(),
789
        0,
790
        &mut id,
791
      )
792
    })?;
793
    Ok(EnvoyGaugeId(id))
794
  }
795

            
796
  fn define_gauge_vec(
797
    &mut self,
798
    name: &str,
799
    labels: &[&str],
800
  ) -> Result<EnvoyGaugeVecId, envoy_dynamic_module_type_metrics_result> {
801
    let labels_ptr = labels.as_ptr();
802
    let labels_size = labels.len();
803
    let mut id: usize = 0;
804
    Result::from(unsafe {
805
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_gauge(
806
        self.raw,
807
        str_to_module_buffer(name),
808
        labels_ptr as *const _ as *mut _,
809
        labels_size,
810
        &mut id,
811
      )
812
    })?;
813
    Ok(EnvoyGaugeVecId(id))
814
  }
815

            
816
  fn define_histogram(
817
    &mut self,
818
    name: &str,
819
  ) -> Result<EnvoyHistogramId, envoy_dynamic_module_type_metrics_result> {
820
    let mut id: usize = 0;
821
    Result::from(unsafe {
822
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_histogram(
823
        self.raw,
824
        str_to_module_buffer(name),
825
        std::ptr::null_mut(),
826
        0,
827
        &mut id,
828
      )
829
    })?;
830
    Ok(EnvoyHistogramId(id))
831
  }
832

            
833
  fn define_histogram_vec(
834
    &mut self,
835
    name: &str,
836
    labels: &[&str],
837
  ) -> Result<EnvoyHistogramVecId, envoy_dynamic_module_type_metrics_result> {
838
    let labels_ptr = labels.as_ptr();
839
    let labels_size = labels.len();
840
    let mut id: usize = 0;
841
    Result::from(unsafe {
842
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_define_histogram(
843
        self.raw,
844
        str_to_module_buffer(name),
845
        labels_ptr as *const _ as *mut _,
846
        labels_size,
847
        &mut id,
848
      )
849
    })?;
850
    Ok(EnvoyHistogramVecId(id))
851
  }
852

            
853
  fn increment_counter(
854
    &self,
855
    id: EnvoyCounterId,
856
    value: u64,
857
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
858
    let EnvoyCounterId(id) = id;
859
    let res = unsafe {
860
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_increment_counter(
861
        self.raw,
862
        id,
863
        std::ptr::null_mut(),
864
        0,
865
        value,
866
      )
867
    };
868
    if res == envoy_dynamic_module_type_metrics_result::Success {
869
      Ok(())
870
    } else {
871
      Err(res)
872
    }
873
  }
874

            
875
  fn increment_counter_vec(
876
    &self,
877
    id: EnvoyCounterVecId,
878
    labels: &[&str],
879
    value: u64,
880
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
881
    let EnvoyCounterVecId(id) = id;
882
    let res = unsafe {
883
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_increment_counter(
884
        self.raw,
885
        id,
886
        labels.as_ptr() as *const _ as *mut _,
887
        labels.len(),
888
        value,
889
      )
890
    };
891
    if res == envoy_dynamic_module_type_metrics_result::Success {
892
      Ok(())
893
    } else {
894
      Err(res)
895
    }
896
  }
897

            
898
  fn set_gauge(
899
    &self,
900
    id: EnvoyGaugeId,
901
    value: u64,
902
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
903
    let EnvoyGaugeId(id) = id;
904
    let res = unsafe {
905
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_set_gauge(
906
        self.raw,
907
        id,
908
        std::ptr::null_mut(),
909
        0,
910
        value,
911
      )
912
    };
913
    if res == envoy_dynamic_module_type_metrics_result::Success {
914
      Ok(())
915
    } else {
916
      Err(res)
917
    }
918
  }
919

            
920
  fn set_gauge_vec(
921
    &self,
922
    id: EnvoyGaugeVecId,
923
    labels: &[&str],
924
    value: u64,
925
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
926
    let EnvoyGaugeVecId(id) = id;
927
    let res = unsafe {
928
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_set_gauge(
929
        self.raw,
930
        id,
931
        labels.as_ptr() as *const _ as *mut _,
932
        labels.len(),
933
        value,
934
      )
935
    };
936
    if res == envoy_dynamic_module_type_metrics_result::Success {
937
      Ok(())
938
    } else {
939
      Err(res)
940
    }
941
  }
942

            
943
  fn increase_gauge(
944
    &self,
945
    id: EnvoyGaugeId,
946
    value: u64,
947
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
948
    let EnvoyGaugeId(id) = id;
949
    let res = unsafe {
950
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_increment_gauge(
951
        self.raw,
952
        id,
953
        std::ptr::null_mut(),
954
        0,
955
        value,
956
      )
957
    };
958
    if res == envoy_dynamic_module_type_metrics_result::Success {
959
      Ok(())
960
    } else {
961
      Err(res)
962
    }
963
  }
964

            
965
  fn increase_gauge_vec(
966
    &self,
967
    id: EnvoyGaugeVecId,
968
    labels: &[&str],
969
    value: u64,
970
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
971
    let EnvoyGaugeVecId(id) = id;
972
    let res = unsafe {
973
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_increment_gauge(
974
        self.raw,
975
        id,
976
        labels.as_ptr() as *const _ as *mut _,
977
        labels.len(),
978
        value,
979
      )
980
    };
981
    if res == envoy_dynamic_module_type_metrics_result::Success {
982
      Ok(())
983
    } else {
984
      Err(res)
985
    }
986
  }
987

            
988
  fn decrease_gauge(
989
    &self,
990
    id: EnvoyGaugeId,
991
    value: u64,
992
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
993
    let EnvoyGaugeId(id) = id;
994
    let res = unsafe {
995
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_decrement_gauge(
996
        self.raw,
997
        id,
998
        std::ptr::null_mut(),
999
        0,
        value,
      )
    };
    if res == envoy_dynamic_module_type_metrics_result::Success {
      Ok(())
    } else {
      Err(res)
    }
  }
  fn decrease_gauge_vec(
    &self,
    id: EnvoyGaugeVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeVecId(id) = id;
    let res = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_decrement_gauge(
        self.raw,
        id,
        labels.as_ptr() as *const _ as *mut _,
        labels.len(),
        value,
      )
    };
    if res == envoy_dynamic_module_type_metrics_result::Success {
      Ok(())
    } else {
      Err(res)
    }
  }
  fn record_histogram_value(
    &self,
    id: EnvoyHistogramId,
    value: u64,
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
    let EnvoyHistogramId(id) = id;
    let res = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_record_histogram_value(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    };
    if res == envoy_dynamic_module_type_metrics_result::Success {
      Ok(())
    } else {
      Err(res)
    }
  }
  fn record_histogram_value_vec(
    &self,
    id: EnvoyHistogramVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), envoy_dynamic_module_type_metrics_result> {
    let EnvoyHistogramVecId(id) = id;
    let res = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_config_record_histogram_value(
        self.raw,
        id,
        labels.as_ptr() as *const _ as *mut _,
        labels.len(),
        value,
      )
    };
    if res == envoy_dynamic_module_type_metrics_result::Success {
      Ok(())
    } else {
      Err(res)
    }
  }
  fn new_timer(&self) -> Box<dyn EnvoyBootstrapExtensionTimer> {
    unsafe {
      let timer_ptr = abi::envoy_dynamic_module_callback_bootstrap_extension_timer_new(self.raw);
      Box::new(EnvoyBootstrapExtensionTimerImpl { raw_ptr: timer_ptr })
    }
  }
  fn register_admin_handler(
    &self,
    path_prefix: &str,
    help_text: &str,
    removable: bool,
    mutates_server_state: bool,
  ) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_register_admin_handler(
        self.raw,
        str_to_module_buffer(path_prefix),
        str_to_module_buffer(help_text),
        removable,
        mutates_server_state,
      )
    }
  }
  fn remove_admin_handler(&self, path_prefix: &str) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_remove_admin_handler(
        self.raw,
        str_to_module_buffer(path_prefix),
      )
    }
  }
  fn enable_cluster_lifecycle(&self) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_enable_cluster_lifecycle(self.raw)
    }
  }
  fn enable_listener_lifecycle(&self) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_enable_listener_lifecycle(self.raw)
    }
  }
}
// Implementation of EnvoyBootstrapExtension
pub(crate) struct EnvoyBootstrapExtensionImpl {
  raw: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
}
impl EnvoyBootstrapExtensionImpl {
  pub(crate) fn new(raw: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr) -> Self {
    Self { raw }
  }
}
impl EnvoyBootstrapExtension for EnvoyBootstrapExtensionImpl {
  fn get_counter_value(&self, name: &str) -> Option<u64> {
    let mut value: u64 = 0;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_get_counter_value(
        self.raw,
        str_to_module_buffer(name),
        &mut value,
      )
    };
    if found {
      Some(value)
    } else {
      None
    }
  }
  fn get_gauge_value(&self, name: &str) -> Option<u64> {
    let mut value: u64 = 0;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_get_gauge_value(
        self.raw,
        str_to_module_buffer(name),
        &mut value,
      )
    };
    if found {
      Some(value)
    } else {
      None
    }
  }
  fn get_histogram_summary(&self, name: &str) -> Option<(u64, f64)> {
    let mut sample_count: u64 = 0;
    let mut sample_sum: f64 = 0.0;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_get_histogram_summary(
        self.raw,
        str_to_module_buffer(name),
        &mut sample_count,
        &mut sample_sum,
      )
    };
    if found {
      Some((sample_count, sample_sum))
    } else {
      None
    }
  }
  fn iterate_counters(&self, callback: &mut dyn FnMut(&str, u64) -> bool) {
    // We use a wrapper struct to pass the closure through the C callback.
    struct CallbackWrapper<'a> {
      callback: &'a mut dyn FnMut(&str, u64) -> bool,
      stopped: bool,
    }
    extern "C" fn counter_iterator_trampoline(
      name: abi::envoy_dynamic_module_type_envoy_buffer,
      value: u64,
      user_data: *mut std::ffi::c_void,
    ) -> abi::envoy_dynamic_module_type_stats_iteration_action {
      let wrapper = unsafe { &mut *(user_data as *mut CallbackWrapper) };
      if wrapper.stopped {
        return abi::envoy_dynamic_module_type_stats_iteration_action::Stop;
      }
      let name_slice = unsafe { std::slice::from_raw_parts(name.ptr as *const u8, name.length) };
      let name_str = std::str::from_utf8(name_slice).unwrap_or("");
      if (wrapper.callback)(name_str, value) {
        abi::envoy_dynamic_module_type_stats_iteration_action::Continue
      } else {
        wrapper.stopped = true;
        abi::envoy_dynamic_module_type_stats_iteration_action::Stop
      }
    }
    let mut wrapper = CallbackWrapper {
      callback,
      stopped: false,
    };
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_iterate_counters(
        self.raw,
        Some(counter_iterator_trampoline),
        &mut wrapper as *mut _ as *mut std::ffi::c_void,
      );
    }
  }
  fn iterate_gauges(&self, callback: &mut dyn FnMut(&str, u64) -> bool) {
    // We use a wrapper struct to pass the closure through the C callback.
    struct CallbackWrapper<'a> {
      callback: &'a mut dyn FnMut(&str, u64) -> bool,
      stopped: bool,
    }
    extern "C" fn gauge_iterator_trampoline(
      name: abi::envoy_dynamic_module_type_envoy_buffer,
      value: u64,
      user_data: *mut std::ffi::c_void,
    ) -> abi::envoy_dynamic_module_type_stats_iteration_action {
      let wrapper = unsafe { &mut *(user_data as *mut CallbackWrapper) };
      if wrapper.stopped {
        return abi::envoy_dynamic_module_type_stats_iteration_action::Stop;
      }
      let name_slice = unsafe { std::slice::from_raw_parts(name.ptr as *const u8, name.length) };
      let name_str = std::str::from_utf8(name_slice).unwrap_or("");
      if (wrapper.callback)(name_str, value) {
        abi::envoy_dynamic_module_type_stats_iteration_action::Continue
      } else {
        wrapper.stopped = true;
        abi::envoy_dynamic_module_type_stats_iteration_action::Stop
      }
    }
    let mut wrapper = CallbackWrapper {
      callback,
      stopped: false,
    };
    unsafe {
      abi::envoy_dynamic_module_callback_bootstrap_extension_iterate_gauges(
        self.raw,
        Some(gauge_iterator_trampoline),
        &mut wrapper as *mut _ as *mut std::ffi::c_void,
      );
    }
  }
}
// Bootstrap Extension Event Hook Implementations
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_config_new(
  envoy_extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  name: abi::envoy_dynamic_module_type_envoy_buffer,
  config: abi::envoy_dynamic_module_type_envoy_buffer,
) -> abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr {
  let mut envoy_extension_config =
    EnvoyBootstrapExtensionConfigImpl::new(envoy_extension_config_ptr);
  let name_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      name.ptr as *const _,
      name.length,
    ))
  };
  let config_slice = unsafe { std::slice::from_raw_parts(config.ptr as *const _, config.length) };
  init_bootstrap_extension_config(
    &mut envoy_extension_config,
    name_str,
    config_slice,
    NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION
      .get()
      .expect("NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION must be set"),
  )
}
pub(crate) fn init_bootstrap_extension_config(
  envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
  name: &str,
  config: &[u8],
  new_extension_config_fn: &NewBootstrapExtensionConfigFunction,
) -> abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr {
  let extension_config = new_extension_config_fn(envoy_extension_config, name, config);
  match extension_config {
    Some(config) => wrap_into_c_void_ptr!(config),
    None => std::ptr::null(),
  }
}
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_config_destroy(
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
) {
  drop_wrapped_c_void_ptr!(extension_config_ptr, BootstrapExtensionConfig);
}
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_new(
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  envoy_extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
) -> abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr {
  let mut envoy_extension = EnvoyBootstrapExtensionImpl::new(envoy_extension_ptr);
  let extension_config = {
    let raw = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
    &**raw
  };
  envoy_dynamic_module_on_bootstrap_extension_new_impl(&mut envoy_extension, extension_config)
}
pub(crate) fn envoy_dynamic_module_on_bootstrap_extension_new_impl(
  envoy_extension: &mut EnvoyBootstrapExtensionImpl,
  extension_config: &dyn BootstrapExtensionConfig,
) -> abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr {
  let extension = extension_config.new_bootstrap_extension(envoy_extension);
  wrap_into_c_void_ptr!(extension)
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_server_initialized(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
  extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr,
) {
  let extension = extension_ptr as *mut Box<dyn BootstrapExtension>;
  let extension = unsafe { &mut *extension };
  extension.on_server_initialized(&mut EnvoyBootstrapExtensionImpl::new(envoy_ptr));
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_worker_thread_initialized(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
  extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr,
) {
  let extension = extension_ptr as *mut Box<dyn BootstrapExtension>;
  let extension = unsafe { &mut *extension };
  extension.on_worker_thread_initialized(&mut EnvoyBootstrapExtensionImpl::new(envoy_ptr));
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_drain_started(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
  extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr,
) {
  let extension = extension_ptr as *mut Box<dyn BootstrapExtension>;
  let extension = unsafe { &mut *extension };
  extension.on_drain_started(&mut EnvoyBootstrapExtensionImpl::new(envoy_ptr));
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_shutdown(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_envoy_ptr,
  extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr,
  completion_callback: abi::envoy_dynamic_module_type_event_cb,
  completion_context: *mut std::os::raw::c_void,
) {
  let extension = extension_ptr as *mut Box<dyn BootstrapExtension>;
  let extension = unsafe { &mut *extension };
  let completion = CompletionCallback::new(completion_callback, completion_context);
  extension.on_shutdown(&mut EnvoyBootstrapExtensionImpl::new(envoy_ptr), completion);
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_destroy(
  extension_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_module_ptr,
) {
  let _ = unsafe { Box::from_raw(extension_ptr as *mut Box<dyn BootstrapExtension>) };
}
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_config_scheduled(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  event_id: u64,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  extension_config.on_scheduled(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    event_id,
  );
}
/// Event hook called by Envoy when an HTTP callout initiated by a bootstrap extension completes.
///
/// # Safety
/// This function is unsafe because it dereferences raw pointers passed from Envoy. The caller
/// must ensure that all pointers are valid and that the memory they point to remains valid for
/// the duration of the function call.
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_http_callout_done(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  callout_id: u64,
  result: abi::envoy_dynamic_module_type_http_callout_result,
  headers: *const abi::envoy_dynamic_module_type_envoy_http_header,
  headers_size: usize,
  body_chunks: *const abi::envoy_dynamic_module_type_envoy_buffer,
  body_chunks_size: usize,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let headers = if headers_size > 0 {
    Some(unsafe {
      std::slice::from_raw_parts(headers as *const (EnvoyBuffer, EnvoyBuffer), headers_size)
    })
  } else {
    None
  };
  let body = if body_chunks_size > 0 {
    Some(unsafe { std::slice::from_raw_parts(body_chunks as *const EnvoyBuffer, body_chunks_size) })
  } else {
    None
  };
  extension_config.on_http_callout_done(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    callout_id,
    result,
    headers,
    body,
  );
}
/// Event hook called by Envoy when a timer created by a bootstrap extension fires.
#[no_mangle]
pub extern "C" fn envoy_dynamic_module_on_bootstrap_extension_timer_fired(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  timer_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_timer_module_ptr,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  // Create a non-owning reference to the timer so the module can re-enable it.
  let timer_ref = EnvoyBootstrapExtensionTimerRef { raw_ptr: timer_ptr };
  extension_config.on_timer_fired(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    &timer_ref,
  );
}
/// Event hook called by Envoy when an admin endpoint registered by a bootstrap extension is
/// requested.
///
/// # Safety
/// This function is unsafe because it dereferences raw pointers passed from Envoy. The caller
/// must ensure that all pointers are valid and that the memory they point to remains valid for
/// the duration of the function call.
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_admin_request(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  method: abi::envoy_dynamic_module_type_envoy_buffer,
  path: abi::envoy_dynamic_module_type_envoy_buffer,
  body: abi::envoy_dynamic_module_type_envoy_buffer,
) -> u32 {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let method_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      method.ptr as *const u8,
      method.length,
    ))
  };
  let path_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      path.ptr as *const u8,
      path.length,
    ))
  };
  let body_slice = unsafe { std::slice::from_raw_parts(body.ptr as *const u8, body.length) };
  let (status_code, response_str) = extension_config.on_admin_request(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    method_str,
    path_str,
    body_slice,
  );
  // Pass the response body to Envoy via the callback. Envoy copies the buffer immediately,
  // so the string only needs to live until the call returns.
  if !response_str.is_empty() {
    let response_buf = abi::envoy_dynamic_module_type_module_buffer {
      ptr: response_str.as_ptr() as *const _,
      length: response_str.len(),
    };
    abi::envoy_dynamic_module_callback_bootstrap_extension_admin_set_response(
      envoy_ptr,
      response_buf,
    );
  }
  status_code
}
/// Event hook called by Envoy when a cluster is added to or updated in the ClusterManager.
///
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_cluster_add_or_update(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  cluster_name: abi::envoy_dynamic_module_type_envoy_buffer,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let cluster_name_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      cluster_name.ptr as *const u8,
      cluster_name.length,
    ))
  };
  extension_config.on_cluster_add_or_update(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    cluster_name_str,
  );
}
/// Event hook called by Envoy when a cluster is removed from the ClusterManager.
///
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_cluster_removal(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  cluster_name: abi::envoy_dynamic_module_type_envoy_buffer,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let cluster_name_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      cluster_name.ptr as *const u8,
      cluster_name.length,
    ))
  };
  extension_config.on_cluster_removal(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    cluster_name_str,
  );
}
/// Event hook called by Envoy when a listener is added to or updated in the ListenerManager.
///
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_listener_add_or_update(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  listener_name: abi::envoy_dynamic_module_type_envoy_buffer,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let listener_name_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      listener_name.ptr as *const u8,
      listener_name.length,
    ))
  };
  extension_config.on_listener_add_or_update(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    listener_name_str,
  );
}
/// Event hook called by Envoy when a listener is removed from the ListenerManager.
///
/// # Safety
///
/// This is an FFI function called by Envoy. All pointer arguments must be valid as guaranteed
/// by the Envoy dynamic module ABI.
#[no_mangle]
pub unsafe extern "C" fn envoy_dynamic_module_on_bootstrap_extension_listener_removal(
  envoy_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_envoy_ptr,
  extension_config_ptr: abi::envoy_dynamic_module_type_bootstrap_extension_config_module_ptr,
  listener_name: abi::envoy_dynamic_module_type_envoy_buffer,
) {
  let extension_config = extension_config_ptr as *const *const dyn BootstrapExtensionConfig;
  let extension_config = unsafe { &**extension_config };
  let listener_name_str = unsafe {
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
      listener_name.ptr as *const u8,
      listener_name.length,
    ))
  };
  extension_config.on_listener_removal(
    &mut EnvoyBootstrapExtensionConfigImpl::new(envoy_ptr),
    listener_name_str,
  );
}