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 catch_unwind;
11
pub mod cert_validator;
12
pub mod cluster;
13
pub mod http;
14
pub mod listener;
15
pub mod load_balancer;
16
pub mod matcher;
17
pub mod network;
18
pub mod udp_listener;
19
pub mod upstream_http_tcp_bridge;
20
pub mod utility;
21
pub use bootstrap::*;
22
pub use buffer::*;
23
pub use catch_unwind::*;
24
pub use cert_validator::*;
25
pub use cluster::*;
26
pub use http::*;
27
pub use listener::*;
28
pub use load_balancer::*;
29
pub use network::*;
30
pub use udp_listener::*;
31
pub use upstream_http_tcp_bridge::*;
32
pub use utility::*;
33

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

            
38
use crate::abi::envoy_dynamic_module_type_metrics_result;
39
use std::any::Any;
40
use std::sync::OnceLock;
41

            
42
pub(crate) fn panic_payload_to_string(payload: Box<dyn Any + Send>) -> String {
43
  match payload.downcast::<String>() {
44
    Ok(s) => *s,
45
    Err(payload) => match payload.downcast::<&str>() {
46
      Ok(s) => s.to_string(),
47
      Err(_) => "<non-string panic payload>".to_string(),
48
    },
49
  }
50
}
51

            
52
/// This module contains the generated bindings for the envoy dynamic modules ABI.
53
///
54
/// This is not meant to be used directly.
55
pub mod abi {
56
  include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
57
}
58

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

            
122
/// Get the concurrency from the server context options.
123
/// # Safety
124
///
125
/// This function must be called on the main thread.
126
pub unsafe fn get_server_concurrency() -> u32 {
127
  unsafe { abi::envoy_dynamic_module_callback_get_concurrency() }
128
}
129

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

            
158
/// Retrieve a previously registered function pointer by name from the process-wide function
159
/// registry. The returned pointer can be cast to the expected function signature and called
160
/// directly.
161
///
162
/// Resolution is typically done once during configuration creation (e.g., in
163
/// `on_http_filter_config_new`) and the result cached for per-request use.
164
///
165
/// This is thread-safe and can be called from any thread.
166
pub fn get_function(key: &str) -> Option<*const std::ffi::c_void> {
167
  let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
168
  let found =
169
    unsafe { abi::envoy_dynamic_module_callback_get_function(str_to_module_buffer(key), &mut ptr) };
170
  if found {
171
    Some(ptr as *const std::ffi::c_void)
172
  } else {
173
    None
174
  }
175
}
176

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

            
208
/// Retrieve a previously registered data pointer by name from the process-wide shared data
209
/// registry. The returned pointer can be cast to the expected data type and used directly.
210
///
211
/// Resolution is typically done once during configuration creation (e.g., in
212
/// `on_http_filter_config_new`) and the result cached for per-request use.
213
///
214
/// This is thread-safe and can be called from any thread.
215
pub fn get_shared_data(key: &str) -> Option<*const std::ffi::c_void> {
216
  let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
217
  let found = unsafe {
218
    abi::envoy_dynamic_module_callback_get_shared_data(str_to_module_buffer(key), &mut ptr)
219
  };
220
  if found {
221
    Some(ptr as *const std::ffi::c_void)
222
  } else {
223
    None
224
  }
225
}
226

            
227
/// Log a trace 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_trace {
234
    ($($arg:tt)*) => {
235
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Trace, $($arg)*)
236
    };
237
}
238

            
239
/// Log a debug 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_debug {
246
    ($($arg:tt)*) => {
247
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Debug, $($arg)*)
248
    };
249
}
250

            
251
/// Log an info 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_info {
258
    ($($arg:tt)*) => {
259
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Info, $($arg)*)
260
    };
261
}
262

            
263
/// Log a warning 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_warn {
270
    ($($arg:tt)*) => {
271
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Warn, $($arg)*)
272
    };
273
}
274

            
275
/// Log an error 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_error {
282
    ($($arg:tt)*) => {
283
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Error, $($arg)*)
284
    };
