envoyproxy / envoyproxy/envoy

Setting the HttpRequestBody buffer from inside an httpCall callback in a WASM filter

Open
#17,208 13 comments 1 reaction 0 assignees View on GitHub
area/wasm no stalebot
Dominant language
C++
Stars
28.9k
Forks
5.6k
Avg merge
1d 20h
Merged PRs (30d)
428

Description

I am writing a custom Envoy filter using the [WASM C++ SDK](https://github.com/proxy-wasm/proxy-wasm-cpp-sdk) and I am trying to make an `httpCall` that is able to `setBuffer` the `WasmBufferType::HttpRequestBody` from inside its callback. The `httpCall` is invoked inside the overriden `onRequestBody` method of the filter's `Context` for the reason I explain below.

So the use case is that the filter intercepts certain incoming HTTP POST requests and passes their body as payload to a sidecar server using `httpCall`, which then does some manipulation on the body. The callback will then update the body of the request and invoke `continueRequest()` so that the POST request continues to its destination.

I am able to successfully update the body of a request as follows (simplified code snippet):
```cpp
// Works fine.
FilterDataStatus FilterContext::onRequestBody(size_t body_buffer_length, bool end_of_stream) {
if (!end_of_stream) {
auto body = getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_buffer_length);
auto updated_body = ...; // some logic that edits the JSON body.
WasmResult result = setBuffer(WasmBufferType::HttpRequestBody, 0, body_buffer_length, updated_body); // This succeeds.
}

return FilterDataStatus::Continue;
}
```

However, if I try to do this via an `httpCall` callback, like this (simplified some custom logic that does not matter to focus on the issue):
```cpp
FilterDataStatus FilterContext::onRequestBody(size_t body_buffer_length, bool end_of_stream) {
if (!end_of_stream) {
auto body = getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_buffer_length);
rootContext()->remoteCall(id(), body->toString()); // This call happens in the root context below.
return FilterDataStatus::StopIterationAndBuffer;
}

return FilterDataStatus::Continue;
}

// Function in the root context.
void FilterRootContext::remoteCall(uint32_t stream_context_id, std::string request_body) {
// some custom logic
...

// Calls the sidecar server, which manipulates the body of the HTTP POST request.
WasmResult http_call_result = httpCall(cluster_name_, headers, request_body, HeaderStringPairs(), 5000,
[stream_context_id, request_body](uint32_t header_count, size_t body_size, uint32_t trailer_count) {
// Callback is triggered inside root context so we need to switch the
// background context from root context to the current stream context.
getContext(stream_context_id)->setEffectiveContext();

auto status = getHeaderMapValue(WasmHeaderMapType::HttpCallResponseHeaders, ":status")->toString(); // 200
if (status != "200") {
// error handling with local response
sendLocalResponse(...);
return;
}

// The sidecar server gives an updated body in its response.
auto response_body = getBufferBytes(WasmBufferType::HttpCallResponseBody, 0, body_size)->toString();

// Returns an empty string! But this shouldn't match the request_body that was bind to this callback?
auto test = getBufferBytes(WasmBufferType::HttpRequestBody, 0, request_body.size())->toString();

// This fails with BadRequest!
WasmResult result = setBuffer(WasmBufferType::HttpRequestBody, 0, request_body.size(), response_body);

// Continue request with the updated body.
continueRequest();
});

// Error handling if httpCall failed ...
}
```

Then the `setBuffer(WasmBufferType::HttpRequestBody, 0, request_body.size(), response_body)` inside the `httpCall` callback fails with BadRequest. And if I try (just for testing) to do `getBufferBytes(WasmBufferType::HttpCallResponseBody, 0, body_size)->toString()` as above to grab the current HTTP POST request body from the context, then it returns the empty string, which is strange. However, I have already set the current stream context via `getContext(stream_context_id)->setEffectiveContext();`, which I thought would allow me to do the above, and get/update the body.

Am I doing something wrong here? Any help would be much appreciated, as I cannot seem to find anything similar in the very few examples or unit tests that are around (I have only seen examples where the `httpCall` callback is updating headers, but here I want to update the body of the request). Thanks a ton!

Contributor guide

Open the contributing guide

Research direction

Start by tracing the onRequestBody entry point, the root-context httpCall callback, setEffectiveContext(), and the setBuffer/getBufferBytes operations shown in the report. Read the WASM C++ SDK examples and unit tests mentioned by the author to determine whether request-body access from an httpCall callback is supported; done means documenting or reproducing the supported behavior and identifying the relevant limitation or fix.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, wasm
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.