Skip to content

Commit b653946

Browse files
authored
feat: get response body (#95)
1 parent b4d14c9 commit b653946

File tree

6 files changed

+269
-4
lines changed

6 files changed

+269
-4
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,28 @@ end
144144
assert(wasm.on_http_response_headers(ctx))
145145
```
146146

147+
### on_http_response_body
148+
149+
`syntax: ok, err = proxy_wasm.on_http_response_body(plugin_ctx)`
150+
151+
Run the HTTP response body filter in the plugin of the given plugin ctx.
152+
This method need to be called in `body_filter_by_lua` phase and may be run
153+
multiple times.
154+
155+
```lua
156+
local plugin, err = proxy_wasm.load("plugin","t/testdata/http_lifecycle/main.go.wasm")
157+
if not plugin then
158+
ngx.log(ngx.ERR, "failed to load wasm ", err)
159+
return
160+
end
161+
local ctx, err = wasm.on_configure(plugin, '{"body":512}')
162+
if not ctx then
163+
ngx.log(ngx.ERR, "failed to create plugin ctx ", err)
164+
return
165+
end
166+
assert(wasm.on_http_response_body(ctx))
167+
```
168+
147169
## proxy-wasm ABI
148170

149171
Implemented proxy-wasm ABI can be found in [proxy_wasm_abi](./proxy_wasm_abi.md).

lib/resty/proxy-wasm.lua

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ local _M = {}
6868
local HTTP_REQUEST_HEADERS = 1
6969
local HTTP_REQUEST_BODY = 2
7070
local HTTP_RESPONSE_HEADERS = 4
71-
--local HTTP_RESPONSE_BODY = 8
71+
local HTTP_RESPONSE_BODY = 8
7272

7373
local RC_NEED_HTTP_CALL = 1
7474

@@ -348,4 +348,26 @@ function _M.on_http_response_headers(plugin_ctx)
348348
end
349349

350350

351+
function _M.on_http_response_body(plugin_ctx)
352+
if type(plugin_ctx) ~= "cdata" then
353+
return nil, "bad plugin ctx"
354+
end
355+
356+
local r = get_request()
357+
if not r then
358+
return nil, "bad request"
359+
end
360+
361+
-- TODO: rewrite this by exporting FFI interfaces in OpenResty to save a copy
362+
local body = ngx.arg[1]
363+
local eof = ngx.arg[2]
364+
local rc = C.ngx_http_wasm_on_http(plugin_ctx, r, HTTP_RESPONSE_BODY, body, #body, eof)
365+
if rc < 0 then
366+
return nil, "failed to run proxy_on_http_response_body"
367+
end
368+
369+
return true
370+
end
371+
372+
351373
return _M

src/http/ngx_http_wasm_api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ proxy_get_buffer_bytes(int32_t type, int32_t start, int32_t size,
487487
break;
488488

489489
case PROXY_BUFFER_TYPE_HTTP_REQUEST_BODY:
490+
case PROXY_BUFFER_TYPE_HTTP_RESPONSE_BODY:
490491
buffer = ngx_http_wasm_get_body();
491492
if (buffer != NULL) {
492493
data = buffer->data;

src/http/ngx_http_wasm_module.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ static ngx_str_t proxy_on_request_body =
5050
ngx_string("proxy_on_request_body");
5151
static ngx_str_t proxy_on_response_headers =
5252
ngx_string("proxy_on_response_headers");
53+
static ngx_str_t proxy_on_response_body =
54+
ngx_string("proxy_on_response_body");
5355
static ngx_str_t proxy_on_http_call_response =
5456
ngx_string("proxy_on_http_call_response");
5557

@@ -678,12 +680,14 @@ ngx_http_wasm_on_http(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_request_t *r
678680

679681
ctx = ngx_http_wasm_get_module_ctx(r);
680682

681-
if (type == HTTP_RESPONSE_HEADERS) {
682-
cb_name = &proxy_on_response_headers;
683+
if (type == HTTP_REQUEST_HEADERS) {
684+
cb_name = &proxy_on_request_headers;
683685
} else if (type == HTTP_REQUEST_BODY) {
684686
cb_name = &proxy_on_request_body;
687+
} else if (type == HTTP_RESPONSE_HEADERS) {
688+
cb_name = &proxy_on_response_headers;
685689
} else {
686-
cb_name = &proxy_on_request_headers;
690+
cb_name = &proxy_on_response_body;
687691
}
688692

689693
if (type == HTTP_REQUEST_HEADERS || type == HTTP_RESPONSE_HEADERS) {

t/http_resp_body.t

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Copyright 2022 Shenzhen ZhiLiu Technology Co., Ltd.
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 t::WASM 'no_plan';
16+
17+
run_tests();
18+
19+
__DATA__
20+
21+
=== TEST 1: sanity
22+
--- config
23+
location /t {
24+
content_by_lua_block {
25+
local wasm = require("resty.proxy-wasm")
26+
local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm"))
27+
ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{}'))
28+
29+
ngx.print("1234")
30+
ngx.print("12345")
31+
}
32+
body_filter_by_lua_block {
33+
local wasm = require("resty.proxy-wasm")
34+
ngx.log(ngx.WARN, ngx.arg[1], " ", ngx.arg[2])
35+
local ctx = ngx.ctx.ctx
36+
assert(wasm.on_http_response_body(ctx))
37+
}
38+
}
39+
--- grep_error_log eval
40+
qr/body size \d+, end of stream \w+/
41+
--- grep_error_log_out
42+
body size 4, end of stream false
43+
body size 5, end of stream false
44+
body size 0, end of stream true
45+
46+
47+
48+
=== TEST 2: flush
49+
--- config
50+
location /t {
51+
content_by_lua_block {
52+
local wasm = require("resty.proxy-wasm")
53+
local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm"))
54+
ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{}'))
55+
56+
ngx.print("1234")
57+
ngx.flush()
58+
ngx.print("12345")
59+
}
60+
body_filter_by_lua_block {
61+
local wasm = require("resty.proxy-wasm")
62+
local ctx = ngx.ctx.ctx
63+
assert(wasm.on_http_response_body(ctx))
64+
}
65+
}
66+
--- grep_error_log eval
67+
qr/body size \d+, end of stream \w+/
68+
--- grep_error_log_out
69+
body size 4, end of stream false
70+
body size 0, end of stream false
71+
body size 5, end of stream false
72+
body size 0, end of stream true
73+
74+
75+
76+
=== TEST 3: no body
77+
--- config
78+
location /t {
79+
content_by_lua_block {
80+
local wasm = require("resty.proxy-wasm")
81+
local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm"))
82+
ngx.ctx.ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}'))
83+
}
84+
body_filter_by_lua_block {
85+
local wasm = require("resty.proxy-wasm")
86+
local ctx = ngx.ctx.ctx
87+
assert(wasm.on_http_response_body(ctx))
88+
}
89+
}
90+
--- grep_error_log eval
91+
qr/body size \d+, end of stream \w+/
92+
--- grep_error_log_out
93+
body size 0, end of stream true
94+
--- error_log
95+
get body err: error status returned by host: not found
96+
97+
98+
99+
=== TEST 4: get body
100+
--- config
101+
location /t {
102+
return 200 "helloworld";
103+
body_filter_by_lua_block {
104+
if not ngx.arg[2] then
105+
return
106+
end
107+
108+
local json = require("cjson")
109+
local wasm = require("resty.proxy-wasm")
110+
local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm"))
111+
local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}'))
112+
local conf = {action = "offset"}
113+
for _, e in ipairs({
114+
{0, 2},
115+
{1, 1},
116+
{1, 2},
117+
{0, 10},
118+
{1, 9},
119+
{8, 2},
120+
{9, 1},
121+
{1, 10},
122+
}) do
123+
conf.start = e[1]
124+
conf.size = e[2]
125+
local ctx = assert(wasm.on_configure(plugin, json.encode(conf)))
126+
ngx.arg[1] = "helloworld"
127+
assert(wasm.on_http_response_body(ctx))
128+
end
129+
}
130+
}
131+
--- grep_error_log eval
132+
qr/get body \[\w+\]/
133+
--- grep_error_log_out
134+
get body [he]
135+
get body [e]
136+
get body [el]
137+
get body [helloworld]
138+
get body [elloworld]
139+
get body [ld]
140+
get body [d]
141+
get body [elloworld]
142+
143+
144+
145+
=== TEST 5: get body, bad cases
146+
--- config
147+
location /t {
148+
return 200 "helloworld";
149+
body_filter_by_lua_block {
150+
if not ngx.arg[2] then
151+
return
152+
end
153+
154+
local json = require("cjson")
155+
local wasm = require("resty.proxy-wasm")
156+
local plugin = assert(wasm.load("plugin", "t/testdata/http_body/main.go.wasm"))
157+
local ctx = assert(wasm.on_configure(plugin, '{"action":"offset"}'))
158+
local conf = {action = "offset"}
159+
for _, e in ipairs({
160+
{-1, 2},
161+
{1, 0},
162+
{1, -1},
163+
{10, 2},
164+
}) do
165+
conf.start = e[1]
166+
conf.size = e[2]
167+
local ctx = assert(wasm.on_configure(plugin, json.encode(conf)))
168+
ngx.arg[1] = "helloworld"
169+
assert(wasm.on_http_response_body(ctx))
170+
end
171+
}
172+
}
173+
--- error_log
174+
[error]
175+
--- grep_error_log eval
176+
qr/error status returned by host: bad argument/
177+
--- grep_error_log_out eval
178+
"error status returned by host: bad argument\n" x 4

t/testdata/http_body/main.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,41 @@ func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.
9393

9494
return types.ActionContinue
9595
}
96+
97+
func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
98+
data, err := proxywasm.GetPluginConfiguration()
99+
if err != nil {
100+
proxywasm.LogErrorf("error reading plugin configuration: %v", err)
101+
return types.ActionContinue
102+
}
103+
104+
proxywasm.LogWarnf("body size %d, end of stream %v", bodySize, endOfStream)
105+
106+
var action string
107+
var conf *fastjson.Value
108+
var p fastjson.Parser
109+
conf, err = p.ParseBytes(data)
110+
if err != nil {
111+
proxywasm.LogErrorf("error decoding plugin configuration: %v", err)
112+
return types.ActionContinue
113+
}
114+
115+
action = string(conf.GetStringBytes("action"))
116+
117+
switch action {
118+
case "offset":
119+
if !endOfStream {
120+
return types.ActionContinue
121+
}
122+
start := conf.GetInt("start")
123+
size := conf.GetInt("size")
124+
body, err := proxywasm.GetHttpRequestBody(start, size)
125+
if err != nil {
126+
proxywasm.LogErrorf("get body err: %v", err)
127+
return types.ActionContinue
128+
}
129+
proxywasm.LogWarnf("get body [%s]", string(body))
130+
}
131+
132+
return types.ActionContinue
133+
}

0 commit comments

Comments
 (0)