Module vars set inside if's are not set

Variables that are used by modules and are set inside ‘if’ blocks in the
nginx.conf file are not being set correctly. My nginx version (-V
output) is:

nginx version: nginx/0.7.64
built by gcc 4.3.3 (Ubuntu 4.3.3-5ubuntu4)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-http_ssl_module
–add-module=…/nginx_http_push_module-0.69
–add-module=…/agentzh-echo-nginx-module-b8a0849

I’m setting $push_channel_id for the nginx_http_push_module using the
following if statement.

location ^~ /fjsub/ {

 push_subscriber long-poll;
 if ($arg_id) {
         set $push_channel_id $arg_id;
 }

}

The variable is set fine outside an ‘if’ statement but when the if
evaluates to true, the variable is not set.

I have duplicated this behaviour with the echo module as follows:

This will print “No”:

location ^~ /echo/ {
set $myText “No”;
if ($remote_addr ~* ‘000’){
set $myText “Yes”;
}
echo $myText;
}

But when I replace ‘000’ with ‘64’ which matches my IP address, nginx
returns a 404 because the value of $myText is empty which means the echo
command didn’t have any effect.

I have duplicated this behavior with only the echo module compiled in
and ssl and push removed.

I’ve already filed this as a bug on GIT for the NHPM (push) module but I
suspect this may be an nginx bug since it is broken across modules.

This bug also exists on the latest development build:

nginx version: nginx/0.8.28
built by gcc 4.3.3 (Ubuntu 4.3.3-5ubuntu4)
configure arguments: --prefix=/usr/local/nginx
–add-module=…/agentzh-echo-nginx-module-b8a0849

What is surprising/confusing is that I’m setting variables inside
conditional statements that are used by proxy_pass and that works fine.
So does the upstream module (proxy_pass module) handle it’s variables
differently to the echo and push modules?

Thanks,

Mark.

Mark M. [email protected]
http://feedjit.com/

On Mon, Nov 23, 2009 at 09:38:17PM -0700, Mark M. wrote:

     set $myText "No";

configure arguments: --prefix=/usr/local/nginx
–add-module=…/agentzh-echo-nginx-module-b8a0849

What is surprising/confusing is that I’m setting variables inside
conditional statements that are used by proxy_pass and that works fine.
So does the upstream module (proxy_pass module) handle it’s variables
differently to the echo and push modules?

The “if” block is ugly hack inside nginx. This is the cause.


Igor S.
http://sysoev.ru/en/

Igor S. wrote:

The “if” block is ugly hack inside nginx. This is the cause.

That’s sad because it’s also nginx.conf’s most useful feature. e.g.

if ($request_uri ~* ‘^/chatSub/(\d+)/$’) {
set $backEndDigit $1;
}

proxy_pass http://10.1.0.:80$backEndDigit/someUrl/:

I run this in production with over 30,000 concurrent connections on the
nginx front end. nginx.conf laughs at apache2.conf and hits on it’s
girlfriend.

Mark.

Awesome, thanks for the fix and insight! If a module writer’s wiki
exists this should be added.

On Tue, Nov 24, 2009 at 12:38 PM, Mark M. [email protected] wrote:

The variable is set fine outside an ‘if’ statement but when the if evaluates
to true, the variable is not set.

Behind the scene, the if directive’s “block” is implemented as a
dynamic location (of type LIF, not LOC). It’s very different from the
if statements’ blocks one usually finds in an everyday programming
language :slight_smile:

I have duplicated this behaviour with the echo module as follows:
This will print “No”:
location ^~ /echo/ {
set $myText “No”;
if ($remote_addr ~* ‘000’){
set $myText “Yes”;
}
echo $myText;
}

Tests have shown that if the “if” condition succeeds here, nginx will
return a 404 error page because of the lack of content handler in that
anonymous location corresponding to the “if” block :slight_smile:

I’ve just released “echo” module v0.22 to allow you to use “echo”
directly in “if” blocks and the following should work as expected:

location ^~ /if {
    set $res miss;
    if ($arg_val ~* '^a') {
        set $res hit;
        echo $res;
    }
    echo $res;
}

GET /if?val=abc yields the “hit” output while GET /if?val=bcd yields
“miss” :slight_smile:

It seems that the standard proxy_pass directive also works in if blocks
(yay!):

location ^~ /if {
    set $res miss;
    if ($arg_val ~* '^a') {
        set $res hit;
        proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res;
    }
    proxy_pass $scheme://127.0.0.1:$server_port/foo?res=$res;
}
location /foo {
    echo "res = $arg_res";
}

You will get the expected behavior :slight_smile:

Feel free to take a look at more related (passing) test cases that
I’ve included in the “echo” module’s test suite:

http://github.com/agentzh/echo-nginx-module/blob/master/test/t/if.t

Have fun with “if” and “echo”!

Regards,
-agentzh

On Wed, Nov 25, 2009 at 10:46 AM, Mark M. [email protected] wrote:

Awesome, thanks for the fix and insight! If a module writer’s wiki exists
this should be added.

I’ve already added some hints regarding “if” block uses to my “echo”
module’s wiki page:

 http://wiki.nginx.org/NginxHttpEchoModule

Cheers,
-agentzh