How do I get http_call_response
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
}
Use dispatch_http_call() and on_http_call_response.
Try self.get_http_request_body(0, _body_size)
Use
dispatch_http_call()andon_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
@alexhu20 If i understood you correctly, this might be what you are looking for :)
@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
@alexhu20 Do you need to make several calls for each request or once (at the beginning/when the filter starts)?
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
@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.
I have something with registering a callback map HashMap<u32, Box<dyn HttpCallHandler>>, 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?
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.
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 https://github.com/proxy-wasm/proxy-wasm-rust-sdk/pull/265