1
#![allow(non_upper_case_globals)]
2
#![allow(non_camel_case_types)]
3
#![allow(non_snake_case)]
4
#![allow(dead_code)]
5
#![allow(clippy::unnecessary_cast)]
6

            
7
pub mod access_log;
8
pub mod bootstrap;
9
pub mod buffer;
10
pub mod cert_validator;
11
pub mod cluster;
12
pub mod http;
13
pub mod listener;
14
pub mod load_balancer;
15
pub mod matcher;
16
pub mod network;
17
pub mod udp_listener;
18
pub mod upstream_http_tcp_bridge;
19
pub mod utility;
20
pub use bootstrap::*;
21
pub use buffer::*;
22
pub use cert_validator::*;
23
pub use cluster::*;
24
pub use http::*;
25
pub use listener::*;
26
pub use load_balancer::*;
27
pub use network::*;
28
pub use udp_listener::*;
29
pub use upstream_http_tcp_bridge::*;
30
pub use utility::*;
31

            
32
#[cfg(test)]
33
#[path = "./lib_test.rs"]
34
mod mod_test;
35

            
36
use crate::abi::envoy_dynamic_module_type_metrics_result;
37
use std::any::Any;
38
use std::sync::OnceLock;
39

            
40
/// This module contains the generated bindings for the envoy dynamic modules ABI.
41
///
42
/// This is not meant to be used directly.
43
pub mod abi {
44
  include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
45
}
46

            
47
/// Declare the init functions for the dynamic module.
48
///
49
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
50
/// loaded.
51
///
52
/// The second argument has [`NewHttpFilterConfigFunction`] type, and it is called when the new HTTP
53
/// filter configuration is created.
54
///
55
/// # Example
56
///
57
/// ```
58
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
59
///
60
/// declare_init_functions!(my_program_init, my_new_http_filter_config_fn);
61
///
62
/// fn my_program_init() -> bool {
63
///   true
64
/// }
65
///
66
/// fn my_new_http_filter_config_fn<EC: EnvoyHttpFilterConfig, EHF: EnvoyHttpFilter>(
67
///   _envoy_filter_config: &mut EC,
68
///   _name: &str,
69
///   _config: &[u8],
70
/// ) -> Option<Box<dyn HttpFilterConfig<EHF>>> {
71
///   Some(Box::new(MyHttpFilterConfig {}))
72
/// }
73
///
74
/// struct MyHttpFilterConfig {}
75
///
76
/// impl<EHF: EnvoyHttpFilter> HttpFilterConfig<EHF> for MyHttpFilterConfig {}
77
/// ```
78
#[macro_export]
79
macro_rules! declare_init_functions {
80
  ($f:ident, $new_http_filter_config_fn:expr, $new_http_filter_per_route_config_fn:expr) => {
81
    #[no_mangle]
82
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
83
      envoy_proxy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_CONFIG_FUNCTION
84
        .get_or_init(|| $new_http_filter_config_fn);
85
      envoy_proxy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_PER_ROUTE_CONFIG_FUNCTION
86
        .get_or_init(|| $new_http_filter_per_route_config_fn);
87
      if ($f()) {
88
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
89
          as *const ::std::os::raw::c_char
90
      } else {
91
        ::std::ptr::null()
92
      }
93
    }
94
  };
95
  ($f:ident, $new_http_filter_config_fn:expr) => {
96
    #[no_mangle]
97
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
98
      envoy_proxy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_CONFIG_FUNCTION
99
        .get_or_init(|| $new_http_filter_config_fn);
100
      if ($f()) {
101
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
102
          as *const ::std::os::raw::c_char
103
      } else {
104
        ::std::ptr::null()
105
      }
106
    }
107
  };
108
}
109

            
110
/// Get the concurrency from the server context options.
111
/// # Safety
112
///
113
/// This function must be called on the main thread.
114
pub unsafe fn get_server_concurrency() -> u32 {
115
  unsafe { abi::envoy_dynamic_module_callback_get_concurrency() }
116
}
117

            
118
/// Register a function pointer under a name in the process-wide function registry.
119
///
120
/// This allows modules loaded in the same process to expose functions that other modules can
121
/// resolve by name and call directly, enabling zero-copy cross-module interactions. For example,
122
/// a bootstrap extension can register a function that returns a pointer to a tenant snapshot,
123
/// and HTTP filters can resolve and call it on every request.
124
///
125
/// Registration is typically done once during bootstrap (e.g., in `on_server_initialized`).
126
/// Duplicate registration under the same key returns `false`.
127
///
128
/// Callers are responsible for agreeing on the function signature out-of-band, since the
129
/// registry stores opaque pointers — analogous to `dlsym` semantics.
130
///
131
/// This is thread-safe and can be called from any thread.
132
///
133
/// # Safety
134
///
135
/// The `function_ptr` must point to a valid function that remains valid for the lifetime of the
136
/// process.
137
pub unsafe fn register_function(key: &str, function_ptr: *const std::ffi::c_void) -> bool {
138
  unsafe {
139
    abi::envoy_dynamic_module_callback_register_function(
140
      str_to_module_buffer(key),
141
      function_ptr as *mut std::ffi::c_void,
142
    )
143
  }
144
}
145

            
146
/// Retrieve a previously registered function pointer by name from the process-wide function
147
/// registry. The returned pointer can be cast to the expected function signature and called
148
/// directly.
149
///
150
/// Resolution is typically done once during configuration creation (e.g., in
151
/// `on_http_filter_config_new`) and the result cached for per-request use.
152
///
153
/// This is thread-safe and can be called from any thread.
154
pub fn get_function(key: &str) -> Option<*const std::ffi::c_void> {
155
  let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
156
  let found =
157
    unsafe { abi::envoy_dynamic_module_callback_get_function(str_to_module_buffer(key), &mut ptr) };
158
  if found {
159
    Some(ptr as *const std::ffi::c_void)
160
  } else {
161
    None
162
  }
163
}
164

            
165
/// Register an opaque data pointer under a name in the process-wide shared data registry.
166
///
167
/// This allows modules loaded in the same process to share arbitrary state — such as runtime
168
/// handles, configuration snapshots, or shared caches — without requiring direct access to
169
/// each other's globals. For example, a bootstrap extension can register a Tokio runtime handle
170
/// and HTTP filters or cluster extensions can retrieve and use it for async operations.
171
///
172
/// Unlike [`register_function`], the shared data registry allows overwriting an existing entry.
173
/// If the key already exists, the data pointer is updated and the function returns `true`. This
174
/// supports patterns where shared state is refreshed (e.g., after a configuration reload).
175
/// Callers are responsible for managing the lifetime of overwritten data pointers.
176
///
177
/// Registration is typically done once during bootstrap (e.g., in `on_server_initialized` or
178
/// `on_scheduled`).
179
///
180
/// This is thread-safe and can be called from any thread.
181
///
182
/// # Safety
183
///
184
/// The `data_ptr` must point to valid data that remains valid for the lifetime of the process.
185
/// Callers are responsible for agreeing on the data type out-of-band, since the registry stores
186
/// opaque pointers.
187
pub unsafe fn register_shared_data(key: &str, data_ptr: *const std::ffi::c_void) -> bool {
188
  unsafe {
189
    abi::envoy_dynamic_module_callback_register_shared_data(
190
      str_to_module_buffer(key),
191
      data_ptr as *mut std::ffi::c_void,
192
    )
193
  }
194
}
195

            
196
/// Retrieve a previously registered data pointer by name from the process-wide shared data
197
/// registry. The returned pointer can be cast to the expected data type and used directly.
198
///
199
/// Resolution is typically done once during configuration creation (e.g., in
200
/// `on_http_filter_config_new`) and the result cached for per-request use.
201
///
202
/// This is thread-safe and can be called from any thread.
203
pub fn get_shared_data(key: &str) -> Option<*const std::ffi::c_void> {
204
  let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
205
  let found = unsafe {
206
    abi::envoy_dynamic_module_callback_get_shared_data(str_to_module_buffer(key), &mut ptr)
207
  };
208
  if found {
209
    Some(ptr as *const std::ffi::c_void)
210
  } else {
211
    None
212
  }
213
}
214

            
215
/// Log a trace message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
216
/// allocated if the log level is not enabled on the Envoy side.
217
///
218
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
219
/// messages.
220
#[macro_export]
221
macro_rules! envoy_log_trace {
222
    ($($arg:tt)*) => {
223
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Trace, $($arg)*)
224
    };
225
}
226

            
227
/// Log a debug message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
228
/// allocated if the log level is not enabled on the Envoy side.
229
///
230
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
231
/// messages.
232
#[macro_export]
233
macro_rules! envoy_log_debug {
234
    ($($arg:tt)*) => {
235
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Debug, $($arg)*)
236
    };
237
}
238

            
239
/// Log an info message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
240
/// allocated if the log level is not enabled on the Envoy side.
241
///
242
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
243
/// messages.
244
#[macro_export]
245
macro_rules! envoy_log_info {
246
    ($($arg:tt)*) => {
247
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Info, $($arg)*)
248
    };
249
}
250

            
251
/// Log a warning message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
252
/// allocated if the log level is not enabled on the Envoy side.
253
///
254
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
255
/// messages.
256
#[macro_export]
257
macro_rules! envoy_log_warn {
258
    ($($arg:tt)*) => {
259
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Warn, $($arg)*)
260
    };
261
}
262

            
263
/// Log an error message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
264
/// allocated if the log level is not enabled on the Envoy side.
265
///
266
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
267
/// messages.
268
#[macro_export]
269
macro_rules! envoy_log_error {
270
    ($($arg:tt)*) => {
271
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Error, $($arg)*)
272
    };
273
}
274

            
275
/// Log a critical message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
276
/// allocated if the log level is not enabled on the Envoy side.
277
///
278
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
279
/// messages.
280
#[macro_export]
281
macro_rules! envoy_log_critical {
282
    ($($arg:tt)*) => {
283
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Critical, $($arg)*)
284
    };
285
}
286

            
287
/// Internal logging macro that handles the actual call to the Envoy logging callback
288
/// used by envoy_log_* macros.
289
#[macro_export]
290
macro_rules! envoy_log {
291
  ($level:expr, $($arg:tt)*) => {
292
    {
293
      #[cfg(not(test))]
294
      unsafe {
295
        // Avoid allocating the message if the log level is not enabled.
296
        if $crate::abi::envoy_dynamic_module_callback_log_enabled($level) {
297
          let message = format!($($arg)*);
298
          let message_bytes = message.as_bytes();
299
          $crate::abi::envoy_dynamic_module_callback_log(
300
            $level,
301
            $crate::abi::envoy_dynamic_module_type_module_buffer {
302
              ptr: message_bytes.as_ptr() as *const ::std::os::raw::c_char,
303
              length: message_bytes.len(),
304
            },
305
          );
306
        }
307
      }
308
      // In unit tests, just print to stderr since the Envoy symbols are not available.
309
      #[cfg(test)]
310
      {
311
        let message = format!($($arg)*);
312
        eprintln!("[{}] {}", stringify!($level), message);
313
      }
314
    }
315
  };
316
}
317

            
318
/// The function signature for the program init function.
319
///
320
/// This is called when the dynamic module is loaded, and it must return true on success, and false
321
/// on failure. When it returns false, the dynamic module will not be loaded.
322
///
323
/// This is useful to perform any process-wide initialization that the dynamic module needs.
324
pub type ProgramInitFunction = fn() -> bool;
325

            
326
/// The function signature for the new HTTP filter configuration function.
327
///
328
/// This is called when a new HTTP filter configuration is created, and it must return a new
329
/// instance of the [`HttpFilterConfig`] object. Returning `None` will cause the HTTP filter
330
/// configuration to be rejected.
331
//
332
// TODO(@mathetake): I guess there would be a way to avoid the use of dyn in the first place.
333
// E.g. one idea is to accept all concrete type parameters for HttpFilterConfig and HttpFilter
334
// traits in declare_init_functions!, and generate the match statement based on that.
335
pub type NewHttpFilterConfigFunction<EC, EHF> = fn(
336
  envoy_filter_config: &mut EC,
337
  name: &str,
338
  config: &[u8],
339
) -> Option<Box<dyn HttpFilterConfig<EHF>>>;
340

            
341
/// The global init function for HTTP filter configurations. This is set via the
342
/// `declare_init_functions` macro, and is not intended to be set directly.
343
pub static NEW_HTTP_FILTER_CONFIG_FUNCTION: OnceLock<
344
  NewHttpFilterConfigFunction<http::EnvoyHttpFilterConfigImpl, http::EnvoyHttpFilterImpl>,
345
> = OnceLock::new();
346

            
347
/// The function signature for the new HTTP filter per-route configuration function.
348
///
349
/// This is called when a new HTTP filter per-route configuration is created. It must return an
350
/// object representing the filter's per-route configuration. Returning `None` will cause the HTTP
351
/// filter configuration to be rejected.
352
/// This config can be later retried by the filter via
353
/// [`EnvoyHttpFilter::get_most_specific_route_config`] method.
354
pub type NewHttpFilterPerRouteConfigFunction =
355
  fn(name: &str, config: &[u8]) -> Option<Box<dyn Any>>;
356

            
357
/// The global init function for HTTP filter per-route configurations. This is set via the
358
/// `declare_init_functions` macro, and is not intended to be set directly.
359
pub static NEW_HTTP_FILTER_PER_ROUTE_CONFIG_FUNCTION: OnceLock<
360
  NewHttpFilterPerRouteConfigFunction,
361
> = OnceLock::new();
362

            
363
// HTTP filter types are in the http module and re-exported above.
364

            
365
pub(crate) fn str_to_module_buffer(s: &str) -> abi::envoy_dynamic_module_type_module_buffer {
366
  abi::envoy_dynamic_module_type_module_buffer {
367
    ptr: s.as_ptr() as *const _ as *mut _,
368
    length: s.len(),
369
  }
370
}
371

            
372
pub(crate) fn strs_to_module_buffers(
373
  strs: &[&str],
374
) -> Vec<abi::envoy_dynamic_module_type_module_buffer> {
375
  strs.iter().map(|s| str_to_module_buffer(s)).collect()
376
}
377

            
378
pub(crate) fn bytes_to_module_buffer(b: &[u8]) -> abi::envoy_dynamic_module_type_module_buffer {
379
  abi::envoy_dynamic_module_type_module_buffer {
380
    ptr: b.as_ptr() as *const _ as *mut _,
381
    length: b.len(),
382
  }
383
}
384

            
385
/// Host count information for a cluster at a specific priority level.
386
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
387
pub struct ClusterHostCount {
388
  /// Total number of hosts in the cluster at this priority level.
389
  pub total: usize,
390
  /// Number of healthy hosts in the cluster at this priority level.
391
  pub healthy: usize,
392
  /// Number of degraded hosts in the cluster at this priority level.
393
  pub degraded: usize,
394
}
395

            
396
/// The identifier for an EnvoyCounter.
397
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
398
#[repr(transparent)]
399
pub struct EnvoyCounterId(usize);
400

            
401
/// The identifier for an EnvoyCounterVec.
402
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
403
#[repr(transparent)]
404
pub struct EnvoyCounterVecId(usize);
405

            
406
/// The identifier for an EnvoyGauge.
407
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
408
#[repr(transparent)]
409
pub struct EnvoyGaugeId(usize);
410

            
411
/// The identifier for an EnvoyGaugeVec.
412
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
413
#[repr(transparent)]
414
pub struct EnvoyGaugeVecId(usize);
415

            
416
/// The identifier for an EnvoyHistogram.
417
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
418
#[repr(transparent)]
419
pub struct EnvoyHistogramId(usize);
420

            
421
/// The identifier for an EnvoyHistogramVec.
422
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423
#[repr(transparent)]
424
pub struct EnvoyHistogramVecId(usize);
425

            
426
impl From<envoy_dynamic_module_type_metrics_result>
427
  for Result<(), envoy_dynamic_module_type_metrics_result>
