diff --git a/config b/config index 62a2f0fc9..bbbf2e493 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_callback.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_callback.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 c2359216f..23e2cb198 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_callbacks_total(ngx_proxy_wasm_exec_t *pwexec) +{ + ngx_queue_t *q; + ngx_uint_t n = 0; + + for (q = ngx_queue_head(&pwexec->fcallbacks); + q != ngx_queue_sentinel(&pwexec->fcallbacks); + q = ngx_queue_next(q), n++) { /* void */ } + + dd("n: %ld", n); + + return n; +} + + +void +ngx_proxy_wasm_foreign_callbacks_cancel(ngx_proxy_wasm_exec_t *pwexec) +{ +#ifdef NGX_WASM_HTTP + ngx_queue_t *q; + ngx_proxy_wasm_foreign_cb_t *cb; + + while (!ngx_queue_empty(&pwexec->fcallbacks)) { + q = ngx_queue_head(&pwexec->fcallbacks); + cb = ngx_queue_data(q, ngx_proxy_wasm_foreign_cb_t, q); + + ngx_log_debug1(NGX_LOG_DEBUG_ALL, pwexec->log, 0, + "proxy_wasm foreign function callback cancelled" + " (callback: %p)", cb); + + ngx_queue_remove(&cb->q); + + ngx_proxy_wasm_foreign_callback_destroy(cb); + } +#endif +} + + /* host handlers */ @@ -1263,6 +1306,7 @@ ngx_proxy_wasm_create_context(ngx_proxy_wasm_filter_t *filter, pwexec->store = ictx->store; ngx_queue_init(&pwexec->calls); + ngx_queue_init(&pwexec->fcallbacks); } 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 1d9c73981..570ca318a 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 = 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_cb_s ngx_proxy_wasm_foreign_cb_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 *call; /* swap pointer for host functions */ + ngx_http_proxy_wasm_dispatch_t *call; /* swap pointer for host functions */ #endif - ngx_queue_t calls; + ngx_queue_t calls; + ngx_proxy_wasm_foreign_cb_t *fcallback; + ngx_queue_t fcallbacks; /* flags */ @@ -415,6 +424,9 @@ 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_callbacks_total( + ngx_proxy_wasm_exec_t *pwexec); +void ngx_proxy_wasm_foreign_callbacks_cancel(ngx_proxy_wasm_exec_t *pwexec); /* host handlers */ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.c b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.c new file mode 100644 index 000000000..eca9ee004 --- /dev/null +++ b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.c @@ -0,0 +1,131 @@ +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + +#include + + +void +ngx_proxy_wasm_foreign_callback_destroy(ngx_proxy_wasm_foreign_cb_t *cb) +{ + + ngx_pfree(cb->pwexec->pool, cb); +} + + +ngx_proxy_wasm_foreign_cb_t * +ngx_proxy_wasm_foreign_callback_alloc(ngx_proxy_wasm_exec_t *pwexec) +{ + ngx_proxy_wasm_foreign_cb_t *cb; + + cb = ngx_palloc(pwexec->pool, sizeof(ngx_proxy_wasm_foreign_cb_t)); + cb->pwexec = pwexec; + + return cb; +} + + +void +ngx_proxy_wasm_foreign_callback(ngx_proxy_wasm_foreign_cb_t *cb) +{ + ngx_proxy_wasm_exec_t *pwexec = cb->pwexec; + ngx_proxy_wasm_err_e ecode = NGX_PROXY_WASM_ERR_NONE; + ngx_proxy_wasm_step_e step = pwexec->parent->step; + + ngx_queue_remove(&cb->q); + pwexec->fcallback = cb; + +#if (NGX_WASM_HTTP) + pwexec->parent->phase = ngx_wasm_phase_lookup(&ngx_http_wasm_subsystem, + NGX_WASM_BACKGROUND_PHASE); +#endif + + ecode = ngx_proxy_wasm_run_step(pwexec, + NGX_PROXY_WASM_STEP_FOREIGN_CALLBACK); + if (ecode != NGX_PROXY_WASM_ERR_NONE) { + /* TODO: error handling */ + } + + pwexec->parent->step = step; + pwexec->fcallback = NULL; + + if (ngx_proxy_wasm_foreign_callbacks_total(pwexec)) { + ngx_log_debug0(NGX_LOG_DEBUG_WASM, pwexec->log, 0, + "proxy_wasm more foreign function callbacks pending..."); + +#if (NGX_WASM_HTTP) + ngx_wasm_yield(&cb->rctx->env); +#endif + 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"); + +#if (NGX_WASM_HTTP) + ngx_wasm_continue(&cb->rctx->env); +#endif + 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_callback_destroy(cb); +} + + +ngx_int_t +ngx_proxy_wasm_foreign_callback_buffer_create(ngx_proxy_wasm_foreign_cb_t *cb, + size_t size) +{ + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_proxy_wasm_exec_t *pwexec = cb->pwexec; + + ngx_wa_assert(pwexec); + ngx_wa_assert(size); + + cl = ngx_alloc_chain_link(pwexec->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + /* TODO: if size exceeds a threshold, split allocation into N buffers */ + + b = ngx_create_temp_buf(pwexec->pool, size); + if (b == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + cb->args_out = cl; + + return NGX_OK; +} + + +ngx_int_t +ngx_proxy_wasm_foreign_callback_buffer_write(ngx_proxy_wasm_foreign_cb_t *cb, + ngx_str_t *data) +{ + size_t b_size; + ngx_buf_t *b = cb->args_out->buf; + + b_size = b->end - b->start; + + ngx_wa_assert(data->len <= b_size); + + if (data->len <= b_size) { + b->last = ngx_cpymem(b->last, data->data, data->len); + } + + /* TODO: data->len > b_size */ + + return NGX_OK; +} diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.h b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.h new file mode 100644 index 000000000..c574246aa --- /dev/null +++ b/src/common/proxy_wasm/ngx_proxy_wasm_foreign_callback.h @@ -0,0 +1,28 @@ +#ifndef _NGX_PROXY_WASM_FOREIGN_CALLBACK_H_INCLUDED_ +#define _NGX_PROXY_WASM_FOREIGN_CALLBACK_H_INCLUDED_ + + +#include +#include + + +struct ngx_proxy_wasm_foreign_cb_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; +}; + + +ngx_proxy_wasm_foreign_cb_t * ngx_proxy_wasm_foreign_callback_alloc( + ngx_proxy_wasm_exec_t *pwexec); +void ngx_proxy_wasm_foreign_callback(ngx_proxy_wasm_foreign_cb_t *cb); +ngx_int_t ngx_proxy_wasm_foreign_callback_buffer_create( + ngx_proxy_wasm_foreign_cb_t *cb, size_t size); +ngx_int_t ngx_proxy_wasm_foreign_callback_buffer_write( + ngx_proxy_wasm_foreign_cb_t *cb, ngx_str_t *data); +void ngx_proxy_wasm_foreign_callback_destroy(ngx_proxy_wasm_foreign_cb_t *cb); +#endif diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index efde7b98f..25b05fe03 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -11,9 +11,13 @@ #include #include #include +#if (NGX_WASM_LUA) +#include +#endif #ifdef NGX_WASM_HTTP #include #endif +#include #ifdef NGX_WASM_HTTP @@ -29,16 +33,16 @@ static ngx_chain_t * ngx_proxy_wasm_get_buffer_helper(ngx_wavm_instance_t *instance, ngx_proxy_wasm_buffer_type_e buf_type, unsigned *none, char **trapmsg) { -#ifdef NGX_WASM_HTTP ngx_chain_t *cl; - ngx_http_wasm_req_ctx_t *rctx; - ngx_http_request_t *r; ngx_proxy_wasm_ctx_t *pwctx; ngx_proxy_wasm_exec_t *pwexec; +#ifdef NGX_WASM_HTTP + ngx_http_wasm_req_ctx_t *rctx; + ngx_http_request_t *r; +#endif pwexec = ngx_proxy_wasm_instance2pwexec(instance); pwctx = pwexec->parent; -#endif switch (buf_type) { @@ -146,6 +150,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_cb_t *cb = pwexec->fcallback; + + if (cb == NULL) { + return NULL; + } + + ngx_wa_assert(cb->args_out); + + return cb->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 +1135,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_callbacks_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_callbacks_cancel(pwexec); + } + break; case NGX_ERROR: @@ -1807,7 +1832,200 @@ ngx_proxy_wasm_hfuncs_increment_metric(ngx_wavm_instance_t *instance, /* custom extension points */ -/* NYI */ + + +#if (NGX_WASM_LUA) +static void +ngx_proxy_wasm_hfuncs_resolve_lua_handler(ngx_resolver_ctx_t *rslv_ctx) +{ + u_char *p; + ngx_str_t args; + ngx_wasm_lua_ctx_t *lctx; + ngx_wasm_socket_tcp_t *sock; + ngx_proxy_wasm_foreign_cb_t *cb; + u_short sa_family = AF_INET; + static size_t ipv6_len = 16; + u_char buf[rslv_ctx->name.len + ipv6_len + 1]; + + sock = (ngx_wasm_socket_tcp_t *) rslv_ctx->data; + lctx = sock->lctx; + cb = (ngx_proxy_wasm_foreign_cb_t *) sock->data; + p = buf; + + ngx_memzero(buf, sizeof(buf)); + + if (rslv_ctx->state || !rslv_ctx->naddrs) { + p++; + goto not_found; + } + + switch (sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + struct sockaddr_in6 *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 */ + struct sockaddr_in *sin = \ + (struct sockaddr_in *) rslv_ctx->addr.sockaddr; + + *(p++) = sizeof(struct in_addr); + p = ngx_cpymem(p, (u_char *) &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; + + if (ngx_proxy_wasm_foreign_callback_buffer_create(cb, args.len) != NGX_OK) { + goto error; + } + + if (ngx_proxy_wasm_foreign_callback_buffer_write(cb, &args) != NGX_OK) { + goto error; + } + + if (lctx->yielded) { + ngx_proxy_wasm_foreign_callback(cb); + + if (rslv_ctx->state == NGX_WASM_LUA_RESOLVE_ERR) { + ngx_wasm_resume(&cb->rctx->env); + } + } + +error: + + ngx_free(rslv_ctx); +} + + +static ngx_int_t +ngx_proxy_wasm_hfuncs_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[]) +{ + 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_exec_t *pwexec; + ngx_proxy_wasm_foreign_cb_t *cb; + ngx_wavm_ptr_t p = 0; + + pwexec = ngx_proxy_wasm_instance2pwexec(instance); + + cb = ngx_proxy_wasm_foreign_callback_alloc(pwexec); + if (cb == NULL) { + goto error; + } + + cb->fcode = NGX_PROXY_WASM_FOREIGN_RESOLVE; + + /* rctx or fake request */ + + if (rctx == NULL) { + if (ngx_http_wasm_create_fake_req_ctx(pwexec, &r, &cb->rctx) + != NGX_OK) + { + goto error; + } + + } else { + cb->rctx = rctx; + } + + sock = ngx_palloc(pwexec->pool, sizeof(ngx_wasm_socket_tcp_t)); + if (sock == NULL) { + goto error; + } + + sock->env = &cb->rctx->env; + sock->log = pwexec->log; + sock->pool = pwexec->pool; + sock->data = cb; + + 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 = cb->args_out->buf; + s = *b->start; /* first byte is the length of the resolved address */ + + p = ngx_proxy_wasm_alloc(pwexec, s); + 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->fcallbacks, &cb->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); +} +#endif + + +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); +#if (NGX_WASM_LUA) + 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_hfuncs_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 +2442,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..11d3dd290 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_util.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_util.c @@ -62,7 +62,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 d8f809116..533373d10 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 @@ -351,6 +352,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_cb_t *cb = pwexec->fcallback; + ngx_http_wasm_req_ctx_t *rctx = cb->rctx; + + ngx_wasm_continue(&rctx->env); + + cl = cb->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, cb->fcode, + args_len); + + return rc; +} + + static ngx_int_t ngx_http_proxy_wasm_ecode(ngx_proxy_wasm_err_e ecode) { @@ -439,6 +466,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/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t b/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t index 7356cd8a7..fe95b6787 100644 --- a/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t +++ b/t/03-proxy_wasm/hfuncs/130-proxy_dispatch_http.t @@ -273,7 +273,8 @@ qq{ } --- config location /t { - proxy_wasm hostcalls 'test=/t/dispatch_http_call \ + proxy_wasm hostcalls 'on=request_headers \ + test=/t/dispatch_http_call \ host=httpbin.org'; echo ok; } diff --git a/t/03-proxy_wasm/hfuncs/300-foreign_resolve.t b/t/03-proxy_wasm/hfuncs/300-foreign_resolve.t new file mode 100644 index 000000000..e5ce4fc3c --- /dev/null +++ b/t/03-proxy_wasm/hfuncs/300-foreign_resolve.t @@ -0,0 +1,100 @@ +# 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 - call_foreign_function() resolve, NXDOMAIN +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm_lua_resolver on; + proxy_wasm hostcalls 'test=/t/resolve \ + name=foo'; + return 200; + } +--- error_log eval +qr/could not resolve foo/ +--- no_error_log +[crit] +[emerg] +[stub] + + + +=== TEST 2: proxy_wasm - call_foreign_function() resolve (no yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/resolve \ + 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 - call_foreign_function() resolve (yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/resolve \ + 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 - call_foreign_function() resolve, multiple calls (yielding) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/resolve \ + 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 5: proxy_wasm - call_foreign_function() resolve, multiple calls (mixed) +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'test=/t/resolve \ + 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] 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/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 9f771c645..31ba28ab2 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/filter.rs @@ -1,7 +1,7 @@ use crate::{test_http::*, types::*}; 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,6 +108,31 @@ impl Context for TestHttp { 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 => { + match args { + Some(bytes) => { + let address_size = bytes[0] as usize; + let name = std::str::from_utf8(&bytes[(address_size + 1)..]).unwrap(); + if address_size > 0 { + let address = &bytes[1..address_size + 1]; + info!("resolved (yielding) {} to {:?}", name, address) + } else { + info!("could not resolve {}", name) + } + } + _ => info!("empty buffer") + } + } + } + } + fn on_done(&mut self) -> bool { info!("[hostcalls] on_done"); 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..85d2fff83 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 @@ -150,6 +150,36 @@ impl TestHttp { get_metric(h_id).unwrap(); } + /* foreign functions */ + "/t/resolve" => { + let mut should_pause = false; + 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), + _ => { + should_pause = true + } + } + } + _ => () + } + } + + if should_pause { + return Action::Pause + } + + } + /* errors */ "/t/trap" => panic!("custom trap"), "/t/error/get_response_body" => {