Introduction

An iRule is a powerful and flexible feature of BIG-IP devices based on F5's exclusive TMOS architecture. iRules provide you with unprecedented control to directly manipulate and manage any IP application traffic. iRules utilizes an easy to learn scripting syntax and enables you to customize how you intercept, inspect, transform, and direct inbound or outbound application traffic. In this series of tech tips, we'll talk about the TCL language, it's usage and control structures, as well as iRule extensions to the TCL language.  Other articles in the series: 

TCL: The String Command

There's a philosophical discussion as to whether or not everything in TCL is a string (and whether that is a good thing or not), but whether or not you subscribe to the debate, strings play a major role in TCL, and so it is with iRules as well.  TCL affords plenty of options on the string command, several of which will be covered in this article:

  • string map
  • string range
  • string trim 
  • string trimleft
  • string trimright

We'll look at concrete examples for these commands, mostly because I (still) find the examples they use in the TCL documentation somewhat lacking.

string map ?-nocase? mapping string

 Replaces substrings in string based on the key-value pairs in mapping. mapping is a list of key value key value ... Each instance of a key in the string will be replaced with its corresponding value. If -nocase is specified, then matching is done without regard to case differences. Both key and value may be multiple characters. Replacement is done in an ordered manner, so the key appearing first in the list will be checked first, and so on.  Here's an example from the forums courtesy of user Hoolio that maps two patterns, substituting https for http and "nothing" for the server port number. 

  when HTTP_RESPONSE {
       # Check if a response is a redirect
       if {[HTTP::is_redirect]}{ 
          log local0. "Original Location: [HTTP::header value Location]"
          # Assume the server will use it's own TCP port in redirects and remove it.  Also replace http:// with https://.
          HTTP::header replace Location [string map -nocase [list http:// https:// ":[LB::server port]" ""] [HTTP::header value Location]]
          log local0. "Updated location (string map): [string map -nocase [list http:// https:// ":[LB::server port]" ""] [HTTP::header value Location]]"
      }  
   } 

String is only iterated over once, so earlier key replacements will have no affect for later key matches. For example:

% string map {abc 1 ab 2 a 3 1 0} 1abcaababcabababc
01321221

What??  That's one of those not so intuitive examples in the TCL documentation.  Actually, though, I like this one.  Let's break it down.  There are four key/value pairs here:

  • abc, if found, will be replaced by a 1
  • ab, if found, will be replaced by a 2
  • a, if found, will be replaced by a 3
  • 1, if found, will be replaced by a 0

String Map Multiple Key/Value Example

Mapping

Original String

Resulting String

1st (abc->1)

1abcaababcabababc

11aab1abab1

2nd (ab->2)

11aab1abab1

11a21221

3rd (a->3)

11a21221

11321221

4th (1->0)

11321221

01321221

Note that with the fourth map, the returned string is 01321221, not 00320220. Why is that? Well, the string is only iterated over once, so earlier key replacements will have no affect for later key matches.

 string range string first last

Returns a range of consecutive characters from string, starting with the character whose index is first and ending with the character whose index is last. An index of 0 refers to the first character of the string. first and last may be specified as for the index method. If first is less than zero then it is treated as if it were zero, and if last is greater than or equal to the length of the string then it is treated as if it were end. If first is greater than last then an empty string is returned. Colin weighs in with an example to strip a known port from the host with string range.  Notice his use of 0 for first and end-5 for last.  Nifty. Of course, this problem has an easier, cleaner solution with the getfield command, but that's a different story...

when HTTP_REQUEST {
  if { [HTTP::host] ends_with "8010" } {
    set http_host [string range [HTTP::host] 0 end-5]
    HTTP::redirect "https://$http_host[HTTP::uri]"
  }
}

In this example, unRuleY provides a solution that strips out the leading characters of a URI: 

when HTTP_REQUEST {
   set uri [HTTP::uri]
   if { $uri starts_with "/axess2" } {
      HTTP::uri [string range $uri 7 end]
      pool pool1
   }
}

string trim string ?chars?

Returns a value equal to string except that any leading or trailing characters present in the string given by chars are removed. If chars is not specified then white space is removed (spaces, tabs, newlines, and carriage returns).  I didn't understand this one early in my iRule writing, along with trimleft & trimright.  Trimleft and trimright remove leading and trailing characters, respectively, while trim removes both leading and trailing characters.  Here's some shell examples to explain the behavior:

% set a ". this is a string ."
. this is a string .
% string trim $a .
 this is a string                                              # notice the . was trimmed, but not the whitespace
% string trim [string trim $a .] " "
this is a string                                               # now the whitespace has been trimmed (both leading and trailing)
% string trim [string trim $a .]
this is a string                                               # same as before, the " " isn't necessary, removed by default when ?chars? isn't specified
% set b [string trim [string trim $a .]]<EOS>
this is a string<EOS>                                    # Just added <EOS> to show the whitespace has in fact been trimmed from the trailing end of the string
%

trimleft and trimright work the same way.  They don't trim the leading/trailing side of the string when matched, they remove those matching characters.  Here's an example from Colin that utilizes trimleft to remove the leading characters in the path.  Notice that the same problem was solved in an above example with string range.  Many of the problems you'll face have multiple solutions, in this case, the directories are specified, which makes the rule less general than a string range command would be, particularly if all your paths lead with /xyz/. 

when HTTP_REQUEST {
  if {[HTTP::host] contains "soa"} {
    if {[HTTP::uri] starts_with "/prd/"} {
      HTTP::uri [string trimleft [HTTP::uri] /prd]
      pool POOL_SOA_PRD
    }
    if {[HTTP::uri] starts_with "/ppd/"} {
      HTTP::uri [string trimleft [HTTP::uri] /ppd]
      pool POOL_SOA_PPD
    } else {
      # THIS IS ONLY HERE SO THAT THEY CAN KEEP TESTING
      pool POOL_SOA_PRD
    }
  }
}

 Happy TCLing!

 

Get the Flash Player to see this player.