id="Body">
Tech Tips on DevCentral
   
You are here: Tutorials > Tech Tips

Current Articles | Search | Syndication

iRules 101 - #04 - Switch

by Joe - 16173 views Article Rating

In a previous article, I discussed the if command which is core to almost every iRule.  For those cases where your need to perform conditional testing on a single value, there is another conditional command that can be used that in most cases is faster and easier to read than its corresponding if counterpart.  This article will discuss the "switch" statement, the cases with which you could use it, and some insights into how it is used. Other articles in the series: 


SYNTAX

The syntax for the switch command is as follows:

switch ?options? string pattern body ?pattern body ...?
switch ?options? string {pattern body ?pattern body ...?}

The switch command matches its string argument against each of the pattern arguments in order. As soon as it finds a pattern that matches string it evaluates the following body argument by passing it recursively to the Tcl interpreter and returns the result of that evaluation. If the last pattern argument is default then it matches anything. If no pattern argument matches string and no default is given, then the switch command returns an empty string.

 

If the initial arguments to switch start with - then they are treated as options. The following options are currently supported:

 

-exact
Use exact matching when comparing string to a pattern. This is the default.
-glob
When matching string to the patterns, use glob-style matching (i.e. the same as implemented by the string match command).
-regexp
When matching string to the patterns, use regular expression matching (as described in the re_syntax reference page).
- -
Marks the end of options. The argument following this one will be treated as string even if it starts with a -.

Two syntaxes are provided for the pattern and body arguments. The first uses a separate argument for each of the patterns and commands; this form is convenient if substitutions are desired on some of the patterns or commands. The second form places all of the patterns and commands together into a single argument; the argument must have proper list structure, with the elements of the list being the patterns and commands. The second form makes it easy to construct multi-line switch commands, since the braces around the whole list make it unnecessary to include a backslash at the end of each line. Since the pattern arguments are in braces in the second form, no command or variable substitutions are performed on them; this makes the behavior of the second form different than the first form in some cases.

"switch" as a replacement for "if"

As you can see from the above syntax, the switch statements performs a comparison on a single value indicated by the "string" argument.  So, it is perfect for a replacement to if commands that performs "and", as well as, "or" logic on a single string value.  You'll see examples like this in iRules all the time

if { [HTTP::uri] equals "/foo" } {
  # do something...
} elseif { ([HTTP::uri] equals "/bar") || ([HTTP::uri] equals "/foobar") } {
  # do something else...
} else {
  # don't do anything...
}

You'll see in this example, the bodies of code are executed (in this example just comments) if a conditional test is passed on the value of the HTTP::uri string. This is a perfect candidate for a switch statement.  The following switch example is equivalent to the above if/elseif block.

switch [HTTP::uri] {
  "/foo" {
    # do something...
  }
  "/bar" -
  "/foobar" {
    # do something else...
  }
  default {
    # don't do anything...
  }
}

The one thing that sticks out is the dash ('-') after the "/bar" string comparison.  This tells the TCL interpretor to do a logical OR with the following line.  So basically it's the same as comparing "/bar" OR "/foobar".  You may not see a high degree of readability improvement when comparing the two options, but it becomes much more apparent when there are many more than 2 or 3 tests.  There are hidden benefits though: Generally, switch commands are faster than if statements due to additional expression evaluations that need to occur with if commands.  Since the switch statements only works on a single comparison value, internal optimizations are able to be made in the evaluation process.  This likely isn't going to be a big difference for one or two comparisons, but it is measurable when more are made.

Different ways to use switch

Comparing "exact" matches.

The switch command as several different comparison modes you can use.  Running switch with the "-exact" argument will perform exact string for string comparisons, similar to how the "equals" operator works on two strings.  There are no wild cards or regular expressions, but it can be highly optimized by not using those features.  The above example was an example of the "-exact" option (-exact is the default if no arguments are specified).

Do you really need a regular expression?

We see this alot in iRules

if { [HTTP::uri] matches_regex "/foo*" } {
  # do something 
}

There is an obvious problem with the above iRule: using a regular expression comparison is overkill.  Regular expressions are very CPU intensive and should only be used when there are no other options.  At a minimum, the matches_regex above should be replaced with "starts_with", or "ends_with" or "contains" in situations where end or middle conditions need to be performed.  Hidden inside the "string" TCL command is the "string match" subcommand.  This performs a "poor man's" regular expression allowing for wildcards and character ranges.  For you Unix heads out there, this is very similar to filename globbing in Unix shells.  The usage for "string match" is as follows:

string match ?-nocase? pattern string
*
Matches any sequence of characters in string, including a null string.
?
Matches any single character in string.
[chars]
Matches any character in the set given by chars. If a sequence of the form x-y appears in chars, then any character between x and y, inclusive, will match. When used with -nocase, the end points of the range are converted to lower case first. Whereas {[A-z]} matches '_' when matching case-sensitively ('_' falls between the 'Z' and 'a'), with -nocase this is considered like {[A-Za-z]} (and probably what was meant in the first place).
\x
Matches the single character x. This provides a way of avoiding the special interpretation of the characters *?[]\ in pattern.

How does this relate to the switch command?  Well, lucky for us, the "-glob" argument allows for comparisons based on the TCL string match comparison format.  In almost all cases, one completely remove regular expressions with carefully placed wildcards and character ranges. 

switch -glob [HTTP::uri] {
  "/foo*" {
    # this will match on any string that starts with "/foo"
  }
  "*bar" {
    # this will match on any string that ends with "bar"
  }
  "/foobar[a-zA-Z]" {
    # This will match with any string that starts with "/foobar" followed by any case character from a-z.
  }
  "/foo?bar" {
    # this will match any string that starts with "/foo", followed by any character, and followed by and ending with "bar".
  }
}

As you see, the "globbing" option in the switch statement is very powerful and likely covers most comparison cases. 

For those cases where match comparsions won't cut it

