Object prototype methods "hasOwnProperty" always return true in r.variables when "set" directive is used only inside some "location" block
Closed this issue · 3 comments
Abstract
If "location" block A defines a value name by "set" directive, and "location" block B does not define, Object prototype methods "hasOwnProperty" always return true in "location" block B with error log "using uninitialized [NAME] variable"
Detail
js_content directive can pass "module.function" which only accept one paramter (r: NginxHTTPRequest)
, but sometimes developer want to pass more paramters by location block with same "module.function". "set" directive may be used as this idea.
Suppose "location" block A defines a value name by set $aaa "111"
and "location" block B does not define that, when js run Object.prototype.hasOwnProperty.call(r.variables, "aaa")
in "location" block B, it always return true, and with a error log "using uninitialized 'aaa' variable". Because it alway returns true, code in "location" block B will get an empty string, that will make an unexpected logical code run.
This behavior should be consider as a bug.
Environment & Version:
CentOS 7
nginx.x86_64 1:1.25.2-1.el7.ngx
nginx-module-njs.x86_64 1:1.25.2+0.8.0-1.el7.ngx
Steps to reproduce:
nginx.conf:
server {
listen 8765;
server_name localhost;
js_import test from test.js;
location /test_exists_value_a {
set $exists_value_a "dddddddd";
js_content test.exists_value_a;
}
location /test_not_exists_value_a {
js_content test.exists_value_a;
}
}
test.js:
function exists_value_a(r){
let res = 0;
if(Object.prototype.hasOwnProperty.call(r.variables, "exists_value_a")){
res = 1;
}
r.return(200, JSON.stringify({
"detect": res,
//"exists_value_a": r.variables["exists_value_a"]
}));
}
export default {exists_value_a};
curl:
curl -v "http://localhost:8765/test_not_exists_value_a"
What is expected?
value "detect" should be 0, no error log produce.
What is actually happening?
curl:
{"detect":1}
error log:
2023/09/05 11:50:05 [warn] 11703#11703: *3 using uninitialized "exists_value_a" variable, client: 127.0.0.1, server: localhost, request: "GET /test_not_exists_value_a HTTP/1.1", host: "localhost:8765"
Hi @HorseLuke,
Here we have situation similar to #667. The nginx variables are global and not local to a location.
set $exists_value_a "dddddddd"
sets a value to $exists_value_a
in location /test_exists_value_a, but the $exists_value_a exists globally once it defined anywhere and njs can reference it.
OK, got it. Closed as "by-design"?
To those who encounter same problem, now the solution is:
(1) define set
with empty string in server block, this is a global define behavior, and solved trigger uninitialized error problem.
(2) check whether the value is empty or not in js_content, do not use hasOwnProperty
check.
nginx.conf:
server {
listen 8765;
server_name localhost;
set $param_for_js_content "";
js_import test from test.js;
location /test_exists_value_a {
set $param_for_js_content "dddddddd";
js_content test.dorun;
}
location /test_not_exists_value_a {
js_content test.dorun;
}
}