285
}
286

            
287
/// Log a critical message to Envoy's logging system with [dynamic_modules] Id. Messages won't be
288
/// allocated if the log level is not enabled on the Envoy side.
289
///
290
/// This accepts the exact same arguments as the `format!` macro, so you can use it to log formatted
291
/// messages.
292
#[macro_export]
293
macro_rules! envoy_log_critical {
294
    ($($arg:tt)*) => {
295
        $crate::envoy_log!($crate::abi::envoy_dynamic_module_type_log_level::Critical, $($arg)*)
296
    };
297
}
298

            
299
/// Internal logging macro that handles the actual call to the Envoy logging callback
300
/// used by envoy_log_* macros.
301
#[macro_export]
302
macro_rules! envoy_log {
303
  ($level:expr, $($arg:tt)*) => {
304
    {
305
      #[cfg(not(test))]
306
      {
307
        let level = $level;
308
        // SAFETY: envoy_dynamic_module_callback_log_enabled and envoy_dynamic_module_callback_log
309
        // are FFI calls provided by the Envoy host.
310
        let enabled = unsafe { $crate::abi::envoy_dynamic_module_callback_log_enabled(level) };
311
        if enabled {
312
          let message = format!($($arg)*);
313
          let message_bytes = message.as_bytes();
314
          unsafe {
315
            $crate::abi::envoy_dynamic_module_callback_log(
316
              level,
317
              $crate::abi::envoy_dynamic_module_type_module_buffer {
318
                ptr: message_bytes.as_ptr() as *const ::std::os::raw::c_char,
319
                length: message_bytes.len(),
320
              },
321
            );
322
          }
323
        }
324
      }
325
      // In unit tests, just print to stderr since the Envoy symbols are not available.
326
      #[cfg(test)]
327
      {
328
        let message = format!($($arg)*);
329
        eprintln!("[{}] {}", stringify!($level), message);
330
      }
331
    }
332
  };
333
}
334

            
335
/// The function signature for the program init function.
336
///
337
/// This is called when the dynamic module is loaded, and it must return true on success, and false
338
/// on failure. When it returns false, the dynamic module will not be loaded.
339
///
340
/// This is useful to perform any process-wide initialization that the dynamic module needs.
341
pub type ProgramInitFunction = fn() -> bool;
342

            
343
/// The function signature for the new HTTP filter configuration function.
344
///
345
/// This is called when a new HTTP filter configuration is created, and it must return a new
346
/// instance of the [`HttpFilterConfig`] object. Returning `None` will cause the HTTP filter
347
/// configuration to be rejected.
348
//
349
// TODO(@mathetake): I guess there would be a way to avoid the use of dyn in the first place.
350
// E.g. one idea is to accept all concrete type parameters for HttpFilterConfig and HttpFilter
351
// traits in declare_init_functions!, and generate the match statement based on that.
352
pub type NewHttpFilterConfigFunction<EC, EHF> = fn(
353
  envoy_filter_config: &mut EC,
354
  name: &str,
355
  config: &[u8],
356
) -> Option<Box<dyn HttpFilterConfig<EHF>>>;
357

            
358
/// The global init function for HTTP filter configurations. This is set via the
359
/// `declare_init_functions` macro, and is not intended to be set directly.
360
pub static NEW_HTTP_FILTER_CONFIG_FUNCTION: OnceLock<
361
  NewHttpFilterConfigFunction<http::EnvoyHttpFilterConfigImpl, http::EnvoyHttpFilterImpl>,
362
> = OnceLock::new();
363

            
364
/// The function signature for the new HTTP filter per-route configuration function.
365
///
366
/// This is called when a new HTTP filter per-route configuration is created. It must return an
367
/// object representing the filter's per-route configuration. Returning `None` will cause the HTTP
368
/// filter configuration to be rejected.
369
/// This config can be later retried by the filter via
370
/// [`EnvoyHttpFilter::get_most_specific_route_config`] method.
371
pub type NewHttpFilterPerRouteConfigFunction =
372
  fn(name: &str, config: &[u8]) -> Option<Box<dyn Any>>;
373

            
374
/// The global init function for HTTP filter per-route configurations. This is set via the
375
/// `declare_init_functions` macro, and is not intended to be set directly.
376
pub static NEW_HTTP_FILTER_PER_ROUTE_CONFIG_FUNCTION: OnceLock<
377
  NewHttpFilterPerRouteConfigFunction,