428
{
429
  fn from(result: envoy_dynamic_module_type_metrics_result) -> Self {
430
    if result == envoy_dynamic_module_type_metrics_result::Success {
431
      Ok(())
432
    } else {
433
      Err(result)
434
    }
435
  }
436
}
437

            
438
/// We wrap the Box<dyn T> in another Box to be able to pass the address of the Box to C, and
439
/// retrieve it back when the C code calls the destroy function via [`drop_wrapped_c_void_ptr!`].
440
/// This is necessary because the Box<dyn T> is a fat pointer, and we can't pass it directly.
441
/// See https://users.rust-lang.org/t/sending-a-boxed-trait-over-ffi/21708 for the exact problem.
442
//
443
// Implementation note: this can be a simple function taking a type parameter, but we have it as
444
// a macro to align with the other macro drop_wrapped_c_void_ptr!.
445
#[macro_export]
446
macro_rules! wrap_into_c_void_ptr {
447
  ($t:expr) => {{
448
    let boxed = Box::new($t);
449
    Box::into_raw(boxed) as *const ::std::os::raw::c_void
450
  }};
451
}
452

            
453
/// This macro is used to drop the Box<dyn T> and the underlying object when the C code calls the
454
/// destroy function. This is a counterpart to [`wrap_into_c_void_ptr!`].
455
//
456
// Implementation note: this cannot be a function as we need to cast as *mut *mut dyn T which is
457
// not feasible via usual function type params.
458
#[macro_export]
459
macro_rules! drop_wrapped_c_void_ptr {
460
  ($ptr:expr, $trait_:ident $(< $($args:ident),* >)?) => {{
461
    let config = $ptr as *mut *mut dyn $trait_$(< $($args),* >)?;
462

            
463
    // Drop the Box<*mut $t>, and then the Box<$t>, which also
464
    // drops the underlying object.
465
    unsafe {
466
      let _outer = Box::from_raw(config);
467
      let _inner = Box::from_raw(*config);
468
    }
469
  }};
470
}
471

            
472
// =============================================================================
473
// Network Filter Support
474
// =============================================================================
475

            
476
/// Declare the init functions for the dynamic module with network filter support only.
477
///
478
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
479
/// loaded.
480
///
481
/// The second argument has [`NewNetworkFilterConfigFunction`] type, and it is called when the new
482
/// network filter configuration is created.
483
///
484
/// Note that if a module needs to support both HTTP and Network filters,
485
/// [`declare_all_init_functions`] should be used instead.
486
#[macro_export]
487
macro_rules! declare_network_filter_init_functions {
488
  ($f:ident, $new_network_filter_config_fn:expr) => {
489
    #[no_mangle]
490
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
491
      envoy_proxy_dynamic_modules_rust_sdk::NEW_NETWORK_FILTER_CONFIG_FUNCTION
492
        .get_or_init(|| $new_network_filter_config_fn);
493
      if ($f()) {
494
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
495
          as *const ::std::os::raw::c_char
496
      } else {
497
        ::std::ptr::null()
498
      }
499
    }
500
  };
501
}
502

            
503
/// Declare the init functions for the dynamic module with any combination of filter types.
504
///
505
/// This macro allows a single module to provide any combination of HTTP, Network, Listener,
506
/// UDP Listener, Bootstrap filters, and Certificate Validators.
507
///
508
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
509
/// loaded.
510
///
511
/// The remaining arguments are keyword-labeled filter config functions. Omitted filters won't be
512
/// registered.
513
/// Supported filters:
514
/// - `http:` — [`NewHttpFilterConfigFunction`] for HTTP filters
515
/// - `network:` — [`NewNetworkFilterConfigFunction`] for Network filters
516
/// - `listener:` — [`NewListenerFilterConfigFunction`] for Listener filters
517
/// - `udp_listener:` — [`NewUdpListenerFilterConfigFunction`] for UDP Listener filters
518
/// - `bootstrap:` — [`NewBootstrapExtensionConfigFunction`] for Bootstrap extensions
519
/// - `cert_validator:` — [`NewCertValidatorConfigFunction`] for TLS certificate validators
520
/// - `upstream_http_tcp_bridge:` — [`NewUpstreamHttpTcpBridgeConfigFunction`] for upstream HTTP TCP
521
///   bridges
522
///
523
/// # Examples
524
///
525
/// HTTP only:
526
/// ```ignore
527
/// declare_all_init_functions!(my_program_init,
528
///     http: my_new_http_filter_config_fn,
529
/// );
530
/// ```
531
///
532
/// Network + UDP Listener:
533
/// ```ignore
534
/// declare_all_init_functions!(my_program_init,
535
///     network: my_new_network_filter_config_fn,
536
///     udp_listener: my_new_udp_listener_filter_config_fn,
537
/// );
538
/// ```
539
#[macro_export]
540
macro_rules! declare_all_init_functions {
541
  ($f:ident, $($filter_type:ident : $filter_fn:expr),+ $(,)?) => {
542
    #[no_mangle]
543
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
544
      $(
545
        declare_all_init_functions!(@register $filter_type : $filter_fn);
546
      )+
547
      if ($f()) {
548
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
549
          as *const ::std::os::raw::c_char
550
      } else {
551
        ::std::ptr::null()
552
      }
553
    }
554
  };
