1
use crate::{
2
  abi,
3
  bytes_to_module_buffer,
4
  drop_wrapped_c_void_ptr,
5
  str_to_module_buffer,
6
  wrap_into_c_void_ptr,
7
  NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION,
8
};
9
use mockall::*;
10

            
11
/// The module-side bridge configuration.
12
///
13
/// This trait must be implemented by the module to handle bridge configuration.
14
/// The object is created when the corresponding Envoy upstream config is loaded, and
15
/// it is dropped when the corresponding Envoy config is destroyed.
16
///
17
/// Implementations must be `Send + Sync` since they may be accessed from multiple threads.
18
pub trait UpstreamHttpTcpBridgeConfig: Send + Sync {
19
  /// Create a new per-request bridge instance.
20
  ///
21
  /// This is called for each HTTP request routed to a cluster using this bridge.
22
  fn new_bridge(
23
    &self,
24
    envoy_bridge: &dyn EnvoyUpstreamHttpTcpBridge,
25
  ) -> Box<dyn UpstreamHttpTcpBridge>;
26
}
27

            
28
/// The module-side per-request bridge instance.
29
///
30
/// This trait must be implemented by the module to handle HTTP-to-TCP protocol bridging.
31
/// One instance is created per HTTP request. The module controls the data flow by calling
32
/// explicit callbacks on `envoy_bridge` (send_upstream_data, send_response, etc.) rather
33
/// than returning status codes.
34
pub trait UpstreamHttpTcpBridge: Send {
35
  /// Called when HTTP request headers are being encoded for the upstream.
36
  ///
37
  /// The module can read request headers via `envoy_bridge` and use `send_upstream_data`
38
  /// or `send_response` to act on the request.
39
  fn on_encode_headers(
40
    &mut self,
41
    envoy_bridge: &dyn EnvoyUpstreamHttpTcpBridge,
42
    end_of_stream: bool,
43
  );
44

            
45
  /// Called when HTTP request body data is being encoded for the upstream.
46
  ///
47
  /// The module can read the current request body data via `envoy_bridge.get_request_buffer()`
48
  /// and use `send_upstream_data` to forward data to the TCP upstream.
49
  fn on_encode_data(&mut self, envoy_bridge: &dyn EnvoyUpstreamHttpTcpBridge, end_of_stream: bool);
50

            
51
  /// Called when HTTP request trailers are being encoded for the upstream.
52
  fn on_encode_trailers(&mut self, envoy_bridge: &dyn EnvoyUpstreamHttpTcpBridge);
53

            
54
  /// Called when raw TCP data is received from the upstream connection.
55
  ///
56
  /// The module should read the TCP data via `envoy_bridge.get_response_buffer()`,
57
  /// process it, and send the HTTP response using `send_response_headers`,
58
  /// `send_response_data`, and `send_response_trailers` on `envoy_bridge`.
59
  fn on_upstream_data(
60
    &mut self,
61
    envoy_bridge: &dyn EnvoyUpstreamHttpTcpBridge,
62
    end_of_stream: bool,
63
  );
64
}
65

            
66
/// Envoy-side bridge operations available to the module.
67
#[automock]
68
#[allow(clippy::needless_lifetimes)]
69
pub trait EnvoyUpstreamHttpTcpBridge: Send {
70
  /// Get a request header value by key at the given index.
71
  ///
72
  /// Returns the header value and the total count of values for the key.
73
  fn get_request_header_value(&self, key: &str, index: usize) -> (Option<Vec<u8>>, usize);
74

            
75
  /// Get the number of request headers.
76
  fn get_request_headers_size(&self) -> usize;
77

            
78
  /// Get all request headers as key-value pairs.
79
  fn get_request_headers(&self) -> Vec<(Vec<u8>, Vec<u8>)>;
80

            
81
  /// Get the current request buffer contents as a contiguous byte vector.
82
  fn get_request_buffer(&self) -> Vec<u8>;
83

            
84
  /// Get the raw TCP response data from the upstream as a contiguous byte vector.
85
  fn get_response_buffer(&self) -> Vec<u8>;
86

            
87
  /// Send transformed data to the TCP upstream connection.
88
  fn send_upstream_data(&self, data: &[u8], end_stream: bool);
89

            
90
  /// Send a complete local response to the downstream client, ending the stream.
91
  fn send_response<'a>(&self, status_code: u32, headers: &'a [(&'a str, &'a [u8])], body: &[u8]);
