Skip to content

Commit 0ad1305

Browse files
committed
Add Promise support for http callout
Signed-off-by: jizhuozhi.george <[email protected]>
1 parent 3f4274e commit 0ad1305

File tree

8 files changed

+575
-0
lines changed

8 files changed

+575
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [HTTP Headers](./examples/http_headers/)
2222
- [HTTP Response body](./examples/http_body/)
2323
- [HTTP Configuration](./examples/http_config/)
24+
- [HTTP Parallel Call](./examples/http_parallel_call/)
2425
- [gRPC Auth (random)](./examples/grpc_auth_random/)
2526

2627
## Articles & blog posts from the community
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
publish = false
3+
name = "proxy-wasm-example-http-parallel-call"
4+
version = "0.0.1"
5+
authors = ["Zhuozhi Ji <[email protected]>"]
6+
description = "Proxy-Wasm plugin example: HTTP parallel call"
7+
license = "Apache-2.0"
8+
edition = "2018"
9+
10+
[lib]
11+
crate-type = ["cdylib"]
12+
13+
[dependencies]
14+
log = "0.4"
15+
proxy-wasm = { path = "../../" }
16+
17+
[profile.release]
18+
lto = true
19+
opt-level = 3
20+
codegen-units = 1
21+
panic = "abort"
22+
strip = "debuginfo"

