Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit 5a6a7b0

Browse files
authored
Merge pull request #76 from wimspaargaren/proxy-set-buffer
feat: support proxy set buffer bytes
2 parents be514f5 + f23f536 commit 5a6a7b0

File tree

11 files changed

+279
-0
lines changed

11 files changed

+279
-0
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ test:
1919
test.e2e:
2020
docker run -it -w /tmp/proxy-wasm-go -v $(shell pwd):/tmp/proxy-wasm-go getenvoy/proxy-wasm-go-sdk-ci:istio-${ISTIO_VERSION} go test -v ./e2e
2121

22+
test.e2e.single:
23+
docker run -it -w /tmp/proxy-wasm-go -v $(shell pwd):/tmp/proxy-wasm-go getenvoy/proxy-wasm-go-sdk-ci:istio-${ISTIO_VERSION} go test -v ./e2e -run ${name}
24+
2225
run:
2326
docker run --entrypoint='/usr/local/bin/envoy' \
2427
-p 18000:18000 -p 8099:8099 \

e2e/e2e_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,28 @@ func TestE2E_http_headers(t *testing.T) {
117117
assert.True(t, strings.Contains(out, "server: envoy"))
118118
}
119119

120+
func TestE2E_http_body(t *testing.T) {
121+
cmd, stdErr := startExample(t, "http_body")
122+
defer func() {
123+
require.NoError(t, cmd.Process.Kill())
124+
}()
125+
126+
req, err := http.NewRequest("GET", envoyEndpoint, bytes.NewBuffer([]byte(`{ "example": "body" }`)))
127+
require.NoError(t, err)
128+
129+
r, err := http.DefaultClient.Do(req)
130+
require.NoError(t, err)
131+
defer r.Body.Close()
132+
133+
out := stdErr.String()
134+
fmt.Println(out)
135+
assert.True(t, strings.Contains(out, "body size: 21"))
136+
assert.True(t, strings.Contains(out, `initial request body: { "example": "body" }`))
137+
assert.True(t, strings.Contains(out, "on http request body finished"))
138+
assert.False(t, strings.Contains(out, "failed to set request body"))
139+
assert.False(t, strings.Contains(out, "failed to get request body"))
140+
}
141+
120142
func TestE2E_network(t *testing.T) {
121143
cmd, stdErr := startExample(t, "network")
122144
defer func() {

examples/http_body/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## http_body
2+
3+
this example replaces the request body
4+
5+
```
6+
2020/10/12 13:41:31 proxy_info_log: body size: 29
7+
2020/10/12 13:41:31 proxy_info_log: initial request body: { "initial": "request body" }
8+
2020/10/12 13:41:31 proxy_info_log: on http request body finished
9+
```

examples/http_body/envoy.yaml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
static_resources:
2+
listeners:
3+
- name: main
4+
address:
5+
socket_address:
6+
address: 0.0.0.0
7+
port_value: 18000
8+
filter_chains:
9+
- filters:
10+
- name: envoy.http_connection_manager
11+
config:
12+
stat_prefix: ingress_http
13+
codec_type: auto
14+
route_config:
15+
name: local_route
16+
virtual_hosts:
17+
- name: local_service
18+
domains:
19+
- "*"
20+
routes:
21+
- match:
22+
prefix: "/"
23+
route:
24+
cluster: web_service
25+
http_filters:
26+
- name: envoy.filters.http.wasm
27+
config:
28+
config:
29+
name: "my_plugin"
30+
root_id: "my_root_id"
31+
vm_config:
32+
vm_id: "my_vm_id"
33+
runtime: "envoy.wasm.runtime.v8"
34+
code:
35+
local:
36+
filename: "./examples/http_body/main.go.wasm"
37+
allow_precompiled: true
38+
- name: envoy.router
39+
config: {}
40+
- name: staticreply
41+
address:
42+
socket_address:
43+
address: 127.0.0.1
44+
port_value: 8099
45+
filter_chains:
46+
- filters:
47+
- name: envoy.http_connection_manager
48+
config:
49+
stat_prefix: ingress_http
50+
codec_type: auto
51+
route_config:
52+
name: local_route
53+
virtual_hosts:
54+
- name: local_service
55+
domains:
56+
- "*"
57+
routes:
58+
- match:
59+
prefix: "/"
60+
direct_response:
61+
status: 200
62+
body:
63+
inline_string: "example body\n"
64+
http_filters:
65+
- name: envoy.router
66+
config: {}
67+
68+
clusters:
69+
- name: web_service
70+
connect_timeout: 0.25s
71+
type: static
72+
lb_policy: round_robin
73+
hosts:
74+
- socket_address:
75+
address: 127.0.0.1
76+
port_value: 8099
77+
admin:
78+
access_log_path: "/dev/null"
79+
address:
80+
socket_address:
81+
address: 0.0.0.0
82+
port_value: 8001

examples/http_body/main.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2020 Tetrate
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+
package main
16+
17+
import (
18+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
19+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
20+
)
21+
22+
func main() {
23+
proxywasm.SetNewHttpContext(newContext)
24+
}
25+
26+
type httpBody struct {
27+
// you must embed the default context so that you need not to reimplement all the methods by yourself
28+
proxywasm.DefaultHttpContext
29+
contextID uint32
30+
}
31+
32+
func newContext(rootContextID, contextID uint32) proxywasm.HttpContext {
33+
return &httpBody{contextID: contextID}
34+
}
35+
36+
// override
37+
func (ctx *httpBody) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
38+
proxywasm.LogInfof("body size: %d", bodySize)
39+
if bodySize != 0 {
40+
initialBody, err := proxywasm.GetHttpRequestBody(0, bodySize)
41+
if err != nil {
42+
proxywasm.LogErrorf("failed to get request body: %v", err)
43+
return types.ActionContinue
44+
}
45+
proxywasm.LogInfof("initial request body: %s", string(initialBody))
46+
47+
b := []byte(`{ "another": "body" }`)
48+
49+
err = proxywasm.SetHttpRequestBody(b)
50+
if err != nil {
51+
proxywasm.LogErrorf("failed to set request body: %v", err)
52+
return types.ActionContinue
53+
}
54+
55+
proxywasm.LogInfof("on http request body finished")
56+
}
57+
58+
return types.ActionContinue
59+
}

examples/http_body/main_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxytest"
9+
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
10+
)
11+
12+
func TestHttpBody_OnHttpRequestBody(t *testing.T) {
13+
opt := proxytest.NewEmulatorOption().
14+
WithNewHttpContext(newContext)
15+
host := proxytest.NewHostEmulator(opt)
16+
defer host.Done()
17+
18+
id := host.HttpFilterInitContext()
19+
host.HttpFilterPutRequestBody(id, []byte(`{ "initial": "request body" }`))
20+
21+
res := host.HttpFilterGetRequestBody(id)
22+
assert.Equal(t, `{ "another": "body" }`, string(res))
23+
24+
logs := host.GetLogs(types.LogLevelInfo)
25+
require.Greater(t, len(logs), 1)
26+
27+
assert.Equal(t, "on http request body finished", logs[len(logs)-1])
28+
assert.Equal(t, `initial request body: { "initial": "request body" }`, logs[len(logs)-2])
29+
assert.Equal(t, "body size: 29", logs[len(logs)-3])
30+
}