92

            
93
  /// Send response headers to the downstream client, optionally ending the stream.
94
  fn send_response_headers<'a>(
95
    &self,
96
    status_code: u32,
97
    headers: &'a [(&'a str, &'a [u8])],
98
    end_stream: bool,
99
  );
100

            
101
  /// Send response body data to the downstream client, optionally ending the stream.
102
  fn send_response_data(&self, data: &[u8], end_stream: bool);
103

            
104
  /// Send response trailers to the downstream client, ending the stream.
105
  fn send_response_trailers<'a>(&self, trailers: &'a [(&'a str, &'a [u8])]);
106
}
107

            
108
const MAX_BUFFER_SLICES: usize = 64;
109

            
110
struct EnvoyUpstreamHttpTcpBridgeImpl {
111
  raw: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
112
}
113

            
114
unsafe impl Send for EnvoyUpstreamHttpTcpBridgeImpl {}
115

            
116
impl EnvoyUpstreamHttpTcpBridgeImpl {
117
  fn new(raw: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr) -> Self {
118
    Self { raw }
119
  }
120

            
121
  fn read_buffer_slices(
122
    &self,
123
    getter: unsafe extern "C" fn(
124
      abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
125
      *mut abi::envoy_dynamic_module_type_envoy_buffer,
126
      *mut usize,
127
    ),
128
  ) -> Vec<u8> {
129
    let mut buffers: Vec<abi::envoy_dynamic_module_type_envoy_buffer> = vec![
130
      abi::envoy_dynamic_module_type_envoy_buffer {
131
        ptr: std::ptr::null_mut(),
132
        length: 0,
133
      };
134
      MAX_BUFFER_SLICES
135
    ];
136
    let mut num_slices: usize = 0;
137
    unsafe {
138
      getter(self.raw, buffers.as_mut_ptr(), &mut num_slices);
139
    }
140
    if num_slices == 0 {
141
      return Vec::new();
142
    }
143
    let mut result = Vec::new();
144
    for buf in buffers.iter().take(num_slices) {
145
      if !buf.ptr.is_null() && buf.length > 0 {
146
        let slice = unsafe { std::slice::from_raw_parts(buf.ptr as *const u8, buf.length) };
147
        result.extend_from_slice(slice);
148
      }
149
    }
150
    result
151
  }
152

            
153
  fn build_module_headers(
154
    headers: &[(&str, &[u8])],
155
  ) -> Vec<abi::envoy_dynamic_module_type_module_http_header> {
156
    headers
157
      .iter()
158
      .map(|(k, v)| abi::envoy_dynamic_module_type_module_http_header {
159
        key_ptr: k.as_ptr() as *const _,
160
        key_length: k.len(),
161
        value_ptr: v.as_ptr() as *const _,
162
        value_length: v.len(),
163
      })
164
      .collect()
165
  }
166
}
167

            
168
impl EnvoyUpstreamHttpTcpBridge for EnvoyUpstreamHttpTcpBridgeImpl {
169
  fn get_request_header_value(&self, key: &str, index: usize) -> (Option<Vec<u8>>, usize) {
170
    let key_buf = str_to_module_buffer(key);
171
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
172
      ptr: std::ptr::null_mut(),
173
      length: 0,
174
    };
175
    let mut total_count: usize = 0;
176
    let found = unsafe {
177
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_get_request_header(
178
        self.raw,
179
        key_buf,
180
        &mut result,
181
        index,
182
        &mut total_count,
183
      )
184
    };
185
    if found && !result.ptr.is_null() {
186
      let slice = unsafe { std::slice::from_raw_parts(result.ptr as *const u8, result.length) };
187
      (Some(slice.to_vec()), total_count)
188
    } else {
189
      (None, total_count)
190
    }
191
  }
192

            
193
  fn get_request_headers_size(&self) -> usize {
194
    unsafe {
195
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_get_request_headers_size(self.raw)
196
    }
197
  }