555

            
556
  (@register http : $fn:expr) => {
557
    envoy_proxy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_CONFIG_FUNCTION
558
      .get_or_init(|| $fn);
559
  };
560
  (@register network : $fn:expr) => {
561
    envoy_proxy_dynamic_modules_rust_sdk::NEW_NETWORK_FILTER_CONFIG_FUNCTION
562
      .get_or_init(|| $fn);
563
  };
564
  (@register listener : $fn:expr) => {
565
    envoy_proxy_dynamic_modules_rust_sdk::NEW_LISTENER_FILTER_CONFIG_FUNCTION
566
      .get_or_init(|| $fn);
567
  };
568
  (@register udp_listener : $fn:expr) => {
569
    envoy_proxy_dynamic_modules_rust_sdk::NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION
570
      .get_or_init(|| $fn);
571
  };
572
  (@register bootstrap : $fn:expr) => {
573
    envoy_proxy_dynamic_modules_rust_sdk::NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION
574
      .get_or_init(|| $fn);
575
  };
576
  (@register cert_validator : $fn:expr) => {
577
    envoy_proxy_dynamic_modules_rust_sdk::NEW_CERT_VALIDATOR_CONFIG_FUNCTION
578
      .get_or_init(|| $fn);
579
  };
580
  (@register upstream_http_tcp_bridge : $fn:expr) => {
581
    envoy_proxy_dynamic_modules_rust_sdk::NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION
582
      .get_or_init(|| $fn);
583
  };
584
}
585

            
586
/// The function signature for the new network filter configuration function.
587
///
588
/// This is called when a new network filter configuration is created, and it must return a new
589
/// [`NetworkFilterConfig`] object. Returning `None` will cause the network filter configuration to
590
/// be rejected.
591
///
592
/// The first argument `envoy_filter_config` is a mutable reference to an
593
/// [`EnvoyNetworkFilterConfig`] object that provides access to Envoy operations.
594
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
595
/// config.
596
/// The third argument `config` is the raw configuration bytes.
597
pub type NewNetworkFilterConfigFunction<EC, ENF> = fn(
598
  envoy_filter_config: &mut EC,
599
  name: &str,
600
  config: &[u8],
601
) -> Option<Box<dyn NetworkFilterConfig<ENF>>>;
602

            
603
/// The global init function for network filter configurations. This is set via the
604
/// `declare_network_filter_init_functions` macro, and is not intended to be set directly.
605
pub static NEW_NETWORK_FILTER_CONFIG_FUNCTION: OnceLock<
606
  NewNetworkFilterConfigFunction<
607
    network::EnvoyNetworkFilterConfigImpl,
608
    network::EnvoyNetworkFilterImpl,
609
  >,
610
> = OnceLock::new();
611

            
612
// =============================================================================
613
// Listener Filter Support
614
// =============================================================================
615

            
616
/// Declare the init functions for the dynamic module with listener filter support only.
617
///
618
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
619
/// loaded.
620
///
621
/// The second argument has [`NewListenerFilterConfigFunction`] type, and it is called when the new
622
/// listener filter configuration is created.
623
#[macro_export]
624
macro_rules! declare_listener_filter_init_functions {
625
  ($f:ident, $new_listener_filter_config_fn:expr) => {
626
    #[no_mangle]
627
    pub extern "C" fn envoy_dynamic_module_on_program_init(
628
      server_factory_context_ptr: abi::envoy_dynamic_module_type_server_factory_context_envoy_ptr,
629
    ) -> *const ::std::os::raw::c_char {
630
      envoy_proxy_dynamic_modules_rust_sdk::NEW_LISTENER_FILTER_CONFIG_FUNCTION
631
        .get_or_init(|| $new_listener_filter_config_fn);
632
      if ($f(server_factory_context_ptr)) {
633
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
634
          as *const ::std::os::raw::c_char
635
      } else {
636
        ::std::ptr::null()
637
      }
638
    }
639
  };
640
}
641

            
642
/// The function signature for the new listener filter configuration function.
643
///
644
/// This is called when a new listener filter configuration is created, and it must return a new
645
/// [`ListenerFilterConfig`] object. Returning `None` will cause the listener filter configuration
646
/// to be rejected.
647
///
648
/// The first argument `envoy_filter_config` is a mutable reference to an
649
/// [`EnvoyListenerFilterConfig`] object that provides access to Envoy operations.
650
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
651
/// config.
652
/// The third argument `config` is the raw configuration bytes.
653
pub type NewListenerFilterConfigFunction<EC, ELF> =
654
  fn(
655
    envoy_filter_config: &mut EC,
656
    name: &str,
657
    config: &[u8],
658
  ) -> Option<Box<dyn listener::ListenerFilterConfig<ELF>>>;
659

            
660
/// The global init function for listener filter configurations. This is set via the
661
/// `declare_listener_filter_init_functions` macro, and is not intended to be set directly.
662
pub static NEW_LISTENER_FILTER_CONFIG_FUNCTION: OnceLock<
663
  NewListenerFilterConfigFunction<
664
    listener::EnvoyListenerFilterConfigImpl,
665
    listener::EnvoyListenerFilterImpl,
666
  >,