proxytest/http.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ func (h *httpHostEmulator) httpHostEmulatorProxyGetBufferBytes(bt types.BufferTy
7676
return types.StatusOK
7777
}
7878

79+
func (h *httpHostEmulator) httpHostEmulatorProxySetBufferBytes(bt types.BufferType, start int, maxSize int,
80+
bufferData *byte, bufferSize int) types.Status {
81+
body := proxywasm.RawBytePtrToByteSlice(bufferData, bufferSize)
82+
active := proxywasm.VMStateGetActiveContextID()
83+
stream := h.httpStreams[active]
84+
switch bt {
85+
case types.BufferTypeHttpRequestBody:
86+
stream.requestBody = body
87+
case types.BufferTypeHttpResponseBody:
88+
stream.responseBody = body
89+
default:
90+
panic("unreachable: maybe a bug in this host emulation or SDK")
91+
}
92+
return types.StatusOK
93+
}
94+
7995
// impl rawhostcall.ProxyWASMHost: delegated from hostEmulator
8096
func (h *httpHostEmulator) httpHostEmulatorProxyGetHeaderMapValue(mapType types.MapType, keyData *byte,
8197
keySize int, returnValueData **byte, returnValueSize *int) types.Status {
@@ -351,6 +367,15 @@ func (h *httpHostEmulator) HttpFilterPutRequestBody(contextID uint32, body []byt
351367
len(body), false) // TODO: allow for specifying end_of_stream
352368
}
353369

370+
func (h *httpHostEmulator) HttpFilterGetRequestBody(contextID uint32) []byte {
371+
cs, ok := h.httpStreams[contextID]
372+
if !ok {
373+
log.Fatalf("invalid context id: %d", contextID)
374+
}
375+
376+
return cs.requestBody
377+
}
378+
354379
// impl HostEmulator
355380
func (h *httpHostEmulator) HttpFilterPutResponseBody(contextID uint32, body []byte) {
356381
cs, ok := h.httpStreams[contextID]
@@ -363,6 +388,15 @@ func (h *httpHostEmulator) HttpFilterPutResponseBody(contextID uint32, body []by
363388
len(body), false) // TODO: allow for specifying end_of_stream
364389
}
365390