198

            
199
  fn get_request_headers(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
200
    let size = self.get_request_headers_size();
201
    if size == 0 {
202
      return Vec::new();
203
    }
204
    let mut headers: Vec<abi::envoy_dynamic_module_type_envoy_http_header> = vec![
205
      abi::envoy_dynamic_module_type_envoy_http_header {
206
        key_ptr: std::ptr::null_mut(),
207
        key_length: 0,
208
        value_ptr: std::ptr::null_mut(),
209
        value_length: 0,
210
      };
211
      size
212
    ];
213
    let ok = unsafe {
214
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_get_request_headers(
215
        self.raw,
216
        headers.as_mut_ptr(),
217
      )
218
    };
219
    if !ok {
220
      return Vec::new();
221
    }
222
    headers
223
      .iter()
224
      .map(|h| unsafe {
225
        (
226
          std::slice::from_raw_parts(h.key_ptr as *const u8, h.key_length).to_vec(),
227
          std::slice::from_raw_parts(h.value_ptr as *const u8, h.value_length).to_vec(),
228
        )
229
      })
230
      .collect()
231
  }
232

            
233
  fn get_request_buffer(&self) -> Vec<u8> {
234
    self.read_buffer_slices(
235
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_get_request_buffer,
236
    )
237
  }
238

            
239
  fn get_response_buffer(&self) -> Vec<u8> {
240
    self.read_buffer_slices(
241
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_get_response_buffer,
242
    )
243
  }
244

            
245
  fn send_upstream_data(&self, data: &[u8], end_stream: bool) {
246
    let buf = bytes_to_module_buffer(data);
247
    unsafe {
248
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_send_upstream_data(
249
        self.raw, buf, end_stream,
250
      );
251
    }
252
  }
253

            
254
  fn send_response(&self, status_code: u32, headers: &[(&str, &[u8])], body: &[u8]) {
255
    let mut header_vec = Self::build_module_headers(headers);
256
    let body_buf = bytes_to_module_buffer(body);
257
    let headers_ptr = if header_vec.is_empty() {
258
      std::ptr::null_mut()
259
    } else {
260
      header_vec.as_mut_ptr()
261
    };
262
    unsafe {
263
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_send_response(
264
        self.raw,
265
        status_code,
266
        headers_ptr,
267
        header_vec.len(),
268
        body_buf,
269
      );
270
    }
271
  }
272

            
273
  fn send_response_headers(&self, status_code: u32, headers: &[(&str, &[u8])], end_stream: bool) {
274
    let mut header_vec = Self::build_module_headers(headers);
275
    let headers_ptr = if header_vec.is_empty() {
276
      std::ptr::null_mut()
277
    } else {
278
      header_vec.as_mut_ptr()
279
    };
280
    unsafe {
281
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_send_response_headers(
282
        self.raw,
283
        status_code,
284
        headers_ptr,
285
        header_vec.len(),
286
        end_stream,
287
      );
288
    }
289
  }
290

            
291
  fn send_response_data(&self, data: &[u8], end_stream: bool) {
292
    let buf = bytes_to_module_buffer(data);
293
    unsafe {
294
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_send_response_data(
295
        self.raw, buf, end_stream,
296
      );
297
    }
298
  }
299

            
300
  fn send_response_trailers(&self, trailers: &[(&str, &[u8])]) {
301
    let mut trailer_vec = Self::build_module_headers(trailers);
302
    let trailers_ptr = if trailer_vec.is_empty() {
303
      std::ptr::null_mut()
304
    } else {
305
      trailer_vec.as_mut_ptr()
306
    };
307
    unsafe {
308
      abi::envoy_dynamic_module_callback_upstream_http_tcp_bridge_send_response_trailers(
309
        self.raw,
310
        trailers_ptr,
311
        trailer_vec.len(),
312
      );
313
    }
314
  }