667
> = OnceLock::new();
668

            
669
// =============================================================================
670
// UDP Listener Filter Support
671
// =============================================================================
672

            
673
/// Declare the init functions for the dynamic module with UDP listener filter support only.
674
///
675
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
676
/// loaded.
677
///
678
/// The second argument has [`NewUdpListenerFilterConfigFunction`] type, and it is called when the
679
/// new UDP listener filter configuration is created.
680
#[macro_export]
681
macro_rules! declare_udp_listener_filter_init_functions {
682
  ($f:ident, $new_udp_listener_filter_config_fn:expr) => {
683
    #[no_mangle]
684
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
685
      envoy_proxy_dynamic_modules_rust_sdk::NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION
686
        .get_or_init(|| $new_udp_listener_filter_config_fn);
687
      if ($f()) {
688
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
689
          as *const ::std::os::raw::c_char
690
      } else {
691
        ::std::ptr::null()
692
      }
693
    }
694
  };
695
}
696

            
697
/// The function signature for the new UDP listener filter configuration function.
698
///
699
/// This is called when a new UDP listener filter configuration is created, and it must return a new
700
/// [`UdpListenerFilterConfig`] object. Returning `None` will cause the filter configuration
701
/// to be rejected.
702
///
703
/// The first argument `envoy_filter_config` is a mutable reference to an
704
/// [`EnvoyUdpListenerFilterConfig`] object that provides access to Envoy operations.
705
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
706
/// config.
707
/// The third argument `config` is the raw configuration bytes.
708
pub type NewUdpListenerFilterConfigFunction<EC, ELF> =
709
  fn(
710
    envoy_filter_config: &mut EC,
711
    name: &str,
712
    config: &[u8],
713
  ) -> Option<Box<dyn udp_listener::UdpListenerFilterConfig<ELF>>>;
714

            
715
/// The global init function for UDP listener filter configurations. This is set via the
716
/// `declare_udp_listener_filter_init_functions` macro, and is not intended to be set directly.
717
pub static NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION: OnceLock<
718
  NewUdpListenerFilterConfigFunction<
719
    udp_listener::EnvoyUdpListenerFilterConfigImpl,
720
    udp_listener::EnvoyUdpListenerFilterImpl,
721
  >,
722
> = OnceLock::new();
723

            
724
// ============================================================================
725
// Bootstrap Extension
726
// ============================================================================
727

            
728
/// A global variable that holds the factory function to create a new bootstrap extension config.
729
/// This is set by the [`declare_bootstrap_init_functions`] macro.
730
pub static NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION: OnceLock<NewBootstrapExtensionConfigFunction> =
731
  OnceLock::new();
732

            
733
/// The type of the factory function that creates a new bootstrap extension configuration.
734
pub type NewBootstrapExtensionConfigFunction = fn(
735
  &mut dyn EnvoyBootstrapExtensionConfig,
736
  &str,
737
  &[u8],
738
) -> Option<Box<dyn BootstrapExtensionConfig>>;
739

            
740
/// Declare the init functions for the bootstrap extension dynamic module.
741
///
742
/// The first argument is the program init function with [`ProgramInitFunction`] type.
743
/// The second argument is the factory function with [`NewBootstrapExtensionConfigFunction`] type.
744
///
745
/// # Example
746
///
747
/// ```
748
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
749
///
750
/// declare_bootstrap_init_functions!(my_program_init, my_new_bootstrap_extension_config_fn);
751
///
752
/// fn my_program_init() -> bool {
753
///   true
754
/// }
755
///
756
/// fn my_new_bootstrap_extension_config_fn(
757
///   _envoy_extension_config: &mut dyn EnvoyBootstrapExtensionConfig,
758
///   _name: &str,
759
///   _config: &[u8],
760
/// ) -> Option<Box<dyn BootstrapExtensionConfig>> {
761
///   Some(Box::new(MyBootstrapExtensionConfig {}))
762
/// }
763
///
764
/// struct MyBootstrapExtensionConfig {}
765
///
766
/// impl BootstrapExtensionConfig for MyBootstrapExtensionConfig {
767
///   fn new_bootstrap_extension(
768
///     &self,
769
///     _envoy_extension: &mut dyn EnvoyBootstrapExtension,
770
///   ) -> Box<dyn BootstrapExtension> {
771
///     Box::new(MyBootstrapExtension {})
772
///   }
773
/// }
774
///
775
/// struct MyBootstrapExtension {}
776
///
777
/// impl BootstrapExtension for MyBootstrapExtension {
778
///   fn on_server_initialized(&mut self, _envoy_extension: &mut dyn EnvoyBootstrapExtension) {
779
///     // Use envoy_log_info! macro for logging.
780
///     // envoy_log_info!("Bootstrap extension initialized!");
781
///   }
782
/// }
783
/// ```
784
#[macro_export]
785
macro_rules! declare_bootstrap_init_functions {
786
  ($f:ident, $new_bootstrap_extension_config_fn:expr) => {
787
    #[no_mangle]
788
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
789
      envoy_proxy_dynamic_modules_rust_sdk::NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION
790
        .get_or_init(|| $new_bootstrap_extension_config_fn);
791
      if ($f()) {
792
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
793
          as *const ::std::os::raw::c_char
794
      } else {
795
        ::std::ptr::null()
796
      }
797
    }
798
  };
