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 do I get http_call_response #257

Open
alexhu20 opened this issue Sep 3, 2024 · 10 comments
Open

How do I get http_call_response #257

alexhu20 opened this issue Sep 3, 2024 · 10 comments

Comments

@alexhu20
Copy link

alexhu20 commented Sep 3, 2024

Description

I have use case that need to make a separate http call in wasm and getting a response back. I only see that the base context has an API called on_http_call_response to fetch the entire response body.

Is there a way to do so in my client implementation?


Another question:
The response body example seems incorrect, I have to do the following in order to buffer the entire response body, am I doing it wrong?

    fn on_http_request_body(&mut self, _body_size: usize, _end_of_stream: bool) -> Action {
        let msg = &format!("[on_http_request_body] ContextID {} on request body.", self.context_id);
        log(Info, msg);

        if !_end_of_stream {
            info!("[on_http_request_body] ContextID {} current body size: {}", self.context_id, self.request_body_length);
            
            if let Some(body_size) = self.get_http_request_body(self.request_body_length, _body_size) {
                let mut body_slice: Vec<u8> = body_size;
                self.request_body.append(&mut body_slice);
            }
            else { 
                info!("[on_http_request_body] ContextID {} else request body error???", self.context_id);
            }
            self.request_body_length += _body_size;
            return Action::Pause
        }
        
        Action::Continue
    }
@antonengelhardt
Copy link

Use dispatch_http_call() and on_http_call_response.


Try self.get_http_request_body(0, _body_size)

@alexhu20
Copy link
Author

alexhu20 commented Sep 3, 2024

Use dispatch_http_call() and on_http_call_response.

Try self.get_http_request_body(0, _body_size)

I see the contract in the base context trait, e.g.:

    fn on_http_call_response(&mut self, _token_id: u32, _num_headers: usize, _body_size: usize, _num_trailers: usize) {
        let _ = 
            self.get_http_call_response_body(0, _body_size)
                .map(|value| {
                    let response_body = String::from_utf8(value.to_vec()).unwrap();
                    info!("[on_http_call_response] token id:{}, response body: {}", _token_id, response_body)
                });
    }

Is this what you are referering to? is there a way to get the response else where? other than in the context

For example I have a client called depdendenClient, and I need to dispatch a http call and getting a response body back then modify the request header

@antonengelhardt
Copy link

@alexhu20 If i understood you correctly, this might be what you are looking for :)

@alexhu20
Copy link
Author

alexhu20 commented Sep 5, 2024

@antonengelhardt yes! this is the one I am talking about. However, is there another way to handle this outside of the context? Reason being we have several outbound calls rely on the dispatch_http_call before send it to upstream

@antonengelhardt
Copy link

@alexhu20 Do you need to make several calls for each request or once (at the beginning/when the filter starts)?

@alexhu20
Copy link
Author

alexhu20 commented Sep 5, 2024

We do make several calls for each request, and rely on the result then determine whether continue with the upstream or terminate send response back to the downstream

@antonengelhardt
Copy link

@alexhu20 You get the token id when you dispatch a call, you can store it in the context struct and you will also get the token id when the response callback is executed. Then you can match it and handle the request furthermore.

Alternatively, you can store the state as an enum in the context struct and work with that.

For both methods, you can check my pinned repo.

@alexhu20
Copy link
Author

alexhu20 commented Sep 10, 2024

I have something with registering a callback map HashMap<u32, Box>, and the HttpCallHandler defines the contract on how to further process the response from the http_call individually

pub struct CustomHttpContext {
    pub http_call_handlers: HashMap<u32, Box<dyn HttpCallHandler>>
}

pub trait HttpCallHandler {
    fn process_http_call(&self, headers: Vec<(String, String)>, body: Option<Vec<u8>>) -> Result<(), Err> {
    }

    fn on_error(&self, status_code: u32, error: WasmPluginError) {
        error!("Error while handle http call {:?}", error)
    }

    fn on_ok(&self) {
        info!("succeed!")
    }
}

impl Context for CustomHttpContext {
    fn on_http_call_response(&mut self, token_id: u32, _num_headers: usize, body_size: usize, _num_trailers: usize) {
        info!("[on_http_call_response] ContextID {}, token_id: {}", self.context_id, token_id);
        let http_call_headers = self.get_http_call_response_headers();
        let http_call_response = self.get_http_call_response_body(0, body_size);

        match self.http_call_handlers.remove(&token_id) {
            None => {}
            Some(response_handler) => {
                info!("[on_http_call_response] has reached");
                match response_handler.process_http_call(http_call_headers, http_call_response) {
                    Ok(_) => {
                        response_handler.on_ok()
                    }
                    Err(e) => {
                        response_handler.on_error(400, e)
                    }
                } 
            }
        }
    }
}

impl HttpContext for CustomHttpContext {
    fn on_http_request_headers(&mut self, num_headers: usize, end_of_stream: bool) -> Action {
        let result = dispatch_http_call(.....);
        match result {
                    Ok(token) => {
                        let http_call = CustomHttpHandler { token_id: token };
                        &self.http_call_handlers.insert(token, Box::new(http_call));
                    }
                    Err(Status) => {
                        error!("error");
                        return Action::Continue;
                    }
                }
    }
}

In this something you'd advise to do so?

@antonengelhardt
Copy link

I am not really sure if this works...

You can dispatch multiple calls in any implementation of RootContext because you can make use of the on_tick function there and by keeping track of some state enum.

@jizhuozhi
Copy link

jizhuozhi commented Oct 13, 2024

Hello, @alexhu20 , I also encountered a similar problem to yours, and referring to asynchronous programming implementations in other languages, I think JavaScript's Promise is very suitable for this scenario (single-threaded execution, driven by other runtime's event loops). Therefore, I implemented this programming paradigm in proxy-wasm and submitted a PR. You can see if the implementation here meets your needs #265

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

3 participants