Forum Discussion

jrich523_118173's avatar
jrich523_118173
Icon for Nimbostratus rankNimbostratus
Apr 05, 2016
Solved

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

    [HTTP::host] contains "foo"
    evaluates to True, then the second part does not evaluate. If it evaluates to False, then the
    if
    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:

    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

    [HTTP::uri]
    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::redirect www.url.com/foo bar/baz
    

    Because of the space,

    HTTP::redirect
    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 "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.

4 Replies

  • Vernon_97235's avatar
    Vernon_97235
    Historic F5 Account

    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

    [HTTP::host] contains "foo"
    evaluates to True, then the second part does not evaluate. If it evaluates to False, then the
    if
    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:

    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

    [HTTP::uri]
    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::redirect www.url.com/foo bar/baz
    

    Because of the space,

    HTTP::redirect
    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 "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.

    • jrich523_118173's avatar
      jrich523_118173
      Icon for Nimbostratus rankNimbostratus
      that is probably one of the best/most detailed answered i've ever received for anything, thanks!
  • 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

    [HTTP::host] contains "foo"
    evaluates to True, then the second part does not evaluate. If it evaluates to False, then the
    if
    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:

    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

    [HTTP::uri]
    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::redirect www.url.com/foo bar/baz
    

    Because of the space,

    HTTP::redirect
    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 "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.

    • jrich523_118173's avatar
      jrich523_118173
      Icon for Nimbostratus rankNimbostratus
      that is probably one of the best/most detailed answered i've ever received for anything, thanks!