envoy_dynamic_modules_rust_sdk/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(dead_code)]
5
6use std::ptr;
7
8mod abi {
9    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
10}
11
12/// Define the init function for the module.
13/// This macro should be used in the root of the module.
14///
15/// ## Arguments
16///
17/// * `$new_filter_fn` - The function that creates a new HttpFilter object: `fn(&str) -> Box<dyn HttpFilter>`.
18///     This function is called for each new filter chain configuration and should return a new HttpFilter object
19///     based on the configuration string.
20///
21/// ## Example
22///
23/// ```
24/// use envoy_dynamic_modules_rust_sdk::*;
25///
26/// struct HelloWorldFilter {}
27/// struct HelloWorldFilterInstance {}
28///
29/// impl HttpFilter for HelloWorldFilter {
30///    fn new_instance(&mut self, _envoy_filter_instance: EnvoyFilterInstance) -> Box<dyn HttpFilterInstance> {
31///       Box::new(HelloWorldFilterInstance {})
32///   }
33/// }
34///
35/// impl HttpFilterInstance for HelloWorldFilterInstance {}
36///
37/// fn new_http_filter(config: &str) -> Box<dyn HttpFilter> {
38///    match config {
39///       "helloworld" => Box::new(HelloWorldFilter {}),
40///      _ => panic!("Unknown config: {}", config),
41///    }
42/// }
43/// init!(new_http_filter);
44///
45// TODO: eliminating the entire use of dyn keyword is possible. One idea is to take types as arguments to the macro,
46// and then generates the enum and dispatching logic accordingly. That way, we can also eliminate the double
47// boxing in raw pointer passing.
48#[macro_export]
49macro_rules! init {
50    ($new_filter_fn:expr) => {
51        #[no_mangle]
52        pub extern "C" fn envoy_dynamic_module_event_program_init() -> usize {
53            unsafe {
54                envoy_dynamic_modules_rust_sdk::NEW_HTTP_FILTER_FN = $new_filter_fn;
55            }
56            0
57        }
58    };
59}
60
61pub static mut NEW_HTTP_FILTER_FN: fn(&str) -> Box<dyn HttpFilter> = |_: &str| {
62    panic!("NEW_HTTP_FILTER_FN is not set");
63};
64
65#[no_mangle]
66unsafe extern "C" fn envoy_dynamic_module_event_http_filter_init(
67    config_ptr: abi::envoy_dynamic_module_type_HttpFilterConfigPtr,
68    config_size: abi::envoy_dynamic_module_type_HttpFilterConfigSize,
69) -> abi::envoy_dynamic_module_type_HttpFilterPtr {
70    // Convert the raw pointer to the str.
71    let config = {
72        let slice = std::slice::from_raw_parts(config_ptr as *const u8, config_size);
73        std::str::from_utf8(slice).unwrap()
74    };
75
76    let boxed_filter = Box::into_raw(NEW_HTTP_FILTER_FN(config));
77    let boxed_filter_ptr = Box::into_raw(Box::new(boxed_filter));
78    boxed_filter_ptr as abi::envoy_dynamic_module_type_HttpFilterPtr
79}
80
81#[no_mangle]
82unsafe extern "C" fn envoy_dynamic_module_event_http_filter_destroy(
83    http_filter: abi::envoy_dynamic_module_type_HttpFilterPtr,
84) {
85    let http_filter = http_filter as *mut *mut dyn HttpFilter;
86    (**http_filter).destroy();
87
88    // Drop the Box<dyn HttpFilter> and the Box<*mut dyn HttpFilter>
89    let _outer = Box::from_raw(http_filter);
90    let _inner = Box::from_raw(*http_filter);
91}
92
93#[no_mangle]
94unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_init(
95    envoy_filter_instance_ptr: abi::envoy_dynamic_module_type_EnvoyFilterInstancePtr,
96    http_filter: abi::envoy_dynamic_module_type_HttpFilterPtr,
97) -> abi::envoy_dynamic_module_type_HttpFilterInstancePtr {
98    let http_filter = http_filter as *mut *mut dyn HttpFilter;
99
100    let instance = {
101        let instance_boxed = (**http_filter).new_instance(EnvoyFilterInstance {
102            raw_addr: envoy_filter_instance_ptr,
103        });
104        Box::into_raw(instance_boxed)
105    };
106
107    let http_filter_instance = Box::into_raw(Box::new(instance));
108    http_filter_instance as abi::envoy_dynamic_module_type_HttpFilterInstancePtr
109}
110
111#[no_mangle]
112unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_request_headers(
113    http_filter_instance: abi::envoy_dynamic_module_type_HttpFilterInstancePtr,
114    request_headers_ptr: abi::envoy_dynamic_module_type_HttpRequestHeadersMapPtr,
115    end_of_stream: abi::envoy_dynamic_module_type_EndOfStream,
116) -> abi::envoy_dynamic_module_type_EventHttpRequestHeadersStatus {
117    let http_filter_instance = http_filter_instance as *mut *mut dyn HttpFilterInstance;
118    let http_filter_instance = &mut **http_filter_instance;
119    http_filter_instance
120        .request_headers(
121            &RequestHeaders {
122                raw: request_headers_ptr,
123            },
124            end_of_stream == 1,
125        )
126        .into()
127}
128
129#[no_mangle]
130unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_request_body(
131    http_filter_instance: abi::envoy_dynamic_module_type_HttpFilterInstancePtr,
132    buffer: abi::envoy_dynamic_module_type_HttpRequestBodyBufferPtr,
133    end_of_stream: abi::envoy_dynamic_module_type_EndOfStream,
134) -> abi::envoy_dynamic_module_type_EventHttpRequestBodyStatus {
135    let http_filter_instance = http_filter_instance as *mut *mut dyn HttpFilterInstance;
136    let http_filter_instance = &mut **http_filter_instance;
137    http_filter_instance
138        .request_body(&RequestBodyBuffer { raw: buffer }, end_of_stream == 1)
139        .into()
140}
141
142#[no_mangle]
143unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_response_headers(
144    http_filter_instance: abi::envoy_dynamic_module_type_HttpFilterInstancePtr,
145    response_headers_map_ptr: abi::envoy_dynamic_module_type_HttpResponseHeaderMapPtr,
146    end_of_stream: abi::envoy_dynamic_module_type_EndOfStream,
147) -> abi::envoy_dynamic_module_type_EventHttpResponseHeadersStatus {
148    let http_filter_instance = http_filter_instance as *mut *mut dyn HttpFilterInstance;
149    let http_filter_instance = &mut **http_filter_instance;
150    http_filter_instance
151        .response_headers(
152            &ResponseHeaders {
153                raw: response_headers_map_ptr,
154            },
155            end_of_stream == 1,
156        )
157        .into()
158}
159
160#[no_mangle]
161unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_response_body(
162    http_filter_instance: abi::envoy_dynamic_module_type_HttpFilterInstancePtr,
163    buffer: abi::envoy_dynamic_module_type_HttpResponseBodyBufferPtr,
164    end_of_stream: abi::envoy_dynamic_module_type_EndOfStream,
165) -> abi::envoy_dynamic_module_type_EventHttpResponseBodyStatus {
166    let http_filter_instance = http_filter_instance as *mut *mut dyn HttpFilterInstance;
167    let http_filter_instance = &mut **http_filter_instance;
168    http_filter_instance
169        .response_body(&ResponseBodyBuffer { raw: buffer }, end_of_stream == 1)
170        .into()
171}
172
173#[no_mangle]
174unsafe extern "C" fn envoy_dynamic_module_event_http_filter_instance_destroy(
175    http_filter_instance: abi::envoy_dynamic_module_type_HttpFilterInstancePtr,
176) {
177    let http_filter_instance = http_filter_instance as *mut *mut dyn HttpFilterInstance;
178    (**http_filter_instance).destroy();
179
180    let _outer = Box::from_raw(http_filter_instance);
181    let _inner = Box::from_raw(&mut **http_filter_instance);
182}
183
184/// A trait that represents a single HTTP filter in the Envoy filter chain.
185/// It is used to create HttpFilterInstance(s) that correspond to each HTTP request.
186///
187/// This is only created once per module instance via the new_http_filter function.
188pub trait HttpFilter {
189    /// This is called for each new HTTP request. This should return a new HttpFilterInstance object to handle the request.
190    ///
191    /// Note that this must be concurrency-safe as it can be called concurrently for multiple requests.
192    ///
193    /// * `envoy_filter_instance` is the Envoy filter object that is used to interact with the underlying Envoy filter.
194    ///   This object is unique for each HTTP request. The object is destroyed when the stream is destroyed.
195    ///   Therefore, after event_http_destroy is called, the methods on this object become no-op.
196    fn new_instance(
197        &mut self,
198        envoy_filter_instance: EnvoyFilterInstance,
199    ) -> Box<dyn HttpFilterInstance>;
200
201    /// destroy is called when this filter is destroyed. E.g. the filter chain configuration is updated and removed from the Envoy.
202    ///
203    /// After this returns, the filter object is destructed.
204    fn destroy(&self) {}
205}
206
207/// HttpFilterInstance is a trait that represents each HTTP request.
208///
209/// This is created for each new HTTP request and is destroyed when the request is completed.
210pub trait HttpFilterInstance {
211    /// This is called when request headers are received.
212    /// The function should return the status of the operation.
213    ///
214    /// * `request_headers` is the reference to the request headers map.
215    /// * `end_of_stream` is a boolean that indicates if this is the headers-only request.
216    fn request_headers(
217        &mut self,
218        _request_headers: &RequestHeaders,
219        _end_of_stream: bool,
220    ) -> RequestHeadersStatus {
221        RequestHeadersStatus::Continue
222    }
223
224    /// This is called when request body data is received.
225    /// The function should return the status of the operation.
226    ///
227    /// * `_request_body_frame` is the reference to the newly arrived request body frame.
228    /// * `end_of_stream` is a boolean that indicates if this is the last data frame.
229    fn request_body(
230        &mut self,
231        _request_body_frame: &RequestBodyBuffer,
232        _end_of_stream: bool,
233    ) -> RequestBodyStatus {
234        RequestBodyStatus::Continue
235    }
236
237    /// This is called when response headers are received.
238    /// The function should return the status of the operation.
239    ///
240    /// * `response_headers` is the reference to the response headers map.
241    /// * `end_of_stream` is a boolean that indicates if this is the headers-only response.
242    fn response_headers(
243        &mut self,
244        _response_headers: &ResponseHeaders,
245        _end_of_stream: bool,
246    ) -> ResponseHeadersStatus {
247        ResponseHeadersStatus::Continue
248    }
249
250    /// This is called when response body data is received.
251    /// The function should return the status of the operation.
252    ///
253    /// * `_response_body_frame` is the reference to the newly arrived response body frame.
254    /// * `end_of_stream` is a boolean that indicates if this is the last data frame.
255    fn response_body(
256        &mut self,
257        _response_body_frame: &ResponseBodyBuffer,
258        _end_of_stream: bool,
259    ) -> ResponseBodyStatus {
260        ResponseBodyStatus::Continue
261    }
262
263    /// This is called when the stream is completed or when the stream is reset.
264    ///
265    /// After this returns, this object is destructed.
266    fn destroy(&mut self) {}
267}
268
269/// An opaque object that represents the underlying Envoy Http filter instance.
270/// This is used to interact with it from the module code.
271///
272/// This is a shallow wrapper around the raw pointer to the Envoy filter instance.
273/// Can be copied and stored somewhere else. However, the object MUST NOT be used after the
274/// [`HttpFilterInstance::destroy`] for the corresponding filter instance is called.
275///
276#[derive(Debug, Clone, Copy)]
277pub struct EnvoyFilterInstance {
278    raw_addr: abi::envoy_dynamic_module_type_EnvoyFilterInstancePtr,
279}
280
281impl EnvoyFilterInstance {
282    /// Used to resume the request processing after the filter has stopped it.
283    pub fn continue_request(&self) {
284        unsafe { abi::envoy_dynamic_module_http_continue_request(self.raw_addr) }
285    }
286
287    /// Used to resume the response processing after the filter has stopped it.
288    pub fn continue_response(&self) {
289        unsafe { abi::envoy_dynamic_module_http_continue_response(self.raw_addr) }
290    }
291
292    /// Returns the entire request body buffer.
293    pub fn get_request_body_buffer(&self) -> RequestBodyBuffer {
294        let buffer =
295            unsafe { abi::envoy_dynamic_module_http_get_request_body_buffer(self.raw_addr) };
296        RequestBodyBuffer { raw: buffer }
297    }
298
299    /// Returns the entire request body buffer.
300    pub fn get_response_body_buffer(&self) -> ResponseBodyBuffer {
301        let buffer =
302            unsafe { abi::envoy_dynamic_module_http_get_response_body_buffer(self.raw_addr) };
303        ResponseBodyBuffer { raw: buffer }
304    }
305
306    /// Sends the response to the downstream.
307    ///
308    /// * `status_code` is the HTTP status code.
309    /// * `headers` is the list of headers. Each header is a tuple of key and value.
310    /// * `body` is the response body.
311    pub fn send_response(&self, status_code: u32, headers: &[(&[u8], &[u8])], body: &[u8]) {
312        let headers_ptr = if headers.is_empty() {
313            ptr::null()
314        } else {
315            &headers[0] as *const _ as *const u8
316        };
317        let headers_size = headers.len();
318        let body_ptr = body.as_ptr();
319        let body_size = body.len();
320        unsafe {
321            abi::envoy_dynamic_module_http_send_response(
322                self.raw_addr,
323                status_code,
324                headers_ptr as usize,
325                headers_size,
326                body_ptr as usize,
327                body_size,
328            )
329        }
330    }
331}
332
333/// An opaque object that represents the underlying Envoy Http request headers map.
334/// This is used to interact with it from the module code.
335///
336/// This is a shallow wrapper around the raw pointer to the Envoy request headers map.
337/// However, the object MUST NOT be used after the [`HttpFilterInstance::request_headers`].
338///
339#[derive(Debug, Clone, Copy)]
340pub struct RequestHeaders {
341    raw: abi::envoy_dynamic_module_type_HttpRequestHeadersMapPtr,
342}
343
344impl RequestHeaders {
345    /// Returns the first header value for the given key. To handle multiple values, use the [`RequestHeaders::values`] method.
346    pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
347        let key_ptr = key.as_ptr();
348        let key_size = key.len();
349
350        let mut result_ptr: *const u8 = ptr::null();
351        let mut result_size: usize = 0;
352
353        let total = unsafe {
354            abi::envoy_dynamic_module_http_get_request_header_value(
355                self.raw,
356                key_ptr as *const _ as usize,
357                key_size,
358                &mut result_ptr as *mut _ as usize,
359                &mut result_size as *mut _ as usize,
360            )
361        };
362
363        if total == 0 {
364            return None;
365        }
366
367        let result_slice = unsafe { std::slice::from_raw_parts(result_ptr, result_size) };
368        Some(result_slice)
369    }
370
371    /// Returns all the header values for the given key.
372    pub fn values(&self, key: &[u8]) -> Vec<&[u8]> {
373        let key_ptr = key.as_ptr();
374        let key_size = key.len();
375
376        let mut result_ptr: *const u8 = ptr::null();
377        let mut result_size: usize = 0;
378
379        let mut values = Vec::new();
380        let total = unsafe {
381            abi::envoy_dynamic_module_http_get_request_header_value(
382                self.raw,
383                key_ptr as *const _ as usize,
384                key_size,
385                &mut result_ptr as *mut _ as usize,
386                &mut result_size as *mut _ as usize,
387            )
388        };
389
390        if total == 0 {
391            return values;
392        }
393
394        values = Vec::with_capacity(total);
395        values.push(unsafe { std::slice::from_raw_parts(result_ptr, result_size) });
396
397        for i in 1..total {
398            unsafe {
399                abi::envoy_dynamic_module_http_get_request_header_value_nth(
400                    self.raw,
401                    key_ptr as *const _ as usize,
402                    key_size,
403                    &mut result_ptr as *mut _ as usize,
404                    &mut result_size as *mut _ as usize,
405                    i,
406                );
407            }
408            values.push(unsafe { std::slice::from_raw_parts(result_ptr, result_size) });
409        }
410
411        values
412    }
413
414    /// Sets the value for the given key. If multiple values are set for the same key,
415    /// this removes all the previous values and sets the new single value.
416    pub fn set(&self, key: &[u8], value: &[u8]) {
417        let key_ptr = key.as_ptr();
418        let key_size = key.len();
419        let value_ptr = value.as_ptr();
420        let value_size = value.len();
421
422        unsafe {
423            abi::envoy_dynamic_module_http_set_request_header(
424                self.raw,
425                key_ptr as *const _ as usize,
426                key_size,
427                value_ptr as *const _ as usize,
428                value_size,
429            )
430        }
431    }
432
433    /// Removes the value for the given key. If multiple values are set for the same key,
434    /// this removes all the values.
435    pub fn remove(&self, key: &[u8]) {
436        let key_ptr = key.as_ptr();
437        let key_size = key.len();
438
439        unsafe {
440            abi::envoy_dynamic_module_http_set_request_header(
441                self.raw,
442                key_ptr as *const _ as usize,
443                key_size,
444                0,
445                0,
446            )
447        }
448    }
449}
450
451/// An opaque object that represents the underlying Envoy Http request body buffer.
452/// This is used to interact with it from the module code. The buffer consists of multiple slices.
453/// Each slice is a contiguous memory region.
454///
455/// This corresponds to either a frame of the request body or the whole body.
456///
457/// This is a shallow wrapper around the raw pointer to the Envoy request body buffer.
458///
459/// TODO: implement the `std::io::Read` trait for this object.
460#[derive(Debug, Clone, Copy)]
461pub struct RequestBodyBuffer {
462    raw: abi::envoy_dynamic_module_type_HttpRequestBodyBufferPtr,
463}
464
465impl RequestBodyBuffer {
466    /// Returns the number of bytes in the buffer.
467    pub fn length(&self) -> usize {
468        unsafe { abi::envoy_dynamic_module_http_get_request_body_buffer_length(self.raw) }
469    }
470
471    /// Returns the number of slices in the buffer.
472    pub fn slices_count(&self) -> usize {
473        unsafe { abi::envoy_dynamic_module_http_get_request_body_buffer_slices_count(self.raw) }
474    }
475
476    /// Returns the slices of the buffer.
477    /// The slices are the contiguous memory regions that represent the buffer.
478    pub fn slices(&self) -> Vec<&mut [u8]> {
479        let mut slices = Vec::new();
480        let slices_count = self.slices_count();
481        for i in 0..slices_count {
482            let mut slice_ptr: *mut u8 = ptr::null_mut();
483            let mut slice_size: usize = 0;
484            unsafe {
485                abi::envoy_dynamic_module_http_get_request_body_buffer_slice(
486                    self.raw,
487                    i,
488                    &mut slice_ptr as *mut _ as usize,
489                    &mut slice_size as *mut _ as usize,
490                );
491            }
492            slices.push(unsafe { std::slice::from_raw_parts_mut(slice_ptr, slice_size) });
493        }
494        slices
495    }
496
497    fn slice_at(&self, index: usize) -> Option<&mut [u8]> {
498        if index >= self.slices_count() {
499            return None;
500        }
501
502        let mut slice_ptr: *mut u8 = ptr::null_mut();
503        let mut slice_size: usize = 0;
504        unsafe {
505            abi::envoy_dynamic_module_http_get_request_body_buffer_slice(
506                self.raw,
507                index,
508                &mut slice_ptr as *mut _ as usize,
509                &mut slice_size as *mut _ as usize,
510            );
511        }
512
513        Some(unsafe { std::slice::from_raw_parts_mut(slice_ptr, slice_size) })
514    }
515
516    /// Copies the entire buffer into a single contiguous Vec<u8> managed in Rust.
517    pub fn copy(&self) -> Vec<u8> {
518        let mut buffer = Vec::new();
519        let slices = self.slices();
520        for slice in slices {
521            buffer.extend_from_slice(slice);
522        }
523        buffer
524    }
525
526    /// Returns a reader that implements the [`std::io::Read`] trait.
527    pub fn reader(&self) -> RequestBodyBufferReader {
528        RequestBodyBufferReader::from(*self)
529    }
530
531    /// Appends the given data to the buffer.
532    ///
533    /// After this operation, previous slices might be invalidated.
534    pub fn append(&self, data: &[u8]) {
535        let data_ptr = data.as_ptr();
536        let data_size = data.len();
537        unsafe {
538            abi::envoy_dynamic_module_http_append_request_body_buffer(
539                self.raw,
540                data_ptr as *const _ as usize,
541                data_size,
542            )
543        }
544    }
545
546    /// Prepends the given data to the buffer.
547    ///
548    /// After this operation, previous slices might be invalidated.
549    pub fn prepend(&self, data: &[u8]) {
550        let data_ptr = data.as_ptr();
551        let data_size = data.len();
552        unsafe {
553            abi::envoy_dynamic_module_http_prepend_request_body_buffer(
554                self.raw,
555                data_ptr as *const _ as usize,
556                data_size,
557            )
558        }
559    }
560
561    /// Drains the buffer by the given size.
562    ///
563    /// After this operation, previous slices might be invalidated.
564    pub fn drain(&self, size: usize) {
565        unsafe {
566            abi::envoy_dynamic_module_http_drain_request_body_buffer(self.raw, size);
567        }
568    }
569
570    /// Replaces the entire buffer with the given data.
571    ///
572    /// After this operation, previous slices might be invalidated.
573    pub fn replace(&self, data: &[u8]) {
574        self.drain(self.length());
575        self.append(data);
576    }
577}
578
579pub struct RequestBodyBufferReader {
580    buffer: RequestBodyBuffer,
581    // The current index of the slice.
582    current_slice_index: usize,
583    // The current offset in the current slice.
584    current_slice_offset: usize,
585}
586
587impl From<RequestBodyBuffer> for RequestBodyBufferReader {
588    fn from(buffer: RequestBodyBuffer) -> Self {
589        RequestBodyBufferReader {
590            buffer,
591            current_slice_index: 0,
592            current_slice_offset: 0,
593        }
594    }
595}
596
597impl std::io::Read for RequestBodyBufferReader {
598    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
599        // TODO: below obviously can be optimized. But for now, this is fine for the PoC.
600        let mut total_read = 0;
601        while total_read < buf.len() {
602            let current_slice = match self.buffer.slice_at(self.current_slice_index) {
603                Some(slice) => slice,
604                None => break,
605            };
606            let current_slice_len = current_slice.len();
607            if self.current_slice_offset >= current_slice_len {
608                self.current_slice_offset = 0;
609                self.current_slice_index += 1;
610                continue;
611            }
612
613            let remaining = buf.len() - total_read;
614            let remaining_slice = current_slice_len - self.current_slice_offset;
615            let read_size = std::cmp::min(remaining, remaining_slice);
616            buf[total_read..total_read + read_size].copy_from_slice(
617                &current_slice[self.current_slice_offset..self.current_slice_offset + read_size],
618            );
619            self.current_slice_offset += read_size;
620            total_read += read_size;
621        }
622        Ok(total_read)
623    }
624}
625
626/// An opaque object that represents the underlying Envoy Http response headers map.
627/// This is used to interact with it from the module code.
628///
629/// This is a shallow wrapper around the raw pointer to the Envoy response headers map.
630///
631#[derive(Debug, Clone, Copy)]
632pub struct ResponseHeaders {
633    raw: abi::envoy_dynamic_module_type_HttpResponseHeaderMapPtr,
634}
635
636impl ResponseHeaders {
637    /// Returns the first header value for the given key. To handle multiple values, use the [`ResponseHeaders::values`] method.
638    pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
639        let key_ptr = key.as_ptr();
640        let key_size = key.len();
641
642        let mut result_ptr: *const u8 = ptr::null();
643        let mut result_size: usize = 0;
644
645        let total = unsafe {
646            abi::envoy_dynamic_module_http_get_response_header_value(
647                self.raw,
648                key_ptr as *const _ as usize,
649                key_size,
650                &mut result_ptr as *mut _ as usize,
651                &mut result_size as *mut _ as usize,
652            )
653        };
654
655        if total == 0 {
656            return None;
657        }
658
659        Some(unsafe { std::slice::from_raw_parts(result_ptr, result_size) })
660    }
661
662    /// Returns all the header values for the given key.
663    pub fn values(&self, key: &[u8]) -> Vec<&[u8]> {
664        let key_ptr = key.as_ptr();
665        let key_size = key.len();
666
667        let mut result_ptr: *const u8 = ptr::null();
668        let mut result_size: usize = 0;
669
670        let mut values = Vec::new();
671        let total = unsafe {
672            abi::envoy_dynamic_module_http_get_response_header_value(
673                self.raw,
674                key_ptr as *const _ as usize,
675                key_size,
676                &mut result_ptr as *mut _ as usize,
677                &mut result_size as *mut _ as usize,
678            )
679        };
680
681        if total == 0 {
682            return values;
683        }
684
685        values = Vec::with_capacity(total);
686        values.push(unsafe { std::slice::from_raw_parts(result_ptr, result_size) });
687
688        for i in 1..total {
689            unsafe {
690                abi::envoy_dynamic_module_http_get_response_header_value_nth(
691                    self.raw,
692                    key_ptr as *const _ as usize,
693                    key_size,
694                    &mut result_ptr as *mut _ as usize,
695                    &mut result_size as *mut _ as usize,
696                    i,
697                );
698            }
699            values.push(unsafe { std::slice::from_raw_parts(result_ptr, result_size) });
700        }
701
702        values
703    }
704
705    /// Sets the value for the given key. If multiple values are set for the same key,
706    pub fn set(&self, key: &[u8], value: &[u8]) {
707        let key_ptr = key.as_ptr();
708        let key_size = key.len();
709        let value_ptr = value.as_ptr();
710        let value_size = value.len();
711
712        unsafe {
713            abi::envoy_dynamic_module_http_set_response_header(
714                self.raw,
715                key_ptr as *const _ as usize,
716                key_size,
717                value_ptr as *const _ as usize,
718                value_size,
719            )
720        }
721    }
722
723    /// Removes the value for the given key. If multiple values are set for the same key,
724    pub fn remove(&self, key: &[u8]) {
725        let key_ptr = key.as_ptr();
726        let key_size = key.len();
727
728        unsafe {
729            abi::envoy_dynamic_module_http_set_response_header(
730                self.raw,
731                key_ptr as *const _ as usize,
732                key_size,
733                0,
734                0,
735            )
736        }
737    }
738}
739
740/// An opaque object that represents the underlying Envoy Http response body buffer.
741/// This is used to interact with it from the module code. The buffer consists of one or more slices.
742/// The slices are the contiguous memory regions that represent the buffer.
743///
744/// This corresponds to either a frame of the response body or the whole body.
745///
746/// This is a shallow wrapper around the raw pointer to the Envoy response body buffer.
747#[derive(Debug, Clone, Copy)]
748pub struct ResponseBodyBuffer {
749    raw: abi::envoy_dynamic_module_type_HttpResponseBodyBufferPtr,
750}
751
752impl ResponseBodyBuffer {
753    /// Returns the number of bytes in the buffer.
754    pub fn length(&self) -> usize {
755        unsafe { abi::envoy_dynamic_module_http_get_response_body_buffer_length(self.raw) }
756    }
757
758    /// Returns the number of slices in the buffer.
759    pub fn slices_count(&self) -> usize {
760        unsafe { abi::envoy_dynamic_module_http_get_response_body_buffer_slices_count(self.raw) }
761    }
762
763    /// Returns the slices of the buffer.
764    pub fn slices(&self) -> Vec<&mut [u8]> {
765        let mut slices = Vec::new();
766        let slices_count = self.slices_count();
767        for i in 0..slices_count {
768            let mut slice_ptr: *mut u8 = ptr::null_mut();
769            let mut slice_size: usize = 0;
770            unsafe {
771                abi::envoy_dynamic_module_http_get_response_body_buffer_slice(
772                    self.raw,
773                    i,
774                    &mut slice_ptr as *mut _ as usize,
775                    &mut slice_size as *mut _ as usize,
776                );
777            }
778            slices.push(unsafe { std::slice::from_raw_parts_mut(slice_ptr, slice_size) });
779        }
780        slices
781    }
782
783    fn slice_at(&self, index: usize) -> Option<&mut [u8]> {
784        if index >= self.slices_count() {
785            return None;
786        }
787
788        let mut slice_ptr: *mut u8 = ptr::null_mut();
789        let mut slice_size: usize = 0;
790        unsafe {
791            abi::envoy_dynamic_module_http_get_response_body_buffer_slice(
792                self.raw,
793                index,
794                &mut slice_ptr as *mut _ as usize,
795                &mut slice_size as *mut _ as usize,
796            );
797        }
798
799        Some(unsafe { std::slice::from_raw_parts_mut(slice_ptr, slice_size) })
800    }
801
802    /// Copies the entire buffer into a single contiguous Vec<u8> managed in Rust.
803    pub fn copy(&self) -> Vec<u8> {
804        let mut buffer = Vec::new();
805        let slices = self.slices();
806        for slice in slices {
807            buffer.extend_from_slice(slice);
808        }
809        buffer
810    }
811
812    /// Returns a reader that implements the [`std::io::Read`] trait.
813    pub fn reader(&self) -> ResponseBodyBufferReader {
814        ResponseBodyBufferReader::from(*self)
815    }
816
817    /// Appends the given data to the buffer.
818    ///
819    /// After this operation, previous slices might be invalidated.
820    pub fn append(&self, data: &[u8]) {
821        let data_ptr = data.as_ptr();
822        let data_size = data.len();
823        unsafe {
824            abi::envoy_dynamic_module_http_append_response_body_buffer(
825                self.raw,
826                data_ptr as *const _ as usize,
827                data_size,
828            )
829        }
830    }
831
832    /// Prepends the given data to the buffer.
833    ///
834    /// After this operation, previous slices might be invalidated.
835    pub fn prepend(&self, data: &[u8]) {
836        let data_ptr = data.as_ptr();
837        let data_size = data.len();
838        unsafe {
839            abi::envoy_dynamic_module_http_prepend_response_body_buffer(
840                self.raw,
841                data_ptr as *const _ as usize,
842                data_size,
843            )
844        }
845    }
846
847    /// Drains the buffer by the given size.
848    ///
849    /// After this operation, previous slices might be invalidated.
850    pub fn drain(&self, size: usize) {
851        unsafe {
852            abi::envoy_dynamic_module_http_drain_response_body_buffer(self.raw, size);
853        }
854    }
855
856    /// Replaces the entire buffer with the given data.
857    ///
858    /// After this operation, previous slices might be invalidated.
859    pub fn replace(&self, data: &[u8]) {
860        self.drain(self.length());
861        self.append(data);
862    }
863}
864
865/// This implements the [`std::io::Read`] trait for the [`ResponseBodyBuffer`] object.]
866pub struct ResponseBodyBufferReader {
867    buffer: ResponseBodyBuffer,
868    // The current index of the slice.
869    current_slice_index: usize,
870    // The current offset in the current slice.
871    current_slice_offset: usize,
872}
873
874impl From<ResponseBodyBuffer> for ResponseBodyBufferReader {
875    fn from(buffer: ResponseBodyBuffer) -> Self {
876        ResponseBodyBufferReader {
877            buffer,
878            current_slice_index: 0,
879            current_slice_offset: 0,
880        }
881    }
882}
883
884impl std::io::Read for ResponseBodyBufferReader {
885    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
886        // TODO: below obviously can be optimized. But for now, this is fine for the PoC.
887        let mut total_read = 0;
888        while total_read < buf.len() {
889            let current_slice = match self.buffer.slice_at(self.current_slice_index) {
890                Some(slice) => slice,
891                None => break,
892            };
893            let current_slice_len = current_slice.len();
894            if self.current_slice_offset >= current_slice_len {
895                self.current_slice_offset = 0;
896                self.current_slice_index += 1;
897                continue;
898            }
899
900            let remaining = buf.len() - total_read;
901            let remaining_slice = current_slice_len - self.current_slice_offset;
902            let read_size = std::cmp::min(remaining, remaining_slice);
903            buf[total_read..total_read + read_size].copy_from_slice(
904                &current_slice[self.current_slice_offset..self.current_slice_offset + read_size],
905            );
906            self.current_slice_offset += read_size;
907            total_read += read_size;
908        }
909        Ok(total_read)
910    }
911}
912
913/// The status of the processing after the [`HttpFilterInstance::request_headers`] is called.
914pub enum RequestHeadersStatus {
915    /// Should be returned when the operation should continue.
916    Continue,
917    /// This indicates that Envoy shouldn't continue from processing the headers and should
918    /// stop filter iteration. In other words, [`HttpFilterInstance::request_body`]
919    /// will be called while not sending headers to the upstream. The header processing can be
920    /// resumed by either calling [`EnvoyFilterInstance::continue_request`], or returns
921    /// continue status from the [`HttpFilterInstance::request_body`].
922    StopIteration,
923    _reserved,
924    /// This indicates that Envoy should stop all iteration and continue to buffer the request body
925    /// until the limit is reached. When the limit is reached, Envoy will stop buffering and returns 500
926    /// to the client. This means that [`HttpFilterInstance::request_body`] will not be called.
927    ///
928    /// The header processing can be resumed by either calling [`EnvoyFilterInstance::continue_request`], or
929    /// returns continue status from the [`HttpFilterInstance::request_body`].
930    StopAllIterationAndBuffer,
931}
932
933impl From<RequestHeadersStatus> for abi::envoy_dynamic_module_type_EventHttpRequestHeadersStatus {
934    fn from(val: RequestHeadersStatus) -> Self {
935        match val {
936            RequestHeadersStatus::Continue => {
937                abi::envoy_dynamic_module_type_EventHttpRequestHeadersStatusContinue
938            }
939            RequestHeadersStatus::StopIteration => {
940                abi::envoy_dynamic_module_type_EventHttpRequestHeadersStatusStopIteration
941            }
942            RequestHeadersStatus::StopAllIterationAndBuffer => {
943                abi::envoy_dynamic_module_type_EventHttpRequestHeadersStatusStopAllIterationAndBuffer
944            }
945            _ => {
946                panic!("Invalid EventHttpRequestHeadersStatus")
947            }
948        }
949    }
950}
951
952/// The status of the processing after the [`HttpFilterInstance::response_headers`] is called.
953pub enum RequestBodyStatus {
954    /// Should be returned when the operation should continue.
955    Continue,
956    /// This indicates that Envoy shouldn't continue from processing the body frame and should stop iteration,
957    /// but continue buffering the body until the limit is reached. When the limit is reached,
958    /// Envoy will stop buffering and returns 500 to the client.
959    ///
960    /// This stops sending body data to the upstream, so if the module wants to continue sending body
961    /// data, it should call [`EnvoyFilterInstance::continue_request`] or return continue status in the
962    /// subsequent [`HttpFilterInstance::request_body`] calls.
963    StopIterationAndBuffer,
964}
965
966impl From<RequestBodyStatus> for abi::envoy_dynamic_module_type_EventHttpRequestBodyStatus {
967    fn from(val: RequestBodyStatus) -> Self {
968        match val {
969            RequestBodyStatus::Continue => {
970                abi::envoy_dynamic_module_type_EventHttpRequestBodyStatusContinue
971            }
972            RequestBodyStatus::StopIterationAndBuffer => {
973                abi::envoy_dynamic_module_type_EventHttpRequestBodyStatusStopIterationAndBuffer
974            }
975        }
976    }
977}
978
979/// The status of the processing after the [`HttpFilterInstance::response_headers`] is called.
980pub enum ResponseHeadersStatus {
981    /// Should be returned when the operation should continue.
982    Continue,
983    /// This indicates that Envoy shouldn't continue from processing the headers and should
984    /// stop filter iteration. In other words, [`HttpFilterInstance::response_body`]
985    /// will be called while not sending headers to the upstream. The header processing can be
986    /// resumed by either calling [`EnvoyFilterInstance::continue_response`], or returns
987    /// continue status from the [`HttpFilterInstance::response_body`].
988    StopIteration,
989    _reserved,
990    /// This indicates that Envoy should stop all iteration and continue to buffer the response body
991    /// until the limit is reached. When the limit is reached, Envoy will stop buffering and returns 500
992    /// to the client. This means that [`HttpFilterInstance::response_body`] will not be called.
993    ///
994    /// The header processing can be resumed by either calling [`EnvoyFilterInstance::continue_response`], or
995    /// returns continue status from the [`HttpFilterInstance::response_body`].
996    StopAllIterationAndBuffer,
997}
998
999impl From<ResponseHeadersStatus> for abi::envoy_dynamic_module_type_EventHttpResponseHeadersStatus {
1000    fn from(val: ResponseHeadersStatus) -> Self {
1001        match val {
1002            ResponseHeadersStatus::Continue => {
1003                abi::envoy_dynamic_module_type_EventHttpResponseHeadersStatusContinue
1004            }
1005            ResponseHeadersStatus::StopIteration => {
1006                abi::envoy_dynamic_module_type_EventHttpResponseHeadersStatusStopIteration
1007            }
1008            ResponseHeadersStatus::StopAllIterationAndBuffer => {
1009                abi::envoy_dynamic_module_type_EventHttpResponseHeadersStatusStopAllIterationAndBuffer
1010            }
1011            _ => {
1012                panic!("Invalid EventHttpResponseHeadersStatus")
1013            }
1014        }
1015    }
1016}
1017
1018/// The status of the processing after the [`HttpFilterInstance::response_body`] is called.
1019pub enum ResponseBodyStatus {
1020    /// Should be returned when the operation should continue.
1021    Continue,
1022    /// This indicates that Envoy shouldn't continue from processing the body frame and should stop iteration,
1023    /// but continue buffering the body until the limit is reached. When the limit is reached,
1024    /// Envoy will stop buffering and returns 500 to the client.
1025    ///
1026    /// This stops sending body data to the upstream, so if the module wants to continue sending body
1027    /// data, it should call [`EnvoyFilterInstance::continue_responses`] or return continue status in the
1028    /// subsequent [`HttpFilterInstance::response_body`] calls.
1029    StopIterationAndBuffer,
1030}
1031
1032impl From<ResponseBodyStatus> for abi::envoy_dynamic_module_type_EventHttpResponseBodyStatus {
1033    fn from(val: ResponseBodyStatus) -> Self {
1034        match val {
1035            ResponseBodyStatus::Continue => {
1036                abi::envoy_dynamic_module_type_EventHttpResponseBodyStatusContinue
1037            }
1038            ResponseBodyStatus::StopIterationAndBuffer => {
1039                abi::envoy_dynamic_module_type_EventHttpResponseBodyStatusStopIterationAndBuffer
1040            }
1041        }
1042    }
1043}