Forum Discussion

ShinRyuX's avatar
ShinRyuX
Icon for Nimbostratus rankNimbostratus
Mar 29, 2016

Any way to shorten this switch case iRule?

Here is my iRule.

when HTTP_REQUEST {
    switch -glob [string tolower [HTTP::path]] {
        "/dir1" -
        "/dir1/" {
            HTTP::respond 301 Location "http://www.domain1.com"
        }
        "/dir1/dir2" -
        "/dir1/dir2/" {
            HTTP::respond 301 Location "http://www.domain2.com"
        }
    }
}

I thought of putting "dir1*" but it would interfere with my 2nd condition.

16 Replies

  • Hello,

    The switch condition are taken in the order, so you can define a /dir1* but after /dir1/dir2* for example.

    when HTTP_REQUEST {
        switch -glob [string tolower [HTTP::path]] {
            "/dir1/dir2*" {
                HTTP::respond 301 Location "http://www.domain2.com"
            }
            "/dir1*" {
                HTTP::respond 301 Location "http://www.domain1.com"
            }
        }
    }
    
    • ShinRyuX's avatar
      ShinRyuX
      Icon for Nimbostratus rankNimbostratus
      Hello, That solution would work but this iRule is actually a simplified version of my current iRule where I have over 100 redirect links along with uri parameters extracted from original URL. I would have to re-order the conditions in ways to prevent any conflicts from occuring. Is there anything that can be done without re-arraging the order? I tried few things like putting "dir1?" or even "dir1[ ?]" but they dont satisfy both conditinos i want. Thank You
    • Yann_Desmarest_'s avatar
      Yann_Desmarest_
      Icon for Nacreous rankNacreous
      What is the logic behind your redirect links ? Maybe, you can extract patterns from your request uri and change the Location value accordingly with a string map command
    • ShinRyuX's avatar
      ShinRyuX
      Icon for Nimbostratus rankNimbostratus
      This is the full logic when HTTP_REQUEST { Extract URL parameters set urlPath "[string tolower [HTTP::path]]?" if {[string tolower [HTTP::uri]] contains "?"} { set newUri [string map -nocase [list $urlPath "?"] [HTTP::uri]] } else { set newUri "" } Redirection Logic case switch -glob [string tolower [HTTP::path]] { "/file1.html" { HTTP::respond 301 Location "http://www.newdomain.com/en-US/destination1$newUri" } "/dir1" - "/dir1/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination2$newUri" } "/dir1/dir2" - "/dir1/dir2/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination3$newUri" } "/dir1/dir2/dir3" - "/dir1/dir2/dir3/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination4$newUri" } ... } The list goes on but the logic remains the same.
  • Hello,

    The switch condition are taken in the order, so you can define a /dir1* but after /dir1/dir2* for example.

    when HTTP_REQUEST {
        switch -glob [string tolower [HTTP::path]] {
            "/dir1/dir2*" {
                HTTP::respond 301 Location "http://www.domain2.com"
            }
            "/dir1*" {
                HTTP::respond 301 Location "http://www.domain1.com"
            }
        }
    }
    
    • ShinRyuX's avatar
      ShinRyuX
      Icon for Nimbostratus rankNimbostratus
      Hello, That solution would work but this iRule is actually a simplified version of my current iRule where I have over 100 redirect links along with uri parameters extracted from original URL. I would have to re-order the conditions in ways to prevent any conflicts from occuring. Is there anything that can be done without re-arraging the order? I tried few things like putting "dir1?" or even "dir1[ ?]" but they dont satisfy both conditinos i want. Thank You
    • Yann_Desmarest's avatar
      Yann_Desmarest
      Icon for Cirrus rankCirrus
      What is the logic behind your redirect links ? Maybe, you can extract patterns from your request uri and change the Location value accordingly with a string map command
    • ShinRyuX's avatar
      ShinRyuX
      Icon for Nimbostratus rankNimbostratus
      This is the full logic when HTTP_REQUEST { Extract URL parameters set urlPath "[string tolower [HTTP::path]]?" if {[string tolower [HTTP::uri]] contains "?"} { set newUri [string map -nocase [list $urlPath "?"] [HTTP::uri]] } else { set newUri "" } Redirection Logic case switch -glob [string tolower [HTTP::path]] { "/file1.html" { HTTP::respond 301 Location "http://www.newdomain.com/en-US/destination1$newUri" } "/dir1" - "/dir1/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination2$newUri" } "/dir1/dir2" - "/dir1/dir2/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination3$newUri" } "/dir1/dir2/dir3" - "/dir1/dir2/dir3/" { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination4$newUri" } ... } The list goes on but the logic remains the same.
  • Use -glob flag if you have wildcards or other expressions. With your current iRule, the -glob flag is not mandatory.

     

    You may also want to have a look at LTM Local Traffic policy feature. I'd use a Local Traffic Policy over iRules these days whenever possible.

     

    When it comes to shortening the iRule, Yann got the same solution posted a bit before me (removed mine)

     

  • Arie's avatar
    Arie
    Icon for Altostratus rankAltostratus

    As far as I know Local Traffic Policies still don't allow control over the redirect type (301, 302, etc.). That being the case a policy may not be the best solution since often a 301 would be preferable.

     

    It's disappointing that F5 is still not conforming to the RFCs on this. Hoping to see that soon!

     

  • My comment on top didnt come out right. Here is the full Logic of my iRule.

    when HTTP_REQUEST {
         Extract URL parameters
        set urlPath "[string tolower [HTTP::path]]?"
        if {[string tolower [HTTP::uri]] contains "?"} {
            set newUri [string map -nocase [list $urlPath "?"] [HTTP::uri]]
        }
        else {
            set newUri ""
        }
    
         Redirection Logic case
        switch -glob [string tolower [HTTP::path]] {
            "/file1.html" {
                HTTP::respond 301 Location "http://www.newdomain.com/en-US/destination1$newUri"
            }
            "/dir1" -
            "/dir1/" {
                HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination2$newUri"
            }
            "/dir1/dir2" -
            "/dir1/dir2/" {
                HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination3$newUri"
            }
            "/dir1/dir2/dir3" -
            "/dir1/dir2/dir3/" {
                HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/destination4$newUri"
            }
                    ...
    }
    

    The list goes on but the logic remains the same.

    • Yann_Desmarest's avatar
      Yann_Desmarest
      Icon for Cirrus rankCirrus
      Maybe you can define a data-group of type string. The string is dirX and the value is destinationX. Then you apply a trimleft on the HTTP::path to get the last part of the uri, then check the presence of that uri part within the data-group using a command similar to if { [class match $trimuri equals "uripart_dg" ] } { HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/[class lookup $trimuri uripart_dg]$newUri" } and finally apply th
  • Maybe you can define a data-group of type string. The string is dirX and the value is destinationX. Then you apply a trimleft on the HTTP::path to get the last part of the uri, then check the presence of that uri part within the data-group using a command similar to :

    if { [class match $trimuri equals "uripart_dg" ] } {
        HTTP::respond 301 Location "http://www.newdomain.com/fr-FR/[class lookup $trimuri uripart_dg]$newUri"
    }
    
  • I believe that the data group answer is the best one if you are in fact not performing globbing. Here is how I would do it (though not tested!):

    tmsh create ltm data-group internal redirector-base type string { records add { \
        "/file1.html" { data "http://www.newdomain.com/en-US/destination1" } \
        "/dir1" { data "http://www.newdomain.com/en-US/destination2" } \
        "/dir1/" { data "http://www.newdomain.com/en-US/destination2" } \
        ... etc ...
        } }
    

    (You could -- and probably should -- do this with an imported external data-group, but I use an internal group for illustration).

    Then the rule:

    when HTTP_REQUEST {
        set base [class lookup "[HTTP::path]" redirector-base]
        
        if { $base ne "" } {
             Extract URL parameters
            set urlPath "[string tolower [HTTP::path]]?"
            if {[string tolower [HTTP::uri]] contains "?"} {
                set newUri [string map -nocase [list $urlPath "?"] [HTTP::uri]]
            }
            else {
                set newUri ""
            }
    
            HTTP::respond 301 Location "$base$newURI" 
        }
    }
    

    Note a few things:

    1. This will not work if you need to employ globbing (though, strictly speaking, you could iterate through a list of glob matchers that are keys in a data-group, but that may be [considerably] less efficient than the switch);
    2. I moved the
      $newURI
      computation inside of the conditional. If there is no match, there's no point in performing the calculation;
    3. You use a pattern that I very commonly see, namely:
      [string tolower [HTTP::path]]
      . While it is true that some filesystems are case-insensitive, RFC 2616 states that everything but the scheme (i.e., the leading 'http' in this case) and the hostname should be treated in a case-sensitive manner. See RFC 2616 3.2.3 [1]. As a practical matter, "flattening" the case of the path costs CPU cycles and I believe it to be of no practical value, especially if users are not typically typing in the uri-path part, but rather they are getting to these locations via hyperlinks.

    [1] Specifically:

       When comparing two URIs to decide if they match or not, a client
       SHOULD use a case-sensitive octet-by-octet comparison of the entire
       URIs, with these exceptions:
          - A port that is empty or not given is equivalent to the default
            port for that URI-reference;
          - Comparisons of host names MUST be case-insensitive;
          - Comparisons of scheme names MUST be case-insensitive;
          - An empty abs_path is equivalent to an abs_path of "/".