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

How to modify HTTP response body? #19

Open
soraliu opened this issue Jun 2, 2020 · 13 comments
Open

How to modify HTTP response body? #19

soraliu opened this issue Jun 2, 2020 · 13 comments

Comments

@soraliu
Copy link

soraliu commented Jun 2, 2020

I found that there are a lot of materials about modifying HTTP response headers. I want to inject some static codes into the HTTP response body, and how can I do it?

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>title</title>
  </head>
  <body>
    body
  </body>
</html>

I want to inject some codes into HTML.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>title</title>
    <!-- the following codes are injected by envoy-filter -->
    <meta name="custom" content="value" />
  </head>
  <body>
    body
  </body>
</html>

I would be very appreciative if somebody can help me.

@soraliu
Copy link
Author

soraliu commented Jun 3, 2020

@yuval-k Hi man, may I ask for your help? 😂

@yuval-k
Copy link
Member

yuval-k commented Jun 16, 2020

Hi!
I'm not sure if it is possible with the current ABI; will have to check if the upstream ABI has updated to allow for this...

@RichiCoder1
Copy link

Would be very interested in this. Could potentially enable something like https://github.com/inikulin/parse5/blob/master/packages/parse5-html-rewriting-stream/docs/index.md or https://github.com/cloudflare/lol-html at the proxy level (assuming there's also a way to get body chunks)

@esnible
Copy link

esnible commented Aug 30, 2020

My guess is that we are expected to return StopIterationAndBuffer from onResponseBody(), then later use the buffers in a call to send_local_response(). I haven't gotten this working yet but that is the route I am attempting.

@RichiCoder1
Copy link

@esnible The appropriate APIs exists it looks like. See the Rust sdk for an example. https://github.com/proxy-wasm/proxy-wasm-rust-sdk/blob/master/examples/http_body.rs

Specifically set_response_body which appears to just be an abstraction of set_buffer which may or may not exist in this SDK?

@yuval-k
Copy link
Member

yuval-k commented Aug 31, 2020

i would not use send_local_reply for this purpose; i believe send_local_reply also sends the headers, so not sure if it can be called when orResponseBody happens;

what you probably want to do is manipulate the buffer as it goes through onResponseBody. this way you also minimize memory footprint

@esnible
Copy link

esnible commented Aug 31, 2020

@yuval-k I have a user who wants to use WASM to recreate his current NGINX configuration which uses error_page and proxy_pass to fetch UI from one microservice to replace the error page generated by a different microservice.

I'm not an NGINX guy. We thought maybe an EnvoyFilter could detect HTTP status codes on text/html pages, and if it saw a problem do httpCall() and use the results of that call as the body to the original call, keeping the original headers.

What do you think?

@ceastman-ibm
Copy link

ceastman-ibm commented Aug 31, 2020

Here is the current workaround that I have: @esnible and I are working on this together:

function envoy_on_response(response_handle)
headers = response_handle:headers()
streamInfo = response_handle:streamInfo()
dynamicMetadata = streamInfo:dynamicMetadata()
requestData = dynamicMetadata:get("request_headers")

-- This section of code is for redirecting to maintenance if a 4xx or 5xx status code is detected

if string.match(headers:get(":status"), '[4-5].*') and requestData["accept"] ~= nil and string.find(string.lower(requestData["accept"]), "application/json") == nil then
maintenance_url = "https://"..requestData["request_authority"].."/maintenance/"..headers:get(":status")
headers:replace("location", maintenance_url)
headers:replace(":status", "302")
end

basically the goal is that the browser url doesn't change(redirect), it just displays a nice custom html page instead of the enovy test status page.

the maintenance microservice is inside the istio mesh.

@cortex93
Copy link

Hi, any news on that ?

All other sdks (cpp, rust, go, zig) are importing proxy_set_buffer_bytes to allow to modify the BufferTypeValues.HttpResponseBody.

The goal is to be able to do the same thing as in this envoy sample https://github.com/envoyproxy/envoy/blob/6834081f835d609606e77ef84d734e30fc403d58/examples/wasm-cc/envoy_filter_http_wasm_updated_example.cc#L83

@yuval-k
Copy link
Member

yuval-k commented Jul 29, 2021

@cortex93 whatever you can do with other WASM envoy runtime, you should be able to do with this one as well.

@cortex93
Copy link

@cortex93 whatever you can do with other WASM envoy runtime, you should be able to do with this one as well.

I may have missed something but https://github.com/solo-io/proxy-runtime/blob/master/assembly/imports.ts is not importing proxy_set_buffer_bytes and this sdk is not exporting some helper as others sdk.

It seems clear to you but this thread is all about that and after more than a year, no one come with a working solution. Maybe you can share a sample ?

@kathuriasumit
Copy link

Hi @yuval-k, I am trying out the proxy_set_buffer_bytes implementation, This basically overrides the bytes in existing buffer, What if there is a need to enrich response body and required additional bytes in buffer? Do you have any suggestion for this use case?

@kathuriasumit
Copy link

Hi @yuval-k, I am trying out the proxy_set_buffer_bytes implementation, This basically overrides the bytes in existing buffer, What if there is a need to enrich response body and required additional bytes in buffer? Do you have any suggestion for this use case?

Sharing below one of working approach.

onResponseBody(body_buffer_length: usize, end_of_stream: bool): FilterDataStatusValues {
   if (!end_of_stream) {
     this.upstream_response_size = this.upstream_response_size + u32(body_buffer_length);
     return FilterDataStatusValues.StopIterationAndBuffer;
   }
   // read the response
   let original_response_body_buffer = get_buffer_bytes(BufferTypeValues.HttpResponseBody, 0, 
   this.upstream_response_size);
   let response_body_buffer = ...;
   // update the buffer
   set_buffer_bytes(BufferTypeValues.HttpResponseBody, 0, response_body_buffer.byteLength, response_body_buffer);
   return FilterDataStatusValues.Continue
   }
 
onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues {
  // update the content length header
  stream_context.headers.response.replace("content-length", new_response_content_length.toString());
  }
   ```
   
   
   

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants