Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(proxy-wasm) foreign function support #626

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions config
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ NGX_WASMX_DEPS="\
$ngx_addon_dir/src/common/metrics/ngx_wa_metrics.h \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm.h \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_maps.h \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_properties.h"
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_properties.h \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.h"

NGX_WASMX_SRCS="\
$ngx_addon_dir/src/ngx_wasmx.c \
Expand All @@ -160,8 +161,9 @@ NGX_WASMX_SRCS="\
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm.c \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_host.c \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_maps.c \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_util.c \
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_properties.c \
casimiro marked this conversation as resolved.
Show resolved Hide resolved
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_util.c"
$ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.c"

# wasm

Expand Down
93 changes: 92 additions & 1 deletion docs/PROXY_WASM.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,95 @@ impl Context for ExampleRootContext {

[Back to TOC](#table-of-contents)

### Foreign Function Calls

Proxy-Wasm filters can invoke host-specific functions, i.e. not part of the
Proxy-Wasm ABI specification, by invoking `proxy_call_foreign_function` with the
name of the host-specific function along with its arguments.

`proxy_call_foreign_function` may return a vector of bytes containing the
host-specific function's returned value, a value representing a failure (e.g.,
function not found), or even an empty vector of bytes indicating that the
invoked function will return asynchronously later.

When the function finally returns, the handler `on_foreign_function` is invoked
with the host-specific function's id and the size in bytes of its returned
value, which can be retrieved by calling `get_buffer` accordingly.

ngx_wasm_module supports the following host-specific functions:

#### `resolve_lua`

Resolves a name using the Lua DNS resolver.

Expects the name to be resolved as its single argument.

Returns either 4 bytes representing an IPv4 address, or 16 bytes representing an
IPv6 address.

**Supported Contexts**

- `on_request_headers`
- `on_request_body`
- `on_tick`
- `on_dispatch_response`
- `on_foreign_function`

Example:
```rust
pub enum WasmxForeignFunction {
ResolveLua = 0,
}

pub fn resolve_lua_callback(args: Option<Vec<u8>>) {
match args {
Some(args) => {
let address_size = args[0] as usize;
let name = std::str::from_utf8(&args[(address_size + 1)..]).unwrap();

if address_size > 0 {
let address = &args[1..address_size + 1];
info!("resolved (yielding) {} to {:?}", name, address);
} else {
info!("could not resolve {}", name)
}
}
_ => info!("empty args"),
}
}

let name = "a_name";

match call_foreign_function("resolve_lua", Some(name.as_bytes())) {
Ok(ret) => match ret {
Some(bytes) => info!("resolved (no yielding) {} to {:?}", name, bytes),
None => info!("yielded while resolving {}", name),
},
Err(_) => info!("failed calling resolve_lua"),
}

fn on_foreign_function(&mut self, function_id: u32, args_size: usize) {
let f: WasmxForeignFunction = unsafe { ::std::mem::transmute(function_id) };
let args = get_buffer(BufferType::CallData, 0, args_size).unwrap();

match f {
WasmxForeignFunction::ResolveLua => {
lua_resolver_callback(args);
}
}
}

```

> Notes

This function requires the directive `proxy_wasm_lua_resolver` to be set to
`on`, see [proxy_wasm_lua_resolver].

This function may return asynchronously.

[Back to TOC](#table-of-contents)

## Supported Specifications

This section describes the current state of support for the Proxy-Wasm
Expand Down Expand Up @@ -576,6 +665,8 @@ SDK ABI `0.2.1`) and their present status in ngx_wasm_module:
`on_done` | :heavy_check_mark: | HTTP context done handler.
*Shared memory queues* | |
`on_queue_ready` | :x: | *NYI*
*Custom extension points* | |
`on_foreign_function` | :heavy_check_mark: |

"*NYI*" stands for "Not Yet Implemented".

Expand Down Expand Up @@ -658,7 +749,7 @@ SDK ABI `0.2.1`) and their present status in ngx_wasm_module:
`proxy_record_metric` | :heavy_check_mark: |
`proxy_increment_metric` | :heavy_check_mark: |
*Custom extension points* | |
`proxy_call_foreign_function` | :x: |
`proxy_call_foreign_function` | :heavy_check_mark: |

[Back to TOC](#table-of-contents)

Expand Down
67 changes: 45 additions & 22 deletions src/common/proxy_wasm/ngx_proxy_wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <ngx_proxy_wasm.h>
#include <ngx_proxy_wasm_properties.h>
#include <ngx_proxy_wasm_foreign_call.h>
#ifdef NGX_WASM_HTTP
#include <ngx_http_proxy_wasm.h>
#endif
Expand Down Expand Up @@ -440,8 +441,8 @@ ngx_proxy_wasm_ctx_destroy(ngx_proxy_wasm_ctx_t *pwctx)
ngx_pfree(pwctx->pool, pwctx->root_id.data);
}