378
> = OnceLock::new();
379

            
380
// HTTP filter types are in the http module and re-exported above.
381

            
382
pub(crate) fn str_to_module_buffer(s: &str) -> abi::envoy_dynamic_module_type_module_buffer {
383
  abi::envoy_dynamic_module_type_module_buffer {
384
    ptr: s.as_ptr() as *const _ as *mut _,
385
    length: s.len(),
386
  }
387
}
388

            
389
pub(crate) fn strs_to_module_buffers(
390
  strs: &[&str],
391
) -> Vec<abi::envoy_dynamic_module_type_module_buffer> {
392
  strs.iter().map(|s| str_to_module_buffer(s)).collect()
393
}
394

            
395
pub(crate) fn bytes_to_module_buffer(b: &[u8]) -> abi::envoy_dynamic_module_type_module_buffer {
396
  abi::envoy_dynamic_module_type_module_buffer {
397
    ptr: b.as_ptr() as *const _ as *mut _,
398
    length: b.len(),
399
  }
400
}
401

            
402
/// Host count information for a cluster at a specific priority level.
403
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404
pub struct ClusterHostCount {
405
  /// Total number of hosts in the cluster at this priority level.
406
  pub total: usize,
407
  /// Number of healthy hosts in the cluster at this priority level.
408
  pub healthy: usize,
409
  /// Number of degraded hosts in the cluster at this priority level.
410
  pub degraded: usize,
411
}
412

            
413
/// The identifier for an EnvoyCounter.
414
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
415
#[repr(transparent)]
416
pub struct EnvoyCounterId(usize);
417

            
418
/// The identifier for an EnvoyCounterVec.
419
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
420
#[repr(transparent)]
421
pub struct EnvoyCounterVecId(usize);
422

            
423
/// The identifier for an EnvoyGauge.
424
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
425
#[repr(transparent)]
426
pub struct EnvoyGaugeId(usize);
427

            
428
/// The identifier for an EnvoyGaugeVec.
429
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
430
#[repr(transparent)]
431
pub struct EnvoyGaugeVecId(usize);
432

            
433
/// The identifier for an EnvoyHistogram.
434
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
435
#[repr(transparent)]
436
pub struct EnvoyHistogramId(usize);
437

            
438
/// The identifier for an EnvoyHistogramVec.
439
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
440
#[repr(transparent)]
441
pub struct EnvoyHistogramVecId(usize);
442

            
443
impl From<envoy_dynamic_module_type_metrics_result>
444
  for Result<(), envoy_dynamic_module_type_metrics_result>
445
{
446
  fn from(result: envoy_dynamic_module_type_metrics_result) -> Self {
447
    if result == envoy_dynamic_module_type_metrics_result::Success {
448
      Ok(())
449
    } else {
450
      Err(result)
451
    }
452
  }
453
}
454

            
455
/// We wrap the Box<dyn T> in another Box to be able to pass the address of the Box to C, and
456
/// retrieve it back when the C code calls the destroy function via [`drop_wrapped_c_void_ptr!`].
457
/// This is necessary because the Box<dyn T> is a fat pointer, and we can't pass it directly.
458
/// See https://users.rust-lang.org/t/sending-a-boxed-trait-over-ffi/21708 for the exact problem.
459
//
460
// Implementation note: this can be a simple function taking a type parameter, but we have it as
461
// a macro to align with the other macro drop_wrapped_c_void_ptr!.
462
#[macro_export]
463
macro_rules! wrap_into_c_void_ptr {
464
  ($t:expr) => {{
465
    let boxed = Box::new($t);
466
    Box::into_raw(boxed) as *const ::std::os::raw::c_void
467
  }};
468
}
469

            
470
/// This macro is used to drop the Box<dyn T> and the underlying object when the C code calls the
471
/// destroy function. This is a counterpart to [`wrap_into_c_void_ptr!`].
472
//
473
// Implementation note: this cannot be a function as we need to cast as *mut *mut dyn T which is
474
// not feasible via usual function type params.
475
#[macro_export]
476
macro_rules! drop_wrapped_c_void_ptr {
477
  ($ptr:expr, $trait_:ident $(< $($args:ident),* >)?) => {{
478
    let config = $ptr as *mut *mut dyn $trait_$(< $($args),* >)?;
479

            
480
    // Drop the Box<*mut $t>, and then the Box<$t>, which also
481
    // drops the underlying object.
482
    unsafe {
483
      let _outer = Box::from_raw(config);
484
      let _inner = Box::from_raw(*config);
485
    }
486
  }};
487
}
488

            
489
// =============================================================================
490
// Network Filter Support
491
// =============================================================================
492

            
493
/// Declare the init functions for the dynamic module with network filter support only.
494
///
495
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
496
/// loaded.
497
///
498
/// The second argument has [`NewNetworkFilterConfigFunction`] type, and it is called when the new
499
/// network filter configuration is created.
500
///
501
/// Note that if a module needs to support both HTTP and Network filters,
502
/// [`declare_all_init_functions`] should be used instead.
503
#[macro_export]
504
macro_rules! declare_network_filter_init_functions {
505
  ($f:ident, $new_network_filter_config_fn:expr) => {
506
    #[no_mangle]
507
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
508
      envoy_proxy_dynamic_modules_rust_sdk::NEW_NETWORK_FILTER_CONFIG_FUNCTION
509
        .get_or_init(|| $new_network_filter_config_fn);
510
      if ($f()) {
511
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
512
          as *const ::std::os::raw::c_char
513
      } else {
514
        ::std::ptr::null()
515
      }
516
    }
517
  };