examples/http_parallel_call/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Proxy-Wasm plugin example: HTTP parallel call
2+
3+
Proxy-Wasm plugin that makes multiply HTTP callout and combine responses as final response .
4+
5+
### Building
6+
7+
```sh
8+
$ cargo build --target wasm32-wasi --release
9+
```
10+
11+
### Using in Envoy
12+
13+
This example can be run with [`docker compose`](https://docs.docker.com/compose/install/)
14+
and has a matching Envoy configuration.
15+
16+
```sh
17+
$ docker compose up
18+
```
19+
20+
#### Access granted.
21+
22+
Send HTTP request to `localhost:10000/headers`:
23+
24+
```sh
25+
$ curl localhost:10000/headers
26+
Hello, World!\n
27+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
services:
16+
envoy:
17+
image: envoyproxy/envoy:v1.31-latest
18+
hostname: envoy
19+
ports:
20+
- "10000:10000"
21+
volumes:
22+
- ./envoy.yaml:/etc/envoy/envoy.yaml
23+
- ./target/wasm32-wasi/release:/etc/envoy/proxy-wasm-plugins
24+
networks:
25+
- envoymesh
26+
depends_on:
27+
- httpbin
28+
httpbin:
29+
image: mccutchen/go-httpbin
30+
hostname: httpbin
31+
ports:
32+
- "8080:8080"
33+
networks:
34+
- envoymesh
35+
networks:
36+
envoymesh: {}
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
static_resources:
16+
listeners:
17+
address:
18+
socket_address:
19+
address: 0.0.0.0
20+
port_value: 10000
21+
filter_chains:
22+
- filters:
23+
- name: envoy.filters.network.http_connection_manager
24+
typed_config:
25+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
26+
stat_prefix: ingress_http
27+
codec_type: AUTO
28+
route_config:
29+
name: local_routes
30+
virtual_hosts:
31+
- name: local_service
32+
domains:
33+
- "*"
34+
routes:
35+
- match:
36+
prefix: "/"
37+
route:
38+
cluster: httpbin
39+
http_filters:
40+
- name: envoy.filters.http.wasm
41+
typed_config:
42+
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
43+
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
44+
value:
45+
config:
46+
name: "http_parallel_call"
47+
vm_config:
48+
runtime: "envoy.wasm.runtime.v8"
49+
code:
50+
local:
51+
filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_http_parallel_call.wasm"
52+
- name: envoy.filters.http.router
53+
typed_config:
54+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
55+
clusters:
56+
- name: httpbin
57+
connect_timeout: 5s
58+
type: STRICT_DNS
59+
lb_policy: ROUND_ROBIN
60+
load_assignment:
61+
cluster_name: httpbin
62+
endpoints:
63+
- lb_endpoints:
64+
- endpoint:
65+
address:
66+
socket_address:
67+
address: httpbin
68+
port_value: 8080
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use proxy_wasm::hostcalls;
16+
use proxy_wasm::promise::Promise;
17+
use proxy_wasm::traits::*;
18+
use proxy_wasm::types::*;
19+
use std::collections::HashMap;
20+
use std::rc::Rc;
21+
use std::time::Duration;
22+
23+
proxy_wasm::main! {{
24+
proxy_wasm::set_log_level(LogLevel::Trace);
25+
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(HttpParallelCall::default()) });
26+
}}
27+
28+
#[derive(Default)]
29+
struct HttpParallelCall {
30+
m: HashMap<u32, Rc<Promise<(u32, usize, usize, usize)>>>,
31+
}
32+
33+
impl HttpContext for HttpParallelCall {
34+
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
35+
// "Hello, "
36+
let token1 = self
37+
.dispatch_http_call(
38+
"httpbin",
39+
vec![
40+
(":method", "GET"),
41+
(":path", "/base64/SGVsbG8sIAo="),
42+
(":authority", "httpbin.org"),
43+
],
44+
None,
45+
vec![],
46+
Duration::from_secs(1),
47+
)
48+
.unwrap();
49+
50+
// "World!"
51+
let token2 = self
52+
.dispatch_http_call(
53+
"httpbin",
54+
vec![
55+
(":method", "GET"),
56+
(":path", "/base64/V29ybGQhCg=="),
57+
(":authority", "httpbin.org"),
58+
],
59+
None,
60+
vec![],
61+
Duration::from_secs(1),
62+
)
63+
.unwrap();
64+
65+
let promise1 = Promise::new();
66+
let promise2 = Promise::new();
67+
self.m.insert(token1, promise1.clone());
68+
self.m.insert(token2, promise2.clone());
69+
70+
Promise::all_of(vec![
71+
promise1
72+
.then(|(_, _, _body_size, _)| get_http_call_response_body_string(0, _body_size))
73+
.then(|body| body.unwrap_or_else(|| "".to_string())),
74+
promise2
75+
.then(|(_, _, _body_size, _)| get_http_call_response_body_string(0, _body_size))
76+
.then(|body| body.unwrap_or_else(|| "".to_string())),
77+
])
78+
.then(|results| {
79+
send_http_response(
80+
200,
81+
vec![],
82+
Some(
83+
format!(
84+
"{}{}\n",
85+
results[0].strip_suffix("\n").unwrap(),
86+
results[1].strip_suffix("\n").unwrap()
87+
)
88+
.as_bytes(),
89+
),
90+
);
91+
});
92+
93+
Action::Pause
94+
}
95+
96+
fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
97+
self.set_http_response_header("Powered-By", Some("proxy-wasm"));
98+
Action::Continue
99+
}
100+
}
101+
102+
impl Context for HttpParallelCall {
103+
fn on_http_call_response(
104+
&mut self,
105+
_token_id: u32,
106+
_num_headers: usize,
107+
_body_size: usize,
108+
_num_trailers: usize,
109+
) {
110+
let promise = self.m.remove(&_token_id);
111+
promise
112+
.unwrap()
113+
.fulfill((_token_id, _num_headers, _body_size, _num_trailers));
114+
}
115+
}
116+
117+
fn get_http_call_response_body_string(start: usize, max_size: usize) -> Option<String> {
118+
match hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap() {
119+
None => None,
120+
Some(bytes) => {
121+
let body_string = String::from_utf8(bytes.to_vec()).unwrap();
122+
Some(body_string)
123+
}
124+
}
125+
}
126+
127+
fn send_http_response(status_code: u32, headers: Vec<(&str, &str)>, body: Option<&[u8]>) {
128+
hostcalls::send_http_response(status_code, headers, body).unwrap()
129+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
pub mod hostcalls;
16+
pub mod promise;
1617
pub mod traits;
1718
pub mod types;
1819

0 commit comments

Comments
 (0)