Forum Discussion

Mike_62629's avatar
Mike_62629
Icon for Nimbostratus rankNimbostratus
Apr 07, 2008

Has anyone successfully used dicts in iRules?

Has anyone successfully used dicts in iRules?

I'm currently iterating through a loop doing string comparisons and think it may be a bit more efficient to do a single dictionary lookup instead. I was having trouble getting dicts to pass the iRule Editor's syntax checking. I'm new to both iRules and TCL, so it could just be my own inexperience with the base language.

I plan to move the logic for this outside of the iRule altogether soon, but for now, here's where I am:


set myPool Application-Pool
set myExtensions {.css .gif .gif .ico .jpeg .jpg .js}
set myPath [string tolower [HTTP::path]]
 pull out the extension from the path
set myIndex [string last {.} $myPath]
set myExtension [string range $myPath $myIndex end]
 if the requested object's extension is in the list, serve from cache
foreach extension $myExtensions {
    if { [ $extension equals $myExtension] } {
        set myPool Cache-Pool
    }
}
pool $myPool

I'd like to move toward something like this:


set myExtensions(.css) 1
set myExtensions(.gif) 1
set myExtensions(.ico) 1
set myExtensions(.jpeg) 1
set myExtensions(.jpg) 1
set myExtensions(.js) 1
set myPath [string tolower [HTTP::path]]
 pull out the extension from the path
set myIndex [string last {.} $myPath]
set myExtension [string range $myPath $myIndex end]
if { dict exists $myExtensions $myExtension } {
    pool Cache-Pool
} else {
    pool Application-Pool
}

- please note, this isn't the only logic used to determine cacheability, but it's the component I think could most benefit from refactoring. The list / dictionary is larger, and I'd rather do one lookup instead of 20+ string comparisons for every request. It's a fairly highly trafficked site.
  • Hi,

    Why not creating a string class with all the extension you want to analyze and then do the following:

    
    when HTTP_REQUEST {
    if {[matchclass [HTTP::path] ends_with $::Extension ] } {
    pool Cache-Pool
             } else {
                    pool Application-Pool
             }
    }

    where Extension is the name of your class

    You can create a class in the Data group page.
  • Colin_Walker_12's avatar
    Colin_Walker_12
    Historic F5 Account
    nmenant is on the right track here. There's no need to do a dictionary lookup, although that's an interesting (and sneaky!) way of getting around multiple comparisons.

     

     

    In this case I'd recommend a single class style match, like listed above.

     

     

    Colin
  • By Class do you guys mean a TCL class that's a subclass of string, or is this some F5/BigIP feature?

     

     

    In either case, due to my inexperience with both, do you know of any resources that may be a good start for implementation?
  • Nevermind on that, it seems pretty simple. The DevWiki had some stuff on it.
  • 
    class CacheableExtensions {
        .css
        .gif
        .ico
        .jpg
        .jpeg
        .js
    }
    when HTTP_REQUEST {
        if { [matchlass [HTTP::path] ends_with $::CacheableExtensions] } {
            pool Cache-Pool
        } else {
            pool Application-Pool
        }
    }

    Could it be that simple?
  • Would it be more appropriate to initialize my class within RULE_INIT so it doesnt have to be initialized and destroyed with each request?
  • Yeap, thanks. I kept reading the matchclass wiki page and found out about data groups. What mislead me to think it was to be implemented in code was the fact that it includes a snippet of what the data group ends up looking like in the bigip.conf file. Skimming the doc made me think I had to code it somewhere.

     

     

    As to dict being implemented in 8.5, thats what I thought was the problem, but couldn't find anything that would definitively state the version of TCL used in the BigIp. Thanks for the info, and to everyone who commented, thanks for the help.

     

     

    For those wondering, I ended up with two classes:

     

     

    class CacheableExtensions {
        .css 
        .js
        .gif 
        *SNIP*
        .jpg 
        
    }
    class CacheableDirs {
        /user/docs/
        /user/images/
        /user/videos/
    }
    when HTTP_REQUEST {
        if { [matchclass [string tolower [HTTP::path]] starts_with $::CacheableDirectories] } {
            pool Cache-Pool
        } elseif { [matchclass [string tolower [HTTP::path]] ends_with $::CacheableExtensions] } {
            pool Cache-Pool
        } else {
            pool Application-Pool
        }
    }
  • That's great, except sometimes you really want a dict.

     

    Does anyone have an answer to the original question?

     

     

    I'm trying to save the request headers during HTTP_REQUEST so I can conditionally log them in HTTP_RESPONSE.

     

    My approach is to copy the headers to a dict and iterate over the dict in HTTP_RESPONSE as the request headers are no longer available by that point.

     

     

    Dave
  • Actually - this may still be current:

     

     

    From http://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/342/iRules-101--15--TCL-List-Handling-Commands.aspx

     

    ...

     

    TCL: List Handling Commands

     

     

    Most of the TCL commands for use in manipulating lists are available in iRules:

     

    list - Create a list

     

    split - Split a string into a proper Tcl list

     

    join - Create a string by joining together list elements

     

    concat - Join lists together

     

    llength - Count the number of elements in a list

     

    lindex - Retrieve an element from a list

     

    lrange - Return one or more adjacent elements from a list

     

    linsert - Insert elements into a list

     

    lreplace - Replace elements in a list with new elements

     

    lsort - Sort the elements of a list

     

    lsearch - See if a list contains a particular element

     

    The remaining documented list handling commands, lrepeat, lreverse, & dict, are not available within iRules. We’ll cover the commands from above that get the most traffic here on the forums.

     

     

    ...

     

  • hoolio's avatar
    hoolio
    Icon for Cirrostratus rankCirrostratus
    Hi Dave,

    You could try saving the value of [HTTP::request] split on a \r in HTTP_REQUEST and then use TCL list commands to parse this in the response. I think this should be more efficient than looping through all of the request headers and saving them to a list. You'll also get the full request line from HTTP::request, which can't be retrieved whole in one command otherwise.

    Can you try something like this:

    
    when HTTP_REQUEST {
        Save the request headers split on a carriage return
       set request_headers [split [HTTP::request] \\\r]
    }
    when HTTP_RESPONSE {
       log local0. "request header count: [llength $request_headers]"
    
        Loop through the request headers
       for {set i 0} {$i < [llength $request_headers]} {incr i} {
    
           Trim off the leading \r\n from each list element and log it
          log local0. "$i: [string trimleft [lindex $request_headers $i]]"
       }
    }
    

    Aaron