diff --git a/source/extensions/dynamic_modules/BUILD b/source/extensions/dynamic_modules/BUILD index d46fb97354ce..0461a11f928c 100644 --- a/source/extensions/dynamic_modules/BUILD +++ b/source/extensions/dynamic_modules/BUILD @@ -21,12 +21,20 @@ envoy_cc_library( # Note: with lld, we can use the glob matching to avoid listing all the symbols, but gold and ld are # not able to do that. So we list all the symbols here. "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_header", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_headers", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_headers_count", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_request_header", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_trailer", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_trailers", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_request_trailers_count", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_request_trailer", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_header", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_headers", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_headers_count", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_response_header", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_trailer", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_trailers", + "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_response_trailers_count", "-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_response_trailer", ], deps = [ diff --git a/source/extensions/dynamic_modules/abi.h b/source/extensions/dynamic_modules/abi.h index 535ab74214b1..09db24845465 100644 --- a/source/extensions/dynamic_modules/abi.h +++ b/source/extensions/dynamic_modules/abi.h @@ -127,6 +127,17 @@ typedef char* envoy_dynamic_module_type_buffer_module_ptr; */ typedef char* envoy_dynamic_module_type_buffer_envoy_ptr; +/** + * envoy_dynamic_module_type_Header represents a key-value pair of an HTTP header. The key and value + * are represented as a pair of pointers to the buffer and the length of the buffer. + */ +typedef struct { + envoy_dynamic_module_type_buffer_module_ptr key_ptr; + size_t key_length; + envoy_dynamic_module_type_buffer_module_ptr value_ptr; + size_t value_length; +} envoy_dynamic_module_type_http_header; + /** * envoy_dynamic_module_type_on_http_filter_request_headers_status represents the status of the * filter after processing the HTTP request headers. This corresponds to `FilterHeadersStatus` in @@ -454,6 +465,91 @@ size_t envoy_dynamic_module_callback_http_get_response_trailer( envoy_dynamic_module_type_buffer_envoy_ptr* result_buffer_ptr, size_t* result_buffer_length_ptr, size_t index); +/** + * envoy_dynamic_module_callback_http_get_request_headers_count is called by the module to get the + * number of request headers. Combined with envoy_dynamic_module_callback_http_get_request_headers, + * this can be used to iterate over all request headers. + * + * @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the + * corresponding HTTP filter. + * @return the number of request headers. Returns zero if the headers are not available. + */ +size_t envoy_dynamic_module_callback_http_get_request_headers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr); + +/** + * envoy_dynamic_module_callback_http_get_request_trailers_count is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers_count, but for the request trailers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers_count for more + * details. + */ +size_t envoy_dynamic_module_callback_http_get_request_trailers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr); + +/** + * envoy_dynamic_module_callback_http_get_response_headers_count is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers_count, but for the response headers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers_count for more + * details. + */ +size_t envoy_dynamic_module_callback_http_get_response_headers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr); + +/** + * envoy_dynamic_module_callback_http_get_response_trailers_count is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers_count, but for the response trailers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers_count for more + * details. + */ +size_t envoy_dynamic_module_callback_http_get_response_trailers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr); + +/** + * envoy_dynamic_module_callback_http_get_request_headers is called by the module to get all the + * request headers. The headers are returned as an array of envoy_dynamic_module_type_http_header. + * + * PRECONDITION: The module must ensure that the result_headers is valid and has enough length to + * store all the headers. The module can use + * envoy_dynamic_module_callback_http_get_request_headers_count to get the number of headers before + * calling this function. + * + * @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the + * corresponding HTTP filter. + * @param result_headers is the pointer to the array of envoy_dynamic_module_type_http_header where + * the headers will be stored. + * @return true if the operation is successful, false otherwise. + */ +bool envoy_dynamic_module_callback_http_get_request_headers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers); + +/** + * envoy_dynamic_module_callback_http_get_request_trailers is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers, but for the request trailers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers for more details. + */ +bool envoy_dynamic_module_callback_http_get_request_trailers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers); + +/** + * envoy_dynamic_module_callback_http_get_response_headers is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers, but for the response headers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers for more details. + */ +bool envoy_dynamic_module_callback_http_get_response_headers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers); + +/** + * envoy_dynamic_module_callback_http_get_response_trailers is exactly the same as the + * envoy_dynamic_module_callback_http_get_request_headers, but for the response trailers. + * See the comments on envoy_dynamic_module_callback_http_get_request_headers for more details. + */ +bool envoy_dynamic_module_callback_http_get_response_trailers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers); + /** * envoy_dynamic_module_callback_http_set_request_header is called by the module to set * the value of the request header with the given key. If the header does not exist, it will be diff --git a/source/extensions/dynamic_modules/abi_version.h b/source/extensions/dynamic_modules/abi_version.h index 50408a9f1ae8..dde347fa70a4 100644 --- a/source/extensions/dynamic_modules/abi_version.h +++ b/source/extensions/dynamic_modules/abi_version.h @@ -6,7 +6,7 @@ namespace DynamicModules { #endif // This is the ABI version calculated as a sha256 hash of the ABI header files. When the ABI // changes, this value must change, and the correctness of this value is checked by the test. -const char* kAbiVersion = "d6411c58dd9d3ed6ad3fb46ff64d004fac0e825a2cbf8497324b9ad56d160b43"; +const char* kAbiVersion = "1377c40770c13c244cdf7d62f62535bb38979831288801d951259271bcc9884d"; #ifdef __cplusplus } // namespace DynamicModules diff --git a/source/extensions/dynamic_modules/sdk/rust/src/lib.rs b/source/extensions/dynamic_modules/sdk/rust/src/lib.rs index f34eb34e8547..2c6f91397755 100644 --- a/source/extensions/dynamic_modules/sdk/rust/src/lib.rs +++ b/source/extensions/dynamic_modules/sdk/rust/src/lib.rs @@ -237,6 +237,17 @@ impl EnvoyHttpFilter { ) } + /// Get all request headers. + /// + /// Returns a list of key-value pairs of the request headers. + /// If there are no headers or headers are not available, this returns an empty list. + pub fn get_request_headers(&self) -> Vec<(&[u8], &[u8])> { + self.get_headers_impl( + abi::envoy_dynamic_module_callback_http_get_request_headers_count, + abi::envoy_dynamic_module_callback_http_get_request_headers, + ) + } + /// Set the request header with the given key and value. /// /// This will overwrite the existing value if the header is already present. @@ -281,6 +292,17 @@ impl EnvoyHttpFilter { ) } + /// Get all request trailers. + /// + /// Returns a list of key-value pairs of the request trailers. + /// If there are no trailers or trailers are not available, this returns an empty list. + pub fn get_request_trailers(&self) -> Vec<(&[u8], &[u8])> { + self.get_headers_impl( + abi::envoy_dynamic_module_callback_http_get_request_trailers_count, + abi::envoy_dynamic_module_callback_http_get_request_trailers, + ) + } + /// Set the request trailer with the given key and value. /// /// This will overwrite the existing value if the trailer is already present. @@ -325,6 +347,17 @@ impl EnvoyHttpFilter { ) } + /// Get all response headers. + /// + /// Returns a list of key-value pairs of the response headers. + /// If there are no headers or headers are not available, this returns an empty list. + pub fn get_response_headers(&self) -> Vec<(&[u8], &[u8])> { + self.get_headers_impl( + abi::envoy_dynamic_module_callback_http_get_response_headers_count, + abi::envoy_dynamic_module_callback_http_get_response_headers, + ) + } + /// Set the response header with the given key and value. /// /// This will overwrite the existing value if the header is already present. @@ -370,6 +403,17 @@ impl EnvoyHttpFilter { ) } + /// Get all response trailers. + /// + /// Returns a list of key-value pairs of the response trailers. + /// If there are no trailers or trailers are not available, this returns an empty list. + pub fn get_response_trailers(&self) -> Vec<(&[u8], &[u8])> { + self.get_headers_impl( + abi::envoy_dynamic_module_callback_http_get_response_trailers_count, + abi::envoy_dynamic_module_callback_http_get_response_trailers, + ) + } + /// Set the response trailer with the given key and value. /// /// This will overwrite the existing value if the trailer is already present. @@ -393,6 +437,32 @@ impl EnvoyHttpFilter { } } + /// Implement the common logic for getting all headers/trailers. + fn get_headers_impl( + &self, + count_callback: unsafe extern "C" fn( + filter_envoy_ptr: abi::envoy_dynamic_module_type_http_filter_envoy_ptr, + ) -> usize, + getter_callback: unsafe extern "C" fn( + filter_envoy_ptr: abi::envoy_dynamic_module_type_http_filter_envoy_ptr, + result_buffer_ptr: *mut abi::envoy_dynamic_module_type_http_header, + ) -> bool, + ) -> Vec<(&[u8], &[u8])> { + let count = unsafe { count_callback(self.raw_ptr) }; + let mut headers = vec![(&[][..], &[][..]); count]; + let success = unsafe { + getter_callback( + self.raw_ptr, + headers.as_mut_ptr() as *mut abi::envoy_dynamic_module_type_http_header, + ) + }; + if success { + headers + } else { + Vec::default() + } + } + /// This implements the common logic for getting the header/trailer values. fn get_header_value_impl( &self, diff --git a/source/extensions/filters/http/dynamic_modules/abi_impl.cc b/source/extensions/filters/http/dynamic_modules/abi_impl.cc index 50edd74da391..215006047c35 100644 --- a/source/extensions/filters/http/dynamic_modules/abi_impl.cc +++ b/source/extensions/filters/http/dynamic_modules/abi_impl.cc @@ -114,6 +114,89 @@ bool envoy_dynamic_module_callback_http_set_response_trailer( DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); return setHeaderValueImpl(filter->response_trailers_, key, key_length, value, value_length); } + +size_t envoy_dynamic_module_callback_http_get_request_headers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + if (!filter->request_headers_) { + return 0; + } + return filter->request_headers_->size(); +} + +size_t envoy_dynamic_module_callback_http_get_request_trailers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + if (!filter->request_trailers_) { + return 0; + } + return filter->request_trailers_->size(); +} + +size_t envoy_dynamic_module_callback_http_get_response_headers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + if (!filter->response_headers_) { + return 0; + } + return filter->response_headers_->size(); +} + +size_t envoy_dynamic_module_callback_http_get_response_trailers_count( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + if (!filter->response_trailers_) { + return 0; + } + return filter->response_trailers_->size(); +} + +bool getHeadersImpl(const Http::HeaderMap* map, + envoy_dynamic_module_type_http_header* result_headers) { + if (!map) { + return false; + } + size_t i = 0; + map->iterate([&i, &result_headers](const Http::HeaderEntry& header) -> Http::HeaderMap::Iterate { + auto& key = header.key(); + result_headers[i].key_ptr = const_cast(key.getStringView().data()); + result_headers[i].key_length = key.size(); + auto& value = header.value(); + result_headers[i].value_ptr = const_cast(value.getStringView().data()); + result_headers[i].value_length = value.size(); + i++; + return Http::HeaderMap::Iterate::Continue; + }); + return true; +} + +bool envoy_dynamic_module_callback_http_get_request_headers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + return getHeadersImpl(filter->request_headers_, result_headers); +} + +bool envoy_dynamic_module_callback_http_get_request_trailers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + return getHeadersImpl(filter->request_trailers_, result_headers); +} + +bool envoy_dynamic_module_callback_http_get_response_headers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + return getHeadersImpl(filter->response_headers_, result_headers); +} + +bool envoy_dynamic_module_callback_http_get_response_trailers( + envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, + envoy_dynamic_module_type_http_header* result_headers) { + DynamicModuleHttpFilter* filter = static_cast(filter_envoy_ptr); + return getHeadersImpl(filter->response_trailers_, result_headers); +} } } // namespace HttpFilters } // namespace DynamicModules diff --git a/source/extensions/filters/http/dynamic_modules/filter.h b/source/extensions/filters/http/dynamic_modules/filter.h index 2c872753eb3d..37c5b771294a 100644 --- a/source/extensions/filters/http/dynamic_modules/filter.h +++ b/source/extensions/filters/http/dynamic_modules/filter.h @@ -17,8 +17,7 @@ using namespace Envoy::Http; class DynamicModuleHttpFilter : public Http::StreamFilter, public std::enable_shared_from_this { public: - DynamicModuleHttpFilter(DynamicModuleHttpFilterConfigSharedPtr dynamic_module) - : config_(dynamic_module) {} + DynamicModuleHttpFilter(DynamicModuleHttpFilterConfigSharedPtr config) : config_(config) {} ~DynamicModuleHttpFilter() override = default; /** diff --git a/test/extensions/dynamic_modules/http/abi_impl_test.cc b/test/extensions/dynamic_modules/http/abi_impl_test.cc index b9eec4e98f1f..f2bf32c83f98 100644 --- a/test/extensions/dynamic_modules/http/abi_impl_test.cc +++ b/test/extensions/dynamic_modules/http/abi_impl_test.cc @@ -194,6 +194,84 @@ TEST(ABIImpl, set_header_value) { } } +TEST(ABIImpl, get_headers_count) { + std::vector callbacks = { + envoy_dynamic_module_callback_http_get_request_headers_count, + envoy_dynamic_module_callback_http_get_request_trailers_count, + envoy_dynamic_module_callback_http_get_response_headers_count, + envoy_dynamic_module_callback_http_get_response_trailers_count}; + + DynamicModuleHttpFilter filter{nullptr}; + + // Test with nullptr accessors. + for (auto callback : callbacks) { + EXPECT_EQ(callback(&filter), 0); + } + + std::initializer_list> headers = { + {"single", "value"}, {"multi", "value1"}, {"multi", "value2"}}; + Http::TestRequestHeaderMapImpl request_headers{headers}; + filter.request_headers_ = &request_headers; + Http::TestRequestTrailerMapImpl request_trailers{headers}; + filter.request_trailers_ = &request_trailers; + Http::TestResponseHeaderMapImpl response_headers{headers}; + filter.response_headers_ = &response_headers; + Http::TestResponseTrailerMapImpl response_trailers{headers}; + filter.response_trailers_ = &response_trailers; + + for (auto callback : callbacks) { + EXPECT_EQ(callback(&filter), 3); + } +} + +TEST(ABIImpl, get_headers) { + std::vector + callbacks = {envoy_dynamic_module_callback_http_get_request_headers, + envoy_dynamic_module_callback_http_get_request_trailers, + envoy_dynamic_module_callback_http_get_response_headers, + envoy_dynamic_module_callback_http_get_response_trailers}; + + DynamicModuleHttpFilter filter{nullptr}; + + // Test with nullptr accessors. + for (auto callback : callbacks) { + envoy_dynamic_module_type_http_header result_headers[3]; + EXPECT_FALSE(callback(&filter, result_headers)); + } + + std::initializer_list> headers = { + {"single", "value"}, {"multi", "value1"}, {"multi", "value2"}}; + Http::TestRequestHeaderMapImpl request_headers{headers}; + filter.request_headers_ = &request_headers; + Http::TestRequestTrailerMapImpl request_trailers{headers}; + filter.request_trailers_ = &request_trailers; + Http::TestResponseHeaderMapImpl response_headers{headers}; + filter.response_headers_ = &response_headers; + Http::TestResponseTrailerMapImpl response_trailers{headers}; + filter.response_trailers_ = &response_trailers; + + for (auto callback : callbacks) { + envoy_dynamic_module_type_http_header result_headers[3]; + EXPECT_TRUE(callback(&filter, result_headers)); + + EXPECT_EQ(result_headers[0].key_length, 6); + EXPECT_EQ(std::string(result_headers[0].key_ptr, result_headers[0].key_length), "single"); + EXPECT_EQ(result_headers[0].value_length, 5); + EXPECT_EQ(std::string(result_headers[0].value_ptr, result_headers[0].value_length), "value"); + + EXPECT_EQ(result_headers[1].key_length, 5); + EXPECT_EQ(std::string(result_headers[1].key_ptr, result_headers[1].key_length), "multi"); + EXPECT_EQ(result_headers[1].value_length, 6); + EXPECT_EQ(std::string(result_headers[1].value_ptr, result_headers[1].value_length), "value1"); + + EXPECT_EQ(result_headers[2].key_length, 5); + EXPECT_EQ(std::string(result_headers[2].key_ptr, result_headers[2].key_length), "multi"); + EXPECT_EQ(result_headers[2].value_length, 6); + EXPECT_EQ(std::string(result_headers[2].value_ptr, result_headers[2].value_length), "value2"); + } +} + } // namespace HttpFilters } // namespace DynamicModules } // namespace Extensions diff --git a/test/extensions/dynamic_modules/test_data/rust/http.rs b/test/extensions/dynamic_modules/test_data/rust/http.rs index 8e3d07b312a4..a2a50473be1e 100644 --- a/test/extensions/dynamic_modules/test_data/rust/http.rs +++ b/test/extensions/dynamic_modules/test_data/rust/http.rs @@ -65,6 +65,18 @@ impl HttpFilter for HeaderCallbacksFilter { .expect("header new not found"); assert_eq!(std::str::from_utf8(&new_value).unwrap(), "value"); + // Test all getter API. + let all_headers = envoy_filter.get_request_headers(); + assert_eq!(all_headers.len(), 4); + assert_eq!(std::str::from_utf8(&all_headers[0].0).unwrap(), "single"); + assert_eq!(std::str::from_utf8(&all_headers[0].1).unwrap(), "value"); + assert_eq!(std::str::from_utf8(&all_headers[1].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_headers[1].1).unwrap(), "value1"); + assert_eq!(std::str::from_utf8(&all_headers[2].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_headers[2].1).unwrap(), "value2"); + assert_eq!(std::str::from_utf8(&all_headers[3].0).unwrap(), "new"); + assert_eq!(std::str::from_utf8(&all_headers[3].1).unwrap(), "value"); + abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::Continue } @@ -103,6 +115,18 @@ impl HttpFilter for HeaderCallbacksFilter { .expect("trailer new not found"); assert_eq!(std::str::from_utf8(&new_value).unwrap(), "value"); + // Test all getter API. + let all_trailers = envoy_filter.get_request_trailers(); + assert_eq!(all_trailers.len(), 4); + assert_eq!(std::str::from_utf8(&all_trailers[0].0).unwrap(), "single"); + assert_eq!(std::str::from_utf8(&all_trailers[0].1).unwrap(), "value"); + assert_eq!(std::str::from_utf8(&all_trailers[1].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_trailers[1].1).unwrap(), "value1"); + assert_eq!(std::str::from_utf8(&all_trailers[2].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_trailers[2].1).unwrap(), "value2"); + assert_eq!(std::str::from_utf8(&all_trailers[3].0).unwrap(), "new"); + assert_eq!(std::str::from_utf8(&all_trailers[3].1).unwrap(), "value"); + abi::envoy_dynamic_module_type_on_http_filter_request_trailers_status::Continue } @@ -134,6 +158,18 @@ impl HttpFilter for HeaderCallbacksFilter { .expect("header new not found"); assert_eq!(std::str::from_utf8(&new_value).unwrap(), "value"); + // Test all getter API. + let all_headers = envoy_filter.get_response_headers(); + assert_eq!(all_headers.len(), 4); + assert_eq!(std::str::from_utf8(&all_headers[0].0).unwrap(), "single"); + assert_eq!(std::str::from_utf8(&all_headers[0].1).unwrap(), "value"); + assert_eq!(std::str::from_utf8(&all_headers[1].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_headers[1].1).unwrap(), "value1"); + assert_eq!(std::str::from_utf8(&all_headers[2].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_headers[2].1).unwrap(), "value2"); + assert_eq!(std::str::from_utf8(&all_headers[3].0).unwrap(), "new"); + assert_eq!(std::str::from_utf8(&all_headers[3].1).unwrap(), "value"); + abi::envoy_dynamic_module_type_on_http_filter_response_headers_status::Continue } @@ -172,6 +208,18 @@ impl HttpFilter for HeaderCallbacksFilter { .expect("trailer new not found"); assert_eq!(std::str::from_utf8(&new_value).unwrap(), "value"); + // Test all getter API. + let all_trailers = envoy_filter.get_response_trailers(); + assert_eq!(all_trailers.len(), 4); + assert_eq!(std::str::from_utf8(&all_trailers[0].0).unwrap(), "single"); + assert_eq!(std::str::from_utf8(&all_trailers[0].1).unwrap(), "value"); + assert_eq!(std::str::from_utf8(&all_trailers[1].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_trailers[1].1).unwrap(), "value1"); + assert_eq!(std::str::from_utf8(&all_trailers[2].0).unwrap(), "multi"); + assert_eq!(std::str::from_utf8(&all_trailers[2].1).unwrap(), "value2"); + assert_eq!(std::str::from_utf8(&all_trailers[3].0).unwrap(), "new"); + assert_eq!(std::str::from_utf8(&all_trailers[3].1).unwrap(), "value"); + abi::envoy_dynamic_module_type_on_http_filter_response_trailers_status::Continue } }