799
}
800

            
801
// =================================================================================================
802
// Cluster Dynamic Module
803
// =================================================================================================
804

            
805
/// The type of the factory function that creates a new cluster configuration.
806
///
807
/// The `envoy_cluster_metrics` parameter provides access to metric-defining and metric-recording
808
/// callbacks. The caller receives an `Arc` so it can be stored and used at runtime
809
/// (e.g., during cluster lifecycle events) for recording metrics.
810
pub type NewClusterConfigFunction = fn(
811
  name: &str,
812
  config: &[u8],
813
  envoy_cluster_metrics: std::sync::Arc<dyn cluster::EnvoyClusterMetrics>,
814
) -> Option<Box<dyn cluster::ClusterConfig>>;
815

            
816
/// Global storage for the cluster config factory function.
817
pub static NEW_CLUSTER_CONFIG_FUNCTION: OnceLock<NewClusterConfigFunction> = OnceLock::new();
818

            
819
/// Declare the init functions for the cluster dynamic module.
820
///
821
/// The first argument is the program init function with [`ProgramInitFunction`] type.
822
/// The second argument is the factory function with [`NewClusterConfigFunction`] type.
823
///
824
/// # Example
825
///
826
/// ```ignore
827
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
828
/// use std::sync::Arc;
829
///
830
/// declare_cluster_init_functions!(my_program_init, my_new_cluster_config_fn);
831
///
832
/// fn my_program_init() -> bool {
833
///   true
834
/// }
835
///
836
/// fn my_new_cluster_config_fn(
837
///   _name: &str,
838
///   _config: &[u8],
839
///   envoy_cluster_metrics: Arc<dyn EnvoyClusterMetrics>,
840
/// ) -> Option<Box<dyn ClusterConfig>> {
841
///   let counter_id = envoy_cluster_metrics.define_counter("my_counter").ok()?;
842
///   Some(Box::new(MyClusterConfig { counter_id, envoy_cluster_metrics }))
843
/// }
844
///
845
/// struct MyClusterConfig {
846
///   counter_id: EnvoyCounterId,
847
///   envoy_cluster_metrics: Arc<dyn EnvoyClusterMetrics>,
848
/// }
849
///
850
/// impl ClusterConfig for MyClusterConfig {
851
///   fn new_cluster(
852
///     &self,
853
///     _envoy_cluster: &dyn EnvoyCluster,
854
///   ) -> Box<dyn Cluster> {
855
///     Box::new(MyCluster {
856
///       counter_id: self.counter_id,
857
///       envoy_cluster_metrics: self.envoy_cluster_metrics.clone(),
858
///     })
859
///   }
860
/// }
861
///
862
/// struct MyCluster {
863
///   counter_id: EnvoyCounterId,
864
///   envoy_cluster_metrics: Arc<dyn EnvoyClusterMetrics>,
865
/// }
866
///
867
/// impl Cluster for MyCluster {
868
///   fn on_init(&mut self, envoy_cluster: &dyn EnvoyCluster) {
869
///     self.envoy_cluster_metrics.increment_counter(self.counter_id, 1).ok();
870
///     envoy_cluster.pre_init_complete();
871
///   }
872
///
873
///   fn new_load_balancer(&self, _envoy_lb: &dyn EnvoyClusterLoadBalancer) -> Box<dyn ClusterLb> {
874
///     Box::new(MyClusterLb {})
875
///   }
876
/// }
877
///
878
/// struct MyClusterLb {}
879
///
880
/// impl ClusterLb for MyClusterLb {
881
///   fn choose_host(
882
///     &mut self,
883
///     _context: Option<&dyn ClusterLbContext>,
884
///     _async_completion: Box<dyn EnvoyAsyncHostSelectionComplete>,
885
///   ) -> HostSelectionResult {
886
///     HostSelectionResult::NoHost
887
///   }
888
/// }
889
/// ```
890
#[macro_export]
891
macro_rules! declare_cluster_init_functions {
892
  ($f:ident, $new_cluster_config_fn:expr) => {
893
    #[no_mangle]
894
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
895
      envoy_proxy_dynamic_modules_rust_sdk::NEW_CLUSTER_CONFIG_FUNCTION
896
        .get_or_init(|| $new_cluster_config_fn);
897
      if ($f()) {
898
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
899
          as *const ::std::os::raw::c_char
900
      } else {
901
        ::std::ptr::null()
902
      }
903
    }
904
  };
905
}
906

            
907
// =================================================================================================
908
// Load Balancer Dynamic Module Support
909
// =================================================================================================
910

            
911
/// The function signature for creating a new load balancer configuration.
912
///
913
/// The `envoy_lb_config` parameter provides access to metric-defining and metric-recording
914
/// callbacks. The caller receives an `Arc` so it can be stored and used at runtime
915
/// (e.g., in `choose_host`) for recording metrics.
916
pub type NewLoadBalancerConfigFunction = fn(
917
  name: &str,
918
  config: &[u8],
919
  envoy_lb_config: std::sync::Arc<dyn load_balancer::EnvoyLbConfig>,
920
) -> Option<Box<dyn load_balancer::LoadBalancerConfig>>;
921

            
922
/// Global function for creating load balancer configurations.
923
pub static NEW_LOAD_BALANCER_CONFIG_FUNCTION: OnceLock<NewLoadBalancerConfigFunction> =
924
  OnceLock::new();
