Skip to content

Commit

Permalink
dynamic_modules: adds header-related callbacks (#37753)
Browse files Browse the repository at this point in the history
Commit Message: dynamic_modules: adds header-related callbacks 
Additional Description:

This adds the following callback functions that can be called by
modules:
* envoy_dynamic_module_callback_http_get_request_header
* envoy_dynamic_module_callback_http_get_request_headers
* envoy_dynamic_module_callback_http_get_request_headers_count
* envoy_dynamic_module_callback_http_set_request_header
* envoy_dynamic_module_callback_http_get_request_trailer
* envoy_dynamic_module_callback_http_get_request_trailers
* envoy_dynamic_module_callback_http_get_request_trailers_count
* envoy_dynamic_module_callback_http_set_request_trailer
* envoy_dynamic_module_callback_http_get_response_header
* envoy_dynamic_module_callback_http_get_response_headers
* envoy_dynamic_module_callback_http_get_response_headers_count
* envoy_dynamic_module_callback_http_set_response_header
* envoy_dynamic_module_callback_http_get_response_trailer
* envoy_dynamic_module_callback_http_get_response_trailers
* envoy_dynamic_module_callback_http_get_response_trailers_count
* envoy_dynamic_module_callback_http_set_response_trailer

They provide the header getter and setter functionality to dynamic
modules.

Risk Level: low
Testing: done
Docs Changes: n/a
Release Notes: n/a
Platform Specific Features: n/a

---------

Signed-off-by: Takeshi Yoneda <[email protected]>
  • Loading branch information
mathetake authored Dec 31, 2024
1 parent d41fd9b commit 871e410
Show file tree
Hide file tree
Showing 25 changed files with 2,282 additions and 101 deletions.
22 changes: 22 additions & 0 deletions source/extensions/dynamic_modules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ envoy_cc_library(
"abi.h",
"dynamic_modules.h",
],
linkopts = [
# Export callback symbols so that they can be found by the dynamic modules when loaded via dlopen.
#
# 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 = [
":abi_version_lib",
"//envoy/common:exception_lib",
Expand Down
263 changes: 262 additions & 1 deletion source/extensions/dynamic_modules/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@
// this restriction and hopefully provide a wider compatibility guarantee. Until then, Envoy
// checks the hash of the ABI header files to ensure that the dynamic modules are built against the
// same version of the ABI.
//
// There are three kinds defined in this file:
//
// * Types: type definitions used in the ABI.
// * Events Hooks: functions that modules must implement to handle events from Envoy.
// * Callbacks: functions that Envoy implements and modules can call to interact with Envoy.
//
// Types are prefixed with "envoy_dynamic_module_type_". Event Hooks are prefixed with
// "envoy_dynamic_module_on_". Callbacks are prefixed with "envoy_dynamic_module_callback_".
//
// Some functions are specified/defined under the assumptions that all dynamic modules are trusted
// and have the same privilege level as the main Envoy program. This is because they run inside the
// Envoy process, hence they can access all the memory and resources that the main Envoy process
// can, which makes it impossible to enforce any security boundaries between Envoy and the modules
// by nature. For example, we assume that modules will not try to pass invalid pointers to Envoy
// intentionally.

#ifdef __cplusplus
#include <cstdbool>
Expand Down Expand Up @@ -80,7 +96,7 @@ typedef const void* envoy_dynamic_module_type_http_filter_config_module_ptr;
* OWNERSHIP: Envoy owns the pointer, and can be accessed by the module until the filter is
* destroyed, i.e. envoy_dynamic_module_on_http_filter_destroy is called.
*/
typedef const void* envoy_dynamic_module_type_http_filter_envoy_ptr;
typedef void* envoy_dynamic_module_type_http_filter_envoy_ptr;

/**
* envoy_dynamic_module_type_http_filter_module_ptr is a pointer to an in-module HTTP filter
Expand All @@ -93,6 +109,35 @@ typedef const void* envoy_dynamic_module_type_http_filter_envoy_ptr;
*/
typedef const void* envoy_dynamic_module_type_http_filter_module_ptr;

/**
* envoy_dynamic_module_type_buffer_module_ptr is a pointer to a buffer in the module. A buffer
* represents a contiguous block of memory in bytes.
*
* OWNERSHIP: The module is responsible for managing the lifetime of the pointer. It depends on the
* context where the buffer is used. See for the specific event hook or callback for more details.
*/
typedef char* envoy_dynamic_module_type_buffer_module_ptr;

/**
* envoy_dynamic_module_type_buffer_envoy_ptr is a pointer to a buffer in Envoy. A buffer represents
* a contiguous block of memory in bytes.
*
* OWNERSHIP: Envoy owns the pointer. The lifetime depends on the context where the buffer is used.
* See for the specific event hook or callback for more details.
*/
typedef char* envoy_dynamic_module_type_buffer_envoy_ptr;

/**
* envoy_dynamic_module_type_Header represents a key-value pair of an HTTP header owned by Envoy's
* HeaderMap.
*/
typedef struct {
envoy_dynamic_module_type_buffer_envoy_ptr key_ptr;
size_t key_length;
envoy_dynamic_module_type_buffer_envoy_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
Expand Down Expand Up @@ -345,6 +390,222 @@ envoy_dynamic_module_on_http_filter_response_trailers(
void envoy_dynamic_module_on_http_filter_destroy(
envoy_dynamic_module_type_http_filter_module_ptr filter_module_ptr);

// -----------------------------------------------------------------------------
// -------------------------------- Callbacks ----------------------------------
// -----------------------------------------------------------------------------
//
// Callbacks are functions implemented by Envoy that can be called by the module to interact with
// Envoy. The name of a callback must be prefixed with "envoy_dynamic_module_callback_".

/**
* envoy_dynamic_module_callback_http_get_request_header is called by the module to get the
* value of the request header with the given key. Since a header can have multiple values, the
* index is used to get the specific value. This returns the number of values for the given key, so
* it can be used to iterate over all values by starting from 0 and incrementing the index until the
* return value.
*
* PRECONDITION: Envoy does not check the validity of the key as well as the result_buffer_ptr
* and result_buffer_length_ptr. The module must ensure that these values are valid, e.g.
* non-null pointers.
*
* @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the
* corresponding HTTP filter.
* @param key is the key of the request header.
* @param key_length is the length of the key.
* @param result_buffer_ptr is the pointer to the pointer variable where the pointer to the buffer
* of the value will be stored. If the key does not exist or the index is out of range, this will be
* set to nullptr.
* @param result_buffer_length_ptr is the pointer to the variable where the length of the buffer
* will be stored. If the key does not exist or the index is out of range, this will be set to 0.
* @param index is the index of the header value in the list of values for the given key.
* @return the number of values for the given key, regardless of whether the value is found or not.
*
* Note that a header value is not guaranteed to be a valid UTF-8 string. The module must be careful
* when interpreting the value as a string in the language of the module.
*
* The buffer pointed by the pointer stored in result_buffer_ptr is owned by Envoy, and they are
* guaranteed to be valid until the end of the current event hook unless the setter callback is
* called.
*/
size_t envoy_dynamic_module_callback_http_get_request_header(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
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_trailer is exactly the same as the
* envoy_dynamic_module_callback_http_get_request_header, but for the request trailers.
* See the comments on envoy_dynamic_module_http_get_request_header_value for more details.
*/
size_t envoy_dynamic_module_callback_http_get_request_trailer(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
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_response_header is exactly the same as the
* envoy_dynamic_module_callback_http_get_request_header, but for the response headers.
* See the comments on envoy_dynamic_module_callback_http_get_request_header for more details.
*/
size_t envoy_dynamic_module_callback_http_get_response_header(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
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_response_trailer is exactly the same as the
* envoy_dynamic_module_callback_http_get_request_header, but for the response trailers.
* See the comments on envoy_dynamic_module_callback_http_get_request_header for more details.
*/
size_t envoy_dynamic_module_callback_http_get_response_trailer(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
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. The lifetime of the buffer of key and value of each header is
* guaranteed until the end of the current event hook unless the setter callback are called.
* @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
* created. If the header already exists, all existing values will be removed and the new value will
* be set.
*
* @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the
* corresponding HTTP filter.
* @param key is the key of the header.
* @param key_length is the length of the key.
* @param value is the value of the header.
* @param value_length is the length of the value.
* @return true if the operation is successful, false otherwise.
*
* Note that this only sets the header to the underlying Envoy object. Whether or not the header is
* actually sent to the upstream depends on the phase of the execution and subsequent
* filters. In other words, returning true from this function does not guarantee that the header
* will be sent to the upstream.
*/
bool envoy_dynamic_module_callback_http_set_request_header(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
envoy_dynamic_module_type_buffer_module_ptr value, size_t value_length);

/**
* envoy_dynamic_module_callback_http_set_request_trailer is exactly the same as the
* envoy_dynamic_module_callback_http_set_request_header, but for the request trailers.
* See the comments on envoy_dynamic_module_callback_http_set_request_header for more details.
*/
bool envoy_dynamic_module_callback_http_set_request_trailer(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
envoy_dynamic_module_type_buffer_module_ptr value, size_t value_length);

/**
* envoy_dynamic_module_callback_http_set_response_header is exactly the same as the
* envoy_dynamic_module_callback_http_set_request_header, but for the response headers.
* See the comments on envoy_dynamic_module_callback_http_set_request_header for more details.
*/
bool envoy_dynamic_module_callback_http_set_response_header(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
envoy_dynamic_module_type_buffer_module_ptr value, size_t value_length);

/**
* envoy_dynamic_module_callback_http_set_response_trailer is exactly the same as the
* envoy_dynamic_module_callback_http_set_request_header, but for the response trailers.
* See the comments on envoy_dynamic_module_callback_http_set_request_header for more details.
*/
bool envoy_dynamic_module_callback_http_set_response_trailer(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr,
envoy_dynamic_module_type_buffer_module_ptr key, size_t key_length,
envoy_dynamic_module_type_buffer_module_ptr value, size_t value_length);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/dynamic_modules/abi_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "d8a554f2e3d4cc9070b91c3f6d3c0aa5300babd4c760a97288f666da8b6f259f";
const char* kAbiVersion = "27dbe7918b67b12bfcf43e050ce142a300feb73fb3bd5779c754d1d1d3c8645f";

#ifdef __cplusplus
} // namespace DynamicModules
Expand Down
12 changes: 12 additions & 0 deletions source/extensions/dynamic_modules/sdk/rust/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,15 @@ rust_library(
normal = True,
) + [":build_script"],
)

# This is exactly the same as the above target, but with the testing feature enabled.
rust_library(
name = "envoy_proxy_dynamic_modules_rust_sdk_for_testing",
srcs = glob(["src/**/*.rs"]),
crate_features = ["testing"],
crate_name = "envoy_proxy_dynamic_modules_rust_sdk",
edition = "2021",
deps = all_crate_deps(
normal = True,
) + [":build_script"],
)
Loading

0 comments on commit 871e410

Please sign in to comment.