From 10f73c2c59bbe6cb1298ca257e9ebddb9a3d1ea6 Mon Sep 17 00:00:00 2001 From: Caio Ramos Casimiro Date: Fri, 15 Nov 2024 12:54:20 +0000 Subject: [PATCH] wip --- config | 2 + src/common/proxy_wasm/ngx_proxy_wasm.c | 45 +++ src/common/proxy_wasm/ngx_proxy_wasm.h | 55 ++-- .../proxy_wasm/ngx_proxy_wasm_foreign_call.c | 281 ++++++++++++++++++ .../proxy_wasm/ngx_proxy_wasm_foreign_call.h | 33 ++ src/common/proxy_wasm/ngx_proxy_wasm_host.c | 59 +++- .../proxy_wasm/ngx_proxy_wasm_host_lua.c | 0 src/common/proxy_wasm/ngx_proxy_wasm_util.c | 5 +- src/http/proxy_wasm/ngx_http_proxy_wasm.c | 30 ++ .../proxy_wasm/ngx_http_proxy_wasm_dispatch.c | 9 + .../hfuncs/140-proxy_call_foreign_function.t | 43 +++ .../hfuncs/141-proxy_resolve_lua.t | 188 ++++++++++++ .../140-proxy_foreign_function_resolve_lua.t | 169 +++++++++++ t/lib/Cargo.lock | 34 ++- .../context-checks/src/hostcalls.rs | 28 +- .../context-checks/src/lib.rs | 1 + t/lib/proxy-wasm-tests/hostcalls/Cargo.toml | 3 +- .../proxy-wasm-tests/hostcalls/src/filter.rs | 35 ++- .../hostcalls/src/foreign_callbacks/mod.rs | 18 ++ t/lib/proxy-wasm-tests/hostcalls/src/lib.rs | 39 ++- .../hostcalls/src/tests/mod.rs | 26 ++ .../hostcalls/src/types/mod.rs | 5 + .../hostcalls/src/types/test_http.rs | 21 ++ 23 files changed, 1096 insertions(+), 33 deletions(-) create mode 100644 src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.c create mode 100644 src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.h create mode 100644 src/common/proxy_wasm/ngx_proxy_wasm_host_lua.c create mode 100644 t/03-proxy_wasm/hfuncs/140-proxy_call_foreign_function.t create mode 100644 t/03-proxy_wasm/hfuncs/141-proxy_resolve_lua.t create mode 100644 t/03-proxy_wasm/hfuncs/contexts/140-proxy_foreign_function_resolve_lua.t create mode 100644 t/lib/proxy-wasm-tests/hostcalls/src/foreign_callbacks/mod.rs diff --git a/config b/config index 62a2f0fc9..634eb71e7 100644 --- a/config +++ b/config @@ -143,6 +143,7 @@ NGX_WASMX_DEPS="\ $ngx_addon_dir/src/common/shm/ngx_wa_shm_queue.h \ $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_foreign_call.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" @@ -158,6 +159,7 @@ NGX_WASMX_SRCS="\ $ngx_addon_dir/src/common/metrics/ngx_wa_metrics.c \ $ngx_addon_dir/src/common/metrics/ngx_wa_histogram.c \ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm.c \ + $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.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_properties.c \ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm.c b/src/common/proxy_wasm/ngx_proxy_wasm.c index f2eada95e..0cd1cd01d 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm.c @@ -5,6 +5,7 @@ #include #include +#include #ifdef NGX_WASM_HTTP #include #endif @@ -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: pwexec->in_tick = 1; rc = ngx_proxy_wasm_on_tick(pwexec); @@ -927,6 +931,45 @@ ngx_proxy_wasm_dispatch_calls_cancel(ngx_proxy_wasm_exec_t *pwexec) } +ngx_uint_t +ngx_proxy_wasm_foreign_calls_total(ngx_proxy_wasm_exec_t *pwexec) +{ + ngx_queue_t *q; + ngx_uint_t n = 0; + + for (q = ngx_queue_head(&pwexec->foreign_calls); + q != ngx_queue_sentinel(&pwexec->foreign_calls); + q = ngx_queue_next(q), n++) { /* void */ } + + dd("n: %ld", n); + + return n; +} + + +void +ngx_proxy_wasm_foreign_calls_cancel(ngx_proxy_wasm_exec_t *pwexec) +{ +#ifdef NGX_WASM_HTTP + ngx_queue_t *q; + ngx_proxy_wasm_foreign_call_t *call; + + while (!ngx_queue_empty(&pwexec->foreign_calls)) { + q = ngx_queue_head(&pwexec->foreign_calls); + call = ngx_queue_data(q, ngx_proxy_wasm_foreign_call_t, q); + + ngx_log_debug1(NGX_LOG_DEBUG_ALL, pwexec->log, 0, + "proxy_wasm foreign function callback cancelled" + " (callback: %p)", call); + + ngx_queue_remove(&call->q); + + ngx_proxy_wasm_foreign_call_destroy(call); + } +#endif +} + + /* host handlers */ @@ -1146,6 +1189,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter, rexec->ictx = ictx; ngx_queue_init(&rexec->dispatch_calls); + ngx_queue_init(&rexec->foreign_calls); log = filter->log; @@ -1263,6 +1307,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter, pwexec->store = ictx->store; ngx_queue_init(&pwexec->dispatch_calls); + ngx_queue_init(&pwexec->foreign_calls); } else { if (in->ictx != ictx) { diff --git a/src/common/proxy_wasm/ngx_proxy_wasm.h b/src/common/proxy_wasm/ngx_proxy_wasm.h index 6da1bc71f..456ec78d3 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm.h +++ b/src/common/proxy_wasm/ngx_proxy_wasm.h @@ -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; @@ -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; @@ -147,6 +148,11 @@ 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; @@ -154,43 +160,46 @@ 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; 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; #ifdef NGX_WASM_HTTP ngx_http_proxy_wasm_dispatch_t *dispatch_call; /* swap pointer for host functions */ #endif ngx_queue_t dispatch_calls; + ngx_proxy_wasm_foreign_call_t *foreign_call; + ngx_queue_t foreign_calls; /* flags */ @@ -415,6 +424,8 @@ 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_foreign_calls_total(ngx_proxy_wasm_exec_t *pwexec); +void ngx_proxy_wasm_foreign_calls_cancel(ngx_proxy_wasm_exec_t *pwexec); /* host handlers */ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.c b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.c new file mode 100644 index 000000000..82b8f950f --- /dev/null +++ b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.c @@ -0,0 +1,281 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#if (NGX_WASM_LUA) +#include +#endif +#include + + +void +ngx_proxy_wasm_foreign_call_destroy(ngx_proxy_wasm_foreign_call_t *call) +{ + ngx_pfree(call->pwexec->pool, call); +} + + +#if (NGX_WASM_HTTP) +#if (NGX_WASM_LUA) +static void +ngx_proxy_wasm_foreign_call(ngx_proxy_wasm_foreign_call_t *call) +{ + ngx_proxy_wasm_exec_t *pwexec = call->pwexec; + ngx_proxy_wasm_step_e step = pwexec->parent->step; + + ngx_queue_remove(&call->q); + + pwexec->foreign_call = call; + + pwexec->parent->phase = ngx_wasm_phase_lookup(&ngx_http_wasm_subsystem, + NGX_WASM_BACKGROUND_PHASE); + + ngx_proxy_wasm_run_step(pwexec, NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK); + + pwexec->parent->step = step; + pwexec->foreign_call = NULL; + + if (ngx_proxy_wasm_foreign_calls_total(pwexec)) { + ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, + "proxy_wasm more foreign function callbacks pending..."); + + ngx_wasm_yield(&call->rctx->env); + ngx_proxy_wasm_ctx_set_next_action(pwexec->parent, + NGX_PROXY_WASM_ACTION_PAUSE); + + } else if (ngx_proxy_wasm_dispatch_calls_total(pwexec)) { + ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, + "proxy_wasm http dispatch calls pending..."); + + ngx_wasm_yield(&call->rctx->env); + ngx_proxy_wasm_ctx_set_next_action(pwexec->parent, + NGX_PROXY_WASM_ACTION_PAUSE); + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, + "proxy_wasm last foreign function callback handled"); + + ngx_wasm_continue(&call->rctx->env); + ngx_proxy_wasm_ctx_set_next_action(pwexec->parent, + NGX_PROXY_WASM_ACTION_CONTINUE); + + /* resume current step if unfinished */ + ngx_proxy_wasm_resume(pwexec->parent, pwexec->parent->phase, step); + } + + ngx_proxy_wasm_foreign_call_destroy(call); +} + + +static void +ngx_proxy_wasm_hfuncs_resolve_lua_handler(ngx_resolver_ctx_t *rslv_ctx) +{ +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + struct sockaddr_in *sin; + u_char *p; + ngx_buf_t *b; + ngx_str_t args; + ngx_wasm_lua_ctx_t *lctx; + ngx_wasm_socket_tcp_t *sock; + ngx_proxy_wasm_foreign_call_t *call; + u_short sa_family = AF_INET; + u_char buf[rslv_ctx->name.len + +#if (NGX_HAVE_INET6) + sizeof(struct in6_addr) + 1]; +#else + sizeof(struct in_addr) + 1]; +#endif + + if (rslv_ctx->addr.socklen == sizeof(struct sockaddr_in6)) { + sa_family = AF_INET6; + } + + sock = (ngx_wasm_socket_tcp_t *) rslv_ctx->data; + lctx = sock->lctx; + call = (ngx_proxy_wasm_foreign_call_t *) sock->data; + p = buf; + + ngx_memzero(buf, sizeof(buf)); + + if (rslv_ctx->state || !rslv_ctx->naddrs) { + p++; /* 1st byte is address length; 0 if address not found */ + goto not_found; + } + + ngx_wa_assert(rslv_ctx->naddrs == 1); + + switch (sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) rslv_ctx->addr.sockaddr; + *(p++) = sizeof(struct in6_addr); + p = ngx_cpymem(p, &sin6->sin6_addr, sizeof(struct in6_addr)); + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) rslv_ctx->addr.sockaddr; + *(p++) = sizeof(struct in_addr); + p = ngx_cpymem(p, &sin->sin_addr, sizeof(struct in_addr)); + } + +not_found: + + p = ngx_cpymem(p, rslv_ctx->name.data, rslv_ctx->name.len); + args.data = buf; + args.len = p - buf; + + call->args_out = ngx_wasm_chain_get_free_buf(call->pwexec->pool, + &call->rctx->free_bufs, + args.len, buf_tag, 1); + + if (call->args_out == NULL) { + goto error; + } + + b = call->args_out->buf; + b->last = ngx_cpymem(b->last, args.data, args.len); + + if (lctx->yielded) { + ngx_proxy_wasm_foreign_call(call); + + if (rslv_ctx->state == NGX_WASM_LUA_RESOLVE_ERR) { + ngx_wasm_resume(&call->rctx->env); + } + } + +error: + + ngx_free(rslv_ctx); +} +#endif /* NGX_WASM_LUA */ + + +ngx_int_t +ngx_proxy_wasm_foreign_call_resolve_lua(ngx_wavm_instance_t *instance, + ngx_http_wasm_req_ctx_t *rctx, ngx_str_t *fargs, ngx_wavm_ptr_t *ret_data, + int32_t *ret_size, wasm_val_t rets[]) +{ + ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec( + instance); +#if (NGX_WASM_LUA) + size_t s; + ngx_int_t rc; + ngx_buf_t *b; + ngx_http_request_t *r; + ngx_resolver_ctx_t *rslv_ctx; + ngx_wasm_socket_tcp_t *sock; + ngx_proxy_wasm_foreign_call_t *call; + ngx_wavm_ptr_t p; + + /* check context */ + + switch (pwexec->parent->step) { + case NGX_PROXY_WASM_STEP_REQ_HEADERS: + case NGX_PROXY_WASM_STEP_REQ_BODY: + case NGX_PROXY_WASM_STEP_TICK: + case NGX_PROXY_WASM_STEP_DISPATCH_RESPONSE: + case NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK: + break; + default: + return ngx_proxy_wasm_result_trap(pwexec, + "can only call resolve_lua " + "during " + "\"on_request_headers\", " + "\"on_request_body\", " + "\"on_dispatch_response\", " + "\"on_tick\", " + "\"on_foreign_function\"", + rets, NGX_WAVM_BAD_USAGE); + } + + /* check name */ + + if (!fargs->len) { + return ngx_proxy_wasm_result_trap(pwexec, + "cannot resolve, missing name", + rets, NGX_WAVM_BAD_USAGE); + } + + call = ngx_pcalloc(pwexec->pool, sizeof(ngx_proxy_wasm_foreign_call_t)); + if (call == NULL) { + goto error; + } + + call->pwexec = pwexec; + call->fcode = NGX_PROXY_WASM_FOREIGN_RESOLVE_LUA; + + /* rctx or fake request */ + + if (rctx == NULL) { + if (ngx_http_wasm_create_fake_rctx(pwexec, &r, &call->rctx) != NGX_OK) { + goto error; + } + + } else { + call->rctx = rctx; + } + + sock = ngx_pcalloc(pwexec->pool, sizeof(ngx_wasm_socket_tcp_t)); + if (sock == NULL) { + goto error; + } + + sock->env = &call->rctx->env; + sock->log = pwexec->log; + sock->pool = pwexec->pool; + sock->data = call; + + rslv_ctx = ngx_calloc(sizeof(ngx_resolver_ctx_t), pwexec->log); + if (rslv_ctx == NULL) { + goto error; + } + + rslv_ctx->name.data = fargs->data; + rslv_ctx->name.len = fargs->len; + rslv_ctx->handler = ngx_proxy_wasm_hfuncs_resolve_lua_handler; + rslv_ctx->data = sock; + + rc = ngx_wasm_lua_resolver_resolve(rslv_ctx); + + switch (rc) { + case NGX_OK: + b = call->args_out->buf; + s = *b->start; /* first byte is the length of the resolved address */ + + p = ngx_proxy_wasm_alloc(pwexec, s); + if (!p) { + goto error; + } + + if (!ngx_wavm_memory_memcpy(instance->memory, p, b->start + 1, s)) { + return ngx_proxy_wasm_result_invalid_mem(rets); + } + + *ret_data = p; + *ret_size = s; + + return ngx_proxy_wasm_result_ok(rets); + + case NGX_AGAIN: + ngx_queue_insert_head(&pwexec->foreign_calls, &call->q); + return ngx_proxy_wasm_result_ok(rets); + + default: + break; + } + +error: + + return ngx_proxy_wasm_result_trap(pwexec, "failed resolving name", + rets, NGX_WAVM_ERROR); +#else + + return ngx_proxy_wasm_result_trap(pwexec, + "cannot resolve, no lua support", + rets, NGX_WAVM_ERROR); +#endif +} +#endif /* NGX_WASM_HTTP */ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.h b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.h new file mode 100644 index 000000000..aa24760f5 --- /dev/null +++ b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_call.h @@ -0,0 +1,33 @@ +#ifndef _NGX_PROXY_WASM_FOREIGN_CALLBACK_H_INCLUDED_ +#define _NGX_PROXY_WASM_FOREIGN_CALLBACK_H_INCLUDED_ + + +#include +#include +#include +#include +#ifdef NGX_WASM_HTTP +#include +#endif + + +struct ngx_proxy_wasm_foreign_call_s { + ngx_queue_t q; + ngx_proxy_wasm_exec_t *pwexec; +#if (NGX_WASM_HTTP) + ngx_http_wasm_req_ctx_t *rctx; +#endif + ngx_proxy_wasm_foreign_function_e fcode; + ngx_chain_t *args_out; +}; + + +void ngx_proxy_wasm_foreign_call_destroy(ngx_proxy_wasm_foreign_call_t *call); + + +#if (NGX_WASM_HTTP) +ngx_int_t ngx_proxy_wasm_foreign_call_resolve_lua(ngx_wavm_instance_t *instance, + ngx_http_wasm_req_ctx_t *rctx, ngx_str_t *fargs, ngx_wavm_ptr_t *ret_data, + int32_t *ret_size, wasm_val_t rets[]); +#endif +#endif diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index 3151312d7..335d4be86 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef NGX_WASM_HTTP #include #endif @@ -34,9 +35,11 @@ ngx_proxy_wasm_get_buffer_helper(ngx_wavm_instance_t *instance, ngx_http_wasm_req_ctx_t *rctx; ngx_http_request_t *r; ngx_proxy_wasm_ctx_t *pwctx; +#endif ngx_proxy_wasm_exec_t *pwexec; pwexec = ngx_proxy_wasm_instance2pwexec(instance); +#ifdef NGX_WASM_HTTP pwctx = pwexec->parent; #endif @@ -146,6 +149,19 @@ ngx_proxy_wasm_get_buffer_helper(ngx_wavm_instance_t *instance, } #endif + case NGX_PROXY_WASM_BUFFER_FOREIGN_FUNCTION_ARGUMENTS: + { + ngx_proxy_wasm_foreign_call_t *call = pwexec->foreign_call; + + if (call == NULL) { + return NULL; + } + + ngx_wa_assert(call->args_out); + + return call->args_out; + } + default: ngx_wavm_log_error(NGX_LOG_WASM_NYI, instance->log, NULL, "NYI - get_buffer bad buf_type: %d", buf_type); @@ -1118,6 +1134,14 @@ ngx_proxy_wasm_hfuncs_send_local_response(ngx_wavm_instance_t *instance, ngx_proxy_wasm_dispatch_calls_cancel(pwexec); } + if (ngx_proxy_wasm_foreign_calls_total(pwexec)) { + ngx_proxy_wasm_log_error(NGX_LOG_NOTICE, pwexec->log, 0, + "local response produced, cancelling " + "pending foreign function callbacks"); + + ngx_proxy_wasm_foreign_calls_cancel(pwexec); + } + break; case NGX_ERROR: @@ -1807,7 +1831,38 @@ ngx_proxy_wasm_hfuncs_increment_metric(ngx_wavm_instance_t *instance, /* custom extension points */ -/* NYI */ + + +static ngx_int_t +ngx_proxy_wasm_hfuncs_call_foreign_function(ngx_wavm_instance_t *instance, + wasm_val_t args[], wasm_val_t rets[]) +{ + ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); +#ifdef NGX_WASM_HTTP + ngx_str_t fname, fargs; + int32_t *ret_size; + ngx_wavm_ptr_t *ret_data = 0; + ngx_http_wasm_req_ctx_t *rctx = ngx_http_proxy_wasm_get_rctx(instance); + + fname.len = args[1].of.i32; + fname.data = NGX_WAVM_HOST_LIFT_SLICE(instance, args[0].of.i32, fname.len); + + fargs.len = args[3].of.i32; + fargs.data = NGX_WAVM_HOST_LIFT_SLICE(instance, args[2].of.i32, fargs.len); + + ret_data = NGX_WAVM_HOST_LIFT(instance, args[4].of.i32, ngx_wavm_ptr_t); + ret_size = NGX_WAVM_HOST_LIFT(instance, args[5].of.i32, int32_t); + + if (ngx_str_eq(fname.data, fname.len, "resolve_lua", -1)) { + return ngx_proxy_wasm_foreign_call_resolve_lua(instance, rctx, &fargs, + ret_data, ret_size, + rets); + } +#endif + + return ngx_proxy_wasm_result_trap(pwexec, "unknown foreign function", + rets, NGX_WAVM_ERROR); +} /* legacy */ @@ -2224,7 +2279,7 @@ static ngx_wavm_host_func_def_t ngx_proxy_wasm_hfuncs[] = { ngx_wavm_arity_i32x5, ngx_wavm_arity_i32 }, { ngx_string("proxy_call_foreign_function"), /* 0.2.0 && 0.2.1 */ - &ngx_proxy_wasm_hfuncs_nop, /* NYI */ + &ngx_proxy_wasm_hfuncs_call_foreign_function, ngx_wavm_arity_i32x6, ngx_wavm_arity_i32 }, diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host_lua.c b/src/common/proxy_wasm/ngx_proxy_wasm_host_lua.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_util.c b/src/common/proxy_wasm/ngx_proxy_wasm_util.c index c99f5d255..90cc392ef 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_util.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_util.c @@ -41,7 +41,8 @@ static ngx_str_t ngx_proxy_wasm_steplist[] = { ngx_string("on_log"), ngx_string("on_done"), ngx_string("on_tick"), - ngx_string("on_dispatch_response") + ngx_string("on_dispatch_response"), + ngx_string("on_foreign_function") }; @@ -62,7 +63,7 @@ ngx_proxy_wasm_step_name(ngx_proxy_wasm_step_e step) ngx_str_t *name; ngx_wa_assert(step); - ngx_wa_assert(step <= NGX_PROXY_WASM_STEP_DISPATCH_RESPONSE); + ngx_wa_assert(step <= NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK); name = &ngx_proxy_wasm_steplist[step]; diff --git a/src/http/proxy_wasm/ngx_http_proxy_wasm.c b/src/http/proxy_wasm/ngx_http_proxy_wasm.c index d44dafc4d..782fb9882 100644 --- a/src/http/proxy_wasm/ngx_http_proxy_wasm.c +++ b/src/http/proxy_wasm/ngx_http_proxy_wasm.c @@ -3,6 +3,7 @@ #endif #include "ddebug.h" +#include #include @@ -357,6 +358,32 @@ ngx_http_proxy_wasm_on_dispatch_response(ngx_proxy_wasm_exec_t *pwexec) } +static ngx_int_t +ngx_http_proxy_wasm_on_foreign_function(ngx_proxy_wasm_exec_t *pwexec) +{ + size_t args_len = 0; + ngx_int_t rc; + ngx_chain_t *cl; + ngx_proxy_wasm_filter_t *filter = pwexec->filter; + ngx_proxy_wasm_foreign_call_t *call = pwexec->foreign_call; + ngx_http_wasm_req_ctx_t *rctx = call->rctx; + + ngx_wasm_continue(&rctx->env); + + cl = call->args_out; + if (cl) { + args_len = cl->buf->last - cl->buf->start; + } + + rc = ngx_wavm_instance_call_funcref(pwexec->ictx->instance, + filter->proxy_on_custom_callback, + NULL, pwexec->id, call->fcode, + args_len); + + return rc; +} + + static ngx_int_t ngx_http_proxy_wasm_ecode(ngx_proxy_wasm_err_e ecode) { @@ -445,6 +472,9 @@ ngx_http_proxy_wasm_resume(ngx_proxy_wasm_exec_t *pwexec, case NGX_PROXY_WASM_STEP_DISPATCH_RESPONSE: rc = ngx_http_proxy_wasm_on_dispatch_response(pwexec); break; + case NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK: + rc = ngx_http_proxy_wasm_on_foreign_function(pwexec); + break; default: ngx_proxy_wasm_log_error(NGX_LOG_WASM_NYI, pwexec->log, 0, "NYI - proxy-wasm step: %d", step); diff --git a/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c b/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c index 3c8a6734e..4dd7b20ab 100644 --- a/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c +++ b/src/http/proxy_wasm/ngx_http_proxy_wasm_dispatch.c @@ -915,6 +915,15 @@ ngx_http_proxy_wasm_dispatch_resume_handler(ngx_wasm_socket_tcp_t *sock) ngx_proxy_wasm_ctx_set_next_action(pwexec->parent, NGX_PROXY_WASM_ACTION_PAUSE); + } else if (ngx_proxy_wasm_foreign_calls_total(pwexec)) { + ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, + "proxy_wasm foreign function callbacks pending..."); + + rc = NGX_AGAIN; + ngx_wasm_yield(&rctx->env); + ngx_proxy_wasm_ctx_set_next_action(pwexec->parent, + NGX_PROXY_WASM_ACTION_PAUSE); + } else { ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, "proxy_wasm last http dispatch call handled"); diff --git a/t/03-proxy_wasm/hfuncs/140-proxy_call_foreign_function.t b/t/03-proxy_wasm/hfuncs/140-proxy_call_foreign_function.t new file mode 100644 index 000000000..628f19d98 --- /dev/null +++ b/t/03-proxy_wasm/hfuncs/140-proxy_call_foreign_function.t @@ -0,0 +1,43 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; + +plan_tests(5); +run_tests(); + +__DATA__ + +=== TEST 1: proxy_wasm - call_foreign_function(), unknown function +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/call_foreign_function \ + name=foo'; + } +--- error_code: 500 +--- error_log eval +qr/host trap \(internal error\): unknown foreign function/ +--- no_error_log +[crit] +[emerg] +[stub] + + + +=== TEST 2: proxy_wasm - call_foreign_function(), resolve_lua (no lua support) +--- skip_eval: 5: $::nginxV =~ m/openresty/ +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/call_foreign_function \ + name=resolve_lua'; + } +--- error_code: 500 +--- error_log eval +qr/host trap \(internal error\): cannot resolve, no lua support/ +--- no_error_log +[crit] +[emerg] +[stub] diff --git a/t/03-proxy_wasm/hfuncs/141-proxy_resolve_lua.t b/t/03-proxy_wasm/hfuncs/141-proxy_resolve_lua.t new file mode 100644 index 000000000..fe811d7d5 --- /dev/null +++ b/t/03-proxy_wasm/hfuncs/141-proxy_resolve_lua.t @@ -0,0 +1,188 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(5); +run_tests(); + +__DATA__ + +=== TEST 1: proxy_wasm - resolve_lua, NXDOMAIN +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=foo'; + return 200; + } +--- error_log eval +qr/could not resolve foo/ +--- no_error_log +[crit] +[emerg] +[stub] + + + +=== TEST 2: proxy_wasm - resolve_lua (no yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=localhost'; + return 200; + } +--- error_log eval +qr/resolved \(no yielding\) localhost to \[127, 0, 0, 1\]/ +--- no_error_log +[error] +[crit] +[emerg] + + + +=== TEST 3: proxy_wasm - resolve_lua (yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=httpbin.org'; + return 200; + } +--- error_log eval +qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/ +--- no_error_log +[error] +[crit] +[emerg] + + + +=== TEST 4: proxy_wasm - resolve_lua, IPv6 record +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=ipv6.google.com'; + return 200; + } +--- error_log eval +qr/resolved \(yielding\) ipv6\.google\.com to \[\d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+, \d+\]/ +--- no_error_log +[error] +[crit] +[emerg] + + + +=== TEST 5: proxy_wasm - resolve_lua, multiple calls (yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=httpbin.org,wikipedia.org'; + return 200; + } +--- error_log eval +[ + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/, + qr/resolved \(yielding\) wikipedia\.org to \[\d+, \d+, \d+, \d+\]/ +] +--- no_error_log +[error] +[crit] + + + +=== TEST 6: proxy_wasm - resolve_lua, multiple calls (mixed) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve_lua \ + name=localhost,httpbin.org'; + return 200; + } +--- error_log eval +[ + qr/resolved \(no yielding\) localhost to \[127, 0, 0, 1\]/, + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/ +] +--- no_error_log +[error] +[crit] + + + +=== TEST 7: proxy_wasm - resolve_lua, on_tick +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'tick_period=500 \ + on_tick=resolve_lua \ + name=localhost,httpbin.org'; + return 200; + } +--- error_log eval +[ + qr/resolved \(no yielding\) localhost to \[127, 0, 0, 1\]/, + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/ +] +--- no_error_log +[error] +[crit] + + + +=== TEST 8: proxy_wasm - resolve_lua, on_http_call_response +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/dispatch_http_call \ + host=127.0.0.1:$TEST_NGINX_SERVER_PORT \ + path=/dispatch \ + on_http_call_response=resolve_lua \ + name=httpbin.org,wikipedia.org'; + return 200; + } + + location /dispatch { + return 200; + } +--- error_log eval +[ + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/, + qr/resolved \(yielding\) wikipedia\.org to \[\d+, \d+, \d+, \d+\]/ +] +--- no_error_log +[error] +[crit] + + + +=== TEST 9: proxy_wasm - resolve_lua, on_foreign_function +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/resolve_lua \ + on_foreign_function=resolve_lua \ + name=httpbin.org'; + return 200; + } +--- error_log eval +[ + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/, + qr/resolved \(yielding\) httpbin\.org to \[\d+, \d+, \d+, \d+\]/ +] +--- no_error_log +[error] +[crit] diff --git a/t/03-proxy_wasm/hfuncs/contexts/140-proxy_foreign_function_resolve_lua.t b/t/03-proxy_wasm/hfuncs/contexts/140-proxy_foreign_function_resolve_lua.t new file mode 100644 index 000000000..f77e54694 --- /dev/null +++ b/t/03-proxy_wasm/hfuncs/contexts/140-proxy_foreign_function_resolve_lua.t @@ -0,0 +1,169 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_hup(); +skip_no_debug(); +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: proxy_wasm contexts - call_resolve_lua on_vm_start +--- main_config + wasm { + module context_checks $TEST_NGINX_CRATES_DIR/context_checks.wasm 'call_resolve_lua'; + } +--- config + location /t { + proxy_wasm context_checks; + return 200; + } +--- must_die: 0 +--- error_log +[error] +can only call resolve_lua during "on_request_headers", "on_request_body", "on_dispatch_response", "on_tick", "on_foreign_function" +--- no_error_log +[crit] + + + +=== TEST 2: proxy_wasm contexts - call_resolve_lua on_configure +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_configure=call_resolve_lua'; + return 200; + } +--- must_die: 0 +--- error_log +[error] +can only call resolve_lua during "on_request_headers", "on_request_body", "on_dispatch_response", "on_tick", "on_foreign_function" +--- no_error_log +[crit] + + + +=== TEST 3: proxy_wasm contexts - call_resolve_lua on_tick +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_tick=call_resolve_lua'; + return 200; + } +--- error_log +[error] +cannot resolve, missing name +--- no_error_log +[crit] + + + +=== TEST 4: proxy_wasm contexts - call_resolve_lua on_http_dispatch_response +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_http_dispatch_response=call_resolve_lua \ + host=127.0.0.1:$TEST_NGINX_SERVER_PORT'; + return 200; + } + + location /dispatch { + return 200; + } +--- error_log +[error] +cannot resolve, missing name +--- no_error_log +[crit] + + + +=== TEST 5: proxy_wasm contexts - call_resolve_lua on_request_headers +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_request_headers=call_resolve_lua'; + return 200; + } +--- error_code: 500 +--- ignore_response_body +--- error_log +[error] +cannot resolve, missing name +--- no_error_log +[crit] + + + +=== TEST 6: proxy_wasm contexts - call_resolve_lua on_request_body +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_request_body=call_resolve_lua'; + echo ok; + } +--- request +POST /t +payload +--- error_code: 500 +--- ignore_response_body +--- error_log +[error] +cannot resolve, missing name +--- no_error_log +[crit] + + + +=== TEST 7: proxy_wasm contexts - call_resolve_lua on_response_headers +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_response_headers=call_resolve_lua'; + echo ok; + } +--- ignore_response_body +--- error_log +[error] +can only call resolve_lua during "on_request_headers", "on_request_body", "on_dispatch_response", "on_tick", "on_foreign_function" +--- no_error_log +[crit] + + + +=== TEST 8: proxy_wasm contexts - call_resolve_lua on_response_body +--- load_nginx_modules: ngx_http_echo_module +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_response_body=call_resolve_lua'; + echo ok; + } +--- ignore_response_body +--- error_log +[error] +can only call resolve_lua during "on_request_headers", "on_request_body", "on_dispatch_response", "on_tick", "on_foreign_function" +--- no_error_log +[crit] + + + +=== TEST 9: proxy_wasm contexts - call_resolve_lua on_log +--- wasm_modules: context_checks +--- config + location /t { + proxy_wasm context_checks 'on_log=call_resolve_lua'; + return 200; + } +--- error_log +[error] +can only call resolve_lua during "on_request_headers", "on_request_body", "on_dispatch_response", "on_tick", "on_foreign_function" +--- no_error_log +[crit] diff --git a/t/lib/Cargo.lock b/t/lib/Cargo.lock index 4e418ff3c..1552a028f 100644 --- a/t/lib/Cargo.lock +++ b/t/lib/Cargo.lock @@ -156,6 +156,12 @@ dependencies = [ "quote", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "failure" version = "0.1.8" @@ -171,6 +177,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "getrandom" version = "0.2.15" @@ -207,6 +219,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hex" version = "0.4.3" @@ -220,7 +243,7 @@ dependencies = [ "enum-utils", "http", "log", - "proxy-wasm 0.2.2", + "proxy-wasm 0.2.3-dev", "urlencoding", ] @@ -355,6 +378,15 @@ dependencies = [ "log", ] +[[package]] +name = "proxy-wasm" +version = "0.2.3-dev" +source = "git+https://github.com/casimiro/proxy-wasm-rust-sdk.git?branch=foreign-function-callback#501e78c45c5b3b203d51ae8bb8ebf03509fb55e1" +dependencies = [ + "hashbrown 0.15.1", + "log", +] + [[package]] name = "quote" version = "1.0.37" diff --git a/t/lib/proxy-wasm-tests/context-checks/src/hostcalls.rs b/t/lib/proxy-wasm-tests/context-checks/src/hostcalls.rs index 66a939603..bf0f3308c 100644 --- a/t/lib/proxy-wasm-tests/context-checks/src/hostcalls.rs +++ b/t/lib/proxy-wasm-tests/context-checks/src/hostcalls.rs @@ -1,7 +1,7 @@ use crate::*; use log::*; use proxy_wasm::types::*; -use std::ptr::null_mut; +use std::ptr::{null, null_mut}; #[allow(improper_ctypes)] extern "C" { @@ -455,3 +455,29 @@ pub fn increment_metric(_ctx: &TestContext) { info!("increment_metric status: {}", status as u32); } } + +#[allow(improper_ctypes)] +extern "C" { + fn proxy_call_foreign_function( + function_name_data: *const u8, + function_name_size: usize, + arguments_data: *const u8, + arguments_size: usize, + results_data: *mut *mut u8, + results_size: *mut usize, + ) -> Status; +} + +pub fn call_resolve_lua(_ctx: &TestContext) { + let name = "resolve_lua"; + + let mut ret_data: *mut u8 = null_mut(); + let mut ret_size: usize = 0; + + unsafe { + let status = proxy_call_foreign_function( + name.as_ptr(), name.len(), null(), 0, &mut ret_data, &mut ret_size); + + info!("resolve_lua status: {}", status as u32); + } +} diff --git a/t/lib/proxy-wasm-tests/context-checks/src/lib.rs b/t/lib/proxy-wasm-tests/context-checks/src/lib.rs index 1d63167e6..14be08b66 100644 --- a/t/lib/proxy-wasm-tests/context-checks/src/lib.rs +++ b/t/lib/proxy-wasm-tests/context-checks/src/lib.rs @@ -23,6 +23,7 @@ impl TestContext { "increment_metric" => increment_metric(self), "record_metric" => record_metric(self), "get_metric" => get_metric(self), + "call_resolve_lua" => call_resolve_lua(self), "set_tick_period" => set_tick_period(self), "add_request_header" => add_request_header(self, arg), "add_response_header" => add_response_header(self, arg), diff --git a/t/lib/proxy-wasm-tests/hostcalls/Cargo.toml b/t/lib/proxy-wasm-tests/hostcalls/Cargo.toml index dad9e5d85..66e168d55 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/Cargo.toml +++ b/t/lib/proxy-wasm-tests/hostcalls/Cargo.toml @@ -8,7 +8,8 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -proxy-wasm = "0.2" +#proxy-wasm = "0.2" +proxy-wasm = { git = "https://github.com/casimiro/proxy-wasm-rust-sdk.git", branch = "foreign-function-callback" } log = "0.4" http = "0.2" enum-utils = "0.1.2" diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs b/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs index f3c966c35..9de73fd6c 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs @@ -1,7 +1,8 @@ -use crate::{test_http::*, types::*}; +use crate::{test_http::*, types::*, tests::*, foreign_callbacks::*}; use http::StatusCode; use log::*; -use proxy_wasm::{traits::*, types::*}; +use proxy_wasm::{traits::*, types::*, hostcalls::*}; + impl Context for TestHttp { fn on_http_call_response( @@ -108,12 +109,42 @@ impl Context for TestHttp { Some(format!("called {} times", self.n_sync_calls + 1).as_str()), ); } + "resolve_lua" => { + self.pending_callbacks = test_proxy_resolve_lua(self); + if self.pending_callbacks > 0 { + return; + } + } _ => {} } self.resume_http_request() } + fn on_foreign_function(&mut self, function_id: u32, args_size: usize) { + info!("[hostcalls] on_foreign_function!"); + + let f: WasmxForeignFunction = unsafe { ::std::mem::transmute(function_id) }; + let args = get_buffer(BufferType::ForeignFunctionArguments, 0, args_size).unwrap(); + + match f { + WasmxForeignFunction::ResolveLua => { + resolve_lua_callback(args); + } + } + + match self.get_config("on_foreign_function").unwrap_or("") { + "resolve_lua" => { test_proxy_resolve_lua(self); } + _ => (), + } + + self.pending_callbacks -= 1; + + if self.pending_callbacks == 0 { + self.send_plain_response(StatusCode::OK, Some("resolved")); + } + } + fn on_done(&mut self) -> bool { info!("[hostcalls] on_done"); diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/foreign_callbacks/mod.rs b/t/lib/proxy-wasm-tests/hostcalls/src/foreign_callbacks/mod.rs new file mode 100644 index 000000000..ec362e071 --- /dev/null +++ b/t/lib/proxy-wasm-tests/hostcalls/src/foreign_callbacks/mod.rs @@ -0,0 +1,18 @@ +use log::*; + +pub fn resolve_lua_callback(args: Option>) { + 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") + } +} diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/lib.rs b/t/lib/proxy-wasm-tests/hostcalls/src/lib.rs index f5719536e..3331d3774 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/lib.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/lib.rs @@ -3,10 +3,11 @@ mod filter; mod tests; mod types; +mod foreign_callbacks; -use crate::{tests::*, types::test_http::*, types::test_root::*, types::*}; +use crate::{tests::*, types::test_http::*, types::test_root::*, types::*, foreign_callbacks::*}; use log::*; -use proxy_wasm::{traits::*, types::*}; +use proxy_wasm::{traits::*, types::*, hostcalls::*}; use std::{collections::BTreeMap, collections::HashMap, time::Duration}; proxy_wasm::main! {{ @@ -114,6 +115,26 @@ impl RootContext for TestRoot { self.n_sync_calls += 1; } "set_property" => test_set_property(self), + "resolve_lua" => { + let names = self + .config + .get("name") + .map(|x| x.as_str()) + .expect("expected a name argument"); + + for name in names.split(",") { + info!("attempting to resolve {}", name); + match call_foreign_function("resolve_lua", Some(name.as_bytes())) { + Ok(ret) => { + match ret { + Some(bytes) => info!("resolved (no yielding) {} to {:?}", name, bytes), + _ => () + } + } + _ => () + } + } + } "dispatch" => { let mut timeout = Duration::from_secs(0); let mut headers = Vec::new(); @@ -201,6 +222,7 @@ impl RootContext for TestRoot { on_phases: phases, metrics: self.metrics.clone(), n_sync_calls: 0, + pending_callbacks: 0, })) } } @@ -226,4 +248,17 @@ impl Context for TestRoot { None => {} } } + + fn on_foreign_function(&mut self, function_id: u32, args_size: usize) { + info!("[hostcalls] on_foreign_function!"); + + let f: WasmxForeignFunction = unsafe { ::std::mem::transmute(function_id) }; + let args = get_buffer(BufferType::ForeignFunctionArguments, 0, args_size).unwrap(); + + match f { + WasmxForeignFunction::ResolveLua => { + resolve_lua_callback(args); + } + } + } } diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs b/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs index ae4ffb612..571dd00e2 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs @@ -695,3 +695,29 @@ pub(crate) fn test_proxy_get_header_map_value_misaligned_return_data(_ctx: &Test ); } } + +pub(crate) fn test_proxy_resolve_lua(ctx: &TestHttp) -> u32 { + let mut pending_callbacks = 0; + let names = ctx + .config + .get("name") + .map(|x| x.as_str()) + .expect("expected a name argument"); + + for name in names.split(",") { + info!("attempting to resolve {}", name); + match call_foreign_function("resolve_lua", Some(name.as_bytes())) { + Ok(ret) => { + match ret { + Some(bytes) => info!("resolved (no yielding) {} to {:?}", name, bytes), + _ => { + pending_callbacks += 1; + } + } + } + _ => () + } + } + + return pending_callbacks; +} diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/types/mod.rs b/t/lib/proxy-wasm-tests/hostcalls/src/types/mod.rs index 315efbf9b..6e0f23124 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/types/mod.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/types/mod.rs @@ -16,6 +16,11 @@ pub enum TestPhase { Log, } +#[repr(u32)] +pub enum WasmxForeignFunction { + ResolveLua = 0, +} + pub trait TestContext { fn get_config(&self, name: &str) -> Option<&str>; fn get_metrics_mapping(&self) -> &BTreeMap; diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs index 6601ea288..bfa3a8c60 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs @@ -8,6 +8,7 @@ pub struct TestHttp { pub config: HashMap, pub metrics: BTreeMap, pub n_sync_calls: usize, + pub pending_callbacks: u32, } impl TestHttp { @@ -150,6 +151,26 @@ impl TestHttp { get_metric(h_id).unwrap(); } + /* foreign functions */ + "/t/call_foreign_function" => { + let name = self.config.get("name").expect("expected a name argument"); + + let ret = match self.config.get("arg") { + Some(arg) => call_foreign_function(name, Some((&arg).as_bytes())).unwrap(), + None => call_foreign_function(name, None).unwrap(), + }.unwrap(); + + info!("foreign function {} returned {:?}", name, ret); + } + "/t/resolve_lua" => { + self.pending_callbacks = test_proxy_resolve_lua(self); + + if self.pending_callbacks > 0 { + return Action::Pause + } + + } + /* errors */ "/t/trap" => panic!("custom trap"), "/t/error/get_response_body" => {