518
}
519

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

            
573
  (@register http : $fn:expr) => {
574
    envoy_proxy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_CONFIG_FUNCTION
575
      .get_or_init(|| $fn);
576
  };
577
  (@register network : $fn:expr) => {
578
    envoy_proxy_dynamic_modules_rust_sdk::NEW_NETWORK_FILTER_CONFIG_FUNCTION
579
      .get_or_init(|| $fn);
580
  };
581
  (@register listener : $fn:expr) => {
582
    envoy_proxy_dynamic_modules_rust_sdk::NEW_LISTENER_FILTER_CONFIG_FUNCTION
583
      .get_or_init(|| $fn);
584
  };
585
  (@register udp_listener : $fn:expr) => {
586
    envoy_proxy_dynamic_modules_rust_sdk::NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION
587
      .get_or_init(|| $fn);
588
  };
589
  (@register bootstrap : $fn:expr) => {
590
    envoy_proxy_dynamic_modules_rust_sdk::NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION
591
      .get_or_init(|| $fn);
592
  };
593
  (@register cert_validator : $fn:expr) => {
594
    envoy_proxy_dynamic_modules_rust_sdk::NEW_CERT_VALIDATOR_CONFIG_FUNCTION
595
      .get_or_init(|| $fn);
596
  };
597
  (@register upstream_http_tcp_bridge : $fn:expr) => {
598
    envoy_proxy_dynamic_modules_rust_sdk::NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION
599
      .get_or_init(|| $fn);
600
  };
601
}
602

            
603
/// The function signature for the new network filter configuration function.
604
///
605
/// This is called when a new network filter configuration is created, and it must return a new
606
/// [`NetworkFilterConfig`] object. Returning `None` will cause the network filter configuration to
607
/// be rejected.
608
///
609
/// The first argument `envoy_filter_config` is a mutable reference to an
610
/// [`EnvoyNetworkFilterConfig`] object that provides access to Envoy operations.
611
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
612
/// config.
613
/// The third argument `config` is the raw configuration bytes.
614
pub type NewNetworkFilterConfigFunction<EC, ENF> = fn(
615
  envoy_filter_config: &mut EC,
616
  name: &str,
617
  config: &[u8],
618
) -> Option<Box<dyn NetworkFilterConfig<ENF>>>;
619

            
620
/// The global init function for network filter configurations. This is set via the
621
/// `declare_network_filter_init_functions` macro, and is not intended to be set directly.
622
pub static NEW_NETWORK_FILTER_CONFIG_FUNCTION: OnceLock<
623
  NewNetworkFilterConfigFunction<
624
    network::EnvoyNetworkFilterConfigImpl,
625
    network::EnvoyNetworkFilterImpl,
626
  >,
627
> = OnceLock::new();
628

            
629
// =============================================================================
630
// Listener Filter Support
631
// =============================================================================
632

            
633
/// Declare the init functions for the dynamic module with listener filter support only.
634
///
635
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
636
/// loaded.
637
///
638
/// The second argument has [`NewListenerFilterConfigFunction`] type, and it is called when the new
639
/// listener filter configuration is created.
640
#[macro_export]
641
macro_rules! declare_listener_filter_init_functions {
642
  ($f:ident, $new_listener_filter_config_fn:expr) => {
643
    #[no_mangle]
644
    pub extern "C" fn envoy_dynamic_module_on_program_init(
645
      server_factory_context_ptr: abi::envoy_dynamic_module_type_server_factory_context_envoy_ptr,
646
    ) -> *const ::std::os::raw::c_char {
647
      envoy_proxy_dynamic_modules_rust_sdk::NEW_LISTENER_FILTER_CONFIG_FUNCTION
648
        .get_or_init(|| $new_listener_filter_config_fn);
649
      if ($f(server_factory_context_ptr)) {
650
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
651
          as *const ::std::os::raw::c_char
652
      } else {
653
        ::std::ptr::null()
654
      }
655
    }
656
  };
657
}
658

            
659
/// The function signature for the new listener filter configuration function.
660
///
661
/// This is called when a new listener filter configuration is created, and it must return a new
662
/// [`ListenerFilterConfig`] object. Returning `None` will cause the listener filter configuration
663
/// to be rejected.
664
///
665
/// The first argument `envoy_filter_config` is a mutable reference to an
666
/// [`EnvoyListenerFilterConfig`] object that provides access to Envoy operations.
667
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
668
/// config.
669
/// The third argument `config` is the raw configuration bytes.
670
pub type NewListenerFilterConfigFunction<EC, ELF> =
671
  fn(
672
    envoy_filter_config: &mut EC,
673
    name: &str,
674
    config: &[u8],
675
  ) -> Option<Box<dyn listener::ListenerFilterConfig<ELF>>>;