315
}
316

            
317
// Upstream HTTP TCP Bridge Event Hook Implementations
318

            
319
#[no_mangle]
320
pub extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_config_new(
321
  _config_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_config_envoy_ptr,
322
  name: abi::envoy_dynamic_module_type_envoy_buffer,
323
  config: abi::envoy_dynamic_module_type_envoy_buffer,
324
) -> abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_config_module_ptr {
325
  let name_str = unsafe {
326
    std::str::from_utf8_unchecked(std::slice::from_raw_parts(
327
      name.ptr as *const _,
328
      name.length,
329
    ))
330
  };
331
  let config_slice = unsafe { std::slice::from_raw_parts(config.ptr as *const _, config.length) };
332
  let new_config_fn = NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION
333
    .get()
334
    .expect("NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION must be set");
335
  match new_config_fn(name_str, config_slice) {
336
    Some(config) => wrap_into_c_void_ptr!(config),
337
    None => std::ptr::null(),
338
  }
339
}
340

            
341
#[no_mangle]
342
unsafe extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_config_destroy(
343
  config_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_config_module_ptr,
344
) {
345
  drop_wrapped_c_void_ptr!(config_module_ptr, UpstreamHttpTcpBridgeConfig);
346
}
347

            
348
#[no_mangle]
349
unsafe extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_new(
350
  config_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_config_module_ptr,
351
  bridge_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
352
) -> abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr {
353
  let config = config_module_ptr as *const *const dyn UpstreamHttpTcpBridgeConfig;
354
  let config = &**config;
355
  let envoy_bridge = EnvoyUpstreamHttpTcpBridgeImpl::new(bridge_envoy_ptr);
356
  let bridge = config.new_bridge(&envoy_bridge);
357
  wrap_into_c_void_ptr!(bridge)
358
}
359

            
360
#[no_mangle]
361
pub extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_encode_headers(
362
  bridge_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
363
  bridge_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr,
364
  end_of_stream: bool,
365
) {
366
  let bridge = bridge_module_ptr as *mut Box<dyn UpstreamHttpTcpBridge>;
367
  let bridge = unsafe { &mut *bridge };
368
  let envoy_bridge = EnvoyUpstreamHttpTcpBridgeImpl::new(bridge_envoy_ptr);
369
  bridge.on_encode_headers(&envoy_bridge, end_of_stream);
370
}
371

            
372
#[no_mangle]
373
pub extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_encode_data(
374
  bridge_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
375
  bridge_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr,
376
  end_of_stream: bool,
377
) {
378
  let bridge = bridge_module_ptr as *mut Box<dyn UpstreamHttpTcpBridge>;
379
  let bridge = unsafe { &mut *bridge };
380
  let envoy_bridge = EnvoyUpstreamHttpTcpBridgeImpl::new(bridge_envoy_ptr);
381
  bridge.on_encode_data(&envoy_bridge, end_of_stream);
382
}
383

            
384
#[no_mangle]
385
pub extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_encode_trailers(
386
  bridge_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
387
  bridge_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr,
388
) {
389
  let bridge = bridge_module_ptr as *mut Box<dyn UpstreamHttpTcpBridge>;
390
  let bridge = unsafe { &mut *bridge };
391
  let envoy_bridge = EnvoyUpstreamHttpTcpBridgeImpl::new(bridge_envoy_ptr);
392
  bridge.on_encode_trailers(&envoy_bridge);
393
}
394

            
395
#[no_mangle]
396
pub extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_on_upstream_data(
397
  bridge_envoy_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_envoy_ptr,
398
  bridge_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr,
399
  end_of_stream: bool,
400
) {
401
  let bridge = bridge_module_ptr as *mut Box<dyn UpstreamHttpTcpBridge>;
402
  let bridge = unsafe { &mut *bridge };
403
  let envoy_bridge = EnvoyUpstreamHttpTcpBridgeImpl::new(bridge_envoy_ptr);
404
  bridge.on_upstream_data(&envoy_bridge, end_of_stream);
405
}
406

            
407
#[no_mangle]
408
unsafe extern "C" fn envoy_dynamic_module_on_upstream_http_tcp_bridge_destroy(
409
  bridge_module_ptr: abi::envoy_dynamic_module_type_upstream_http_tcp_bridge_module_ptr,
410
) {
411
  drop_wrapped_c_void_ptr!(bridge_module_ptr, UpstreamHttpTcpBridge);
412
}
413

            
414
/// Declare the init functions for the upstream HTTP TCP bridge dynamic module.
415
///
416
/// The first argument is the program init function with [`ProgramInitFunction`] type.
417
/// The second argument is the factory function with [`NewUpstreamHttpTcpBridgeConfigFunction`]
418
/// type.
419
#[macro_export]
420
macro_rules! declare_upstream_http_tcp_bridge_init_functions {
421
  ($f:ident, $new_bridge_config_fn:expr) => {
422
    #[no_mangle]
423
    pub extern "C" fn envoy_dynamic_module_on_program_init() -> *const ::std::os::raw::c_char {
424
      envoy_proxy_dynamic_modules_rust_sdk::NEW_UPSTREAM_HTTP_TCP_BRIDGE_CONFIG_FUNCTION
425
        .get_or_init(|| $new_bridge_config_fn);
426
      if ($f()) {
427
        envoy_proxy_dynamic_modules_rust_sdk::abi::envoy_dynamic_modules_abi_version.as_ptr()
428
          as *const ::std::os::raw::c_char
429
      } else {
430
        ::std::ptr::null()
431
      }
432
    }
433
  };
434
}