925

            
926
/// Declare the init functions for a load balancer dynamic module.
927
///
928
/// This macro generates the necessary `extern "C"` functions for the load balancer module.
929
///
930
/// # Example
931
///
932
/// ```ignore
933
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
934
/// use std::sync::Arc;
935
///
936
/// fn program_init() -> bool {
937
///   true
938
/// }
939
///
940
/// fn new_lb_config(
941
///   name: &str,
942
///   config: &[u8],
943
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
944
/// ) -> Option<Box<dyn LoadBalancerConfig>> {
945
///   let counter_id = envoy_lb_config.define_counter("my_counter").ok()?;
946
///   Some(Box::new(MyLbConfig { counter_id, envoy_lb_config }))
947
/// }
948
///
949
/// declare_load_balancer_init_functions!(program_init, new_lb_config);
950
///
951
/// struct MyLbConfig {
952
///   counter_id: EnvoyCounterId,
953
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
954
/// }
955
///
956
/// impl LoadBalancerConfig for MyLbConfig {
957
///   fn new_load_balancer(&self, _envoy_lb: &dyn EnvoyLoadBalancer) -> Box<dyn LoadBalancer> {
958
///     Box::new(MyLoadBalancer {
959
///       next_index: 0,
960
///       counter_id: self.counter_id,
961
///       envoy_lb_config: self.envoy_lb_config.clone(),
962
///     })
963
///   }
964
/// }
965
///
966
/// struct MyLoadBalancer {
967
///   next_index: usize,
968
///   counter_id: EnvoyCounterId,
969
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
970
/// }
971
///
972
/// impl LoadBalancer for MyLoadBalancer {
973
///   fn choose_host(&mut self, envoy_lb: &dyn EnvoyLoadBalancer) -> Option<HostSelection> {
974
///     let count = envoy_lb.get_healthy_hosts_count(0);
975
///     if count == 0 {
976
///       return None;
977
///     }
978
///     let index = self.next_index % count;
979
///     self.next_index += 1;
980
///     self.envoy_lb_config.increment_counter(self.counter_id, 1).ok();
981
///     Some(HostSelection::at_default_priority(index as u32))
982
///   }
983
/// }
984
/// ```
985
#[macro_export]
986
macro_rules! declare_load_balancer_init_functions {
987
  ($f:ident, $new_lb_config_fn:expr) => {
988
    #[no_mangle]
989
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
990
      envoy_proxy_dynamic_modules_rust_sdk::NEW_LOAD_BALANCER_CONFIG_FUNCTION
991
        .get_or_init(|| $new_lb_config_fn);
992
      if ($f()) {
993
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
994
          as *const ::std::os::raw::c_char
995
      } else {
996
        ::std::ptr::null()
997
      }
998
    }
999
  };
}
// =============================================================================
// Certificate Validator
// =============================================================================
/// The function signature for creating a new cert validator configuration.
pub type NewCertValidatorConfigFunction =
  fn(name: &str, config: &[u8]) -> Option<Box<dyn cert_validator::CertValidatorConfig>>;
/// Global function for creating cert validator configurations.
pub static NEW_CERT_VALIDATOR_CONFIG_FUNCTION: OnceLock<NewCertValidatorConfigFunction> =
  OnceLock::new();
/// Declare the init functions for a cert validator dynamic module.
///
/// This macro generates the necessary `extern "C"` functions for the cert validator module.
///
/// # Example
///
/// ```ignore
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
/// use envoy_proxy_dynamic_modules_rust_sdk::cert_validator::*;
///
/// fn program_init() -> bool {
///   true
/// }
///
/// fn new_cert_validator_config(
///   name: &str,
///   config: &[u8],
/// ) -> Option<Box<dyn CertValidatorConfig>> {
///   Some(Box::new(MyCertValidatorConfig {}))
/// }
///
/// declare_cert_validator_init_functions!(program_init, new_cert_validator_config);
///
/// struct MyCertValidatorConfig {}
///
/// impl CertValidatorConfig for MyCertValidatorConfig {
///   fn do_verify_cert_chain(
///     &self,
///     _envoy_cert_validator: &EnvoyCertValidator,
///     certs: &[&[u8]],
///     host_name: &str,
///     is_server: bool,
///   ) -> ValidationResult {
///     ValidationResult::successful()
///   }
///
///   fn get_ssl_verify_mode(&self, _handshaker_provides_certificates: bool) -> i32 {
///     0x03
///   }
///
///   fn update_digest(&self) -> &[u8] {
///     b"my_cert_validator"
///   }
/// }
/// ```
#[macro_export]
macro_rules! declare_cert_validator_init_functions {
  ($f:ident, $new_cert_validator_config_fn:expr) => {
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
      envoy_proxy_dynamic_modules_rust_sdk::NEW_CERT_VALIDATOR_CONFIG_FUNCTION
        .get_or_init(|| $new_cert_validator_config_fn);
      if ($f()) {
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
          as *const ::std::os::raw::c_char
      } else {
        ::std::ptr::null()
      }
    }
  };
}
// =================================================================================================
// Upstream HTTP TCP Bridge Dynamic Module Support
// =================================================================================================
/// The type of the factory function that creates a new upstream HTTP TCP bridge configuration.
pub type NewUpstreamHttpTcpBridgeConfigFunction =
  fn(name: &str, config: &[u8]) -> Option<Box<dyn UpstreamHttpTcpBridgeConfig>>;
/// Global storage for the upstream HTTP TCP bridge config factory function.
pub static NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION: OnceLock<
  NewUpstreamHttpTcpBridgeConfigFunction,
> = OnceLock::new();