if (pwctx->call_status.data) {
ngx_pfree(pwctx->pool, pwctx->call_status.data);
if (pwctx->dispatch_call_status.data) {
ngx_pfree(pwctx->pool, pwctx->dispatch_call_status.data);
}

if (pwctx->response_status.data) {
Expand Down Expand Up @@ -839,6 +840,9 @@ ngx_proxy_wasm_run_step(ngx_proxy_wasm_exec_t *pwexec,
case NGX_PROXY_WASM_STEP_DISPATCH_RESPONSE:
rc = filter->subsystem->resume(pwexec, step, &action);
break;
case NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK:
rc = filter->subsystem->resume(pwexec, step, &action);
break;
case NGX_PROXY_WASM_STEP_TICK:
ngx_wa_assert(pwexec->root_id == NGX_PROXY_WASM_ROOT_CTX_ID);
pwctx->rexec = pwexec;
Expand Down Expand Up @@ -892,13 +896,13 @@ ngx_proxy_wasm_run_step(ngx_proxy_wasm_exec_t *pwexec,


ngx_uint_t
ngx_proxy_wasm_dispatch_calls_total(ngx_proxy_wasm_exec_t *pwexec)
ngx_proxy_wasm_dispatch_ops_total(ngx_proxy_wasm_exec_t *pwexec)
{
ngx_queue_t *q;
ngx_uint_t n = 0;

for (q = ngx_queue_head(&pwexec->calls);
q != ngx_queue_sentinel(&pwexec->calls);
for (q = ngx_queue_head(&pwexec->dispatch_ops);
q != ngx_queue_sentinel(&pwexec->dispatch_ops);
q = ngx_queue_next(q), n++) { /* void */ }

dd("n: %ld", n);
Expand All @@ -908,25 +912,44 @@ ngx_proxy_wasm_dispatch_calls_total(ngx_proxy_wasm_exec_t *pwexec)


void
ngx_proxy_wasm_dispatch_calls_cancel(ngx_proxy_wasm_exec_t *pwexec)
ngx_proxy_wasm_dispatch_ops_cancel(ngx_proxy_wasm_exec_t *pwexec)
{
#ifdef NGX_WASM_HTTP
ngx_queue_t *q;
ngx_http_proxy_wasm_dispatch_t *call;
ngx_queue_t *q;
ngx_proxy_wasm_dispatch_op_t *dop;

while (!ngx_queue_empty(&pwexec->calls)) {
q = ngx_queue_head(&pwexec->calls);
call = ngx_queue_data(q, ngx_http_proxy_wasm_dispatch_t, q);
while (!ngx_queue_empty(&pwexec->dispatch_ops)) {
q = ngx_queue_head(&pwexec->dispatch_ops);
dop = ngx_queue_data(q, ngx_proxy_wasm_dispatch_op_t, q);

ngx_log_debug1(NGX_LOG_DEBUG_ALL, pwexec->log, 0,
"proxy_wasm http dispatch cancelled (dispatch: %p)",
call);
#if (NGX_DEBUG)
/* though valid, clang complains if prev/next pointers aren't checked */

ngx_queue_remove(&call->q);
if (!dop->q.next || !dop->q.prev) {
return;
}
#endif

ngx_http_proxy_wasm_dispatch_destroy(call);
}
ngx_queue_remove(&dop->q);

switch (dop->type) {
#ifdef NGX_WASM_HTTP
case NGX_PROXY_WASM_DISPATCH_HTTP_CALL:
ngx_log_debug1(NGX_LOG_DEBUG_ALL, pwexec->log, 0,
"proxy_wasm http dispatch cancelled (dispatch: %p)",
dop->call);

ngx_http_proxy_wasm_dispatch_destroy(dop->call.http);
break;
#endif
default: /* NGX_PROXY_WASM_DISPATCH_FOREIGN_CALL */
ngx_log_debug1(NGX_LOG_DEBUG_ALL, pwexec->log, 0,
"proxy_wasm foreign function callback cancelled "
"(callback: %p)", dop->call);

ngx_proxy_wasm_foreign_call_destroy(dop->call.foreign);
break;
}
}
}


Expand Down Expand Up @@ -1148,7 +1171,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter,
rexec->filter = filter;
rexec->ictx = ictx;

ngx_queue_init(&rexec->calls);
ngx_queue_init(&rexec->dispatch_ops);

log = filter->log;

Expand Down Expand Up @@ -1266,7 +1289,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter,
pwexec->ictx = ictx;
pwexec->store = ictx->store;

ngx_queue_init(&pwexec->calls);
ngx_queue_init(&pwexec->dispatch_ops);

} else {
if (in->ictx != ictx) {
Expand Down Expand Up @@ -1393,11 +1416,11 @@ ngx_proxy_wasm_on_done(ngx_proxy_wasm_exec_t *pwexec)

#if 0
#ifdef NGX_WASM_HTTP
call = pwexec->call;
call = pwexec->dispatch_call;
if (call) {
ngx_http_proxy_wasm_dispatch_destroy(call);

pwexec->call = NULL;
pwexec->dispatch_call = NULL;
}
#endif
#endif
Expand Down
Loading
Loading