How to use Or/And correctly
I am looking at a couple of irules and im seeing a rather strange difference.
one looks like this..
METHOD 1:
if { [HTTP::host] contains "whatever" or "something }
and the other looks like
METHOD 2:
if { [HTTP::host] contains "whatever" or [HTTP::host] contains "something" }
I also noticed that this second method caused a TCL runtime error. The statement inside the if is simply a
HTTP::redirect www.url.com[HTTP:uri]
due to how simple the rule is i cant see anything that would cause a TCL error other than the format of the OR setup.
There is one other way I can think of to write something like this,
METHOD 3:
if { ([HTTP::host] contains "whatever") or ([HTTP::host] contains "something") }
which depending on how its processed may or may not matter at all.
what is the prefer method of writing this?
As with most languages, Tcl employs short-circuiting for logical operators, including the sugar operators ("and", "or" and "not") provided to iRules. As a consequence, when the following statement is evaluated:
if { [HTTP::host] contains "foo" or "bar" }
and it turns out that
evaluates to True, then the second part does not evaluate. If it evaluates to False, then the[HTTP::host] contains "foo"
evaluates the literal string "bar", which isn't a boolean and will cause a run-time error. To test this, I use the trivial iRule:if
when HTTP_REQUEST { if { [HTTP::host] contains "example.com" or "foo.com" } { log local0. "Yes: [HTTP::host]" } }
I then submit two requests. The first sets the Host header to "www.example.com" and the second sets it to "www.foo.com". Here is what ends up in /var/log/ltm:
Apr 5 17:06:48 b201 info tmm3[25219]: Rule /Common/test-or : Yes: www.example.com Apr 5 17:07:01 b201 err tmm[25219]: 01220001:3: TCL error: /Common/test-or - can't use non-numeric string as operand of "||" while executing "if { [HTTP::host] contains "example.com" or "foo.com" } { log local0. "Yes: [HTTP::host]" }"
So, just as I say, when the first condition matches, it's fine, but if does not, and the second condition is evaluated, a run-time error is raised.
Your second conditional is fine, and the run-time error is probably being raised in the body. I tested the following:
when HTTP_REQUEST { if { [HTTP::host] contains "example.com" or [HTTP::host] contains "foo.com" } { log local0. "Yes: [HTTP::host]" } }
with the Host header set to "www.example.com" and "www.foo.com". /var/log/ltm shows the following:
Apr 5 17:09:59 b201 info tmm1[25219]: Rule /Common/test-or : Yes: www.example.com Apr 5 17:10:01 b201 info tmm2[25219]: Rule /Common/test-or : Yes: www.foo.com
The problem likely arises because you did not quote the parameter passed to
:HTTP::redirect
HTTP::redirect www.url.com[HTTP:uri]
In this case
could contain characters that Tcl won't accept in a statement. Remember that, when you employ the substitution operator (that is, [...]), the substitution occurs first, then the statement is evaluated. Imagine that[HTTP::uri]
contains the (admittedly illegal) character ' ' (i.e., a space). It would result in a statement that looks like this:[HTTP::uri]
HTTP::redirect www.url.com/foo bar/baz
Because of the space,
is passed two parameters, but it only accepts one. There are other character combinations that can cause Tcl to hiccough. Anyhow, your best bet is to do this:HTTP::redirect
HTTP::redirect "www.url.com[HTTP::uri]"
If that doesn't solve your problem, you should pass along the log message (/var/log/ltm) associated with the failure.
In your case, there is no difference between Method 2 and Method 3. They will result in the same outcome. However, as you mention, parenthesis can be useful if you need to force order-of-evaluation. They don't hurt, in this case, and for longish conditional statements, they can make things clearer.