For those situations where you need a bit more indepth checking, the "-regexp" argument is available.  It is beyond the scope of this article to describe regular expressions in great detail.  If you so desire, you can view the documentation on "re_syntax" (http://www.tcl.tk/man/tcl8.4/TclCmd/re_syntax.htm).  Good luck!!!

Since we don't recommend the use of regular expresions, I'm going to omit one from this article.  The format is much the same as the -glob option, but replace the comparison strings with your desired regular expression.

Things that don't work easily with switch commands

Multiple comparison sources

If you are trying to compare multiple values in your if/else command like the following

if { ([HTTP::host] equals "www.foo.com") and ([HTTP::uri] starts_with "/foo") } {
  # do something
}


to use a switch statement, you will have to do something like the following:

switch [HTTP::host] {
  "www.foo.com" {
    switch -glob [HTTP::uri] {
      "/foo*" {
        # do something
      }
    }
  }
}

Logical AND Comparisons

The ability to chain conditionals together with a dash ('-') makes it very easy to do "if (a or b) do c" type test, but it doesn't work for "if (a and b) do c)".  To build a logical AND comparison, you'll have to either to an embedded switch or if within the body of the switch condition.  The above example illustrates this.

Simple "boolean" type tests

If commands are often used to test not only string comparisons, but other boolean tests such as the length of a string with the "string length" command, or numeric comparisons such as "greater than".  There is no way use these types of tests within a switch statement.  We'll, actually you could, but you would have to first perform the test and then put the return value into a string format and then feed that string into a switch.  For these types of conditionals, you are better off sticking with using "if".

Conclusion

If you find your iRule being filled with line after line of if/elseif/elsef/elseif/... commands, then think about switching to making use of the higher performing "switch" command.  It will not only make your iRules run faster (for string comparisons), but in most cases, it is easier to read and maintain.

Get the Flash Player to see this player.


Rate This Article:

COMMENTS

posted @ Monday, November 26, 2007 5:56 AM by Andy Herrman   

In your example using wildcards you have a switch for both "/foo*" and "/foobar[a-zA-Z]". The second is simply a subset of the first.

What would TCL do in this case? I'm guessing it would hit the first one ("/foo*"), evaluate that, and then leave the switch statement, as that's the standard switch behavior in other languages. Is that right?

posted @ Monday, November 26, 2007 9:40 AM by Host Account   

I guess that was a bad example. TCL will pick the first match which in this case would be the "/foo*" condition. I was just showing different options for the sake of illustrating how to use the different matching characters. Thanks for pointing this out!

posted @ Tuesday, May 04, 2010 8:24 PM   

posted @ Tuesday, May 24, 2011 4:26 PM by Prakash   

how about boolean OR with the operator "|" ? It didn't work for me atleast.

switch [HTTP::host] {
"foo|foo.com|www.foo.com" {
switch -glob [HTTP::uri] {
"/foo*" {
# do something
}
}
}
}

posted @ Wednesday, June 01, 2011 9:32 AM by Joe-h   

The bad news: You can't use logical operators within a switch comparison. The good news: for "Or" comparisons, the TCL implementors developed a way to specify a single set of commands to execute if any of a given set of matches compare. You would separate them with a dash like this:

switch [HTTP::host] {
"foo" -
"foo.com" -
"www.foo.com" {
switch -glob [HTTP::uri] {
"/foo*" {
#do something
}
}
}

-Joe
Only registered users may post comments.
  

TechTips by Category

Filter by:
ARX Import Restrictions for NetApp Volumes by jmccarron (30 Views)
Two-Factor Authentication With Google Authenticator And APM by watkins (297 Views)
Controlling a Pool Members Ratio and Priority Group with iControl by Joe (243 Views)
F5 ARX WAN Optimization with BIG-IP WAN Optimization Manager (WOM) by mfabiano (284 Views)
iRules Concepts: Tcl, The How and Why by Colin (957 Views)
Populating Tables With CSV Data Via Sideband Connections by watkins (958 Views)
Introduction to iStats Part 1: Overview by Colin (1069 Views)
Google reCAPTCHA Verification With Sideband Connections by watkins (1429 Views)
Transparent Web Application Bot Protection by Joe (1265 Views)
v11: iRules Data Group Updates by citizen_elah (2927 Views)
HTTP Request Cloning via iRules, Part 1 by Colin (1341 Views)
Managing Ramcache Entries with Pycontrol by citizen_elah (909 Views)
iRules Concepts: Connection States and Command Suspension by Colin (1727 Views)
v11.1: DNS Blackhole with iRules by citizen_elah (2089 Views)
CodeShare Refresh: HTTP Session Limit by Colin (1399 Views)
Page 1 of 4First   Previous   [1]  2  3  4  Next   Last   
  

Most Viewed Tech Tips

Mitigating Slow HTTP Post DDoS Attacks With iRules by watkins
(6045 Views) Published on Friday, November 05, 2010
APM Session Invalidation Using ASM by Colin
(6030 Views) Published on Monday, October 17, 2011
Web Application Login Integration with APM by Colin
(5800 Views) Published on Monday, April 18, 2011
iRules Data Group Formatting Rules by citizen_elah
(5490 Views) Published on Tuesday, March 29, 2011
Multiple Certs, One VIP: TLS Server Name Indication via iRules by Colin
(5422 Views) Published on Tuesday, April 05, 2011
One Time Passwords via an SMS Gateway with BIG-IP Access Policy Manager by citizen_elah
(5386 Views) Published on Tuesday, February 08, 2011
BIG-IP APM–Customized Logon Page by citizen_elah
(5104 Views) Published on Tuesday, June 21, 2011
SSL Profiles Part 3: Certificate Chain Implementation by citizen_elah
(5050 Views) Published on Wednesday, December 01, 2010
SSL Profiles: Part 1 by citizen_elah
(4747 Views) Published on Wednesday, November 17, 2010
v11: RDP Access via BIG-IP APM–Part 3 by citizen_elah
(4715 Views) Published on Tuesday, October 04, 2011
Automating Web App Deployments with Opscode Chef and iControl by watkins
(4565 Views) Published on Friday, July 08, 2011
  

 Top Contributors

Techgeeeg
fatmcgav
mikand
MarkM
Chris Phillips
genseek
K-Dubb
adiezma
Ankush Narang
Beinhard
  

93,050 Members in 191 Countries and Growing!

Join DevCentral Today!

About DevCentral

F5 DevCentral is your source for the best technical documentation, discussion forums, blogs, media and more related to application delivery networking.

So dive in, meet your peers, and get familiar with DevCentral. We hope it makes your job easier and helps you get more from your F5 investment. If new to DevCentral, check out the Getting Started section. And if you have any problems, or think something could be easier to use, let us know.

Got It !

We've received your comment and transmitted it directly to DevCentral HQ.

Thanks for taking time to let us know what's on your mind. At DevCentral | Community Matters!

Get In Touch With Us

Have questions, suggestions or just want to get something off your chest?

Use our handy form below to Direct Connect with DevCentral Mission Control.

Send Us Feedback      or