Skip to content

Commit

Permalink
Add Promise support for http callout
Browse files Browse the repository at this point in the history
Signed-off-by: jizhuozhi.george <jizhuozhi.george@bytedance.com>
  • Loading branch information
jizhuozhi committed Oct 11, 2024
1 parent 3f4274e commit 0ad1305
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [HTTP Headers](./examples/http_headers/)
- [HTTP Response body](./examples/http_body/)
- [HTTP Configuration](./examples/http_config/)
- [HTTP Parallel Call](./examples/http_parallel_call/)
- [gRPC Auth (random)](./examples/grpc_auth_random/)

## Articles & blog posts from the community
Expand Down
22 changes: 22 additions & 0 deletions examples/http_parallel_call/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
publish = false
name = "proxy-wasm-example-http-parallel-call"
version = "0.0.1"
authors = ["Zhuozhi Ji <jizhuozhi.george@google.com>"]
description = "Proxy-Wasm plugin example: HTTP parallel call"
license = "Apache-2.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
log = "0.4"
proxy-wasm = { path = "../../" }

[profile.release]
lto = true
opt-level = 3
codegen-units = 1
panic = "abort"
strip = "debuginfo"
27 changes: 27 additions & 0 deletions examples/http_parallel_call/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Proxy-Wasm plugin example: HTTP parallel call

Proxy-Wasm plugin that makes multiply HTTP callout and combine responses as final response .

### Building

```sh
$ cargo build --target wasm32-wasi --release
```

### Using in Envoy

This example can be run with [`docker compose`](https://docs.docker.com/compose/install/)
and has a matching Envoy configuration.

```sh
$ docker compose up
```

#### Access granted.

Send HTTP request to `localhost:10000/headers`:

```sh
$ curl localhost:10000/headers
Hello, World!\n
```
36 changes: 36 additions & 0 deletions examples/http_parallel_call/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

services:
envoy:
image: envoyproxy/envoy:v1.31-latest
hostname: envoy
ports:
- "10000:10000"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
- ./target/wasm32-wasi/release:/etc/envoy/proxy-wasm-plugins
networks:
- envoymesh
depends_on:
- httpbin
httpbin:
image: mccutchen/go-httpbin
hostname: httpbin
ports:
- "8080:8080"
networks:
- envoymesh
networks:
envoymesh: {}
68 changes: 68 additions & 0 deletions examples/http_parallel_call/envoy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

static_resources:
listeners:
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_routes
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: httpbin
http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
name: "http_parallel_call"
vm_config:
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_parallel_call.wasm"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: httpbin
connect_timeout: 5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: httpbin
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: httpbin
port_value: 8080
129 changes: 129 additions & 0 deletions examples/http_parallel_call/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proxy_wasm::hostcalls;
use proxy_wasm::promise::Promise;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use std::collections::HashMap;
use std::rc::Rc;
use std::time::Duration;

proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(HttpParallelCall::default()) });
}}

#[derive(Default)]
struct HttpParallelCall {
m: HashMap<u32, Rc<Promise<(u32, usize, usize, usize)>>>,
}

impl HttpContext for HttpParallelCall {
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
// "Hello, "
let token1 = self
.dispatch_http_call(
"httpbin",
vec![
(":method", "GET"),
(":path", "/base64/SGVsbG8sIAo="),
(":authority", "httpbin.org"),
],
None,
vec![],
Duration::from_secs(1),
)
.unwrap();

// "World!"
let token2 = self
.dispatch_http_call(
"httpbin",
vec![
(":method", "GET"),
(":path", "/base64/V29ybGQhCg=="),
(":authority", "httpbin.org"),
],
None,
vec![],
Duration::from_secs(1),
)
.unwrap();

let promise1 = Promise::new();
let promise2 = Promise::new();
self.m.insert(token1, promise1.clone());
self.m.insert(token2, promise2.clone());

Promise::all_of(vec![
promise1
.then(|(_, _, _body_size, _)| get_http_call_response_body_string(0, _body_size))
.then(|body| body.unwrap_or_else(|| "".to_string())),
promise2
.then(|(_, _, _body_size, _)| get_http_call_response_body_string(0, _body_size))
.then(|body| body.unwrap_or_else(|| "".to_string())),
])
.then(|results| {
send_http_response(
200,
vec![],
Some(
format!(
"{}{}\n",
results[0].strip_suffix("\n").unwrap(),
results[1].strip_suffix("\n").unwrap()
)
.as_bytes(),
),
);
});

Action::Pause
}

fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
self.set_http_response_header("Powered-By", Some("proxy-wasm"));
Action::Continue
}
}

impl Context for HttpParallelCall {
fn on_http_call_response(
&mut self,
_token_id: u32,
_num_headers: usize,
_body_size: usize,
_num_trailers: usize,
) {
let promise = self.m.remove(&_token_id);
promise
.unwrap()
.fulfill((_token_id, _num_headers, _body_size, _num_trailers));
}
}

fn get_http_call_response_body_string(start: usize, max_size: usize) -> Option<String> {
match hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() {
None => None,
Some(bytes) => {
let body_string = String::from_utf8(bytes.to_vec()).unwrap();
Some(body_string)
}
}
}

fn send_http_response(status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>) {
hostcalls::send_http_response(status_code, headers, body).unwrap()
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

pub mod hostcalls;
pub mod promise;
pub mod traits;
pub mod types;

Expand Down
Loading

0 comments on commit 0ad1305

Please sign in to comment.