Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic_modules: adds send response #37856

Merged
merged 17 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/extensions/dynamic_modules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ envoy_cc_library(
"-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",
"-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_send_response",
"-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_dynamic_metadata_number",
"-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_get_dynamic_metadata_number",
"-Wl,--export-dynamic-symbol=envoy_dynamic_module_callback_http_set_dynamic_metadata_string",
Expand Down
33 changes: 31 additions & 2 deletions source/extensions/dynamic_modules/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,19 @@ 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 owned by Envoy's
* HeaderMap.
* envoy_dynamic_module_type_module_http_header represents a key-value pair of an HTTP header owned
* by the module.
*/
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_module_http_header;

/**
* envoy_dynamic_module_type_http_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;
Expand Down Expand Up @@ -610,6 +621,24 @@ bool envoy_dynamic_module_callback_http_set_response_trailer(
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_send_response is called by the module to send the response
* to the downstream.
*
* @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the
* corresponding HTTP filter.
* @param status_code is the status code of the response.
* @param headers_vector is the array of envoy_dynamic_module_type_module_http_header that contains
* the headers of the response.
* @param headers_vector_size is the size of the headers_vector.
* @param body is the pointer to the buffer of the body of the response.
* @param body_length is the length of the body.
*/
void envoy_dynamic_module_callback_http_send_response(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, uint32_t status_code,
envoy_dynamic_module_type_module_http_header* headers_vector, size_t headers_vector_size,
envoy_dynamic_module_type_buffer_module_ptr body, size_t body_length);

// ------------------------ Dynamic Metadata Callbacks -------------------------

/**
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 = "96ecb1011dfbd8375cab07853e6491d8ac30d4fe60e685c10ec39a0674c57a25";
const char* kAbiVersion = "271744e1b0090ad28c0006e53e760a445f4ec0a27767d9ac24a1ae87795d8c01";

#ifdef __cplusplus
} // namespace DynamicModules
Expand Down
29 changes: 28 additions & 1 deletion source/extensions/dynamic_modules/sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,16 @@ pub trait EnvoyHttpFilter {
/// Returns true if the operation is successful.
fn set_response_trailer(&mut self, key: &str, value: &[u8]) -> bool;

/// Send a response to the downstream with the given status code, headers, and body.
///
/// The headers are passed as a list of key-value pairs.
fn send_response<'a, 'b, 'c>(
&mut self,
status_code: u32,
headers: Vec<(&'a str, &'b [u8])>,
body: Option<&'c [u8]>,
);
bplotnick marked this conversation as resolved.
Show resolved Hide resolved

/// Get the number-typed dynamic metadata value with the given key.
/// If the metadata is not found or is the wrong type, this returns `None`.
fn get_dynamic_metadata_number(&self, namespace: &str, key: &str) -> Option<f64>;
Expand Down Expand Up @@ -477,7 +487,6 @@ impl EnvoyHttpFilter for EnvoyHttpFilterImpl {
}
}


fn get_response_trailer_value(&self, key: &str) -> Option<EnvoyBuffer> {
self.get_header_value_impl(
key,
Expand Down Expand Up @@ -515,6 +524,24 @@ impl EnvoyHttpFilter for EnvoyHttpFilterImpl {
}
}

fn send_response(&mut self, status_code: u32, headers: Vec<(&str, &[u8])>, body: Option<&[u8]>) {
let body_ptr = body.map(|s| s.as_ptr()).unwrap_or(std::ptr::null());
let body_length = body.map(|s| s.len()).unwrap_or(0);

let headers_ptr = headers.as_ptr() as *mut abi::envoy_dynamic_module_type_module_http_header;
bplotnick marked this conversation as resolved.
Show resolved Hide resolved

unsafe {
abi::envoy_dynamic_module_callback_http_send_response(
self.raw_ptr,
status_code,
headers_ptr,
headers.len(),
body_ptr as *mut _,
body_length,
)
}
}

fn get_dynamic_metadata_number(&self, namespace: &str, key: &str) -> Option<f64> {
let namespace_ptr = namespace.as_ptr();
let namespace_size = namespace.len();
Expand Down
25 changes: 25 additions & 0 deletions source/extensions/filters/http/dynamic_modules/abi_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,31 @@ bool envoy_dynamic_module_callback_http_get_response_trailers(
return getHeadersImpl(filter->response_trailers_, result_headers);
}

void envoy_dynamic_module_callback_http_send_response(
envoy_dynamic_module_type_http_filter_envoy_ptr filter_envoy_ptr, uint32_t status_code,
envoy_dynamic_module_type_module_http_header* headers_vector, size_t headers_vector_size,
envoy_dynamic_module_type_buffer_module_ptr body_ptr, size_t body_length) {
DynamicModuleHttpFilter* filter = static_cast<DynamicModuleHttpFilter*>(filter_envoy_ptr);

std::function<void(ResponseHeaderMap & headers)> modify_headers = nullptr;
if (headers_vector != nullptr && headers_vector_size != 0) {
modify_headers = [headers_vector, headers_vector_size](ResponseHeaderMap& headers) {
for (size_t i = 0; i < headers_vector_size; i++) {
const auto& header = &headers_vector[i];
const absl::string_view key(static_cast<const char*>(header->key_ptr), header->key_length);
const absl::string_view value(static_cast<const char*>(header->value_ptr),
header->value_length);
headers.addCopy(Http::LowerCaseString(key), value);
}
};
}
const absl::string_view body =
body_ptr ? absl::string_view(static_cast<const char*>(body_ptr), body_length) : "";

bplotnick marked this conversation as resolved.
Show resolved Hide resolved
filter->sendLocalReply(static_cast<Http::Code>(status_code), body, modify_headers, 0,
"dynamic_module");
}

/**
* Helper to get the metadata namespace from the stream info.
* @param filter_envoy_ptr is the pointer to the DynamicModuleHttpFilter object of the
Expand Down
38 changes: 36 additions & 2 deletions source/extensions/filters/http/dynamic_modules/filter.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include "source/extensions/filters/http/dynamic_modules/filter.h"

#include "envoy/server/filter_config.h"

namespace Envoy {
namespace Extensions {
namespace DynamicModules {
Expand All @@ -17,19 +15,31 @@ void DynamicModuleHttpFilter::onDestroy() { config_->on_http_filter_destroy_(in_

FilterHeadersStatus DynamicModuleHttpFilter::decodeHeaders(RequestHeaderMap& headers,
bool end_of_stream) {
if (sent_local_reply_) {
return FilterHeadersStatus::Continue;
}

request_headers_ = &headers;
const envoy_dynamic_module_type_on_http_filter_request_headers_status status =
config_->on_http_filter_request_headers_(thisAsVoidPtr(), in_module_filter_, end_of_stream);
return static_cast<FilterHeadersStatus>(status);
};

FilterDataStatus DynamicModuleHttpFilter::decodeData(Buffer::Instance&, bool end_of_stream) {
if (sent_local_reply_) {
return FilterDataStatus::Continue;
}

const envoy_dynamic_module_type_on_http_filter_request_body_status status =
config_->on_http_filter_request_body_(thisAsVoidPtr(), in_module_filter_, end_of_stream);
return static_cast<FilterDataStatus>(status);
};

FilterTrailersStatus DynamicModuleHttpFilter::decodeTrailers(RequestTrailerMap& trailers) {
if (sent_local_reply_) {
return FilterTrailersStatus::Continue;
}

request_trailers_ = &trailers;
const envoy_dynamic_module_type_on_http_filter_request_trailers_status status =
config_->on_http_filter_request_trailers_(thisAsVoidPtr(), in_module_filter_);
Expand All @@ -48,19 +58,31 @@ Filter1xxHeadersStatus DynamicModuleHttpFilter::encode1xxHeaders(ResponseHeaderM

FilterHeadersStatus DynamicModuleHttpFilter::encodeHeaders(ResponseHeaderMap& headers,
bool end_of_stream) {
if (sent_local_reply_) {
return FilterHeadersStatus::Continue;
}

response_headers_ = &headers;
const envoy_dynamic_module_type_on_http_filter_response_headers_status status =
config_->on_http_filter_response_headers_(thisAsVoidPtr(), in_module_filter_, end_of_stream);
return static_cast<FilterHeadersStatus>(status);
};

FilterDataStatus DynamicModuleHttpFilter::encodeData(Buffer::Instance&, bool end_of_stream) {
if (sent_local_reply_) {
return FilterDataStatus::Continue;
}

const envoy_dynamic_module_type_on_http_filter_response_body_status status =
config_->on_http_filter_response_body_(thisAsVoidPtr(), in_module_filter_, end_of_stream);
return static_cast<FilterDataStatus>(status);
};

FilterTrailersStatus DynamicModuleHttpFilter::encodeTrailers(ResponseTrailerMap& trailers) {
if (sent_local_reply_) {
return FilterTrailersStatus::Continue;
}

response_trailers_ = &trailers;
const envoy_dynamic_module_type_on_http_filter_response_trailers_status status =
config_->on_http_filter_response_trailers_(thisAsVoidPtr(), in_module_filter_);
Expand All @@ -71,6 +93,18 @@ FilterMetadataStatus DynamicModuleHttpFilter::encodeMetadata(MetadataMap&) {
return FilterMetadataStatus::Continue;
}

void DynamicModuleHttpFilter::sendLocalReply(
Code code, absl::string_view body,
std::function<void(ResponseHeaderMap& headers)> modify_headers,
const absl::optional<Grpc::Status::GrpcStatus> grpc_status, absl::string_view details) {
if (sent_local_reply_) {
return;
}

sent_local_reply_ = true;
decoder_callbacks_->sendLocalReply(code, body, modify_headers, grpc_status, details);
}

void DynamicModuleHttpFilter::encodeComplete(){};

} // namespace HttpFilters
Expand Down
8 changes: 8 additions & 0 deletions source/extensions/filters/http/dynamic_modules/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class DynamicModuleHttpFilter : public Http::StreamFilter,
}
void encodeComplete() override;

void sendLocalReply(Code code, absl::string_view body,
std::function<void(ResponseHeaderMap& headers)> modify_headers,
const absl::optional<Grpc::Status::GrpcStatus> grpc_status,
absl::string_view details);

// The callbacks for the filter. They are only valid until onDestroy() is called.
StreamDecoderFilterCallbacks* decoder_callbacks_ = nullptr;
StreamEncoderFilterCallbacks* encoder_callbacks_ = nullptr;
Expand All @@ -73,6 +78,9 @@ class DynamicModuleHttpFilter : public Http::StreamFilter,
}

private:
// This ensures that we do not re-enter the dynamic filter hooks when sending a local reply
bool sent_local_reply_ = false;
bplotnick marked this conversation as resolved.
Show resolved Hide resolved

/**
* This is a helper function to get the `this` pointer as a void pointer which is passed to the
* various event hooks.
Expand Down
Loading