Skip to content

Commit ea91955

Browse files
authored
feat: allow http call/send local response in http callback (#79)
1 parent 9cf8926 commit ea91955

File tree

4 files changed

+229
-104
lines changed

4 files changed

+229
-104
lines changed

lib/resty/proxy-wasm.lua

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local ffi_str = ffi.string
77
local C = ffi.C
88
local get_request = base.get_request
99
local get_string_buf = base.get_string_buf
10+
local NGX_OK = base.FFI_OK
1011

1112

1213
base.allows_subsystem("http")
@@ -189,6 +190,60 @@ do
189190
end
190191

191192

193+
local function handle_http_callback(plugin_ctx, r, res)
194+
local headers_buf, body
195+
local n = 0
196+
-- always trigger http call callback, even the http call failed
197+
if res then
198+
local hdrs = res.headers
199+
for name, value in pairs(hdrs) do
200+
if type(value) == "table" then
201+
n = n + #value
202+
else
203+
n = n + 1
204+
end
205+
end
206+
207+
local i = 0
208+
local raw_buf = get_string_buf(n * ngx_table_size)
209+
headers_buf = ffi_cast(ngx_table_type, raw_buf)
210+
for name, value in pairs(hdrs) do
211+
name = string.lower(name)
212+
if type(value) == "table" then
213+
for _, v in ipairs(value) do
214+
headers_buf[i].key.data = name
215+
headers_buf[i].key.len = #name
216+
headers_buf[i].value.data = v
217+
headers_buf[i].value.len = #v
218+
i = i + 1
219+
end
220+
else
221+
headers_buf[i].key.data = name
222+
headers_buf[i].key.len = #name
223+
headers_buf[i].value.data = value
224+
headers_buf[i].value.len = #value
225+
i = i + 1
226+
end
227+
end
228+
229+
headers_buf[n].key.data = ":status"
230+
headers_buf[n].key.len = 7
231+
local status = tostring(res.status)
232+
headers_buf[n].value.data = status
233+
headers_buf[n].value.len = #status
234+
n = n + 1
235+
236+
if res.body then
237+
body = ffi.new("ngx_str_t")
238+
body.data = res.body
239+
body.len = #res.body
240+
end
241+
end
242+
243+
return C.ngx_http_wasm_on_http_call_resp(plugin_ctx, r, headers_buf, n, body)
244+
end
245+
246+
192247
function _M.on_http_request_headers(plugin_ctx)
193248
if type(plugin_ctx) ~= "cdata" then
194249
return nil, "bad plugin ctx"
@@ -204,73 +259,31 @@ function _M.on_http_request_headers(plugin_ctx)
204259
return nil, "failed to run proxy_on_http_request_headers"
205260
end
206261

207-
if rc >= 100 then
208-
local p = C.ngx_http_wasm_fetch_local_body(r)
209-
if p ~= nil then
210-
local body = ffi_str(p.data, p.len)
211-
ngx.status = rc
212-
ngx.print(body)
213-
end
214-
215-
ngx.exit(rc)
216-
end
217-
218-
if rc == RC_NEED_HTTP_CALL then
219-
local res = send_http_call(r)
220-
local headers_buf, body
221-
local n = 0
222-
-- always trigger http call callback, even the http call failed
223-
if res then
224-
local hdrs = res.headers
225-
for name, value in pairs(hdrs) do
226-
if type(value) == "table" then
227-
n = n + #value
228-
else
229-
n = n + 1
230-
end
262+
while true do
263+
if rc >= 100 then
264+
local p = C.ngx_http_wasm_fetch_local_body(r)
265+
if p ~= nil then
266+
local body = ffi_str(p.data, p.len)
267+
ngx.status = rc
268+
ngx.print(body)
231269
end
232270

233-
local i = 0
234-
local raw_buf = get_string_buf(n * ngx_table_size)
235-
headers_buf = ffi_cast(ngx_table_type, raw_buf)
236-
for name, value in pairs(hdrs) do
237-
name = string.lower(name)
238-
if type(value) == "table" then
239-
for _, v in ipairs(value) do
240-
headers_buf[i].key.data = name
241-
headers_buf[i].key.len = #name
242-
headers_buf[i].value.data = v
243-
headers_buf[i].value.len = #v
244-
i = i + 1
245-
end
246-
else
247-
headers_buf[i].key.data = name
248-
headers_buf[i].key.len = #name
249-
headers_buf[i].value.data = value
250-
headers_buf[i].value.len = #value
251-
i = i + 1
252-
end
253-
end
271+
return ngx.exit(rc)
272+
end
254273

255-
headers_buf[n].key.data = ":status"
256-
headers_buf[n].key.len = 7
257-
local status = tostring(res.status)
258-
headers_buf[n].value.data = status
259-
headers_buf[n].value.len = #status
260-
n = n + 1
261-
262-
if res.body then
263-
body = ffi.new("ngx_str_t")
264-
body.data = res.body
265-
body.len = #res.body
274+
if rc == RC_NEED_HTTP_CALL then
275+
local res = send_http_call(r)
276+
rc = handle_http_callback(plugin_ctx, r, res)
277+
if rc < 0 then
278+
return nil, "failed to run proxy_on_http_call_response"
266279
end
280+
281+
-- continue to handle other rc
267282
end
268283

269-
local rc = C.ngx_http_wasm_on_http_call_resp(plugin_ctx, r, headers_buf, n, body)
270-
if rc < 0 then
271-
return nil, "failed to run proxy_on_http_call_response"
284+
if rc == NGX_OK then
285+
break
272286
end
273-
-- TODO: handle send local & dispatch http call
274287
end
275288

276289
return true

src/http/ngx_http_wasm_module.c

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,35 @@ ngx_http_wasm_fetch_http_ctx(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_reque
600600
}
601601

602602

603+
static ngx_int_t
604+
ngx_http_wasm_handle_rc_before_proxy(ngx_http_wasm_main_conf_t *wmcf,
605+
ngx_http_wasm_ctx_t *ctx, ngx_int_t rc)
606+
{
607+
int32_t code = wmcf->code;
608+
609+
/* reset code for next use */
610+
wmcf->code = 0;
611+
612+
/* the http call is prior to other operation */
613+
if (ctx->callout) {
614+
return RC_NEED_HTTP_CALL;
615+
}
616+
617+
if (rc < 0) {
618+
return rc;
619+
}
620+
621+
if (code >= 100) {
622+
/* Return given http response instead of reaching the upstream.
623+
* The body will be fetched later by ngx_http_wasm_fetch_local_body
624+
* */
625+
return code;
626+
}
627+
628+
return rc;
629+
}
630+
631+
603632
ngx_int_t
604633
ngx_http_wasm_on_http(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_request_t *r,
605634
ngx_http_wasm_phase_t type)
@@ -649,28 +678,7 @@ ngx_http_wasm_on_http(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_request_t *r
649678

650679
ngx_http_wasm_set_state(NULL);
651680

652-
/* the http call is prior to other operation */
653-
if (ctx->callout) {
654-
return RC_NEED_HTTP_CALL;
655-
}
656-
657-
if (rc < 0) {
658-
return rc;
659-
}
660-
661-
if (wmcf->code >= 100) {
662-
int32_t code = wmcf->code;
663-
664-
/* reset code for next use */
665-
wmcf->code = 0;
666-
667-
/* Return given http response instead of reaching the upstream.
668-
* The body will be fetched later by ngx_http_wasm_fetch_local_body
669-
* */
670-
return code;
671-
}
672-
673-
return rc;
681+
return ngx_http_wasm_handle_rc_before_proxy(wmcf, ctx, rc);
674682
}
675683

676684

@@ -698,8 +706,10 @@ ngx_http_wasm_on_http_call_resp(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_re
698706
ngx_log_t *log;
699707
ngx_http_wasm_ctx_t *ctx;
700708
ngx_http_wasm_http_ctx_t *http_ctx;
709+
ngx_http_wasm_main_conf_t *wmcf;
701710

702711
log = r->connection->log;
712+
wmcf = ngx_http_get_module_main_conf(r, ngx_http_wasm_module);
703713

704714
if (!ngx_http_wasm_vm_inited) {
705715
ngx_log_error(NGX_LOG_ERR, log, 0, "miss wasm_vm configuration");
@@ -732,5 +742,5 @@ ngx_http_wasm_on_http_call_resp(ngx_http_wasm_plugin_ctx_t *hwp_ctx, ngx_http_re
732742

733743
ngx_http_wasm_set_state(NULL);
734744

735-
return rc;
745+
return ngx_http_wasm_handle_rc_before_proxy(wmcf, ctx, rc);
736746
}

t/http_call_callback.t

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,66 @@ location /t {
165165
qr/error status returned by host: bad argument/
166166
--- grep_error_log_out eval
167167
"error status returned by host: bad argument\n" x 4
168+
169+
170+
171+
=== TEST 7: call then send
172+
--- config
173+
location /t {
174+
content_by_lua_block {
175+
local json = require("cjson")
176+
local wasm = require("resty.proxy-wasm")
177+
local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm"))
178+
local conf = {host = "127.0.0.1:1980", action = "then_send"}
179+
local ctx = assert(wasm.on_configure(plugin, json.encode(conf)))
180+
assert(wasm.on_http_request_headers(ctx))
181+
}
182+
}
183+
--- error_code: 503
184+
--- grep_error_log eval
185+
qr/run http callback callout id: \d+, plugin ctx id: \d+/
186+
--- grep_error_log_out
187+
run http callback callout id: 0, plugin ctx id: 1
188+
189+
190+
191+
=== TEST 8: call then call
192+
--- config
193+
location /t {
194+
content_by_lua_block {
195+
local json = require("cjson")
196+
local wasm = require("resty.proxy-wasm")
197+
local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm"))
198+
local conf = {host = "127.0.0.1:1980", action = "then_call"}
199+
local ctx = assert(wasm.on_configure(plugin, json.encode(conf)))
200+
assert(wasm.on_http_request_headers(ctx))
201+
}
202+
}
203+
--- error_code: 403
204+
--- grep_error_log eval
205+
qr/run http callback callout id: \d+, plugin ctx id: \d+/
206+
--- grep_error_log_out
207+
run http callback callout id: 0, plugin ctx id: 1
208+
run http callback callout id: 1, plugin ctx id: 1
209+
210+
211+
212+
=== TEST 9: call x 3
213+
--- config
214+
location /t {
215+
content_by_lua_block {
216+
local json = require("cjson")
217+
local wasm = require("resty.proxy-wasm")
218+
local plugin = assert(wasm.load("plugin", "t/testdata/http_call/main.go.wasm"))
219+
local conf = {host = "127.0.0.1:1980", action = "then_call_again"}
220+
local ctx = assert(wasm.on_configure(plugin, json.encode(conf)))
221+
assert(wasm.on_http_request_headers(ctx))
222+
}
223+
}
224+
--- error_code: 401
225+
--- grep_error_log eval
226+
qr/run http callback callout id: \d+, plugin ctx id: \d+/
227+
--- grep_error_log_out
228+
run http callback callout id: 0, plugin ctx id: 1
229+
run http callback callout id: 1, plugin ctx id: 1
230+
run http callback callout id: 2, plugin ctx id: 1

0 commit comments

Comments
 (0)