Skip to content

Commit

Permalink
feat(proxy-wasm) foreign function support
Browse files Browse the repository at this point in the history
Also adds `resolve_lua` foreign function, which allows filter developers
to resolve names using the Lua DNS resolver.
  • Loading branch information
casimiro authored and thibaultcha committed Jan 29, 2025
1 parent 337259e commit c7d085b
Show file tree
Hide file tree
Showing 43 changed files with 1,618 additions and 89 deletions.
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 \
$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
101 changes: 100 additions & 1 deletion docs/PROXY_WASM.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [Host Properties]
- [Nginx Properties]
- [HTTP Dispatches]
- [Foreign Functions]
- [Supported Specifications]
- [Tested SDKs](#tested-sdks)
- [Supported Entrypoints](#supported-entrypoints)
Expand Down Expand Up @@ -493,6 +494,101 @@ impl Context for ExampleRootContext {

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

### Foreign Functions

Proxy-Wasm filters can invoke so-called "foreign functions", which are
host-specific host functions (i.e. only available in one host embedding) that
can extend filters capabilities in that host.

To invoke a foreign function, a filter may call `call_foreign_function` with the
name of the foreign function and its arguments.

`call_foreign_function` may return:

- A vector of bytes containing the foreign function's returned value.
- A value representing a failure (e.g. "function not found").
- Or an empty vector of bytes indicating that the invoked function will return
asynchronously later.

When the function returns asynchronously, the `on_foreign_function` callback is
invoked with the foreign function's id and the size in bytes of its
returned value. This return value can can then be retrieved by calling
`get_buffer` accordingly.

ngx_wasm_module supports the following foreign functions:

#### `resolve_lua`

Resolve an IPv4/IPv6 hostname using the Lua DNS resolver (see
[proxy_wasm_lua_resolver]).

This function expects the name to be resolved as its sole argument.

This function either returns 4 bytes representing an IPv4 address, or 16 bytes
representing an IPv6 address. Note that this return value may be immediate or
asynchronous, as determined by the initial return value (see example below).

**Supported Contexts**

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

Example:

```rust
pub enum WasmxForeignFunctions {
ResolveLua = 0,
}

let hostname = "example.com";

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

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 (after yielding) {} to {:?}", name, address);
} else {
info!("could not resolve {}", name)
}
}
_ => {}
}
}

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

match fid {
WasmxForeignFunctions::ResolveLua => {
lua_resolver_callback(args);
}
}
}
```

> Notes
This function requires the directive `proxy_wasm_lua_resolver` to be enabled,
see [proxy_wasm_lua_resolver].

[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 +672,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 +756,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 Expand Up @@ -878,6 +976,7 @@ Proxy-Wasm SDK.
[Host Properties]: #host-properties
[Nginx Properties]: #nginx-properties
[HTTP Dispatches]: #http-dispatches
[Foreign Functions]: #foreign-functions
[Supported Specifications]: #supported-specifications
[Supported Properties]: #supported-properties
[Examples]: #examples
Expand Down
53 changes: 35 additions & 18 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 @@ -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->dispatch_calls);
q != ngx_queue_sentinel(&pwexec->dispatch_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,38 @@ 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->dispatch_calls)) {
q = ngx_queue_head(&pwexec->dispatch_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);
ngx_wa_assert(dop->q.next && dop->q.prev);
ngx_queue_remove(&dop->q);

ngx_queue_remove(&call->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(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 +1165,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter,
rexec->filter = filter;
rexec->ictx = ictx;

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

log = filter->log;

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

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

} else {
if (in->ictx != ictx) {
Expand Down
81 changes: 55 additions & 26 deletions src/common/proxy_wasm/ngx_proxy_wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ typedef enum {
NGX_PROXY_WASM_STEP_DONE,
NGX_PROXY_WASM_STEP_TICK,
NGX_PROXY_WASM_STEP_DISPATCH_RESPONSE,
NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK,
} ngx_proxy_wasm_step_e;


Expand Down Expand Up @@ -124,7 +125,7 @@ typedef enum {
NGX_PROXY_WASM_BUFFER_GRPC_RECEIVE_BUFFER = 5,
NGX_PROXY_WASM_BUFFER_VM_CONFIGURATION = 6,
NGX_PROXY_WASM_BUFFER_PLUGIN_CONFIGURATION = 7,
NGX_PROXY_WASM_BUFFER_CALL_DATA = 8,
NGX_PROXY_WASM_BUFFER_FOREIGN_FUNCTION_ARGUMENTS = 8,
} ngx_proxy_wasm_buffer_type_e;


Expand All @@ -147,50 +148,76 @@ typedef enum {
} ngx_proxy_wasm_metric_type_e;


typedef enum {
NGX_PROXY_WASM_FOREIGN_RESOLVE_LUA = 0,
} ngx_proxy_wasm_foreign_function_e;


typedef struct ngx_proxy_wasm_ctx_s ngx_proxy_wasm_ctx_t;
typedef struct ngx_proxy_wasm_filter_s ngx_proxy_wasm_filter_t;
typedef struct ngx_proxy_wasm_exec_s ngx_proxy_wasm_exec_t;
typedef struct ngx_proxy_wasm_instance_s ngx_proxy_wasm_instance_t;
#ifdef NGX_WASM_HTTP
typedef struct ngx_http_proxy_wasm_dispatch_s ngx_http_proxy_wasm_dispatch_t;
#endif
typedef struct ngx_proxy_wasm_foreign_call_s ngx_proxy_wasm_foreign_call_t;
typedef ngx_str_t ngx_proxy_wasm_marshalled_map_t;


typedef struct {
ngx_queue_t busy;
ngx_queue_t free;
ngx_queue_t sweep;
ngx_pool_t *pool;
ngx_queue_t busy;
ngx_queue_t free;
ngx_queue_t sweep;
ngx_pool_t *pool;
} ngx_proxy_wasm_store_t;


typedef struct {
ngx_str_t log_prefix;
ngx_log_t *orig_log;
ngx_proxy_wasm_exec_t *pwexec;
ngx_str_t log_prefix;
ngx_log_t *orig_log;
ngx_proxy_wasm_exec_t *pwexec;
} ngx_proxy_wasm_log_ctx_t;


typedef enum {
NGX_PROXY_WASM_DISPATCH_HTTP_CALL,
NGX_PROXY_WASM_DISPATCH_FOREIGN_CALL,
} ngx_proxy_wasm_dispatch_op_e;


typedef struct {
ngx_queue_t q; /* stored by caller */
ngx_proxy_wasm_dispatch_op_e type;

union {
#ifdef NGX_WASM_HTTP
ngx_http_proxy_wasm_dispatch_t *http;
#endif
ngx_proxy_wasm_foreign_call_t *foreign;
} call;
} ngx_proxy_wasm_dispatch_op_t;


struct ngx_proxy_wasm_exec_s {
ngx_uint_t root_id;
ngx_uint_t id;
ngx_uint_t index;
ngx_uint_t tick_period;
ngx_rbtree_node_t node;
ngx_proxy_wasm_err_e ecode;
ngx_pool_t *pool;
ngx_log_t *log;
ngx_proxy_wasm_log_ctx_t log_ctx;
ngx_proxy_wasm_ctx_t *parent;
ngx_proxy_wasm_filter_t *filter;
ngx_proxy_wasm_instance_t *ictx;
ngx_proxy_wasm_store_t *store;
ngx_event_t *ev;
ngx_uint_t root_id;
ngx_uint_t id;
ngx_uint_t index;
ngx_uint_t tick_period;
ngx_rbtree_node_t node;
ngx_proxy_wasm_err_e ecode;
ngx_pool_t *pool;
ngx_log_t *log;
ngx_proxy_wasm_log_ctx_t log_ctx;
ngx_proxy_wasm_ctx_t *parent;
ngx_proxy_wasm_filter_t *filter;
ngx_proxy_wasm_instance_t *ictx;
ngx_proxy_wasm_store_t *store;
ngx_event_t *ev;
ngx_queue_t dispatch_ops;
ngx_proxy_wasm_foreign_call_t *foreign_call; /* swap pointer for host functions */
#ifdef NGX_WASM_HTTP
ngx_http_proxy_wasm_dispatch_t *dispatch_call; /* swap pointer for host functions */
ngx_http_proxy_wasm_dispatch_t *dispatch_call; /* swap pointer for host functions */
#endif
ngx_queue_t dispatch_calls;

/* flags */

Expand Down Expand Up @@ -414,8 +441,10 @@ ngx_int_t ngx_proxy_wasm_resume(ngx_proxy_wasm_ctx_t *pwctx,
ngx_wasm_phase_t *phase, ngx_proxy_wasm_step_e step);
ngx_proxy_wasm_err_e ngx_proxy_wasm_run_step(ngx_proxy_wasm_exec_t *pwexec,
ngx_proxy_wasm_step_e step);
ngx_uint_t 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_uint_t ngx_proxy_wasm_dispatch_ops_total(ngx_proxy_wasm_exec_t *pwexec);
void ngx_proxy_wasm_dispatch_ops_cancel(ngx_proxy_wasm_exec_t *pwexec);
ngx_uint_t ngx_proxy_wasm_foreign_calls_total(ngx_proxy_wasm_exec_t *pwexec);
void ngx_proxy_wasm_foreign_calls_cancel(ngx_proxy_wasm_exec_t *pwexec);


/* host handlers */
Expand Down
Loading

0 comments on commit c7d085b

Please sign in to comment.