676

            
677
/// The global init function for listener filter configurations. This is set via the
678
/// `declare_listener_filter_init_functions` macro, and is not intended to be set directly.
679
pub static NEW_LISTENER_FILTER_CONFIG_FUNCTION: OnceLock<
680
  NewListenerFilterConfigFunction<
681
    listener::EnvoyListenerFilterConfigImpl,
682
    listener::EnvoyListenerFilterImpl,
683
  >,
684
> = OnceLock::new();
685

            
686
// =============================================================================
687
// UDP Listener Filter Support
688
// =============================================================================
689

            
690
/// Declare the init functions for the dynamic module with UDP listener filter support only.
691
///
692
/// The first argument has [`ProgramInitFunction`] type, and it is called when the dynamic module is
693
/// loaded.
694
///
695
/// The second argument has [`NewUdpListenerFilterConfigFunction`] type, and it is called when the
696
/// new UDP listener filter configuration is created.
697
#[macro_export]
698
macro_rules! declare_udp_listener_filter_init_functions {
699
  ($f:ident, $new_udp_listener_filter_config_fn:expr) => {
700
    #[no_mangle]
701
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
702
      envoy_proxy_dynamic_modules_rust_sdk::NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION
703
        .get_or_init(|| $new_udp_listener_filter_config_fn);
704
      if ($f()) {
705
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
706
          as *const ::std::os::raw::c_char
707
      } else {
708
        ::std::ptr::null()
709
      }
710
    }
711
  };
712
}
713

            
714
/// The function signature for the new UDP listener filter configuration function.
715
///
716
/// This is called when a new UDP listener filter configuration is created, and it must return a new
717
/// [`UdpListenerFilterConfig`] object. Returning `None` will cause the filter configuration
718
/// to be rejected.
719
///
720
/// The first argument `envoy_filter_config` is a mutable reference to an
721
/// [`EnvoyUdpListenerFilterConfig`] object that provides access to Envoy operations.
722
/// The second argument `name` is the name of the filter configuration as specified in the Envoy
723
/// config.
724
/// The third argument `config` is the raw configuration bytes.
725
pub type NewUdpListenerFilterConfigFunction<EC, ELF> =
726
  fn(
727
    envoy_filter_config: &mut EC,
728
    name: &str,
729
    config: &[u8],
730
  ) -> Option<Box<dyn udp_listener::UdpListenerFilterConfig<ELF>>>;
731

            
732
/// The global init function for UDP listener filter configurations. This is set via the
733
/// `declare_udp_listener_filter_init_functions` macro, and is not intended to be set directly.
734
pub static NEW_UDP_LISTENER_FILTER_CONFIG_FUNCTION: OnceLock<
735
  NewUdpListenerFilterConfigFunction<
736
    udp_listener::EnvoyUdpListenerFilterConfigImpl,
737
    udp_listener::EnvoyUdpListenerFilterImpl,
738
  >,
739
> = OnceLock::new();
740

            
741
// ============================================================================
742
// Bootstrap Extension
743
// ============================================================================
744

            
745
/// A global variable that holds the factory function to create a new bootstrap extension config.
746
/// This is set by the [`declare_bootstrap_init_functions`] macro.
747
pub static NEW_BOOTSTRAP_EXTENSION_CONFIG_FUNCTION: OnceLock<NewBootstrapExtensionConfigFunction> =
748
  OnceLock::new();
