Skip to content

Commit 998ce82

Browse files
authored
Add support for setting HTTP bodies. (#2)
Signed-off-by: Gregory Brail <[email protected]>
1 parent a7c35a0 commit 998ce82

File tree

5 files changed

+121
-0
lines changed

5 files changed

+121
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ crate-type = ["cdylib"]
4343
name = "http_headers"
4444
path = "examples/http_headers.rs"
4545
crate-type = ["cdylib"]
46+
47+
[[example]]
48+
name = "http_body"
49+
path = "examples/http_body.rs"
50+
crate-type = ["cdylib"]

examples/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,15 @@ rust_binary(
3636
"//cargo:log",
3737
],
3838
)
39+
40+
rust_binary(
41+
name = "http_body",
42+
srcs = ["http_body.rs"],
43+
crate_type = "cdylib",
44+
edition = "2018",
45+
out_binary = True,
46+
deps = [
47+
"//:proxy_wasm",
48+
"//cargo:log",
49+
],
50+
)

examples/http_body.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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::traits::*;
16+
use proxy_wasm::types::*;
17+
18+
#[no_mangle]
19+
pub fn _start() {
20+
proxy_wasm::set_log_level(LogLevel::Trace);
21+
proxy_wasm::set_http_context(|_, _| -> Box<dyn HttpContext> { Box::new(HttpBody::new()) });
22+
}
23+
24+
#[derive(Default)]
25+
struct HttpBody {
26+
total_body_size: usize,
27+
}
28+
29+
impl HttpBody {
30+
fn new() -> HttpBody {
31+
Default::default()
32+
}
33+
}
34+
35+
impl Context for HttpBody {}
36+
37+
impl HttpContext for HttpBody {
38+
fn on_http_response_headers(&mut self, _: usize) -> Action {
39+
// If there is a Content-Length header and we change the length of
40+
// the body later, then clients will break. So remove it.
41+
// We must do this here, because once we exit this function we
42+
// can no longer modify the response headers.
43+
self.set_http_response_header("content-length", None);
44+
// Don't continue to the next callout in the chain because we might
45+
// modify the body.
46+
Action::Pause
47+
}
48+
49+
fn on_http_response_body(&mut self, body_size: usize, end_of_stream: bool) -> Action {
50+
self.total_body_size += body_size;
51+
if !end_of_stream {
52+
// Wait -- we'll be called again when the complete body is buffered
53+
// at the host side.
54+
return Action::Pause;
55+
}
56+
57+
// Replace the message body if it contains the text "secret".
58+
// Since we returned "Pause" previuously, this will return the whole body.
59+
// However, we have to calculate the size ourselves.
60+
if let Some(body_bytes) = self.get_http_response_body(0, self.total_body_size) {
61+
let body_str = String::from_utf8(body_bytes).unwrap();
62+
if body_str.find("secret").is_some() {
63+
let new_body = format!(
64+
"Original message body ({} bytes) redacted.",
65+
self.total_body_size
66+
);
67+
self.set_http_response_body(0, self.total_body_size, &new_body.into_bytes());
68+
}
69+
}
70+
Action::Continue
71+
}
72+
}

src/hostcalls.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,30 @@ pub fn get_buffer(
127127
}
128128
}
129129

130+
extern "C" {
131+
fn proxy_set_buffer_bytes(
132+
buffer_type: BufferType,
133+
start: usize,
134+
size: usize,
135+
buffer_data: *const u8,
136+
buffer_size: usize,
137+
) -> Status;
138+
}
139+
140+
pub fn set_buffer(
141+
buffer_type: BufferType,
142+
start: usize,
143+
size: usize,
144+
value: &[u8],
145+
) -> Result<(), Status> {
146+
unsafe {
147+
match proxy_set_buffer_bytes(buffer_type, start, size, value.as_ptr(), value.len()) {
148+
Status::Ok => Ok(()),
149+
status => panic!("unexpected status: {}", status as u32),
150+
}
151+
}
152+
}
153+
130154
extern "C" {
131155
fn proxy_get_header_map_pairs(
132156
map_type: MapType,

src/traits.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ pub trait HttpContext: Context {
184184
hostcalls::get_buffer(BufferType::HttpRequestBody, start, max_size).unwrap()
185185
}
186186

187+
fn set_http_request_body(&self, start: usize, size: usize, value: &[u8]) {
188+
hostcalls::set_buffer(BufferType::HttpRequestBody, start, size, value).unwrap()
189+
}
190+
187191
fn on_http_request_trailers(&mut self, _num_trailers: usize) -> Action {
188192
Action::Continue
189193
}
@@ -244,6 +248,10 @@ pub trait HttpContext: Context {
244248
hostcalls::get_buffer(BufferType::HttpResponseBody, start, max_size).unwrap()
245249
}
246250

251+
fn set_http_response_body(&self, start: usize, size: usize, value: &[u8]) {
252+
hostcalls::set_buffer(BufferType::HttpResponseBody, start, size, value).unwrap()
253+
}
254+
247255
fn on_http_response_trailers(&mut self, _num_trailers: usize) -> Action {
248256
Action::Continue
249257
}

0 commit comments

Comments
 (0)