391+
func (h *httpHostEmulator) HttpFilterGetResponseBody(contextID uint32) []byte {
392+
cs, ok := h.httpStreams[contextID]
393+
if !ok {
394+
log.Fatalf("invalid context id: %d", contextID)
395+
}
396+
397+
return cs.responseBody
398+
}
399+
366400
// impl HostEmulator
367401
func (h *httpHostEmulator) HttpFilterCompleteHttpStream(contextID uint32) {
368402
proxywasm.ProxyOnDone(contextID)

proxytest/proxytest.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ type HostEmulator interface {
4141
HttpFilterPutRequestTrailers(contextID uint32, headers [][2]string)
4242
HttpFilterPutResponseTrailers(contextID uint32, headers [][2]string)
4343
HttpFilterPutRequestBody(contextID uint32, body []byte)
44+
HttpFilterGetRequestBody(contextID uint32) []byte
4445
HttpFilterPutResponseBody(contextID uint32, body []byte)
46+
HttpFilterGetResponseBody(contextID uint32) []byte
4547
HttpFilterCompleteHttpStream(contextID uint32)
4648
HttpFilterGetCurrentStreamAction(contextID uint32) types.Action
4749
HttpFilterGetSentLocalResponse(contextID uint32) *LocalHttpResponse
@@ -116,6 +118,15 @@ func (h *hostEmulator) ProxyGetBufferBytes(bt types.BufferType, start int, maxSi
116118
}
117119
}
118120

121+
func (h *hostEmulator) ProxySetBufferBytes(bt types.BufferType, start int, maxSize int, bufferData *byte, bufferSize int) types.Status {
122+
switch bt {
123+
case types.BufferTypeHttpRequestBody, types.BufferTypeHttpResponseBody:
124+
return h.httpHostEmulatorProxySetBufferBytes(bt, start, maxSize, bufferData, bufferSize)
125+
default:
126+
panic("unreachable: maybe a bug in this host emulation or SDK")
127+
}
128+
}
129+
119130
// impl rawhostcall.ProxyWASMHost
120131
func (h *hostEmulator) ProxyGetHeaderMapValue(mapType types.MapType, keyData *byte,
121132
keySize int, returnValueData **byte, returnValueSize *int) types.Status {

proxywasm/hostcall.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ func GetHttpRequestBody(start, maxSize int) ([]byte, error) {
128128
return ret, types.StatusToError(st)
129129
}
130130

131+
func SetHttpRequestBody(body []byte) error {
132+
var bufferData *byte
133+
if len(body) != 0 {
134+
bufferData = &body[0]
135+
}
136+
st := rawhostcall.ProxySetBufferBytes(types.BufferTypeHttpRequestBody, 0, len(body), bufferData, len(body))
137+
return types.StatusToError(st)
138+
}
139+
131140
func GetHttpRequestTrailers() ([][2]string, error) {
132141
ret, st := getMap(types.MapTypeHttpRequestTrailers)
133142
return ret, types.StatusToError(st)
@@ -189,6 +198,15 @@ func GetHttpResponseBody(start, maxSize int) ([]byte, error) {
189198
return ret, types.StatusToError(st)
190199
}
191200

201+
func SetHttpResponseBody(body []byte) error {
202+
var bufferData *byte
203+
if len(body) != 0 {
204+
bufferData = &body[0]
205+
}
206+
st := rawhostcall.ProxySetBufferBytes(types.BufferTypeHttpResponseBody, 0, len(body), bufferData, len(body))
207+
return types.StatusToError(st)
208+
}
209+
192210
func GetHttpResponseTrailers() ([][2]string, error) {
193211
ret, st := getMap(types.MapTypeHttpResponseTrailers)
194212
return ret, types.StatusToError(st)

proxywasm/rawhostcall/rawhostcall.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func ProxySetHeaderMapPairs(mapType types.MapType, mapData *byte, mapSize int) t
6666
//export proxy_get_buffer_bytes
6767
func ProxyGetBufferBytes(bt types.BufferType, start int, maxSize int, returnBufferData **byte, returnBufferSize *int) types.Status
6868

69+
//export proxy_set_buffer_bytes
70+
func ProxySetBufferBytes(bt types.BufferType, start int, maxSize int, bufferData *byte, bufferSize int) types.Status
71+
6972
//export proxy_continue_stream
7073
func ProxyContinueStream(streamType types.StreamType) types.Status
7174

0 commit comments

Comments
 (0)