749

            
750
/// The type of the factory function that creates a new bootstrap extension configuration.
751
pub type NewBootstrapExtensionConfigFunction = fn(
752
  &mut dyn EnvoyBootstrapExtensionConfig,
753
  &str,
754
  &[u8],
755
) -> Option<Box<dyn BootstrapExtensionConfig>>;
756

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

            
818
// =================================================================================================
819
// Cluster Dynamic Module
820
// =================================================================================================
821

            
822
/// The type of the factory function that creates a new cluster configuration.
823
///
824
/// The `envoy_cluster_metrics` parameter provides access to metric-defining and metric-recording
825
/// callbacks. The caller receives an `Arc` so it can be stored and used at runtime
826
/// (e.g., during cluster lifecycle events) for recording metrics.
827
pub type NewClusterConfigFunction = fn(
828
  name: &str,
829
  config: &[u8],
830
  envoy_cluster_metrics: std::sync::Arc<dyn cluster::EnvoyClusterMetrics>,
831
) -> Option<Box<dyn cluster::ClusterConfig>>;
832

            
833
/// Global storage for the cluster config factory function.
834
pub static NEW_CLUSTER_CONFIG_FUNCTION: OnceLock<NewClusterConfigFunction> = OnceLock::new();
835

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

            
924
// =================================================================================================
925
// Load Balancer Dynamic Module Support
926
// =================================================================================================
927

            
928
/// The function signature for creating a new load balancer configuration.
929
///
930
/// The `envoy_lb_config` parameter provides access to metric-defining and metric-recording
931
/// callbacks. The caller receives an `Arc` so it can be stored and used at runtime
932
/// (e.g., in `choose_host`) for recording metrics.
933
pub type NewLoadBalancerConfigFunction = fn(
934
  name: &str,
935
  config: &[u8],
936
  envoy_lb_config: std::sync::Arc<dyn load_balancer::EnvoyLbConfig>,
937
) -> Option<Box<dyn load_balancer::LoadBalancerConfig>>;
938

            
939
/// Global function for creating load balancer configurations.
940
pub static NEW_LOAD_BALANCER_CONFIG_FUNCTION: OnceLock<NewLoadBalancerConfigFunction> =
941
  OnceLock::new();
942

            
943
/// Declare the init functions for a load balancer dynamic module.
944
///
945
/// This macro generates the necessary `extern "C"` functions for the load balancer module.
946
///
947
/// # Example
948
///
949
/// ```ignore
950
/// use envoy_proxy_dynamic_modules_rust_sdk::*;
951
/// use std::sync::Arc;
952
///
953
/// fn program_init() -> bool {
954
///   true
955
/// }
956
///
957
/// fn new_lb_config(
958
///   name: &str,
959
///   config: &[u8],
960
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
961
/// ) -> Option<Box<dyn LoadBalancerConfig>> {
962
///   let counter_id = envoy_lb_config.define_counter("my_counter").ok()?;
963
///   Some(Box::new(MyLbConfig { counter_id, envoy_lb_config }))
964
/// }
965
///
966
/// declare_load_balancer_init_functions!(program_init, new_lb_config);
967
///
968
/// struct MyLbConfig {
969
///   counter_id: EnvoyCounterId,
970
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
971
/// }
972
///
973
/// impl LoadBalancerConfig for MyLbConfig {
974
///   fn new_load_balancer(&self, _envoy_lb: &dyn EnvoyLoadBalancer) -> Box<dyn LoadBalancer> {
975
///     Box::new(MyLoadBalancer {
976
///       next_index: 0,
977
///       counter_id: self.counter_id,
978
///       envoy_lb_config: self.envoy_lb_config.clone(),
979
///     })
980
///   }
981
/// }
982
///
983
/// struct MyLoadBalancer {
984
///   next_index: usize,
985
///   counter_id: EnvoyCounterId,
986
///   envoy_lb_config: Arc<dyn EnvoyLbConfig>,
987
/// }
988
///
989
/// impl LoadBalancer for MyLoadBalancer {
990
///   fn choose_host(&mut self, envoy_lb: &dyn EnvoyLoadBalancer) -> Option<HostSelection> {
991
///     let count = envoy_lb.get_healthy_hosts_count(0);
992
///     if count == 0 {
993
///       return None;
994
///     }
995
///     let index = self.next_index % count;
996
///     self.next_index += 1;
997
///     self.envoy_lb_config.increment_counter(self.counter_id, 1).ok();
998
///     Some(HostSelection::at_default_priority(index as u32))
999
///   }
/// }
/// ```
#[macro_export]
macro_rules! declare_load_balancer_init_functions {
  ($f:ident, $new_lb_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_LOAD_BALANCER_CONFIG_FUNCTION
        .get_or_init(|| $new_lb_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()
      }
    }
  };
}
// =============================================================================
// 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();