diff --git a/lib/resty/wasmx.lua b/lib/resty/wasmx.lua index e61da77c0..3fddad748 100644 --- a/lib/resty/wasmx.lua +++ b/lib/resty/wasmx.lua @@ -15,13 +15,13 @@ ffi.cdef [[ ngx_str_t *config; } ngx_wasm_filter_t; - typedef intptr_t ngx_int_t; typedef unsigned char u_char; + typedef intptr_t ngx_int_t; typedef struct ngx_wavm_t ngx_wasm_vm_t; typedef struct ngx_wasm_ops_plan_t ngx_wasm_plan_t; - typedef ngx_int_t (*ngx_wasm_host_prop_fn_t)(void *data, ngx_str_t *key, - ngx_str_t *value); + typedef ngx_int_t (*ngx_proxy_wasm_properties_ffi_handler_pt)(void *data, + ngx_str_t *key, ngx_str_t *value, ngx_str_t *err); ngx_wasm_vm_t *ngx_wasm_ffi_main_vm(); ]] @@ -52,7 +52,7 @@ function _M.get_err_ptr() local errbuf = get_string_buf(ERR_BUF_SIZE) local errlen = get_size_ptr() - errlen[0] = ERR_BUF_SIZE + errlen[0] = 0 return errbuf, errlen end diff --git a/lib/resty/wasmx/proxy_wasm.lua b/lib/resty/wasmx/proxy_wasm.lua index 7934c4fd6..d7003e7b2 100644 --- a/lib/resty/wasmx/proxy_wasm.lua +++ b/lib/resty/wasmx/proxy_wasm.lua @@ -12,7 +12,10 @@ local pcall = pcall local ffi_gc = ffi.gc local ffi_new = ffi.new local ffi_str = ffi.string +local tonumber = tonumber local tostring = tostring +local getfenv = getfenv +local require = require local subsystem = ngx.config.subsystem local get_request = wasmx.get_request local get_err_ptr = wasmx.get_err_ptr @@ -40,32 +43,34 @@ local _M = { if subsystem == "http" then ffi.cdef [[ - ngx_int_t ngx_http_wasm_ffi_plan_new(ngx_wasm_vm_t *vm, - ngx_wasm_filter_t *filters, - size_t n_filters, - ngx_wasm_plan_t **out, - u_char *err, size_t *errlen); - ngx_int_t ngx_http_wasm_ffi_plan_free(ngx_wasm_plan_t *plan); - ngx_int_t ngx_http_wasm_ffi_plan_load(ngx_wasm_plan_t *plan); - ngx_int_t ngx_http_wasm_ffi_plan_attach(ngx_http_request_t *r, - ngx_wasm_plan_t *plan, - unsigned int isolation); - ngx_int_t ngx_http_wasm_ffi_start(ngx_http_request_t *r); - ngx_int_t ngx_http_wasm_ffi_set_property(ngx_http_request_t *r, - ngx_str_t *key, - ngx_str_t *value); - ngx_int_t ngx_http_wasm_ffi_get_property(ngx_http_request_t *r, - ngx_str_t *key, - ngx_str_t *out); - ngx_int_t ngx_http_wasm_ffi_set_host_property(ngx_http_request_t *r, - ngx_str_t *key, - ngx_str_t *value, - unsigned is_const, - unsigned retrieve); - ngx_int_t ngx_http_wasm_ffi_set_property_setter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn); - ngx_int_t ngx_http_wasm_ffi_set_property_getter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn); + int ngx_http_wasm_ffi_plan_new(ngx_wasm_vm_t *vm, + ngx_wasm_filter_t *filters, + size_t n_filters, + ngx_wasm_plan_t **out, + u_char *err, size_t *errlen); + int ngx_http_wasm_ffi_plan_free(ngx_wasm_plan_t *plan); + int ngx_http_wasm_ffi_plan_load(ngx_wasm_plan_t *plan); + int ngx_http_wasm_ffi_plan_attach(ngx_http_request_t *r, + ngx_wasm_plan_t *plan, + unsigned int isolation); + int ngx_http_wasm_ffi_start(ngx_http_request_t *r); + int ngx_http_wasm_ffi_set_property(ngx_http_request_t *r, + ngx_str_t *key, + ngx_str_t *value, + u_char *err, size_t *errlen); + int ngx_http_wasm_ffi_get_property(ngx_http_request_t *r, + ngx_str_t *key, + ngx_str_t *out, + u_char *err, size_t *errlen); + int ngx_http_wasm_ffi_set_host_property(ngx_http_request_t *r, + ngx_str_t *key, + ngx_str_t *value, + unsigned is_const, + unsigned retrieve); + int ngx_http_wasm_ffi_set_host_properties_setter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn); + int ngx_http_wasm_ffi_set_host_properties_getter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn); ]] else @@ -78,86 +83,6 @@ local ffi_ops_type = ffi.typeof("ngx_wasm_plan_t *") local ffi_pops_type = ffi.typeof("ngx_wasm_plan_t *[1]") -local lua_prop_setter -local lua_prop_getter -local c_prop_setter -local c_prop_getter - - -do - -- LuaJIT C callbacks are a limited resource: - -- We can't create one of these on each request, - -- so we prepare these wrappers, which in turn call a Lua function. - - local function store_c_value(r, ckey, cvalue, lvalue, is_const) - if lvalue ~= nil then - local value = tostring(lvalue) - cvalue.data = value - cvalue.len = #value - - else - cvalue.data = nil - cvalue.len = 0 - end - - return C.ngx_http_wasm_ffi_set_host_property(r, ckey, cvalue, - is_const and 1 or 0, - 1) - end - - c_prop_setter = ffi.cast("ngx_wasm_host_prop_fn_t", function(r, ckey, - cvalue) - local lkey = ffi_str(ckey.data, ckey.len) - local lval - if cvalue.data ~= nil then - lval = ffi_str(cvalue.data, cvalue.len) - end - - local pok, ok, lvalue, is_const = pcall(lua_prop_setter, lkey, lval) - if not pok then - ngx.log(ngx.ERR, "error setting property from Lua: ", ok) - return FFI_ERROR - end - - if not ok then - local err = lvalue or "unknown error" - ngx.log(ngx.ERR, "error setting property: ", err) - return FFI_ERROR - end - - return store_c_value(r, ckey, cvalue, lvalue, is_const) - end) - - - c_prop_getter = ffi.cast("ngx_wasm_host_prop_fn_t", function(r, ckey, - cvalue) - local lkey = ffi_str(ckey.data, ckey.len) - - local pok, ok, lvalue, is_const = pcall(lua_prop_getter, lkey) - if not pok then - ngx.log(ngx.ERR, "error getting property from Lua: ", ok) - return FFI_ERROR - end - - if not ok then - if lvalue then - ngx.log(ngx.ERR, "error setting property: ", lvalue) - return FFI_ERROR - end - - return FFI_DECLINED - end - - local rc = store_c_value(r, ckey, cvalue, lvalue, is_const) - if rc == FFI_OK and lvalue == nil then - return FFI_DECLINED - end - - return rc - end) -end - - function _M.new(filters) if type(filters) ~= "table" then error("filters must be a table", 2) @@ -329,12 +254,19 @@ function _M.set_property(key, value) error("no request found", 2) end + local errbuf, errlen = get_err_ptr() local ckey = ffi_new("ngx_str_t", { data = key, len = #key }) - local cvalue = ffi_new("ngx_str_t", { data = value, len = value and #value or 0 }) + local cvalue = ffi_new("ngx_str_t", { + data = value, + len = value and #value or 0 + }) - local rc = C.ngx_http_wasm_ffi_set_property(r, ckey, cvalue) + local rc = C.ngx_http_wasm_ffi_set_property(r, ckey, cvalue, errbuf, errlen) if rc == FFI_ERROR then - return nil, "unknown error" + local err = errlen and tonumber(errlen[0]) > 0 + and ffi_str(errbuf, errlen[0]) + or "unknown error" + return nil, err end if rc == FFI_DECLINED then @@ -355,12 +287,16 @@ function _M.get_property(key) error("no request found", 2) end + local errbuf, errlen = get_err_ptr() local ckey = ffi_new("ngx_str_t", { data = key, len = #key }) local cvalue = ffi_new("ngx_str_t", { data = nil, len = 0 }) - local rc = C.ngx_http_wasm_ffi_get_property(r, ckey, cvalue) + local rc = C.ngx_http_wasm_ffi_get_property(r, ckey, cvalue, errbuf, errlen) if rc == FFI_ERROR then - return nil, "unknown error", ERROR + local err = errlen and tonumber(errlen[0]) > 0 + and ffi_str(errbuf, errlen[0]) + or "unknown error" + return nil, err end if rc == FFI_DECLINED then @@ -371,72 +307,179 @@ function _M.get_property(key) end ---- --- Define a setter function for host-managed properties. --- --- @param setter The setter function is the handler for updating properties. --- Its type signature is `function(string, string): boolean, string`, --- where the inputs are the property key and value and the results are: --- * `truthy` - success setting the property --- * `truthy, value` - success, cache property value (useful if hosts --- sets only a setter but not a getter) --- * `truthy, value, truthy` - success, constant property value: further --- requests to the same property during a request are cached by --- ngx_wasm_module and do not invoke the Lua getter. --- * `falsy, err` - failure, and an optional error message --- @return true on success setting the getter, or nil and an error message. -function _M.set_property_setter(setter) - if type(setter) ~= "function" then - error("setter must be a function", 2) +do + --- + -- Custom host properties setters/getters + -- + local lua_props_setter + local lua_props_getter + local c_props_setter + local c_props_getter + + + local function set_r(r) + local pok, exdata = pcall(require, "thread.exdata") + if pok and exdata then + exdata(r) + + else + getfenv(0).__ngx_req = r + end end - local r = get_request() - if not r then - error("no request found", 2) + + -- LuaJIT C callbacks are a limited resource; we can't create one of these + -- on each request so we prepare these wrappers, which in turn call a Lua + -- function. + + + local function store_c_value(r, ckey, cvalue, lvalue, is_const, retrieve) + if lvalue ~= nil then + local value = tostring(lvalue) + cvalue.data = value + cvalue.len = #value + + else + cvalue.data = nil + cvalue.len = 0 + end + + return C.ngx_http_wasm_ffi_set_host_property(r, ckey, cvalue, + is_const and 1 or 0, + retrieve and 1 or 0) end - lua_prop_setter = setter - local rc = C.ngx_http_wasm_ffi_set_property_setter(r, c_prop_setter) - if rc == FFI_OK then + c_props_setter = ffi.cast("ngx_proxy_wasm_properties_ffi_handler_pt", + function(r, ckey, cvalue, cerr) + set_r(r) + + local lkey = ffi_str(ckey.data, ckey.len) + local lval + if cvalue.data ~= nil then + lval = ffi_str(cvalue.data, cvalue.len) + end + + local pok, ok, lvalue, is_const = pcall(lua_props_setter, lkey, lval) + if not pok then + local err = "error in property setter: " .. ok + cerr.data = err + cerr.len = #err + return FFI_ERROR + end + + if not ok then + local err = lvalue and lvalue or "unknown error" + cerr.data = err + cerr.len = #err + return FFI_ERROR + end + + return store_c_value(r, ckey, cvalue, lvalue, is_const, false) + end) + + + c_props_getter = ffi.cast("ngx_proxy_wasm_properties_ffi_handler_pt", + function(r, ckey, cvalue, cerr) + set_r(r) + + local lkey = ffi_str(ckey.data, ckey.len) + + local pok, ok, lvalue, is_const = pcall(lua_props_getter, lkey) + if not pok then + local err = "error in property getter: " .. ok + cerr.data = err + cerr.len = #err + return FFI_ERROR + end + + if not ok then + if lvalue then + cerr.data = lvalue + cerr.len = #lvalue + return FFI_ERROR + end + + return FFI_DECLINED + end + + local rc = store_c_value(r, ckey, cvalue, lvalue, is_const, true) + if rc == FFI_OK and lvalue == nil then + return FFI_DECLINED + end + + return rc + end) + + + --- + -- Define a setter function for host-managed properties. + -- + -- @param setter The setter function is the handler for updating properties. + -- Its type signature is `function(string, string): boolean, string`, + -- where the inputs are the property key and value and the results may be: + -- * `truthy` - success setting the property. + -- * `truthy, value` - success, cache property value (useful if hosts + -- sets only a setter but not a getter). + -- * `truthy, value, truthy` - success, constant property value: further + -- requests to the same property during a request are cached by + -- ngx_wasm_module and do not invoke the Lua getter. + -- * `falsy, err` - failure, and an optional error message. + -- @return true on success setting the setter, or nil and an error message. + function _M.set_host_properties_setter(setter) + if type(setter) ~= "function" then + error("setter must be a function", 2) + end + + local r = get_request() + if not r then + error("no request found", 2) + end + + lua_props_setter = setter + + local rc = C.ngx_http_wasm_ffi_set_host_properties_setter(r, c_props_setter) + if rc ~= FFI_OK then + return nil, "could not set host properties setter" + end + return true end - return nil, "could not set property setter" -end + --- + -- Define a getter function for host-managed properties. + -- + -- @param getter The getter function is the handler for resolving + -- properties. + -- Its type signature is `function(string): boolean, string, boolean`, + -- where the input is the property key and the results may be: + -- * `truthy, value` - success, property value. + -- * `truthy, value, truthy` - success, constant property value: further + -- requests to the same property during a request are cached by + -- ngx_wasm_module and do not invoke the Lua getter. + -- * `falsy` - property not found. + -- * `falsy, err` - failure, error message. + -- @return true on success setting the getter, or nil and an error message. + function _M.set_host_properties_getter(getter) + if type(getter) ~= "function" then + error("getter must be a function", 2) + end ---- --- Define a getter function for host-managed properties. --- --- @param getter The getter function is the handler for resolving properties. --- Its type signature is `function(string): boolean, string, boolean`, --- where the input is the property key and the results are: --- * `truthy, value` - success, property value --- * `truthy, value, truthy` - success, constant property value: further --- requests to the same property during a request are cached by --- ngx_wasm_module and do not invoke the Lua getter. --- * `falsy` - property not found --- * `falsy, err` - failure, error message --- @return true on success setting the getter, or nil and an error message. -function _M.set_property_getter(getter) - if type(getter) ~= "function" then - error("getter must be a function", 2) - end + local r = get_request() + if not r then + error("no request found", 2) + end - local r = get_request() - if not r then - error("no request found", 2) - end + lua_props_getter = getter - lua_prop_getter = getter + local rc = C.ngx_http_wasm_ffi_set_host_properties_getter(r, c_props_getter) + if rc ~= FFI_OK then + return nil, "could not set host properties getter" + end - local rc = C.ngx_http_wasm_ffi_set_property_getter(r, c_prop_getter) - if rc == FFI_OK then return true end - - return nil, "could not set property getter" end diff --git a/src/common/lua/ngx_wasm_lua_ffi.c b/src/common/lua/ngx_wasm_lua_ffi.c index aaa5b54af..7ca26d6cb 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.c +++ b/src/common/lua/ngx_wasm_lua_ffi.c @@ -34,7 +34,8 @@ ngx_http_wasm_ffi_plan_new(ngx_wavm_t *vm, mcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_wasm_module); if (mcf == NULL) { /* no http{} block */ - *errlen = ngx_snprintf(err, *errlen, "%V", &NGX_WASM_STR_NO_HTTP) + *errlen = ngx_snprintf(err, NGX_WASM_LUA_FFI_MAX_ERRLEN, + "%V", &NGX_WASM_STR_NO_HTTP) - err; return NGX_ERROR; } @@ -57,9 +58,10 @@ ngx_http_wasm_ffi_plan_new(ngx_wavm_t *vm, &mcf->store, vm); if (rc != NGX_OK) { if (rc == NGX_ABORT) { - *errlen = ngx_snprintf(err, *errlen, + *errlen = ngx_snprintf(err, NGX_WASM_LUA_FFI_MAX_ERRLEN, "no \"%V\" module defined", - ffi_filter->name) - err; + ffi_filter->name) + - err; } return NGX_ERROR; @@ -197,8 +199,10 @@ get_pwctx(ngx_http_request_t *r) ngx_int_t ngx_http_wasm_ffi_set_property(ngx_http_request_t *r, - ngx_str_t *key, ngx_str_t *value) + ngx_str_t *key, ngx_str_t *value, u_char *err, size_t *errlen) { + ngx_int_t rc; + ngx_str_t e = { 0, NULL }; ngx_http_wasm_req_ctx_t *rctx; ngx_proxy_wasm_ctx_t *pwctx; @@ -213,14 +217,22 @@ ngx_http_wasm_ffi_set_property(ngx_http_request_t *r, return NGX_ERROR; } - return ngx_proxy_wasm_properties_set(pwctx, key, value); + rc = ngx_proxy_wasm_properties_set(pwctx, key, value, &e); + if (rc == NGX_ERROR && e.len) { + *errlen = ngx_snprintf(err, NGX_WASM_LUA_FFI_MAX_ERRLEN, "%V", &e) + - err; + } + + return rc; } ngx_int_t ngx_http_wasm_ffi_get_property(ngx_http_request_t *r, - ngx_str_t *key, ngx_str_t *value) + ngx_str_t *key, ngx_str_t *value, u_char *err, size_t *errlen) { + ngx_int_t rc; + ngx_str_t e = { 0, NULL }; ngx_http_wasm_req_ctx_t *rctx; ngx_proxy_wasm_ctx_t *pwctx; @@ -239,7 +251,13 @@ ngx_http_wasm_ffi_get_property(ngx_http_request_t *r, return NGX_ERROR; } - return ngx_proxy_wasm_properties_get(pwctx, key, value); + rc = ngx_proxy_wasm_properties_get(pwctx, key, value, &e); + if (rc == NGX_ERROR && e.len) { + *errlen = ngx_snprintf(err, NGX_WASM_LUA_FFI_MAX_ERRLEN, "%V", &e) + - err; + } + + return rc; } @@ -247,41 +265,44 @@ ngx_int_t ngx_http_wasm_ffi_set_host_property(ngx_http_request_t *r, ngx_str_t *key, ngx_str_t *value, unsigned is_const, unsigned retrieve) { - ngx_proxy_wasm_ctx_t *pwctx = get_pwctx(r); + ngx_proxy_wasm_ctx_t *pwctx; + pwctx = get_pwctx(r); if (pwctx == NULL) { return NGX_ERROR; } - return ngx_proxy_wasm_properties_set_host(pwctx, key, value, is_const, - retrieve); + return ngx_proxy_wasm_properties_set_host(pwctx, key, value, + is_const, retrieve); } ngx_int_t -ngx_http_wasm_ffi_set_property_setter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn) +ngx_http_wasm_ffi_set_host_properties_setter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn) { - ngx_proxy_wasm_ctx_t *pwctx = get_pwctx(r); + ngx_proxy_wasm_ctx_t *pwctx; + pwctx = get_pwctx(r); if (pwctx == NULL) { return NGX_ERROR; } - return ngx_proxy_wasm_properties_set_host_prop_setter(pwctx, fn, r); + return ngx_proxy_wasm_properties_set_ffi_setter(pwctx, fn, r); } ngx_int_t -ngx_http_wasm_ffi_set_property_getter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn) +ngx_http_wasm_ffi_set_host_properties_getter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn) { - ngx_proxy_wasm_ctx_t *pwctx = get_pwctx(r); + ngx_proxy_wasm_ctx_t *pwctx; + pwctx = get_pwctx(r); if (pwctx == NULL) { return NGX_ERROR; } - return ngx_proxy_wasm_properties_set_host_prop_getter(pwctx, fn, r); + return ngx_proxy_wasm_properties_set_ffi_getter(pwctx, fn, r); } #endif diff --git a/src/common/lua/ngx_wasm_lua_ffi.h b/src/common/lua/ngx_wasm_lua_ffi.h index 1d87e22d1..7722150d9 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.h +++ b/src/common/lua/ngx_wasm_lua_ffi.h @@ -11,6 +11,9 @@ #endif +#define NGX_WASM_LUA_FFI_MAX_ERRLEN 256 + + ngx_wavm_t *ngx_wasm_ffi_main_vm(); @@ -30,15 +33,15 @@ ngx_int_t ngx_http_wasm_ffi_plan_attach(ngx_http_request_t *r, ngx_wasm_ops_plan_t *plan, ngx_uint_t isolation); ngx_int_t ngx_http_wasm_ffi_start(ngx_http_request_t *r); ngx_int_t ngx_http_wasm_ffi_set_property(ngx_http_request_t *r, - ngx_str_t *key, ngx_str_t *value); + ngx_str_t *key, ngx_str_t *value, u_char *err, size_t *errlen); ngx_int_t ngx_http_wasm_ffi_get_property(ngx_http_request_t *r, - ngx_str_t *key, ngx_str_t *value); + ngx_str_t *key, ngx_str_t *value, u_char *err, size_t *errlen); ngx_int_t ngx_http_wasm_ffi_set_host_property(ngx_http_request_t *r, ngx_str_t *key, ngx_str_t *value, unsigned is_const, unsigned retrieve); -ngx_int_t ngx_http_wasm_ffi_set_property_setter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn); -ngx_int_t ngx_http_wasm_ffi_set_property_getter(ngx_http_request_t *r, - ngx_wasm_host_prop_fn_t fn); +ngx_int_t ngx_http_wasm_ffi_set_host_properties_setter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn); +ngx_int_t ngx_http_wasm_ffi_set_host_properties_getter(ngx_http_request_t *r, + ngx_proxy_wasm_properties_ffi_handler_pt fn); #endif diff --git a/src/common/proxy_wasm/ngx_proxy_wasm.h b/src/common/proxy_wasm/ngx_proxy_wasm.h index 943e26f9c..0e7398b06 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm.h +++ b/src/common/proxy_wasm/ngx_proxy_wasm.h @@ -157,12 +157,8 @@ 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 ngx_str_t ngx_proxy_wasm_marshalled_map_t; -typedef ngx_int_t (*ngx_wasm_host_prop_fn_t)(void *data, ngx_str_t *key, - ngx_str_t *value); - typedef struct { ngx_queue_t busy; @@ -206,57 +202,64 @@ struct ngx_proxy_wasm_exec_s { }; +#if (NGX_WASM_LUA) +typedef ngx_int_t (*ngx_proxy_wasm_properties_ffi_handler_pt)(void *data, + ngx_str_t *key, ngx_str_t *value, ngx_str_t *err); +#endif + + struct ngx_proxy_wasm_ctx_s { - ngx_uint_t id; /* r->connection->number */ - ngx_uint_t nfilters; - ngx_array_t pwexecs; - ngx_uint_t isolation; - ngx_proxy_wasm_store_t store; - ngx_proxy_wasm_context_type_e type; - ngx_log_t *log; - ngx_pool_t *pool; - ngx_pool_t *parent_pool; - void *data; + ngx_uint_t id; /* r->connection->number */ + ngx_uint_t nfilters; + ngx_array_t pwexecs; + ngx_uint_t isolation; + ngx_proxy_wasm_store_t store; + ngx_proxy_wasm_context_type_e type; + ngx_log_t *log; + ngx_pool_t *pool; + ngx_pool_t *parent_pool; + void *data; /* control */ - ngx_wasm_phase_t *phase; - ngx_proxy_wasm_action_e action; - ngx_proxy_wasm_step_e step; - ngx_proxy_wasm_step_e last_step; - ngx_uint_t exec_index; + ngx_wasm_phase_t *phase; + ngx_proxy_wasm_action_e action; + ngx_proxy_wasm_step_e step; + ngx_proxy_wasm_step_e last_step; + ngx_uint_t exec_index; /* cache */ - size_t req_body_len; - ngx_str_t authority; - ngx_str_t scheme; - ngx_str_t path; /* r->uri + r->args */ - ngx_str_t start_time; /* r->start_sec + r->start_msec */ - ngx_str_t upstream_address; /* 1st part of ngx.upstream_addr */ - ngx_str_t upstream_port; /* 2nd part of ngx.upstsream_addr */ - ngx_str_t connection_id; /* r->connection->number */ - ngx_str_t mtls; /* ngx.https && ngx.ssl_client_verify */ - ngx_str_t root_id; /* pwexec->root_id */ - ngx_str_t call_status; /* dispatch response status */ - ngx_str_t response_status; /* response status */ - ngx_uint_t call_code; - ngx_uint_t response_code; + size_t req_body_len; + ngx_str_t authority; + ngx_str_t scheme; + ngx_str_t path; /* r->uri + r->args */ + ngx_str_t start_time; /* r->start_sec + r->start_msec */ + ngx_str_t upstream_address; /* 1st part of ngx.upstream_addr */ + ngx_str_t upstream_port; /* 2nd part of ngx.upstsream_addr */ + ngx_str_t connection_id; /* r->connection->number */ + ngx_str_t mtls; /* ngx.https && ngx.ssl_client_verify */ + ngx_str_t root_id; /* pwexec->root_id */ + ngx_str_t call_status; /* dispatch response status */ + ngx_str_t response_status; /* response status */ + ngx_uint_t call_code; + ngx_uint_t response_code; /* host properties */ - ngx_rbtree_t host_props_tree; - ngx_rbtree_node_t host_props_sentinel; - ngx_wasm_host_prop_fn_t host_prop_getter; - ngx_wasm_host_prop_fn_t host_prop_setter; - void *host_prop_getter_data; - void *host_prop_setter_data; + ngx_rbtree_t host_props_tree; + ngx_rbtree_node_t host_props_sentinel; +#if (NGX_WASM_LUA) + ngx_proxy_wasm_properties_ffi_handler_pt host_props_ffi_getter; + ngx_proxy_wasm_properties_ffi_handler_pt host_props_ffi_setter; + void *host_props_ffi_handler_data; /* r */ +#endif /* flags */ - unsigned main:1; /* r->main */ - unsigned ready:1; - unsigned req_headers_in_access:1; + unsigned main:1; /* r->main */ + unsigned ready:1; + unsigned req_headers_in_access:1; }; diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index 3fb19deed..610b87f73 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -847,9 +847,12 @@ static ngx_int_t ngx_proxy_wasm_hfuncs_get_property(ngx_wavm_instance_t *instance, wasm_val_t args[], wasm_val_t rets[]) { + char trapmsg[NGX_MAX_ERROR_STR]; ngx_int_t rc; ngx_str_t path, value; + ngx_str_t err = { 0, NULL }; int32_t *ret_size; + u_char *dotted_path, *last; ngx_proxy_wasm_exec_t *pwexec; ngx_wavm_ptr_t *ret_data, p = 0; @@ -861,12 +864,26 @@ ngx_proxy_wasm_hfuncs_get_property(ngx_wavm_instance_t *instance, pwexec = ngx_proxy_wasm_instance2pwexec(instance); - rc = ngx_proxy_wasm_properties_get(pwexec->parent, &path, &value); + rc = ngx_proxy_wasm_properties_get(pwexec->parent, &path, &value, &err); switch (rc) { case NGX_DECLINED: return ngx_proxy_wasm_result_notfound(rets); case NGX_ERROR: + if (err.len) { + ngx_proxy_wasm_properties_unmarsh_path(&path, &dotted_path); + + last = ngx_slprintf((u_char *) &trapmsg, + (u_char *) trapmsg + NGX_MAX_ERROR_STR - 1, + "could not get \"%s\": %V", + dotted_path, + &err); + *last++ = '\0'; + + return ngx_proxy_wasm_result_trap(pwexec, trapmsg, rets, + NGX_WAVM_ERROR); + } + return ngx_proxy_wasm_result_err(rets); default: ngx_wasm_assert(rc == NGX_OK); @@ -894,9 +911,12 @@ static ngx_int_t ngx_proxy_wasm_hfuncs_set_property(ngx_wavm_instance_t *instance, wasm_val_t args[], wasm_val_t rets[]) { - ngx_proxy_wasm_exec_t *pwexec; - ngx_str_t path, value; + char trapmsg[NGX_MAX_ERROR_STR]; ngx_int_t rc; + ngx_str_t path, value; + ngx_str_t err = { 0, NULL }; + u_char *dotted_path, *last; + ngx_proxy_wasm_exec_t *pwexec; path.len = args[1].of.i32; path.data = NGX_WAVM_HOST_LIFT_SLICE(instance, args[0].of.i32, path.len); @@ -905,17 +925,32 @@ ngx_proxy_wasm_hfuncs_set_property(ngx_wavm_instance_t *instance, pwexec = ngx_proxy_wasm_instance2pwexec(instance); - rc = ngx_proxy_wasm_properties_set(pwexec->parent, &path, &value); + rc = ngx_proxy_wasm_properties_set(pwexec->parent, &path, &value, &err); switch (rc) { case NGX_DECLINED: return ngx_proxy_wasm_result_notfound(rets); case NGX_ERROR: + if (err.len) { + ngx_proxy_wasm_properties_unmarsh_path(&path, &dotted_path); + + last = ngx_slprintf((u_char *) &trapmsg, + (u_char *) trapmsg + NGX_MAX_ERROR_STR - 1, + "could not set \"%s\": %V", + dotted_path, + &err); + *last++ = '\0'; + + return ngx_proxy_wasm_result_trap(pwexec, trapmsg, rets, + NGX_WAVM_ERROR); + } + return ngx_proxy_wasm_result_err(rets); default: ngx_wasm_assert(rc == NGX_OK); - return ngx_proxy_wasm_result_ok(rets); } + + return ngx_proxy_wasm_result_ok(rets); } diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_properties.c b/src/common/proxy_wasm/ngx_proxy_wasm_properties.c index bc26b43fa..f692443ee 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_properties.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_properties.c @@ -51,10 +51,11 @@ static size_t host_prefix_len; typedef struct { - ngx_str_node_t sn; - ngx_str_t value; - unsigned is_const:1; - unsigned negative_cache:1; + ngx_str_node_t sn; + ngx_str_t value; + ngx_pool_t *pool; + unsigned is_const:1; + unsigned is_const_null:1; } host_props_node_t; @@ -78,6 +79,11 @@ not_supported(ngx_proxy_wasm_ctx_t *pwctx, ngx_str_t *path, ngx_str_t *value) } +#ifdef NGX_WASM_HTTP +static const size_t request_headers_prefix_len = 16; /* request.headers. */ +static const size_t response_headers_prefix_len = 17; /* response.headers. */ + + static ngx_uint_t hash_str(u_char *src, size_t n) { @@ -94,11 +100,6 @@ hash_str(u_char *src, size_t n) } -#ifdef NGX_WASM_HTTP -static const size_t request_headers_prefix_len = 16; /* request.headers. */ -static const size_t response_headers_prefix_len = 17; /* response.headers. */ - - static ngx_int_t get_map_value(ngx_proxy_wasm_ctx_t *pwctx, ngx_str_t *name, ngx_str_t *value, ngx_proxy_wasm_map_type_e map_type) @@ -707,6 +708,28 @@ ngx_proxy_wasm_properties_init(ngx_conf_t *cf) } +static u_char * +replace_nulls_by_dots(ngx_str_t *from, u_char *to) +{ + size_t i; + + for (i = 0; i < from->len; i++) { + to[i] = from->data[i] == '\0' ? '.' : from->data[i]; + } + + return to; +} + + +void +ngx_proxy_wasm_properties_unmarsh_path(ngx_str_t *from, u_char **to) +{ + *to = from->data; + + (void) replace_nulls_by_dots(from, *to); +} + + static ngx_int_t ngx_proxy_wasm_properties_get_ngx(ngx_proxy_wasm_ctx_t *pwctx, ngx_str_t *path, ngx_str_t *value) @@ -874,42 +897,44 @@ ngx_proxy_wasm_properties_get_host(ngx_proxy_wasm_ctx_t *pwctx, } #endif - hash = hash_str(path->data, path->len); + hash = ngx_crc32_long(path->data, path->len); hpn = (host_props_node_t *) ngx_str_rbtree_lookup(&pwctx->host_props_tree, path, hash); - if (!hpn) { + if (hpn == NULL) { return NGX_DECLINED; } - /* stored value is not const: need to call getter again */ - if (!hpn->is_const && pwctx->host_prop_getter) { +#if (NGX_WASM_LUA) + if (pwctx->host_props_ffi_getter && !hpn->is_const) { + /* stored value is not const: need to call getter again */ return NGX_DECLINED; } +#endif - value->data = hpn->value.data; - value->len = hpn->value.len; - - if (hpn->negative_cache) { + if (hpn->is_const_null) { return NGX_BUSY; } + value->data = hpn->value.data; + value->len = hpn->value.len; + return NGX_OK; } static ngx_inline ngx_int_t -set_hpn_value(host_props_node_t *hpn, ngx_str_t *value, unsigned is_const, - ngx_pool_t *pool) +set_hpn_value(host_props_node_t *hpn, ngx_str_t *value, unsigned is_const) { - unsigned char *new_data; + u_char *new_data; if (value->data == NULL) { + ngx_wasm_assert(is_const); new_data = NULL; - hpn->negative_cache = 1; + hpn->is_const_null = 1; } else { - new_data = ngx_pstrdup(pool, value); + new_data = ngx_pstrdup(hpn->pool, value); if (new_data == NULL) { return NGX_ERROR; } @@ -928,7 +953,6 @@ ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, ngx_str_t *path, ngx_str_t *value, unsigned is_const, unsigned retrieve) { uint32_t hash; - ngx_int_t rc; host_props_node_t *hpn; #ifdef NGX_WASM_HTTP ngx_http_wasm_req_ctx_t *rctx = pwctx->data; @@ -940,13 +964,13 @@ ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, } #endif - hash = hash_str(path->data, path->len); + hash = ngx_crc32_long(path->data, path->len); hpn = (host_props_node_t *) ngx_str_rbtree_lookup(&pwctx->host_props_tree, path, hash); - if (hpn) { if (hpn->is_const) { + /* cannot overwrite a const */ return NGX_DECLINED; } @@ -954,13 +978,11 @@ ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, if (value->data == NULL && !is_const) { ngx_rbtree_delete(&pwctx->host_props_tree, &hpn->sn.node); - return NGX_OK; } - rc = set_hpn_value(hpn, value, is_const, pwctx->pool); - if (rc != NGX_OK) { - return rc; + if (set_hpn_value(hpn, value, is_const) != NGX_OK) { + return NGX_ERROR; } } else if (value->data == NULL && !is_const) { @@ -973,20 +995,24 @@ ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, return NGX_ERROR; } + hpn->pool = pwctx->pool; hpn->sn.node.key = hash; hpn->sn.str.len = path->len; hpn->sn.str.data = (u_char *) hpn + sizeof(host_props_node_t); ngx_memcpy(hpn->sn.str.data, path->data, path->len); - rc = set_hpn_value(hpn, value, is_const, pwctx->pool); - if (rc != NGX_OK) { - return rc; + if (set_hpn_value(hpn, value, is_const) != NGX_OK) { + return NGX_ERROR; } ngx_rbtree_insert(&pwctx->host_props_tree, &hpn->sn.node); } if (retrieve) { + /** + * Ensure the string lifetime will still be correct once back + * in the Lua FFI. + */ value->data = hpn->value.data; } @@ -994,28 +1020,15 @@ ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, } -static u_char * -replace_nulls_by_dots(ngx_str_t *from, u_char *to) -{ - size_t i; - - for (i = 0; i < from->len; i++) { - to[i] = from->data[i] == '\0' ? '.' : from->data[i]; - } - - return to; -} - - ngx_int_t ngx_proxy_wasm_properties_get(ngx_proxy_wasm_ctx_t *pwctx, - ngx_str_t *path, ngx_str_t *value) + ngx_str_t *path, ngx_str_t *value, ngx_str_t *err) { u_char dotted_path_buf[path->len]; - ngx_str_t p = { path->len, NULL }; + ngx_int_t rc; ngx_uint_t key; + ngx_str_t p = { path->len, NULL }; pwm2ngx_mapping_t *m; - ngx_int_t rc; p.data = replace_nulls_by_dots(path, dotted_path_buf); key = ngx_hash_key(p.data, p.len); @@ -1044,7 +1057,7 @@ ngx_proxy_wasm_properties_get(ngx_proxy_wasm_ctx_t *pwctx, { /* host variable */ - /* even if there is a getter, try reading const value first */ + /* even if there is an FFI getter, try reading const value first */ rc = ngx_proxy_wasm_properties_get_host(pwctx, &p, value); if (rc == NGX_OK) { return NGX_OK; @@ -1053,10 +1066,14 @@ ngx_proxy_wasm_properties_get(ngx_proxy_wasm_ctx_t *pwctx, return NGX_DECLINED; } - if (pwctx->host_prop_getter) { - return pwctx->host_prop_getter(pwctx->host_prop_getter_data, - &p, value); +#if (NGX_WASM_LUA) + if (pwctx->host_props_ffi_getter) { + /* custom FFI getter */ + return pwctx->host_props_ffi_getter( + pwctx->host_props_ffi_handler_data, &p, value, err + ); } +#endif return rc; } @@ -1067,7 +1084,7 @@ ngx_proxy_wasm_properties_get(ngx_proxy_wasm_ctx_t *pwctx, ngx_int_t ngx_proxy_wasm_properties_set(ngx_proxy_wasm_ctx_t *pwctx, - ngx_str_t *path, ngx_str_t *value) + ngx_str_t *path, ngx_str_t *value, ngx_str_t *err) { u_char dotted_path_buf[path->len]; ngx_str_t p = { path->len, NULL }; @@ -1093,12 +1110,16 @@ ngx_proxy_wasm_properties_set(ngx_proxy_wasm_ctx_t *pwctx, if (p.len > host_prefix_len && ngx_memcmp(p.data, host_prefix, host_prefix_len) == 0) { - /* host variable */ - if (pwctx->host_prop_setter) { - return pwctx->host_prop_setter(pwctx->host_prop_setter_data, - &p, value); +#if (NGX_WASM_LUA) + if (pwctx->host_props_ffi_setter) { + /* custom FFI setter */ + return pwctx->host_props_ffi_setter( + pwctx->host_props_ffi_handler_data, &p, value, err + ); } +#endif + /* host variable */ return ngx_proxy_wasm_properties_set_host(pwctx, &p, value, 0, 0); } @@ -1106,9 +1127,10 @@ ngx_proxy_wasm_properties_set(ngx_proxy_wasm_ctx_t *pwctx, } +#if (NGX_WASM_LUA) ngx_int_t -ngx_proxy_wasm_properties_set_host_prop_setter(ngx_proxy_wasm_ctx_t *pwctx, - ngx_wasm_host_prop_fn_t fn, void *data) +ngx_proxy_wasm_properties_set_ffi_setter(ngx_proxy_wasm_ctx_t *pwctx, + ngx_proxy_wasm_properties_ffi_handler_pt fn, void *data) { #ifdef NGX_WASM_HTTP ngx_http_wasm_req_ctx_t *rctx = pwctx->data; @@ -1121,16 +1143,16 @@ ngx_proxy_wasm_properties_set_host_prop_setter(ngx_proxy_wasm_ctx_t *pwctx, } #endif - pwctx->host_prop_setter = fn; - pwctx->host_prop_setter_data = data; + pwctx->host_props_ffi_setter = fn; + pwctx->host_props_ffi_handler_data = data; return NGX_OK; } ngx_int_t -ngx_proxy_wasm_properties_set_host_prop_getter(ngx_proxy_wasm_ctx_t *pwctx, - ngx_wasm_host_prop_fn_t fn, void *data) +ngx_proxy_wasm_properties_set_ffi_getter(ngx_proxy_wasm_ctx_t *pwctx, + ngx_proxy_wasm_properties_ffi_handler_pt fn, void *data) { #ifdef NGX_WASM_HTTP ngx_http_wasm_req_ctx_t *rctx = pwctx->data; @@ -1143,8 +1165,9 @@ ngx_proxy_wasm_properties_set_host_prop_getter(ngx_proxy_wasm_ctx_t *pwctx, } #endif - pwctx->host_prop_getter = fn; - pwctx->host_prop_getter_data = data; + pwctx->host_props_ffi_getter = fn; + pwctx->host_props_ffi_handler_data = data; return NGX_OK; } +#endif diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_properties.h b/src/common/proxy_wasm/ngx_proxy_wasm_properties.h index 9843fd229..4ebe19e22 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_properties.h +++ b/src/common/proxy_wasm/ngx_proxy_wasm_properties.h @@ -5,16 +5,21 @@ void ngx_proxy_wasm_properties_init(ngx_conf_t *cf); +void ngx_proxy_wasm_properties_unmarsh_path(ngx_str_t *from, u_char **to); ngx_int_t ngx_proxy_wasm_properties_get(ngx_proxy_wasm_ctx_t *pwctx, - ngx_str_t *path, ngx_str_t *value); + ngx_str_t *path, ngx_str_t *value, ngx_str_t *err); ngx_int_t ngx_proxy_wasm_properties_set(ngx_proxy_wasm_ctx_t *pwctx, - ngx_str_t *path, ngx_str_t *value); + ngx_str_t *path, ngx_str_t *value, ngx_str_t *err); ngx_int_t ngx_proxy_wasm_properties_set_host(ngx_proxy_wasm_ctx_t *pwctx, ngx_str_t *path, ngx_str_t *value, unsigned is_const, unsigned retrieve); -ngx_int_t ngx_proxy_wasm_properties_set_host_prop_setter( - ngx_proxy_wasm_ctx_t *pwctx, ngx_wasm_host_prop_fn_t fn, void *data); -ngx_int_t ngx_proxy_wasm_properties_set_host_prop_getter( - ngx_proxy_wasm_ctx_t *pwctx, ngx_wasm_host_prop_fn_t fn, void *data); +#if (NGX_WASM_LUA) +ngx_int_t ngx_proxy_wasm_properties_set_ffi_setter( + ngx_proxy_wasm_ctx_t *pwctx, ngx_proxy_wasm_properties_ffi_handler_pt fn, + void *data); +ngx_int_t ngx_proxy_wasm_properties_set_ffi_getter( + ngx_proxy_wasm_ctx_t *pwctx, ngx_proxy_wasm_properties_ffi_handler_pt fn, + void *data); +#endif #endif /* _NGX_PROXY_WASM_PROPERTIES_H_INCLUDED_ */ diff --git a/t/03-proxy_wasm/hfuncs/123-proxy_properties_set_ngx.t b/t/03-proxy_wasm/hfuncs/122-proxy_properties_set_ngx.t similarity index 100% rename from t/03-proxy_wasm/hfuncs/123-proxy_properties_set_ngx.t rename to t/03-proxy_wasm/hfuncs/122-proxy_properties_set_ngx.t diff --git a/t/03-proxy_wasm/hfuncs/122-proxy_properties_set_host.t b/t/03-proxy_wasm/hfuncs/123-proxy_properties_set_host.t similarity index 100% rename from t/03-proxy_wasm/hfuncs/122-proxy_properties_set_host.t rename to t/03-proxy_wasm/hfuncs/123-proxy_properties_set_host.t diff --git a/t/04-openresty/ffi/301-proxy_wasm_get_property_ngx.t b/t/04-openresty/ffi/300-proxy_wasm_properties_get_ngx.t similarity index 100% rename from t/04-openresty/ffi/301-proxy_wasm_get_property_ngx.t rename to t/04-openresty/ffi/300-proxy_wasm_properties_get_ngx.t diff --git a/t/04-openresty/ffi/300-proxy_wasm_get_property_host.t b/t/04-openresty/ffi/301-proxy_wasm_properties_get_host.t similarity index 100% rename from t/04-openresty/ffi/300-proxy_wasm_get_property_host.t rename to t/04-openresty/ffi/301-proxy_wasm_properties_get_host.t diff --git a/t/04-openresty/ffi/303-proxy_wasm_set_property_ngx.t b/t/04-openresty/ffi/302-proxy_wasm_properties_set_ngx.t similarity index 100% rename from t/04-openresty/ffi/303-proxy_wasm_set_property_ngx.t rename to t/04-openresty/ffi/302-proxy_wasm_properties_set_ngx.t diff --git a/t/04-openresty/ffi/302-proxy_wasm_set_property_host.t b/t/04-openresty/ffi/303-proxy_wasm_properties_set_host.t similarity index 100% rename from t/04-openresty/ffi/302-proxy_wasm_set_property_host.t rename to t/04-openresty/ffi/303-proxy_wasm_properties_set_host.t diff --git a/t/04-openresty/ffi/304-proxy_wasm_set_property_getter.t b/t/04-openresty/ffi/304-proxy_wasm_host_properties_getter.t similarity index 50% rename from t/04-openresty/ffi/304-proxy_wasm_set_property_getter.t rename to t/04-openresty/ffi/304-proxy_wasm_host_properties_getter.t index e6c8f764f..e45ae0b4f 100644 --- a/t/04-openresty/ffi/304-proxy_wasm_set_property_getter.t +++ b/t/04-openresty/ffi/304-proxy_wasm_host_properties_getter.t @@ -12,7 +12,7 @@ run_tests(); __DATA__ -=== TEST 1: set_property_getter() - setting host properties getter fails on init_worker_by_lua +=== TEST 1: host properties getter - setting getter fails in init_worker_by_lua --- wasm_modules: on_phases --- http_config init_worker_by_lua_block { @@ -25,13 +25,9 @@ __DATA__ assert(proxy_wasm.load(_G.c_plan)) local function getter(key) - if key == "my_key" then - return true, "my_value" - end - return false end - assert(proxy_wasm.set_property_getter(getter)) + assert(proxy_wasm.set_host_properties_getter(getter)) } --- config location /t { @@ -44,20 +40,22 @@ ok --- error_log eval [ qr/\[error\] .*? cannot set host properties getter outside of a request/, - qr/\[error\] .*? init_worker_by_lua(\(nginx\.conf:\d+\))?:\d+: could not set property getter/, + qr/\[error\] .*? init_worker_by_lua.*?: could not set host properties getter/, ] --- no_error_log [crit] -=== TEST 2: set_property_getter() - setting host properties getter works on rewrite_by_lua +=== TEST 2: host properties getter - sanity in rewrite_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -65,46 +63,40 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: my_value/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: my_value +wasmx.my_property: my_value --- no_error_log [error] +[crit] -=== TEST 3: set_property_getter() - setting host properties getter works on access_by_lua +=== TEST 3: host properties getter - sanity in access_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -112,46 +104,40 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: my_value/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: my_value +wasmx.my_property: my_value --- no_error_log [error] +[crit] -=== TEST 4: set_property_getter() - setting host properties getter works on header_filter_by_lua +=== TEST 4: host properties getter - sanity in header_filter_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -159,14 +145,9 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -175,34 +156,33 @@ ok local proxy_wasm = require "resty.wasmx.proxy_wasm" local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_getter(getter)) + assert(proxy_wasm.set_host_properties_getter(getter)) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: my_value/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: my_value +wasmx.my_property: my_value --- no_error_log [error] +[crit] -=== TEST 5: set_property_getter() - setting host properties getter works on body_filter_by_lua +=== TEST 5: host properties getter - sanity in body_filter_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -210,14 +190,9 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -226,34 +201,33 @@ ok local proxy_wasm = require "resty.wasmx.proxy_wasm" local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_getter(getter)) + assert(proxy_wasm.set_host_properties_getter(getter)) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: my_value/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: my_value +wasmx.my_property: my_value --- no_error_log [error] +[crit] -=== TEST 6: set_property_getter() - setting host properties getter works on log_by_lua +=== TEST 6: host properties getter - sanity in log_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=log " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -263,14 +237,9 @@ ok location /t { proxy_wasm_request_headers_in_access on; - proxy_wasm hostcalls 'on=log \ - test=/t/log/property \ - name=wasmx.my_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -279,28 +248,25 @@ ok local proxy_wasm = require "resty.wasmx.proxy_wasm" local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_getter(getter)) + assert(proxy_wasm.set_host_properties_getter(getter)) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_log/, - qr/\[info\] .*? wasmx.my_property: my_value/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: my_value --- no_error_log [error] +[crit] -=== TEST 7: set_property_getter() - setting property getter at startup doesn't reset filter list +=== TEST 7: host properties getter - setting getter at startup does not reset filter list +SKIP? --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { @@ -312,7 +278,6 @@ ok } _G.c_plan = assert(proxy_wasm.new(filters)) - assert(proxy_wasm.load(_G.c_plan)) } --- config @@ -322,41 +287,75 @@ ok access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" + local function getter(key) + return true, "foo" + end + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- grep_error_log eval: qr/wasmx\.\w+_property: \w+/ +--- grep_error_log_out +wasmx.startup_property: foo +--- no_error_log +[error] +[crit] + + + +=== TEST 8: host properties getter - getter returning false and an error +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.some_property" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + access_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" local function getter(key) - if key == "wasmx.startup_property" then - return true, "foo" - end - return false + return false, "some error" end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) - ngx.say("ok") } } --- response_body ok --- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_log/, - qr/\[info\] .*?hostcalls.*? wasmx.startup_property: foo/, -] +qr/host trap \(internal error\): could not get \"wasmx.some_property\": some error/ --- no_error_log -[error] +[crit] +[emerg]] -=== TEST 8: set_property_getter() - getter returning false produces property not found +=== TEST 9: host properties getter - getter returning false produces "property not found" --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.missing_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -364,46 +363,43 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.missing_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local function getter(key) if key == "wasmx.my_property" then return true, "my_value" end + return false end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? property not found: wasmx.missing_property/, -] +--- grep_error_log eval: qr/.*?wasmx.\w+_property/ +--- grep_error_log_out eval +qr/property not found: wasmx.missing_property/ --- no_error_log [error] +[crit] -=== TEST 9: set_property_getter() - getter returning nil produces property not found +=== TEST 10: host properties getter - getter returning nil produces "property not found" --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.missing_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -411,39 +407,32 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.missing_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local function getter(key) - if key == "wasmx.my_property" then - return true, "my_value" - end + return nil end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? property not found: wasmx.missing_property/, -] +--- grep_error_log eval: qr/.*?wasmx.\w+_property/ +--- grep_error_log_out eval +qr/property not found: wasmx.missing_property/ --- no_error_log [error] +[crit] -=== TEST 10: set_property_getter() - getter returning true on 3rd value caches result +=== TEST 11: host properties getter - getter returning true +With and without 3rd return value (caches result) --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { @@ -459,15 +448,13 @@ ok location /t { access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local counter = 0 local function getter(key) counter = counter + 1 - ngx.log(ngx.INFO, "getting ", key, ", counter: ", - tostring(counter)) + print("getting ", key, " counter: ", counter) if key == "wasmx.dyn_property" then return true, tostring(counter) @@ -489,8 +476,8 @@ ok end end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } @@ -500,7 +487,7 @@ ok local function log_property(k) local v = proxy_wasm.get_property(k) - ngx.log(ngx.INFO, k, ": ", type(v), " \"", v, "\"") + print(k, ": ", type(v), " \"", v, "\"") end -- four get_property operations, @@ -525,42 +512,45 @@ ok } --- response_body ok ---- grep_error_log eval: qr/.*wasmx.*property.*/ ---- grep_error_log_out eval -qr/^[^\n]*?\[info\] [^\n]*? getting wasmx.const_property, counter: 1[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.const_property: string "1"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.dyn_property, counter: 2[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.dyn_property: string "2"[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.const_property: string "1"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.dyn_property, counter: 3[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.dyn_property: string "3"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.nil_const_property, counter: 4[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_const_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.nil_dyn_property, counter: 5[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_dyn_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_const_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.nil_dyn_property, counter: 6[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_dyn_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.empty_const_property, counter: 7[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_const_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.empty_dyn_property, counter: 8[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_dyn_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_const_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.empty_dyn_property, counter: 9[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_dyn_property: string ""[^\n]*?$/ +--- grep_error_log eval: qr/(getting )?wasmx\.\w+_property.*?(?= while)/ +--- grep_error_log_out +getting wasmx.const_property counter: 1 +wasmx.const_property: string "1" +getting wasmx.dyn_property counter: 2 +wasmx.dyn_property: string "2" +wasmx.const_property: string "1" +getting wasmx.dyn_property counter: 3 +wasmx.dyn_property: string "3" +getting wasmx.nil_const_property counter: 4 +wasmx.nil_const_property: nil "nil" +getting wasmx.nil_dyn_property counter: 5 +wasmx.nil_dyn_property: nil "nil" +wasmx.nil_const_property: nil "nil" +getting wasmx.nil_dyn_property counter: 6 +wasmx.nil_dyn_property: nil "nil" +getting wasmx.empty_const_property counter: 7 +wasmx.empty_const_property: string "" +getting wasmx.empty_dyn_property counter: 8 +wasmx.empty_dyn_property: string "" +wasmx.empty_const_property: string "" +getting wasmx.empty_dyn_property counter: 9 +wasmx.empty_dyn_property: string "" --- no_error_log [error] [crit] -=== TEST 11: set_property_getter() - errors in getter are caught by pcall in Lua library and don't crash the worker +=== TEST 12: host properties getter - errors are caught when accessed through filters +Produces a wasm host trap. --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -568,20 +558,139 @@ qr/^[^\n]*?\[info\] [^\n]*? getting wasmx.const_property, counter: 1[^\n]*? } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function getter(key) + error("crash!") + end + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/host trap \(internal error\): could not get \"wasmx\.my_property\": error in property getter: rewrite_by_lua.*?: crash\!/ +--- no_error_log +[crit] +[emerg] + + + +=== TEST 13: host properties getter - errors are caught when accessed through proxy_wasm.get_property() +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function getter(key, value) + error("crash!") + end + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) + + local ok, err = proxy_wasm.get_property("wasmx.my_property") + if not ok then + ngx.log(ngx.ERR, err) + end + + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/\[error\] .*? error in property getter: rewrite_by_lua.*?: crash\!/ +--- no_error_log +[crit] +[emerg] + + + +=== TEST 14: host properties getter - getter can ngx.log() +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" local function getter(key) - error("crash!") + print("in getter") + return true, "foo" end - assert(proxy_wasm.set_property_getter(getter)) + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/rewrite_by_lua.*?: in getter/ +--- no_error_log +[error] +[crit] + + +=== TEST 15: host properties getter - getter cannot yield +Produces a wasm host trap. +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function getter(key) + ngx.sleep(1000) + return true, "foo" + end + + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } @@ -589,9 +698,7 @@ qr/^[^\n]*?\[info\] [^\n]*? getting wasmx.const_property, counter: 1[^\n]*? --- response_body ok --- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[error\] .*? error getting property from Lua: rewrite_by_lua\(nginx.conf:[0-9]+\):[0-9]+: crash\!/, -] +qr/host trap \(internal error\): could not get \"wasmx\.my_property\": error in property getter: attempt to yield across C-call boundary/ --- no_error_log +[crit] [emerg] diff --git a/t/04-openresty/ffi/305-proxy_wasm_set_property_setter.t b/t/04-openresty/ffi/305-proxy_wasm_host_properties_setter.t similarity index 55% rename from t/04-openresty/ffi/305-proxy_wasm_set_property_setter.t rename to t/04-openresty/ffi/305-proxy_wasm_host_properties_setter.t index 471385d3d..367ca5f62 100644 --- a/t/04-openresty/ffi/305-proxy_wasm_set_property_setter.t +++ b/t/04-openresty/ffi/305-proxy_wasm_host_properties_setter.t @@ -12,7 +12,7 @@ run_tests(); __DATA__ -=== TEST 1: set_property_setter() - setting host properties setter fails on init_worker_by_lua +=== TEST 1: host properties setter - setting setter fails in init_worker_by_lua --- wasm_modules: on_phases --- http_config init_worker_by_lua_block { @@ -24,14 +24,10 @@ __DATA__ _G.c_plan = assert(proxy_wasm.new(filters)) assert(proxy_wasm.load(_G.c_plan)) - local tbl = {} - local function setter(key, value) - tbl[key] = value - return true end - assert(proxy_wasm.set_property_setter(setter)) + assert(proxy_wasm.set_host_properties_setter(setter)) } --- config location /t { @@ -44,20 +40,22 @@ ok --- error_log eval [ qr/\[error\] .*? cannot set host properties setter outside of a request/, - qr/\[error\] .*? init_worker_by_lua(\(nginx\.conf:\d+\))?:\d+: could not set property setter/, + qr/\[error\] .*? init_worker_by_lua.*?: could not set host properties setter/, ] --- no_error_log [crit] -=== TEST 2: set_property_setter() - setting host properties setter works on rewrite_by_lua +=== TEST 2: host properties setter - sanity in rewrite_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -65,13 +63,8 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local tbl = {} @@ -81,33 +74,34 @@ ok return true, new_value end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.set_property("wasmx.my_property", "my_value")) - assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: MY_VALUE/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: MY_VALUE +wasmx.my_property: MY_VALUE --- no_error_log [error] +[crit] -=== TEST 3: set_property_setter() - setting host properties setter works on access_by_lua +=== TEST 3: host properties setter - sanity in access_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -115,13 +109,8 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) local tbl = {} @@ -131,33 +120,34 @@ ok return true, new_value end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.set_property("wasmx.my_property", "my_value")) - assert(proxy_wasm.start()) ngx.say("ok") } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: MY_VALUE/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: MY_VALUE +wasmx.my_property: MY_VALUE --- no_error_log [error] +[crit] -=== TEST 4: set_property_setter() - setting host properties setter works on header_filter_by_lua +=== TEST 4: host properties setter - sanity in header_filter_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -165,14 +155,9 @@ ok } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -188,30 +173,31 @@ ok return true, new_value end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.set_property("wasmx.my_property", "my_value")) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: MY_VALUE/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: MY_VALUE +wasmx.my_property: MY_VALUE --- no_error_log [error] +[crit] -=== TEST 5: set_property_setter() - setting host properties setter works on body_filter_by_lua +=== TEST 5: host properties setter - sanity in body_filter_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -226,7 +212,6 @@ ok rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -242,30 +227,31 @@ ok return true, new_value end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.set_property("wasmx.my_property", "my_value")) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[info\] .*? wasmx.my_property: MY_VALUE/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: MY_VALUE +wasmx.my_property: MY_VALUE --- no_error_log [error] +[crit] -=== TEST 6: set_property_setter() - setting host properties setter works on log_by_lua +=== TEST 6: host properties setter - sanity in log_by_lua --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=log " .. + "test=/t/log/property " .. + "name=wasmx.my_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -275,14 +261,9 @@ ok location /t { proxy_wasm_request_headers_in_access on; - proxy_wasm hostcalls 'on=log \ - test=/t/log/property \ - name=wasmx.my_property'; - access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" assert(proxy_wasm.attach(_G.c_plan)) - assert(proxy_wasm.start()) ngx.say("ok") } @@ -298,24 +279,23 @@ ok return true, new_value end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.set_property("wasmx.my_property", "my_value")) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_log/, - qr/\[info\] .*? wasmx.my_property: MY_VALUE/, -] +--- grep_error_log eval: qr/wasmx.\w+_property: \w+/ +--- grep_error_log_out +wasmx.my_property: MY_VALUE --- no_error_log [error] +[crit] -=== TEST 7: set_property_setter() - setting property setter at startup doesn't reset filter list +=== TEST 7: host properties setter - setting setter at startup does not reset filter list +SKIP? --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { @@ -323,11 +303,10 @@ ok local filters = { { name = "hostcalls", config = "on=log " .. "test=/t/log/property " .. - "name=wasmx.my_property" }, + "name=wasmx.startup_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) - assert(proxy_wasm.load(_G.c_plan)) } --- config @@ -337,63 +316,53 @@ ok access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) - local function setter(key, value) - if key == "wasmx.my_property" then - return true, "foo" - end - return false + return true, "my_value" end - assert(proxy_wasm.set_property_setter(setter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) assert(proxy_wasm.start()) - ngx.say("ok") } body_filter_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - - assert(proxy_wasm.set_property("wasmx.my_property", "bar")) + assert(proxy_wasm.set_property("wasmx.startup_property", "bar")) } } --- response_body ok ---- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_log/, - qr/\[info\] .*?hostcalls.*? wasmx.my_property: foo/, -] +--- grep_error_log eval: qr/wasmx\.\w+_property: \w+/ +--- grep_error_log_out +wasmx.startup_property: my_value --- no_error_log [error] +[crit] -=== TEST 8: set_property_setter() - setter returning falsy logs custom message or "unknown error" +=== TEST 8: host properties setter - setter returning falsy and an optional error +With and without 2nd return value (err) --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=log " .. + "test=/t/log/property " .. + "name=wasmx.fail1_property" }, } _G.c_plan = assert(proxy_wasm.new(filters)) assert(proxy_wasm.load(_G.c_plan)) } --- config - proxy_wasm hostcalls 'on=log \ - test=/t/log/property \ - name=wasmx.fail1_property'; - location /t { access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) - local function setter(key) + local function setter(key, value) if key == "wasmx.fail1_property" then return nil, "first wasmx property custom error" @@ -408,41 +377,38 @@ ok end end - assert(proxy_wasm.set_property_setter(setter)) + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) - local msg = "result:" for i = 1, 4 do local key = "wasmx.fail" .. i .. "_property" - local ok, err = proxy_wasm.set_property(key, "wat") + local ok, err = proxy_wasm.set_property(key, "my_value") if not ok then - msg = msg .. "\n" .. err + ngx.log(ngx.ERR, key, ": ", err) end end assert(proxy_wasm.start()) - ngx.say(msg) + ngx.say("ok") } } --- response_body -result: -unknown error -unknown error -unknown error -unknown error ---- grep_error_log eval: qr/.*property.*/ ---- grep_error_log_out eval -qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom error[^\n]*? -[^\n]*?\[error\] [^\n]*? error setting property: second wasmx property custom error[^\n]*? -[^\n]*?\[error\] [^\n]*? error setting property: unknown error[^\n]*? -[^\n]*?\[error\] [^\n]*? error setting property: unknown error[^\n]*? -[^\n]*?\[info\] [^\n]*? property not found: wasmx.fail1_property[^\n]*?$/ +ok +--- grep_error_log eval: qr/(property not found:\s)?wasmx\.\w+_property.*?(?=(,|(\s+while)))/ +--- grep_error_log_out +wasmx.fail1_property: first wasmx property custom error +wasmx.fail2_property: second wasmx property custom error +wasmx.fail3_property: unknown error +wasmx.fail4_property: unknown error +property not found: wasmx.fail1_property --- no_error_log [crit] [alert] -=== TEST 9: set_property_setter() - setter returning true on 3rd value caches result +=== TEST 9: host properties setter - setter returning true +With and without 3rd return value (caches result) --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { @@ -458,11 +424,9 @@ qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom location /t { access_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" - assert(proxy_wasm.attach(_G.c_plan)) - - local counter = 0 local tbl = {} + local counter = 0 local function setter(key, value) counter = counter + 1 @@ -473,20 +437,17 @@ qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom tbl[key] = value - ngx.log(ngx.INFO, "setting ", key, " to ", value, ", counter: " .. counter) + print("setting ", key, " to ", value, ", counter: ", counter) return true, value, (key:match("const") and true or false) end - assert(proxy_wasm.set_property_setter(setter)) - local function getter(key) counter = counter + 1 - ngx.log(ngx.INFO, "getting ", key, " via getter, counter: ", counter) + print("getting ", key, " via getter, counter: ", counter) local value = tbl[key] - if value and #value > 0 then value = value .. " " .. tostring(counter) end @@ -494,8 +455,9 @@ qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom return true, value end - assert(proxy_wasm.set_property_getter(getter)) - + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) + assert(proxy_wasm.set_host_properties_getter(getter)) assert(proxy_wasm.start()) ngx.say("ok") } @@ -505,7 +467,7 @@ qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom local function log_property(k) local v = proxy_wasm.get_property(k) - ngx.log(ngx.INFO, k, ": ", type(v), " \"", v, "\"") + print(k, ": ", type(v), " \"", v, "\"") end -- two set_property operations, each will hit the setter. @@ -536,45 +498,48 @@ qr/^[^\n]*?\[error\] [^\n]*? error setting property: first wasmx property custom } --- response_body ok ---- grep_error_log eval: qr/.*wasmx.*property.*/ ---- grep_error_log_out eval -qr/^[^\n]*?\[info\] [^\n]*? setting wasmx.const_property to HELLO 1, counter: 1[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.const_property: string "HELLO 1"[^\n]*? -[^\n]*?\[info\] [^\n]*? setting wasmx.dyn_property to HI 2, counter: 2[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.dyn_property via getter, counter: 3[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.dyn_property: string "HI 2 3"[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.const_property: string "HELLO 1"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.dyn_property via getter, counter: 4[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.dyn_property: string "HI 2 4"[^\n]*? -[^\n]*?\[info\] [^\n]*? setting wasmx.nil_const_property to nil, counter: 5[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_const_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? setting wasmx.nil_dyn_property to nil, counter: 6[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.nil_dyn_property via getter, counter: 7[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_dyn_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_const_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.nil_dyn_property via getter, counter: 8[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.nil_dyn_property: nil "nil"[^\n]*? -[^\n]*?\[info\] [^\n]*? setting wasmx.empty_const_property to , counter: 9[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_const_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? setting wasmx.empty_dyn_property to , counter: 10[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.empty_dyn_property via getter, counter: 11[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_dyn_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_const_property: string ""[^\n]*? -[^\n]*?\[info\] [^\n]*? getting wasmx.empty_dyn_property via getter, counter: 12[^\n]*? -[^\n]*?\[info\] [^\n]*? wasmx.empty_dyn_property: string ""[^\n]*?$/ +--- grep_error_log eval: qr/((g|s)etting )?wasmx\.\w+_property.*?(?= while)/ +--- grep_error_log_out +setting wasmx.const_property to HELLO 1, counter: 1 +wasmx.const_property: string "HELLO 1" +setting wasmx.dyn_property to HI 2, counter: 2 +getting wasmx.dyn_property via getter, counter: 3 +wasmx.dyn_property: string "HI 2 3" +wasmx.const_property: string "HELLO 1" +getting wasmx.dyn_property via getter, counter: 4 +wasmx.dyn_property: string "HI 2 4" +setting wasmx.nil_const_property to nil, counter: 5 +wasmx.nil_const_property: nil "nil" +setting wasmx.nil_dyn_property to nil, counter: 6 +getting wasmx.nil_dyn_property via getter, counter: 7 +wasmx.nil_dyn_property: nil "nil" +wasmx.nil_const_property: nil "nil" +getting wasmx.nil_dyn_property via getter, counter: 8 +wasmx.nil_dyn_property: nil "nil" +setting wasmx.empty_const_property to , counter: 9 +wasmx.empty_const_property: string "" +setting wasmx.empty_dyn_property to , counter: 10 +getting wasmx.empty_dyn_property via getter, counter: 11 +wasmx.empty_dyn_property: string "" +wasmx.empty_const_property: string "" +getting wasmx.empty_dyn_property via getter, counter: 12 +wasmx.empty_dyn_property: string "" --- no_error_log [error] [crit] -=== TEST 10: set_property_setter() - errors in setter are caught by pcall in Lua library and don't crash the worker +=== TEST 10: host properties setter - errors are caught when accessed through filters +Produces a wasm host trap. --- wasm_modules: hostcalls --- http_config init_worker_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" local filters = { - { name = "hostcalls" }, + { name = "hostcalls", config="on=response_body " .. + "test=/t/set_property " .. + "name=wasmx.my_property set=my_value" }, } _G.c_plan = assert(proxy_wasm.new(filters)) @@ -582,24 +547,57 @@ qr/^[^\n]*?\[info\] [^\n]*? setting wasmx.const_property to HELLO 1, counter: 1[ } --- config location /t { - proxy_wasm hostcalls 'on=response_body \ - test=/t/log/property \ - name=wasmx.my_property'; - rewrite_by_lua_block { local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function setter(key, value) + error("crash!") + end + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/host trap \(internal error\): could not set \"wasmx\.my_property\": error in property setter: rewrite_by_lua.*?: crash\!/ +--- no_error_log +[crit] +[emerg] - local tbl = {} + + +=== TEST 11: host properties setter - errors are caught when accessed through proxy_wasm.set_property() +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" local function setter(key, value) error("crash!") end - assert(proxy_wasm.set_property_setter(setter)) + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) local ok, err = proxy_wasm.set_property("wasmx.my_property", "my_value") - assert((not ok) and err == "unknown error") + if not ok then + ngx.log(ngx.ERR, err) + end assert(proxy_wasm.start()) ngx.say("ok") @@ -608,9 +606,88 @@ qr/^[^\n]*?\[info\] [^\n]*? setting wasmx.const_property to HELLO 1, counter: 1[ --- response_body ok --- error_log eval -[ - qr/\[info\] .*? \[hostcalls\] on_response_body/, - qr/\[error\] .*? error setting property from Lua: rewrite_by_lua\(nginx.conf:[0-9]+\):[0-9]+: crash\!/, -] +qr/\[error\] .*? error in property setter: rewrite_by_lua.*?: crash\!/ +--- no_error_log +[crit] +[emerg] + + + +=== TEST 12: host properties setter - setter can ngx.log() +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls", config="on=response_body " .. + "test=/t/set_property " .. + "name=wasmx.my_property set=my_value" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function setter(key, value) + print("in setter") + return true, "my_value" + end + + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/rewrite_by_lua.*?: in setter/ --- no_error_log +[error] +[crit] + + + +=== TEST 13: host properties setter - setter cannot yield +Produces a wasm host trap. +--- wasm_modules: hostcalls +--- http_config + init_worker_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + local filters = { + { name = "hostcalls", config="on=response_body " .. + "test=/t/set_property " .. + "name=wasmx.my_property set=my_value" }, + } + + _G.c_plan = assert(proxy_wasm.new(filters)) + assert(proxy_wasm.load(_G.c_plan)) + } +--- config + location /t { + rewrite_by_lua_block { + local proxy_wasm = require "resty.wasmx.proxy_wasm" + + local function setter(key, value) + ngx.sleep(1000) + return true, "my_value" + end + + assert(proxy_wasm.attach(_G.c_plan)) + assert(proxy_wasm.set_host_properties_setter(setter)) + assert(proxy_wasm.start()) + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +qr/host trap \(internal error\): could not set \"wasmx\.my_property\": error in property setter: attempt to yield across C-call boundary/ +--- no_error_log +[crit] [emerg]