1
use crate::buffer::EnvoyBuffer;
2
use crate::{
3
  abi,
4
  drop_wrapped_c_void_ptr,
5
  str_to_module_buffer,
6
  strs_to_module_buffers,
7
  wrap_into_c_void_ptr,
8
  CompletionCallback,
9
  EnvoyCounterId,
10
  EnvoyCounterVecId,
11
  EnvoyGaugeId,
12
  EnvoyGaugeVecId,
13
  EnvoyHistogramId,
14
  EnvoyHistogramVecId,
15
  NEW_CLUSTER_CONFIG_FUNCTION,
16
};
17
use mockall::*;
18
use std::sync::Arc;
19

            
20
/// The module-side cluster configuration.
21
///
22
/// This trait must be implemented by the module to handle cluster configuration.
23
/// The object is created when the corresponding Envoy cluster configuration is loaded, and
24
/// it is dropped when the corresponding Envoy cluster configuration is destroyed.
25
///
26
/// Implementations must be `Send + Sync` since they may be accessed from multiple threads.
27
pub trait ClusterConfig: Send + Sync {
28
  /// Create a new cluster instance.
29
  ///
30
  /// This is called when a new cluster is created from this configuration.
31
  /// The `envoy_cluster` provides access to Envoy's cluster operations such as
32
  /// adding/removing hosts.
33
  fn new_cluster(&self, envoy_cluster: &dyn EnvoyCluster) -> Box<dyn Cluster>;
34
}
35

            
36
/// The module-side cluster instance.
37
///
38
/// This trait must be implemented by the module to handle cluster lifecycle events.
39
/// The object is created per cluster and is responsible for host discovery.
40
///
41
/// Implementations must be `Send + Sync` since they may be accessed from multiple threads.
42
pub trait Cluster: Send + Sync {
43
  /// Called when cluster initialization begins.
44
  ///
45
  /// The module should perform initial host discovery (e.g., add hosts via
46
  /// [`EnvoyCluster::add_hosts`]) and then call [`EnvoyCluster::pre_init_complete`]
47
  /// to signal that the initial set of hosts is ready.
48
  fn on_init(&mut self, envoy_cluster: &dyn EnvoyCluster);
49

            
50
  /// Create a new load balancer instance for a worker thread.
51
  ///
52
  /// Each worker thread gets its own load balancer instance. The `envoy_lb`
53
  /// provides thread-local access to the cluster's host set.
54
  fn new_load_balancer(&self, envoy_lb: &dyn EnvoyClusterLoadBalancer) -> Box<dyn ClusterLb>;
55

            
56
  /// Called on the main thread when a new event is scheduled via
57
  /// [`EnvoyClusterScheduler::commit`] for this [`Cluster`].
58
  ///
59
  /// * `envoy_cluster` can be used to interact with the underlying Envoy cluster object.
60
  /// * `event_id` is the ID of the event that was scheduled with [`EnvoyClusterScheduler::commit`]
61
  ///   to distinguish multiple scheduled events.
62
  fn on_scheduled(&self, _envoy_cluster: &dyn EnvoyCluster, _event_id: u64) {}
63

            
64
  /// Called when the server initialization is complete (PostInit lifecycle stage).
65
  ///
66
  /// This is called on the main thread after all clusters have finished initialization and
67
  /// before workers are started. This is the appropriate place to start background discovery
68
  /// tasks or establish connections that depend on the server being fully operational.
69
  fn on_server_initialized(&mut self, _envoy_cluster: &dyn EnvoyCluster) {}
70

            
71
  /// Called when Envoy begins draining.
72
  ///
73
  /// This is called on the main thread before workers are stopped. The module can still use
74
  /// cluster operations during drain. This is the appropriate place to stop accepting new hosts,
75
  /// close persistent connections, or de-register from service discovery.
76
  fn on_drain_started(&mut self, _envoy_cluster: &dyn EnvoyCluster) {}
77

            
78
  /// Called when Envoy is about to exit (ShutdownExit lifecycle stage).
79
  ///
80
  /// The module must invoke [`CompletionCallback::done`] exactly once when it has finished
81
  /// cleanup. Envoy will wait for the callback before terminating. This is the appropriate
82
  /// place to flush batched data, close gRPC connections, or signal external systems.
83
  fn on_shutdown(&mut self, _envoy_cluster: &dyn EnvoyCluster, completion: CompletionCallback) {
84
    completion.done();
85
  }
86

            
87
  /// Called on the main thread when an HTTP callout initiated by
88
  /// [`EnvoyCluster::send_http_callout`] receives a response or fails.
89
  ///
90
  /// * `envoy_cluster` can be used to interact with the underlying Envoy cluster object.
91
  /// * `callout_id` is the ID of the callout returned by [`EnvoyCluster::send_http_callout`].
92
  /// * `result` is the result of the callout.
93
  /// * `response_headers` is a list of key-value pairs of the response headers. This is optional.
94
  /// * `response_body` is the response body chunks. This is optional.
95
  fn on_http_callout_done(
96
    &mut self,
97
    _envoy_cluster: &dyn EnvoyCluster,
98
    _callout_id: u64,
99
    _result: abi::envoy_dynamic_module_type_http_callout_result,
100
    _response_headers: Option<&[(EnvoyBuffer, EnvoyBuffer)]>,
101
    _response_body: Option<&[EnvoyBuffer]>,
102
  ) {
103
  }
104
}
105

            
106
/// The result of a host selection operation.
107
///
108
/// This enum represents the three possible outcomes of [`ClusterLb::choose_host`]:
109
/// synchronous success, synchronous failure, or async pending.
110
pub enum HostSelectionResult {
111
  /// A host was selected synchronously.
112
  Selected(abi::envoy_dynamic_module_type_cluster_host_envoy_ptr),
113
  /// No host is available and no async resolution will occur.
114
  NoHost,
115
  /// The module needs to perform async work (e.g., DNS resolution) before selecting a host.
116
  /// The module must eventually call
117
  /// [`EnvoyAsyncHostSelectionComplete::async_host_selection_complete`] to deliver the result,
118
  /// unless [`AsyncHostSelectionHandle::cancel`] is called first.
119
  AsyncPending(Box<dyn AsyncHostSelectionHandle>),
120
}
121

            
122
/// A handle for canceling an in-progress asynchronous host selection.
123
///
124
/// When the stream is destroyed before async host selection completes (e.g., due to a timeout),
125
/// Envoy calls [`AsyncHostSelectionHandle::cancel`]. After cancellation, the module must not
126
/// call the async completion callback for this operation.
127
pub trait AsyncHostSelectionHandle: Send {
128
  /// Cancel the async host selection. After this call, the module must not deliver a result
129
  /// for this operation.
130
  fn cancel(&mut self);
131
}
132

            
133
/// Envoy-side async host selection completion callback.
134
///
135
/// This is passed to [`ClusterLb::choose_host`] and must be stored by the module when returning
136
/// [`HostSelectionResult::AsyncPending`]. The module calls
137
/// [`EnvoyAsyncHostSelectionComplete::async_host_selection_complete`] to deliver the async result.
138
#[automock]
139
pub trait EnvoyAsyncHostSelectionComplete: Send {
140
  /// Deliver the result of an asynchronous host selection.
141
  ///
142
  /// `host` is the selected host pointer, or `None` if host selection failed.
143
  /// `details` is an optional description of the resolution outcome (e.g., error reason).
144
  fn async_host_selection_complete(
145
    &self,
146
    host: Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>,
147
    details: &str,
148
  );
149
}
150

            
151
/// The module-side load balancer instance.
152
///
153
/// This trait must be implemented by the module to select hosts for requests.
154
/// One instance is created per worker thread.
155
pub trait ClusterLb: Send {
156
  /// Select a host for a request.
157
  ///
158
  /// The `context` provides access to per-request information such as downstream headers,
159
  /// hash keys, override host, and retry state. It may be `None` if no context is available
160
  /// (e.g., health check requests).
161
  ///
162
  /// The `async_completion` callback must be used when returning
163
  /// [`HostSelectionResult::AsyncPending`]. The module stores it and later calls
164
  /// [`EnvoyAsyncHostSelectionComplete::async_host_selection_complete`] to deliver the result.
165
  /// For synchronous results, `async_completion` can be ignored.
166
  fn choose_host(
167
    &mut self,
168
    context: Option<&dyn ClusterLbContext>,
169
    async_completion: Box<dyn EnvoyAsyncHostSelectionComplete>,
170
  ) -> HostSelectionResult;
171

            
172
  /// Called when the set of hosts in the cluster changes.
173
  ///
174
  /// The `envoy_lb` provides access to the updated host set and to the addresses of hosts
175
  /// that were added or removed via
176
  /// [`EnvoyClusterLoadBalancer::get_member_update_host_address`].
177
  ///
178
  /// After this callback returns, the standard host query methods reflect the new state.
179
  ///
180
  /// Override this to rebuild internal data structures (e.g., hash rings, address-to-index
181
  /// maps) when the host set changes. The default implementation is a no-op.
182
  fn on_host_membership_update(
183
    &mut self,
184
    _envoy_lb: &dyn EnvoyClusterLoadBalancer,
185
    _num_hosts_added: usize,
186
    _num_hosts_removed: usize,
187
  ) {
188
  }
189
}
190

            
191
/// Per-request context available during [`ClusterLb::choose_host`].
192
///
193
/// This provides access to downstream request information for making load balancing decisions
194
/// such as header-based routing, consistent hashing, and retry-aware host selection.
195
#[automock]
196
pub trait ClusterLbContext {
197
  /// Compute a hash key from the request context for consistent hashing.
198
  ///
199
  /// Returns `Some(hash)` if a hash key was computed, `None` otherwise.
200
  fn compute_hash_key(&self) -> Option<u64>;
201

            
202
  /// Returns the number of downstream request headers.
203
  fn get_downstream_headers_size(&self) -> usize;
204

            
205
  /// Returns all downstream request headers as a vector of (key, value) pairs.
206
  ///
207
  /// Returns `None` if no headers are available.
208
  fn get_downstream_headers(&self) -> Option<Vec<(String, String)>>;
209

            
210
  /// Returns a downstream request header value by key and index.
211
  ///
212
  /// Since a header key can have multiple values, the `index` parameter selects a specific value.
213
  /// Returns `Some((value, total_count))` where `total_count` is the number of values for the key,
214
  /// or `None` if the header was not found at the given index.
215
  fn get_downstream_header(&self, key: &str, index: usize) -> Option<(String, usize)>;
216

            
217
  /// Returns the maximum number of times host selection should be retried if the chosen host
218
  /// is rejected by [`ClusterLbContext::should_select_another_host`].
219
  fn get_host_selection_retry_count(&self) -> u32;
220

            
221
  /// Checks whether the load balancer should reject the given host and retry selection.
222
  ///
223
  /// This is used during retries to avoid selecting hosts that were already attempted.
224
  /// The host is identified by priority and index within the healthy host list at that priority.
225
  fn should_select_another_host(&self, priority: u32, index: usize) -> bool;
226

            
227
  /// Returns the override host address and strict mode flag from the context.
228
  ///
229
  /// Override host allows upstream filters to direct the load balancer to prefer a specific host
230
  /// by address. Returns `Some((address, strict))` if an override host is set, `None` otherwise.
231
  /// When `strict` is true, the load balancer should return no host if the override is not valid.
232
  fn get_override_host(&self) -> Option<(String, bool)>;
233

            
234
  /// Returns the requested server name (SNI) from the downstream connection.
235
  ///
236
  /// Returns `None` if the downstream connection or SNI is not available.
237
  fn get_downstream_connection_sni(&self) -> Option<String>;
238
}
239

            
240
/// Envoy-side cluster operations available to the module.
241
#[automock]
242
pub trait EnvoyCluster: Send + Sync {
243
  /// Add multiple hosts to the cluster in a single batch operation.
244
  ///
245
  /// Each address must be in `ip:port` format (e.g., `127.0.0.1:8080`).
246
  /// Each weight must be between 1 and 128. The `addresses` and `weights` slices must have the
247
  /// same length.
248
  ///
249
  /// This triggers only one priority set update regardless of how many hosts are added, avoiding
250
  /// the overhead of updating the priority set per host.
251
  ///
252
  /// Returns the host pointers if all hosts were added successfully, or `None` if any host failed
253
  /// (e.g., invalid address or weight). On failure, no hosts are added.
254
  fn add_hosts(
255
    &self,
256
    addresses: &[String],
257
    weights: &[u32],
258
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>>;
259

            
260
  /// Remove multiple hosts from the cluster in a single batch operation.
261
  ///
262
  /// The host pointers must have been returned by a previous [`EnvoyCluster::add_hosts`] call.
263
  ///
264
  /// This triggers only one priority set update regardless of how many hosts are removed.
265
  ///
266
  /// Returns the number of hosts that were successfully removed. Hosts not found in the cluster
267
  /// are skipped.
268
  fn remove_hosts(&self, hosts: &[abi::envoy_dynamic_module_type_cluster_host_envoy_ptr]) -> usize;
269

            
270
  /// Signal that the cluster's initial host discovery is complete.
271
  ///
272
  /// This must be called during or after [`Cluster::on_init`] to allow Envoy to start
273
  /// routing traffic to this cluster.
274
  fn pre_init_complete(&self);
275

            
276
  /// Add multiple hosts to the cluster with per-host locality and metadata.
277
  ///
278
  /// Each address must be in `ip:port` format (e.g., `127.0.0.1:8080`).
279
  /// Each weight must be between 1 and 128. All per-host slices must have the same length.
280
  ///
281
  /// `localities` specifies (region, zone, sub_zone) for each host. An empty string indicates
282
  /// no value for that field.
283
  ///
284
  /// `metadata` specifies endpoint metadata as (filter_name, key, value) triples per host.
285
  /// All values are stored as strings. Pass an empty inner slice for hosts with no metadata.
286
  /// The number of triples per host must be the same for all hosts (pad with empty triples
287
  /// if needed) or the outer slice can be empty to skip metadata entirely.
288
  ///
289
  /// Returns the host pointers on success, or `None` if any host failed.
290
  fn add_hosts_with_locality(
291
    &self,
292
    addresses: &[String],
293
    weights: &[u32],
294
    localities: &[(String, String, String)],
295
    metadata: &[Vec<(String, String, String)>],
296
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>>;
297

            
298
  /// Add multiple hosts to the cluster at the specified priority level in a single batch operation.
299
  ///
300
  /// This is the priority-aware version of [`EnvoyCluster::add_hosts`]. Only modules that manage
301
  /// hosts across multiple priority levels need to use this.
302
  ///
303
  /// Each address must be in `ip:port` format (e.g., `127.0.0.1:8080`).
304
  /// Each weight must be between 1 and 128.
305
  ///
306
  /// Returns the host pointers if all hosts were added successfully, or `None` if any host failed.
307
  fn add_hosts_to_priority(
308
    &self,
309
    priority: u32,
310
    addresses: &[String],
311
    weights: &[u32],
312
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>>;
313

            
314
  /// Add multiple hosts to the cluster at the specified priority level with per-host locality and
315
  /// metadata in a single batch operation.
316
  ///
317
  /// This is the priority-aware version of [`EnvoyCluster::add_hosts_with_locality`]. Only modules
318
  /// that manage hosts across multiple priority levels need to use this.
319
  ///
320
  /// Each address must be in `ip:port` format. Each weight must be between 1 and 128.
321
  ///
322
  /// Returns the host pointers on success, or `None` if any host failed.
323
  fn add_hosts_with_locality_to_priority(
324
    &self,
325
    priority: u32,
326
    addresses: &[String],
327
    weights: &[u32],
328
    localities: &[(String, String, String)],
329
    metadata: &[Vec<(String, String, String)>],
330
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>>;
331

            
332
  /// Update the health status of a host.
333
  ///
334
  /// This allows the module to mark hosts as unhealthy, degraded, or healthy based on
335
  /// external health information (e.g., from a custom service discovery system).
336
  ///
337
  /// Returns true if the host was found and updated, false otherwise.
338
  fn update_host_health(
339
    &self,
340
    host: abi::envoy_dynamic_module_type_cluster_host_envoy_ptr,
341
    health_status: abi::envoy_dynamic_module_type_host_health,
342
  ) -> bool;
343

            
344
  /// Look up a host by its address string across all priorities and return the host pointer.
345
  ///
346
  /// This provides O(1) lookup by address using the cross-priority host map.
347
  /// The address must match the format "ip:port" (e.g., "10.0.0.1:8080").
348
  ///
349
  /// Returns the host pointer if found, or `None` if the address is not in the cluster.
350
  fn find_host_by_address(
351
    &self,
352
    address: &str,
353
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>;
354

            
355
  /// Create a new implementation of the [`EnvoyClusterScheduler`] trait.
356
  ///
357
  /// This can be used to schedule an event to the main thread where the cluster is running.
358
  fn new_scheduler(&self) -> Box<dyn EnvoyClusterScheduler>;
359

            
360
  /// Sends an HTTP request to the specified cluster and asynchronously delivers the response
361
  /// via [`Cluster::on_http_callout_done`].
362
  ///
363
  /// This must be called on the main thread. The request requires `:method`, `:path`, and `host`
364
  /// headers to be present. To call from other threads, use the scheduler mechanism to post an
365
  /// event to the main thread first.
366
  ///
367
  /// Returns a tuple of the callout initialization result and the callout ID. The callout ID is
368
  /// only valid if the result is `Success`.
369
  fn send_http_callout<'a>(
370
    &self,
371
    cluster_name: &'a str,
372
    headers: &[(&'a str, &'a [u8])],
373
    body: Option<&'a [u8]>,
374
    timeout_milliseconds: u64,
375
  ) -> (abi::envoy_dynamic_module_type_http_callout_init_result, u64);
376
}
377

            
378
/// Envoy-side load balancer operations available to the module.
379
///
380
/// This trait provides access to the cluster's host set for load balancing decisions.
381
/// It mirrors the standalone load balancer's [`EnvoyLoadBalancer`] trait, operating on the
382
/// cluster's priority set.
383
#[automock]
384
pub trait EnvoyClusterLoadBalancer: Send {
385
  /// Get the number of healthy hosts at the given priority level.
386
  fn get_healthy_host_count(&self, priority: u32) -> usize;
387

            
388
  /// Get a healthy host by index at the given priority level.
389
  ///
390
  /// Returns the host pointer, or `None` if the index is out of bounds.
391
  fn get_healthy_host(
392
    &self,
393
    priority: u32,
394
    index: usize,
395
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>;
396

            
397
  /// Get a host by index within all hosts at the given priority level, regardless of health status.
398
  ///
399
  /// Unlike [`EnvoyClusterLoadBalancer::get_healthy_host`] which only returns healthy hosts, this
400
  /// returns any host at the given index in the full host list.
401
  ///
402
  /// Returns the host pointer, or `None` if the index is out of bounds.
403
  fn get_host(
404
    &self,
405
    priority: u32,
406
    index: usize,
407
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>;
408

            
409
  /// Look up a host by its address string across all priorities in the cluster's priority set.
410
  ///
411
  /// This uses the cross-priority host map internally, providing O(1) lookup by address. The
412
  /// address must match the format "ip:port" (e.g., "10.0.0.1:8080").
413
  ///
414
  /// Unlike [`EnvoyCluster::find_host_by_address`] which operates on the main thread, this is
415
  /// safe to call from worker threads during load balancing decisions.
416
  ///
417
  /// Returns the host pointer if found, or `None` if the address is not in the cluster.
418
  fn find_host_by_address(
419
    &self,
420
    address: &str,
421
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>;
422

            
423
  /// Returns the cluster name.
424
  fn get_cluster_name(&self) -> String;
425

            
426
  /// Returns the number of all hosts at a given priority, regardless of health status.
427
  fn get_hosts_count(&self, priority: u32) -> usize;
428

            
429
  /// Returns the number of degraded hosts at a given priority.
430
  fn get_degraded_hosts_count(&self, priority: u32) -> usize;
431

            
432
  /// Returns the number of priority levels in the cluster.
433
  fn get_priority_set_size(&self) -> usize;
434

            
435
  /// Returns the address of a healthy host by index at a given priority.
436
  fn get_healthy_host_address(&self, priority: u32, index: usize) -> Option<String>;
437

            
438
  /// Returns the weight of a healthy host by index at a given priority.
439
  fn get_healthy_host_weight(&self, priority: u32, index: usize) -> u32;
440

            
441
  /// Returns the health status of a host by index within all hosts at a given priority.
442
  fn get_host_health(
443
    &self,
444
    priority: u32,
445
    index: usize,
446
  ) -> abi::envoy_dynamic_module_type_host_health;
447

            
448
  /// Looks up a host by its address string across all priorities and returns its health status.
449
  /// This provides O(1) lookup by address using the cross-priority host map.
450
  ///
451
  /// The address must match the format "ip:port" (e.g., "10.0.0.1:8080").
452
  fn get_host_health_by_address(
453
    &self,
454
    address: &str,
455
  ) -> Option<abi::envoy_dynamic_module_type_host_health>;
456

            
457
  /// Returns the address of a host by index within all hosts at a given priority.
458
  fn get_host_address(&self, priority: u32, index: usize) -> Option<String>;
459

            
460
  /// Returns the weight of a host by index within all hosts at a given priority.
461
  fn get_host_weight(&self, priority: u32, index: usize) -> u32;
462

            
463
  /// Returns the value of a per-host stat. This provides access to host-level counters and gauges
464
  /// such as total connections, request errors, active requests, and active connections.
465
  fn get_host_stat(
466
    &self,
467
    priority: u32,
468
    index: usize,
469
    stat: abi::envoy_dynamic_module_type_host_stat,
470
  ) -> u64;
471

            
472
  /// Returns the locality information (region, zone, sub_zone) for a host by index within all
473
  /// hosts at a given priority. This enables zone-aware and locality-aware load balancing.
474
  fn get_host_locality(&self, priority: u32, index: usize) -> Option<(String, String, String)>;
475

            
476
  /// Stores an opaque value on a host identified by priority and index. This data is stored per
477
  /// load balancer instance (per worker thread) and can be used for per-host state such as moving
478
  /// averages or request tracking. Use 0 to clear the data.
479
  fn set_host_data(&self, priority: u32, index: usize, data: usize) -> bool;
480

            
481
  /// Retrieves a previously stored opaque value for a host. Returns `None` if the host was not
482
  /// found. Returns `Some(0)` if the host exists but no data was stored.
483
  fn get_host_data(&self, priority: u32, index: usize) -> Option<usize>;
484

            
485
  /// Returns the string metadata value for a host by looking up the given filter name and key in
486
  /// the host's endpoint metadata. Returns `None` if the key was not found or the value is not a
487
  /// string.
488
  fn get_host_metadata_string(
489
    &self,
490
    priority: u32,
491
    index: usize,
492
    filter_name: &str,
493
    key: &str,
494
  ) -> Option<String>;
495

            
496
  /// Returns the number metadata value for a host by looking up the given filter name and key in
497
  /// the host's endpoint metadata. Returns `None` if the key was not found or the value is not a
498
  /// number.
499
  fn get_host_metadata_number(
500
    &self,
501
    priority: u32,
502
    index: usize,
503
    filter_name: &str,
504
    key: &str,
505
  ) -> Option<f64>;
506

            
507
  /// Returns the bool metadata value for a host by looking up the given filter name and key in
508
  /// the host's endpoint metadata. Returns `None` if the key was not found or the value is not a
509
  /// bool.
510
  fn get_host_metadata_bool(
511
    &self,
512
    priority: u32,
513
    index: usize,
514
    filter_name: &str,
515
    key: &str,
516
  ) -> Option<bool>;
517

            
518
  /// Returns the number of locality buckets for the healthy hosts at a given priority.
519
  fn get_locality_count(&self, priority: u32) -> usize;
520

            
521
  /// Returns the number of healthy hosts in a specific locality bucket at a given priority.
522
  fn get_locality_host_count(&self, priority: u32, locality_index: usize) -> usize;
523

            
524
  /// Returns the address of a host within a specific locality bucket at a given priority.
525
  fn get_locality_host_address(
526
    &self,
527
    priority: u32,
528
    locality_index: usize,
529
    host_index: usize,
530
  ) -> Option<String>;
531

            
532
  /// Returns the weight of a locality bucket at a given priority.
533
  fn get_locality_weight(&self, priority: u32, locality_index: usize) -> u32;
534

            
535
  /// Returns the address of an added or removed host during the
536
  /// [`ClusterLb::on_host_membership_update`] callback.
537
  ///
538
  /// This is only valid during the `on_host_membership_update` callback.
539
  ///
540
  /// Set `is_added` to `true` to get an added host address, `false` for a removed host address.
541
  fn get_member_update_host_address(&self, index: usize, is_added: bool) -> Option<String>;
542
}
543

            
544
/// Envoy-side scheduler that dispatches events to the main thread.
545
///
546
/// The scheduler can be used from any thread. When [`EnvoyClusterScheduler::commit`] is called,
547
/// the event is posted to the main thread dispatcher and [`Cluster::on_scheduled`] will be
548
/// invoked on the main thread with the corresponding `event_id`.
549
#[automock]
550
pub trait EnvoyClusterScheduler: Send + Sync {
551
  /// Commit the scheduled event to the main thread.
552
  fn commit(&self, event_id: u64);
553
}
554

            
555
/// Envoy-side metrics interface for the cluster dynamic module.
556
///
557
/// This trait provides the ability to define and record custom metrics (counters, gauges,
558
/// histograms) scoped to the cluster configuration. Metrics should be defined during
559
/// config creation and can be recorded at any point during the cluster lifecycle.
560
///
561
/// Implementations must be `Send + Sync` since they may be accessed from multiple threads.
562
#[automock]
563
#[allow(clippy::needless_lifetimes)]
564
pub trait EnvoyClusterMetrics: Send + Sync {
565
  // -------------------------------------------------------------------------
566
  // Define metrics (call during config creation).
567
  // -------------------------------------------------------------------------
568

            
569
  /// Define a new counter with the given name and no labels.
570
  fn define_counter(
571
    &self,
572
    name: &str,
573
  ) -> Result<EnvoyCounterId, abi::envoy_dynamic_module_type_metrics_result>;
574

            
575
  /// Define a new counter vec with the given name and label names.
576
  fn define_counter_vec<'a>(
577
    &self,
578
    name: &str,
579
    labels: &[&'a str],
580
  ) -> Result<EnvoyCounterVecId, abi::envoy_dynamic_module_type_metrics_result>;
581

            
582
  /// Define a new gauge with the given name and no labels.
583
  fn define_gauge(
584
    &self,
585
    name: &str,
586
  ) -> Result<EnvoyGaugeId, abi::envoy_dynamic_module_type_metrics_result>;
587

            
588
  /// Define a new gauge vec with the given name and label names.
589
  fn define_gauge_vec<'a>(
590
    &self,
591
    name: &str,
592
    labels: &[&'a str],
593
  ) -> Result<EnvoyGaugeVecId, abi::envoy_dynamic_module_type_metrics_result>;
594

            
595
  /// Define a new histogram with the given name and no labels.
596
  fn define_histogram(
597
    &self,
598
    name: &str,
599
  ) -> Result<EnvoyHistogramId, abi::envoy_dynamic_module_type_metrics_result>;
600

            
601
  /// Define a new histogram vec with the given name and label names.
602
  fn define_histogram_vec<'a>(
603
    &self,
604
    name: &str,
605
    labels: &[&'a str],
606
  ) -> Result<EnvoyHistogramVecId, abi::envoy_dynamic_module_type_metrics_result>;
607

            
608
  // -------------------------------------------------------------------------
609
  // Record metrics (call at runtime, e.g., during cluster lifecycle).
610
  // -------------------------------------------------------------------------
611

            
612
  /// Increment a previously defined counter by the given value.
613
  fn increment_counter(
614
    &self,
615
    id: EnvoyCounterId,
616
    value: u64,
617
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
618

            
619
  /// Increment a previously defined counter vec by the given value with label values.
620
  fn increment_counter_vec<'a>(
621
    &self,
622
    id: EnvoyCounterVecId,
623
    labels: &[&'a str],
624
    value: u64,
625
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
626

            
627
  /// Set the value of a previously defined gauge.
628
  fn set_gauge(
629
    &self,
630
    id: EnvoyGaugeId,
631
    value: u64,
632
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
633

            
634
  /// Set the value of a previously defined gauge vec with label values.
635
  fn set_gauge_vec<'a>(
636
    &self,
637
    id: EnvoyGaugeVecId,
638
    labels: &[&'a str],
639
    value: u64,
640
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
641

            
642
  /// Increase a previously defined gauge by the given value.
643
  fn increase_gauge(
644
    &self,
645
    id: EnvoyGaugeId,
646
    value: u64,
647
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
648

            
649
  /// Increase a previously defined gauge vec by the given value with label values.
650
  fn increase_gauge_vec<'a>(
651
    &self,
652
    id: EnvoyGaugeVecId,
653
    labels: &[&'a str],
654
    value: u64,
655
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
656

            
657
  /// Decrease a previously defined gauge by the given value.
658
  fn decrease_gauge(
659
    &self,
660
    id: EnvoyGaugeId,
661
    value: u64,
662
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
663

            
664
  /// Decrease a previously defined gauge vec by the given value with label values.
665
  fn decrease_gauge_vec<'a>(
666
    &self,
667
    id: EnvoyGaugeVecId,
668
    labels: &[&'a str],
669
    value: u64,
670
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
671

            
672
  /// Record a value in a previously defined histogram.
673
  fn record_histogram_value(
674
    &self,
675
    id: EnvoyHistogramId,
676
    value: u64,
677
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
678

            
679
  /// Record a value in a previously defined histogram vec with label values.
680
  fn record_histogram_value_vec<'a>(
681
    &self,
682
    id: EnvoyHistogramVecId,
683
    labels: &[&'a str],
684
    value: u64,
685
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result>;
686
}
687

            
688
struct EnvoyClusterSchedulerImpl {
689
  raw_ptr: abi::envoy_dynamic_module_type_cluster_scheduler_module_ptr,
690
}
691

            
692
unsafe impl Send for EnvoyClusterSchedulerImpl {}
693
unsafe impl Sync for EnvoyClusterSchedulerImpl {}
694

            
695
impl Drop for EnvoyClusterSchedulerImpl {
696
  fn drop(&mut self) {
697
    unsafe {
698
      abi::envoy_dynamic_module_callback_cluster_scheduler_delete(self.raw_ptr);
699
    }
700
  }
701
}
702

            
703
impl EnvoyClusterScheduler for EnvoyClusterSchedulerImpl {
704
  fn commit(&self, event_id: u64) {
705
    unsafe {
706
      abi::envoy_dynamic_module_callback_cluster_scheduler_commit(self.raw_ptr, event_id);
707
    }
708
  }
709
}
710

            
711
impl EnvoyClusterScheduler for Box<dyn EnvoyClusterScheduler> {
712
  fn commit(&self, event_id: u64) {
713
    (**self).commit(event_id);
714
  }
715
}
716

            
717
// Implementations
718

            
719
struct EnvoyClusterImpl {
720
  raw: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
721
}
722

            
723
unsafe impl Send for EnvoyClusterImpl {}
724
unsafe impl Sync for EnvoyClusterImpl {}
725

            
726
impl EnvoyClusterImpl {
727
  fn new(raw: abi::envoy_dynamic_module_type_cluster_envoy_ptr) -> Self {
728
    Self { raw }
729
  }
730
}
731

            
732
impl EnvoyCluster for EnvoyClusterImpl {
733
  fn add_hosts(
734
    &self,
735
    addresses: &[String],
736
    weights: &[u32],
737
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>> {
738
    let empty_localities: Vec<(String, String, String)> = addresses
739
      .iter()
740
      .map(|_| (String::new(), String::new(), String::new()))
741
      .collect();
742
    self.add_hosts_with_locality_to_priority(0, addresses, weights, &empty_localities, &[])
743
  }
744

            
745
  fn add_hosts_with_locality(
746
    &self,
747
    addresses: &[String],
748
    weights: &[u32],
749
    localities: &[(String, String, String)],
750
    metadata: &[Vec<(String, String, String)>],
751
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>> {
752
    self.add_hosts_with_locality_to_priority(0, addresses, weights, localities, metadata)
753
  }
754

            
755
  fn add_hosts_to_priority(
756
    &self,
757
    priority: u32,
758
    addresses: &[String],
759
    weights: &[u32],
760
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>> {
761
    let empty_localities: Vec<(String, String, String)> = addresses
762
      .iter()
763
      .map(|_| (String::new(), String::new(), String::new()))
764
      .collect();
765
    self.add_hosts_with_locality_to_priority(priority, addresses, weights, &empty_localities, &[])
766
  }
767

            
768
  fn add_hosts_with_locality_to_priority(
769
    &self,
770
    priority: u32,
771
    addresses: &[String],
772
    weights: &[u32],
773
    localities: &[(String, String, String)],
774
    metadata: &[Vec<(String, String, String)>],
775
  ) -> Option<Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>> {
776
    let count = addresses.len();
777
    let address_buffers: Vec<abi::envoy_dynamic_module_type_module_buffer> =
778
      addresses.iter().map(|a| str_to_module_buffer(a)).collect();
779
    let region_buffers: Vec<abi::envoy_dynamic_module_type_module_buffer> = localities
780
      .iter()
781
      .map(|(r, ..)| str_to_module_buffer(r))
782
      .collect();
783
    let zone_buffers: Vec<abi::envoy_dynamic_module_type_module_buffer> = localities
784
      .iter()
785
      .map(|(_, z, _)| str_to_module_buffer(z))
786
      .collect();
787
    let sub_zone_buffers: Vec<abi::envoy_dynamic_module_type_module_buffer> = localities
788
      .iter()
789
      .map(|(_, _, s)| str_to_module_buffer(s))
790
      .collect();
791

            
792
    let metadata_pairs_per_host = if metadata.is_empty() {
793
      0
794
    } else {
795
      metadata[0].len()
796
    };
797
    let mut metadata_flat: Vec<abi::envoy_dynamic_module_type_module_buffer> = Vec::new();
798
    if !metadata.is_empty() {
799
      for host_meta in metadata {
800
        for (filter_name, key, value) in host_meta {
801
          metadata_flat.push(str_to_module_buffer(filter_name));
802
          metadata_flat.push(str_to_module_buffer(key));
803
          metadata_flat.push(str_to_module_buffer(value));
804
        }
805
      }
806
    }
807
    let metadata_ptr = if metadata_flat.is_empty() {
808
      std::ptr::null()
809
    } else {
810
      metadata_flat.as_ptr()
811
    };
812

            
813
    let mut result_ptrs: Vec<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr> =
814
      vec![std::ptr::null_mut(); count];
815
    let success = unsafe {
816
      abi::envoy_dynamic_module_callback_cluster_add_hosts(
817
        self.raw,
818
        priority,
819
        address_buffers.as_ptr(),
820
        weights.as_ptr(),
821
        region_buffers.as_ptr(),
822
        zone_buffers.as_ptr(),
823
        sub_zone_buffers.as_ptr(),
824
        metadata_ptr,
825
        metadata_pairs_per_host,
826
        count,
827
        result_ptrs.as_mut_ptr(),
828
      )
829
    };
830
    if success {
831
      Some(result_ptrs)
832
    } else {
833
      None
834
    }
835
  }
836

            
837
  fn update_host_health(
838
    &self,
839
    host: abi::envoy_dynamic_module_type_cluster_host_envoy_ptr,
840
    health_status: abi::envoy_dynamic_module_type_host_health,
841
  ) -> bool {
842
    unsafe {
843
      abi::envoy_dynamic_module_callback_cluster_update_host_health(self.raw, host, health_status)
844
    }
845
  }
846

            
847
  fn find_host_by_address(
848
    &self,
849
    address: &str,
850
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr> {
851
    let address_buffer = str_to_module_buffer(address);
852
    let host = unsafe {
853
      abi::envoy_dynamic_module_callback_cluster_find_host_by_address(self.raw, address_buffer)
854
    };
855
    if host.is_null() {
856
      None
857
    } else {
858
      Some(host)
859
    }
860
  }
861

            
862
  fn remove_hosts(&self, hosts: &[abi::envoy_dynamic_module_type_cluster_host_envoy_ptr]) -> usize {
863
    unsafe {
864
      abi::envoy_dynamic_module_callback_cluster_remove_hosts(self.raw, hosts.as_ptr(), hosts.len())
865
    }
866
  }
867

            
868
  fn pre_init_complete(&self) {
869
    unsafe {
870
      abi::envoy_dynamic_module_callback_cluster_pre_init_complete(self.raw);
871
    }
872
  }
873

            
874
  fn new_scheduler(&self) -> Box<dyn EnvoyClusterScheduler> {
875
    unsafe {
876
      let scheduler_ptr = abi::envoy_dynamic_module_callback_cluster_scheduler_new(self.raw);
877
      Box::new(EnvoyClusterSchedulerImpl {
878
        raw_ptr: scheduler_ptr,
879
      })
880
    }
881
  }
882

            
883
  fn send_http_callout<'a>(
884
    &self,
885
    cluster_name: &'a str,
886
    headers: &[(&'a str, &'a [u8])],
887
    body: Option<&'a [u8]>,
888
    timeout_milliseconds: u64,
889
  ) -> (abi::envoy_dynamic_module_type_http_callout_init_result, u64) {
890
    let body_ptr = body.map(|s| s.as_ptr()).unwrap_or(std::ptr::null());
891
    let body_length = body.map(|s| s.len()).unwrap_or(0);
892

            
893
    let module_headers: Vec<abi::envoy_dynamic_module_type_module_http_header> = headers
894
      .iter()
895
      .map(|(k, v)| abi::envoy_dynamic_module_type_module_http_header {
896
        key_ptr: k.as_ptr() as *const _,
897
        key_length: k.len(),
898
        value_ptr: v.as_ptr() as *const _,
899
        value_length: v.len(),
900
      })
901
      .collect();
902

            
903
    let mut callout_id: u64 = 0;
904

            
905
    let result = unsafe {
906
      abi::envoy_dynamic_module_callback_cluster_http_callout(
907
        self.raw,
908
        &mut callout_id as *mut _ as *mut _,
909
        str_to_module_buffer(cluster_name),
910
        module_headers.as_ptr() as *mut _,
911
        module_headers.len(),
912
        abi::envoy_dynamic_module_type_module_buffer {
913
          ptr: body_ptr as *mut _,
914
          length: body_length,
915
        },
916
        timeout_milliseconds,
917
      )
918
    };
919
    (result, callout_id)
920
  }
921
}
922

            
923
struct EnvoyClusterLoadBalancerImpl {
924
  raw: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
925
}
926

            
927
unsafe impl Send for EnvoyClusterLoadBalancerImpl {}
928

            
929
impl EnvoyClusterLoadBalancerImpl {
930
  fn new(raw: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr) -> Self {
931
    Self { raw }
932
  }
933
}
934

            
935
impl EnvoyClusterLoadBalancer for EnvoyClusterLoadBalancerImpl {
936
  fn get_healthy_host_count(&self, priority: u32) -> usize {
937
    unsafe {
938
      abi::envoy_dynamic_module_callback_cluster_lb_get_healthy_host_count(self.raw, priority)
939
    }
940
  }
941

            
942
  fn get_healthy_host(
943
    &self,
944
    priority: u32,
945
    index: usize,
946
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr> {
947
    let host = unsafe {
948
      abi::envoy_dynamic_module_callback_cluster_lb_get_healthy_host(self.raw, priority, index)
949
    };
950
    if host.is_null() {
951
      None
952
    } else {
953
      Some(host)
954
    }
955
  }
956

            
957
  fn get_host(
958
    &self,
959
    priority: u32,
960
    index: usize,
961
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr> {
962
    let host =
963
      unsafe { abi::envoy_dynamic_module_callback_cluster_lb_get_host(self.raw, priority, index) };
964
    if host.is_null() {
965
      None
966
    } else {
967
      Some(host)
968
    }
969
  }
970

            
971
  fn find_host_by_address(
972
    &self,
973
    address: &str,
974
  ) -> Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr> {
975
    let address_buffer = str_to_module_buffer(address);
976
    let host = unsafe {
977
      abi::envoy_dynamic_module_callback_cluster_lb_find_host_by_address(self.raw, address_buffer)
978
    };
979
    if host.is_null() {
980
      None
981
    } else {
982
      Some(host)
983
    }
984
  }
985

            
986
  fn get_cluster_name(&self) -> String {
987
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
988
      ptr: std::ptr::null(),
989
      length: 0,
990
    };
991
    unsafe {
992
      abi::envoy_dynamic_module_callback_cluster_lb_get_cluster_name(self.raw, &mut result);
993
    }
994
    if result.ptr.is_null() || result.length == 0 {
995
      String::new()
996
    } else {
997
      unsafe {
998
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
999
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      }
    }
  }
  fn get_hosts_count(&self, priority: u32) -> usize {
    unsafe { abi::envoy_dynamic_module_callback_cluster_lb_get_hosts_count(self.raw, priority) }
  }
  fn get_degraded_hosts_count(&self, priority: u32) -> usize {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_degraded_hosts_count(self.raw, priority)
    }
  }
  fn get_priority_set_size(&self) -> usize {
    unsafe { abi::envoy_dynamic_module_callback_cluster_lb_get_priority_set_size(self.raw) }
  }
  fn get_healthy_host_address(&self, priority: u32, index: usize) -> Option<String> {
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_healthy_host_address(
        self.raw,
        priority,
        index,
        &mut result,
      )
    };
    if found && !result.ptr.is_null() && result.length > 0 {
      Some(unsafe {
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      })
    } else {
      None
    }
  }
  fn get_healthy_host_weight(&self, priority: u32, index: usize) -> u32 {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_healthy_host_weight(
        self.raw, priority, index,
      )
    }
  }
  fn get_host_health(
    &self,
    priority: u32,
    index: usize,
  ) -> abi::envoy_dynamic_module_type_host_health {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_health(self.raw, priority, index)
    }
  }
  fn get_host_health_by_address(
    &self,
    address: &str,
  ) -> Option<abi::envoy_dynamic_module_type_host_health> {
    let address_buf = str_to_module_buffer(address);
    let mut result = abi::envoy_dynamic_module_type_host_health::Unhealthy;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_health_by_address(
        self.raw,
        address_buf,
        &mut result,
      )
    };
    if found {
      Some(result)
    } else {
      None
    }
  }
  fn get_host_address(&self, priority: u32, index: usize) -> Option<String> {
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_address(
        self.raw,
        priority,
        index,
        &mut result,
      )
    };
    if found && !result.ptr.is_null() && result.length > 0 {
      Some(unsafe {
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      })
    } else {
      None
    }
  }
  fn get_host_weight(&self, priority: u32, index: usize) -> u32 {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_weight(self.raw, priority, index)
    }
  }
  fn get_host_stat(
    &self,
    priority: u32,
    index: usize,
    stat: abi::envoy_dynamic_module_type_host_stat,
  ) -> u64 {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_stat(self.raw, priority, index, stat)
    }
  }
  fn get_host_locality(&self, priority: u32, index: usize) -> Option<(String, String, String)> {
    let mut region = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let mut zone = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let mut sub_zone = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_locality(
        self.raw,
        priority,
        index,
        &mut region,
        &mut zone,
        &mut sub_zone,
      )
    };
    if found {
      unsafe {
        let region_str = if region.ptr.is_null() || region.length == 0 {
          String::new()
        } else {
          std::str::from_utf8_unchecked(std::slice::from_raw_parts(
            region.ptr as *const u8,
            region.length,
          ))
          .to_string()
        };
        let zone_str = if zone.ptr.is_null() || zone.length == 0 {
          String::new()
        } else {
          std::str::from_utf8_unchecked(std::slice::from_raw_parts(
            zone.ptr as *const u8,
            zone.length,
          ))
          .to_string()
        };
        let sub_zone_str = if sub_zone.ptr.is_null() || sub_zone.length == 0 {
          String::new()
        } else {
          std::str::from_utf8_unchecked(std::slice::from_raw_parts(
            sub_zone.ptr as *const u8,
            sub_zone.length,
          ))
          .to_string()
        };
        Some((region_str, zone_str, sub_zone_str))
      }
    } else {
      None
    }
  }
  fn set_host_data(&self, priority: u32, index: usize, data: usize) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_set_host_data(self.raw, priority, index, data)
    }
  }
  fn get_host_data(&self, priority: u32, index: usize) -> Option<usize> {
    let mut data: usize = 0;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_data(
        self.raw, priority, index, &mut data,
      )
    };
    if found {
      Some(data)
    } else {
      None
    }
  }
  fn get_host_metadata_string(
    &self,
    priority: u32,
    index: usize,
    filter_name: &str,
    key: &str,
  ) -> Option<String> {
    let filter_buf = str_to_module_buffer(filter_name);
    let key_buf = str_to_module_buffer(key);
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_metadata_string(
        self.raw,
        priority,
        index,
        filter_buf,
        key_buf,
        &mut result,
      )
    };
    if found && !result.ptr.is_null() && result.length > 0 {
      Some(unsafe {
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      })
    } else {
      None
    }
  }
  fn get_host_metadata_number(
    &self,
    priority: u32,
    index: usize,
    filter_name: &str,
    key: &str,
  ) -> Option<f64> {
    let filter_buf = str_to_module_buffer(filter_name);
    let key_buf = str_to_module_buffer(key);
    let mut result: f64 = 0.0;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_metadata_number(
        self.raw,
        priority,
        index,
        filter_buf,
        key_buf,
        &mut result,
      )
    };
    if found {
      Some(result)
    } else {
      None
    }
  }
  fn get_host_metadata_bool(
    &self,
    priority: u32,
    index: usize,
    filter_name: &str,
    key: &str,
  ) -> Option<bool> {
    let filter_buf = str_to_module_buffer(filter_name);
    let key_buf = str_to_module_buffer(key);
    let mut result: bool = false;
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_host_metadata_bool(
        self.raw,
        priority,
        index,
        filter_buf,
        key_buf,
        &mut result,
      )
    };
    if found {
      Some(result)
    } else {
      None
    }
  }
  fn get_locality_count(&self, priority: u32) -> usize {
    unsafe { abi::envoy_dynamic_module_callback_cluster_lb_get_locality_count(self.raw, priority) }
  }
  fn get_locality_host_count(&self, priority: u32, locality_index: usize) -> usize {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_locality_host_count(
        self.raw,
        priority,
        locality_index,
      )
    }
  }
  fn get_locality_host_address(
    &self,
    priority: u32,
    locality_index: usize,
    host_index: usize,
  ) -> Option<String> {
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_locality_host_address(
        self.raw,
        priority,
        locality_index,
        host_index,
        &mut result,
      )
    };
    if found && !result.ptr.is_null() && result.length > 0 {
      Some(unsafe {
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      })
    } else {
      None
    }
  }
  fn get_locality_weight(&self, priority: u32, locality_index: usize) -> u32 {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_locality_weight(
        self.raw,
        priority,
        locality_index,
      )
    }
  }
  fn get_member_update_host_address(&self, index: usize, is_added: bool) -> Option<String> {
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null(),
      length: 0,
    };
    let found = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_get_member_update_host_address(
        self.raw,
        index,
        is_added,
        &mut result,
      )
    };
    if found && !result.ptr.is_null() && result.length > 0 {
      Some(unsafe {
        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
          result.ptr as *const u8,
          result.length,
        ))
        .to_string()
      })
    } else {
      None
    }
  }
}
/// Implementation of [`EnvoyClusterMetrics`] that calls into the Envoy ABI.
pub struct EnvoyClusterMetricsImpl {
  raw: abi::envoy_dynamic_module_type_cluster_config_envoy_ptr,
}
// The raw pointer references C++ DynamicModuleClusterConfig which is safe for metric operations
// from any thread.
unsafe impl Send for EnvoyClusterMetricsImpl {}
unsafe impl Sync for EnvoyClusterMetricsImpl {}
fn cluster_metric_result_to_rust(
  res: abi::envoy_dynamic_module_type_metrics_result,
) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
  if res == abi::envoy_dynamic_module_type_metrics_result::Success {
    Ok(())
  } else {
    Err(res)
  }
}
impl EnvoyClusterMetrics for EnvoyClusterMetricsImpl {
  fn define_counter(
    &self,
    name: &str,
  ) -> Result<EnvoyCounterId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_counter(
        self.raw,
        str_to_module_buffer(name),
        std::ptr::null_mut(),
        0,
        &mut id,
      )
    })?;
    Ok(EnvoyCounterId(id))
  }
  fn define_counter_vec(
    &self,
    name: &str,
    labels: &[&str],
  ) -> Result<EnvoyCounterVecId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut label_bufs = strs_to_module_buffers(labels);
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_counter(
        self.raw,
        str_to_module_buffer(name),
        label_bufs.as_mut_ptr(),
        labels.len(),
        &mut id,
      )
    })?;
    Ok(EnvoyCounterVecId(id))
  }
  fn define_gauge(
    &self,
    name: &str,
  ) -> Result<EnvoyGaugeId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_gauge(
        self.raw,
        str_to_module_buffer(name),
        std::ptr::null_mut(),
        0,
        &mut id,
      )
    })?;
    Ok(EnvoyGaugeId(id))
  }
  fn define_gauge_vec(
    &self,
    name: &str,
    labels: &[&str],
  ) -> Result<EnvoyGaugeVecId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut label_bufs = strs_to_module_buffers(labels);
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_gauge(
        self.raw,
        str_to_module_buffer(name),
        label_bufs.as_mut_ptr(),
        labels.len(),
        &mut id,
      )
    })?;
    Ok(EnvoyGaugeVecId(id))
  }
  fn define_histogram(
    &self,
    name: &str,
  ) -> Result<EnvoyHistogramId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_histogram(
        self.raw,
        str_to_module_buffer(name),
        std::ptr::null_mut(),
        0,
        &mut id,
      )
    })?;
    Ok(EnvoyHistogramId(id))
  }
  fn define_histogram_vec(
    &self,
    name: &str,
    labels: &[&str],
  ) -> Result<EnvoyHistogramVecId, abi::envoy_dynamic_module_type_metrics_result> {
    let mut label_bufs = strs_to_module_buffers(labels);
    let mut id: usize = 0;
    Result::from(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_define_histogram(
        self.raw,
        str_to_module_buffer(name),
        label_bufs.as_mut_ptr(),
        labels.len(),
        &mut id,
      )
    })?;
    Ok(EnvoyHistogramVecId(id))
  }
  fn increment_counter(
    &self,
    id: EnvoyCounterId,
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyCounterId(id) = id;
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_increment_counter(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    })
  }
  fn increment_counter_vec(
    &self,
    id: EnvoyCounterVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyCounterVecId(id) = id;
    let mut label_bufs = strs_to_module_buffers(labels);
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_increment_counter(
        self.raw,
        id,
        label_bufs.as_mut_ptr(),
        labels.len(),
        value,
      )
    })
  }
  fn set_gauge(
    &self,
    id: EnvoyGaugeId,
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeId(id) = id;
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_set_gauge(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    })
  }
  fn set_gauge_vec(
    &self,
    id: EnvoyGaugeVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeVecId(id) = id;
    let mut label_bufs = strs_to_module_buffers(labels);
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_set_gauge(
        self.raw,
        id,
        label_bufs.as_mut_ptr(),
        labels.len(),
        value,
      )
    })
  }
  fn increase_gauge(
    &self,
    id: EnvoyGaugeId,
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeId(id) = id;
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_increment_gauge(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    })
  }
  fn increase_gauge_vec(
    &self,
    id: EnvoyGaugeVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeVecId(id) = id;
    let mut label_bufs = strs_to_module_buffers(labels);
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_increment_gauge(
        self.raw,
        id,
        label_bufs.as_mut_ptr(),
        labels.len(),
        value,
      )
    })
  }
  fn decrease_gauge(
    &self,
    id: EnvoyGaugeId,
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeId(id) = id;
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_decrement_gauge(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    })
  }
  fn decrease_gauge_vec(
    &self,
    id: EnvoyGaugeVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyGaugeVecId(id) = id;
    let mut label_bufs = strs_to_module_buffers(labels);
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_decrement_gauge(
        self.raw,
        id,
        label_bufs.as_mut_ptr(),
        labels.len(),
        value,
      )
    })
  }
  fn record_histogram_value(
    &self,
    id: EnvoyHistogramId,
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyHistogramId(id) = id;
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_record_histogram_value(
        self.raw,
        id,
        std::ptr::null_mut(),
        0,
        value,
      )
    })
  }
  fn record_histogram_value_vec(
    &self,
    id: EnvoyHistogramVecId,
    labels: &[&str],
    value: u64,
  ) -> Result<(), abi::envoy_dynamic_module_type_metrics_result> {
    let EnvoyHistogramVecId(id) = id;
    let mut label_bufs = strs_to_module_buffers(labels);
    cluster_metric_result_to_rust(unsafe {
      abi::envoy_dynamic_module_callback_cluster_config_record_histogram_value(
        self.raw,
        id,
        label_bufs.as_mut_ptr(),
        labels.len(),
        value,
      )
    })
  }
}
struct EnvoyAsyncHostSelectionCompleteImpl {
  raw_lb: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
  raw_context: abi::envoy_dynamic_module_type_cluster_lb_context_envoy_ptr,
}
unsafe impl Send for EnvoyAsyncHostSelectionCompleteImpl {}
impl EnvoyAsyncHostSelectionComplete for EnvoyAsyncHostSelectionCompleteImpl {
  fn async_host_selection_complete(
    &self,
    host: Option<abi::envoy_dynamic_module_type_cluster_host_envoy_ptr>,
    details: &str,
  ) {
    let host_ptr = host.unwrap_or(std::ptr::null_mut());
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_async_host_selection_complete(
        self.raw_lb,
        self.raw_context,
        host_ptr,
        str_to_module_buffer(details),
      );
    }
  }
}
struct ClusterLbContextImpl {
  raw_context: abi::envoy_dynamic_module_type_cluster_lb_context_envoy_ptr,
  raw_lb: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
}
impl ClusterLbContextImpl {
  fn new(
    raw_context: abi::envoy_dynamic_module_type_cluster_lb_context_envoy_ptr,
    raw_lb: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
  ) -> Self {
    Self {
      raw_context,
      raw_lb,
    }
  }
}
impl ClusterLbContext for ClusterLbContextImpl {
  fn compute_hash_key(&self) -> Option<u64> {
    let mut hash: u64 = 0;
    let ok = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_compute_hash_key(
        self.raw_context,
        &mut hash,
      )
    };
    if ok {
      Some(hash)
    } else {
      None
    }
  }
  fn get_downstream_headers_size(&self) -> usize {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_downstream_headers_size(
        self.raw_context,
      )
    }
  }
  fn get_downstream_headers(&self) -> Option<Vec<(String, String)>> {
    let size = self.get_downstream_headers_size();
    if size == 0 {
      return None;
    }
    let mut raw_headers = vec![
      abi::envoy_dynamic_module_type_envoy_http_header {
        key_ptr: std::ptr::null_mut(),
        key_length: 0,
        value_ptr: std::ptr::null_mut(),
        value_length: 0,
      };
      size
    ];
    let ok = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_downstream_headers(
        self.raw_context,
        raw_headers.as_mut_ptr(),
      )
    };
    if !ok {
      return None;
    }
    Some(
      raw_headers
        .iter()
        .map(|h| unsafe {
          let key = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
            h.key_ptr as *const u8,
            h.key_length,
          ));
          let value = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
            h.value_ptr as *const u8,
            h.value_length,
          ));
          (key.to_string(), value.to_string())
        })
        .collect(),
    )
  }
  fn get_downstream_header(&self, key: &str, index: usize) -> Option<(String, usize)> {
    let key_buf = str_to_module_buffer(key);
    let mut result_buffer = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null_mut(),
      length: 0,
    };
    let mut total_size: usize = 0;
    let ok = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_downstream_header(
        self.raw_context,
        key_buf,
        &mut result_buffer,
        index,
        &mut total_size,
      )
    };
    if !ok {
      return None;
    }
    let value = unsafe {
      std::str::from_utf8_unchecked(std::slice::from_raw_parts(
        result_buffer.ptr as *const u8,
        result_buffer.length,
      ))
    };
    Some((value.to_string(), total_size))
  }
  fn get_host_selection_retry_count(&self) -> u32 {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_host_selection_retry_count(
        self.raw_context,
      )
    }
  }
  fn should_select_another_host(&self, priority: u32, index: usize) -> bool {
    unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_should_select_another_host(
        self.raw_lb,
        self.raw_context,
        priority,
        index,
      )
    }
  }
  fn get_override_host(&self) -> Option<(String, bool)> {
    let mut address = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null_mut(),
      length: 0,
    };
    let mut strict = false;
    let ok = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_override_host(
        self.raw_context,
        &mut address,
        &mut strict,
      )
    };
    if !ok {
      return None;
    }
    let addr_str = unsafe {
      std::str::from_utf8_unchecked(std::slice::from_raw_parts(
        address.ptr as *const u8,
        address.length,
      ))
    };
    Some((addr_str.to_string(), strict))
  }
  fn get_downstream_connection_sni(&self) -> Option<String> {
    let mut result_buffer = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: std::ptr::null_mut(),
      length: 0,
    };
    let ok = unsafe {
      abi::envoy_dynamic_module_callback_cluster_lb_context_get_downstream_connection_sni(
        self.raw_context,
        &mut result_buffer,
      )
    };
    if !ok {
      return None;
    }
    let sni = unsafe {
      std::str::from_utf8_unchecked(std::slice::from_raw_parts(
        result_buffer.ptr as *const u8,
        result_buffer.length,
      ))
    };
    Some(sni.to_string())
  }
}
// Cluster Event Hook Implementations
/// # 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_cluster_config_new(
  config_envoy_ptr: abi::envoy_dynamic_module_type_cluster_config_envoy_ptr,
  name: abi::envoy_dynamic_module_type_envoy_buffer,
  config: abi::envoy_dynamic_module_type_envoy_buffer,
) -> abi::envoy_dynamic_module_type_cluster_config_module_ptr {
  // SAFETY: Envoy guarantees name and config are valid UTF-8 per the ABI contract.
  let name_str = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
    name.ptr as *const _,
    name.length,
  ));
  let config_slice = std::slice::from_raw_parts(config.ptr as *const _, config.length);
  let new_config_fn = NEW_CLUSTER_CONFIG_FUNCTION
    .get()
    .expect("NEW_CLUSTER_CONFIG_FUNCTION must be set");
  let envoy_cluster_metrics: Arc<dyn EnvoyClusterMetrics> = Arc::new(EnvoyClusterMetricsImpl {
    raw: config_envoy_ptr,
  });
  match new_config_fn(name_str, config_slice, envoy_cluster_metrics) {
    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_cluster_config_destroy(
  config_module_ptr: abi::envoy_dynamic_module_type_cluster_config_module_ptr,
) {
  drop_wrapped_c_void_ptr!(config_module_ptr, ClusterConfig);
}
/// # 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_cluster_new(
  config_module_ptr: abi::envoy_dynamic_module_type_cluster_config_module_ptr,
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
) -> abi::envoy_dynamic_module_type_cluster_module_ptr {
  let config = config_module_ptr as *const *const dyn ClusterConfig;
  let config = &**config;
  let envoy_cluster = EnvoyClusterImpl::new(cluster_envoy_ptr);
  let cluster = config.new_cluster(&envoy_cluster);
  wrap_into_c_void_ptr!(cluster)
}
/// # 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_cluster_init(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
) {
  let cluster = cluster_module_ptr as *mut Box<dyn Cluster>;
  let cluster = &mut *cluster;
  let envoy_cluster = EnvoyClusterImpl::new(cluster_envoy_ptr);
  cluster.on_init(&envoy_cluster);
}
/// # 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_cluster_destroy(
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
) {
  drop_wrapped_c_void_ptr!(cluster_module_ptr, Cluster);
}
/// Wrapper that pairs a module-side load balancer with the Envoy-side LB pointer.
/// The `lb_envoy_ptr` is needed by [`ClusterLbContextImpl::should_select_another_host`] to
/// resolve host pointers from the priority set.
struct ClusterLbWrapper {
  lb: Box<dyn ClusterLb>,
  lb_envoy_ptr: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
}
/// # 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_cluster_lb_new(
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
  lb_envoy_ptr: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
) -> abi::envoy_dynamic_module_type_cluster_lb_module_ptr {
  let cluster = cluster_module_ptr as *const *const dyn Cluster;
  let cluster = &**cluster;
  let envoy_lb = EnvoyClusterLoadBalancerImpl::new(lb_envoy_ptr);
  let lb = cluster.new_load_balancer(&envoy_lb);
  let wrapper = Box::new(ClusterLbWrapper { lb, lb_envoy_ptr });
  Box::into_raw(wrapper) as abi::envoy_dynamic_module_type_cluster_lb_module_ptr
}
/// # 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_cluster_lb_destroy(
  lb_module_ptr: abi::envoy_dynamic_module_type_cluster_lb_module_ptr,
) {
  let wrapper = lb_module_ptr as *mut ClusterLbWrapper;
  let _ = Box::from_raw(wrapper);
}
/// # 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_cluster_lb_choose_host(
  lb_module_ptr: abi::envoy_dynamic_module_type_cluster_lb_module_ptr,
  context_envoy_ptr: abi::envoy_dynamic_module_type_cluster_lb_context_envoy_ptr,
  host_out: *mut abi::envoy_dynamic_module_type_cluster_host_envoy_ptr,
  async_handle_out: *mut abi::envoy_dynamic_module_type_cluster_lb_async_handle_module_ptr,
) {
  let wrapper = &mut *(lb_module_ptr as *mut ClusterLbWrapper);
  let context = if context_envoy_ptr.is_null() {
    None
  } else {
    Some(ClusterLbContextImpl::new(
      context_envoy_ptr,
      wrapper.lb_envoy_ptr,
    ))
  };
  let async_completion = Box::new(EnvoyAsyncHostSelectionCompleteImpl {
    raw_lb: wrapper.lb_envoy_ptr,
    raw_context: context_envoy_ptr,
  });
  let result = wrapper.lb.choose_host(
    context.as_ref().map(|c| c as &dyn ClusterLbContext),
    async_completion,
  );
  match result {
    HostSelectionResult::Selected(host) => {
      *host_out = host;
      *async_handle_out = std::ptr::null_mut();
    },
    HostSelectionResult::NoHost => {
      *host_out = std::ptr::null_mut();
      *async_handle_out = std::ptr::null_mut();
    },
    HostSelectionResult::AsyncPending(handle) => {
      *host_out = std::ptr::null_mut();
      *async_handle_out = Box::into_raw(Box::new(handle))
        as abi::envoy_dynamic_module_type_cluster_lb_async_handle_module_ptr;
    },
  }
}
/// # 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_cluster_lb_cancel_host_selection(
  _lb_module_ptr: abi::envoy_dynamic_module_type_cluster_lb_module_ptr,
  async_handle_module_ptr: abi::envoy_dynamic_module_type_cluster_lb_async_handle_module_ptr,
) {
  let handle = async_handle_module_ptr as *mut Box<dyn AsyncHostSelectionHandle>;
  let mut handle = Box::from_raw(handle);
  handle.cancel();
}
/// # 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_cluster_lb_on_host_membership_update(
  lb_envoy_ptr: abi::envoy_dynamic_module_type_cluster_lb_envoy_ptr,
  lb_module_ptr: abi::envoy_dynamic_module_type_cluster_lb_module_ptr,
  num_hosts_added: usize,
  num_hosts_removed: usize,
) {
  let wrapper = &mut *(lb_module_ptr as *mut ClusterLbWrapper);
  let envoy_lb = EnvoyClusterLoadBalancerImpl::new(lb_envoy_ptr);
  wrapper
    .lb
    .on_host_membership_update(&envoy_lb, num_hosts_added, num_hosts_removed);
}
/// # 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_cluster_scheduled(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
  event_id: u64,
) {
  let cluster = cluster_module_ptr as *const *const dyn Cluster;
  let cluster = &**cluster;
  cluster.on_scheduled(&EnvoyClusterImpl::new(cluster_envoy_ptr), event_id);
}
/// # 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_cluster_server_initialized(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
) {
  let cluster = cluster_module_ptr as *mut Box<dyn Cluster>;
  let cluster = &mut *cluster;
  cluster.on_server_initialized(&EnvoyClusterImpl::new(cluster_envoy_ptr));
}
/// # 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_cluster_drain_started(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
) {
  let cluster = cluster_module_ptr as *mut Box<dyn Cluster>;
  let cluster = &mut *cluster;
  cluster.on_drain_started(&EnvoyClusterImpl::new(cluster_envoy_ptr));
}
/// # 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_cluster_shutdown(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_module_ptr,
  completion_callback: abi::envoy_dynamic_module_type_event_cb,
  completion_context: *mut std::os::raw::c_void,
) {
  let cluster = cluster_module_ptr as *mut Box<dyn Cluster>;
  let cluster = &mut *cluster;
  let completion = CompletionCallback::new(completion_callback, completion_context);
  cluster.on_shutdown(&EnvoyClusterImpl::new(cluster_envoy_ptr), completion);
}
/// # 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_cluster_http_callout_done(
  cluster_envoy_ptr: abi::envoy_dynamic_module_type_cluster_envoy_ptr,
  cluster_module_ptr: abi::envoy_dynamic_module_type_cluster_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 cluster = cluster_module_ptr as *mut Box<dyn Cluster>;
  let cluster = &mut *cluster;
  let headers = if headers_size > 0 {
    Some(std::slice::from_raw_parts(
      headers as *const (EnvoyBuffer, EnvoyBuffer),
      headers_size,
    ))
  } else {
    None
  };
  let body = if body_chunks_size > 0 {
    Some(std::slice::from_raw_parts(
      body_chunks as *const EnvoyBuffer,
      body_chunks_size,
    ))
  } else {
    None
  };
  cluster.on_http_callout_done(
    &EnvoyClusterImpl::new(cluster_envoy_ptr),
    callout_id,
    result,
    headers,
    body,
  );
}