Forum Discussion

shawno_84086's avatar
shawno_84086
Icon for Nimbostratus rankNimbostratus
May 30, 2007

Realtional URI rewrites

I am completely new to irules and I would like some validation, or perhaps a better way to do what I want.

I am deploying a new website that uses a completely new set of URIs. All of the previous URIs do not exist on the new site, but the data is still there using a completely different uri. There are numerous links to these pages out on the web and we would like to preserve their funcitonality.

So, I have a table of begin values and end values.

eg:

OLD NEW

abc xyz

bcd wxy

and i would like them to perform as such:

www.site.com/abc > www.site.com/xyz

www.site.com/bcd > www.site.com/wxy

I have read that you can not use relational data in classes creating irules, so I am guessing that I need an irule looking like this:


when HTTP_REQUEST 
{ if { [HTTP::uri] contains "/abc" } { HTTP::respond 301 Location "http://www.mydomain.com/xyz" }
{ if { [HTTP::uri] contains "/bcd" } { HTTP::respond 301 Location "http://www.mydomain.com/wxy" }
}

Let's just say I have 1000 such URLs that I need to redirect like this and I am not sure of the performance impact of such a large irule, not to mention the managability of it.

Thank you for your help!

Shawn
  • I couldn't quite infer whether those mappings are static or dynamically generated, but if they are static, you can easily create a class for that:

    
    class myURImappings {
     "abc xyz"
     "bcd wxy"
     "cde vwx"
     "def uvw"
    }

    Then your rule is quite simple:

    
    when HTTP_REQUEST {
      if { [matchclass [string tolower [HTTP::uri]] starts_with $::myURImappings] } {
        HTTP::respond 301 Location "http://www.mydomain.com/[findclass [string tolower[HTTP::uri] $::myURImappings " "]"
      }
    }
  • Thank you.

     

     

    Yes the mappings are static.

     

     

    Can you foresee any problems related to the number of mappings?

     

     

    If I had 10,000 mappings would I realize any impact on non-redirected URLs?

     

     

    Shawn
  • There certainly will be an impact, but to what degree depends on several factors, including device hardware, traffic patterns, where the match occurs in the class, etc. I haven’t tested anything to this scale, so I’d recommend doing that so you know where your performance thresholds are.
  • Posted By citizen_elah on 05/31/2007 6:23 AM

     

     

    I couldn't quite infer whether those mappings are static or dynamically generated, but if they are static, you can easily create a class for that:

     

     

    
    class myURImappings {
     "abc xyz"
     "bcd wxy"
     "cde vwx"
     "def uvw"
    }

     

     

    Then your rule is quite simple:

     

     

    
    when HTTP_REQUEST {
      if { [matchclass [string tolower [HTTP::uri]] starts_with $::myURImappings] } {
        HTTP::respond 301 Location "http://www.mydomain.com/[findclass [string tolower[HTTP::uri] $::myURImappings " "]"
      }
    }

     

     

     

    That won't quite work because the value of HTTP::uri will always start with a slash. Also, your findclass call is assuming that the URI equals the value in the class, but the originally supplied code used the contains operator.

     

     

    We don't have measurements for class lists of 10000+ entries, but you'll have to expect that it will have a performance impact (you can't get 10000 string comparisons for free).

     

     

    If you can supply some real examples of what you are trying to map from/to there might be a way to use some tricks to limit the number of mappings needed. For instance, if you need to map:

     

     

    /aaa/foo -> /ZZZ/foo

     

    /aaa/bar -> /ZZZ/bar

     

    ...

     

     

     

    Then you can do some string splitting and just extract the first element of the URI and look that up, then dynamically build the redirect/respond retaining the original trailing section of the URI. But without examples/requirements I don't know if that will work for you.

     

     

    -Joe
  • Rumor has it that switches are faster, I don't know this for a fact, however I do use switches wherever possible and have not run into any problems yet 😄

    
    when HTTP_REQUEST {
     switch -glob [string tolower [HTTP::uri]] {
      "/abc*" { HTTP::respond 301 Location "http://www.mydomain.com/xyz" }
      "/bcd*" { HTTP::respond 301 Location "http://www.mydomain.com/wxy" }
      "/cde*" { HTTP::respond 301 Location "http://www.mydomain.com/vwx" }
      "/def*" { HTTP::respond 301 Location "http://www.mydomain.com/uvw" }
     }
    }
  • Forgot the leading slash on my class example, good catch, Joe. I did assume the requirement was a URI starting with the pattern, not containing it.

     

     

  • There is really no simple correlation between the previous URIs and the new ones. The previous ones used named IDs and the new ones use numbered ids.

     

    here is an example:

     

     

    /productgridview.aspx?pCatID=$ProductCategory&cCatID=$CategoryDefinition >

     

     

    /Pages/ProductGridView/ProductGridView.aspx?N=126+4294959927+20

     

     

    They are all going to be in relatively this format - pCatID and cCatID are being replaced with an N value.

     

     

    /productgridview.aspx?pCatID=$ProductCategory1&cCatID=$CategoryDefinition1 -> /Pages/ProductGridView/ProductGridView.aspx?N=126+4294959927+20

     

    /productgridview.aspx?pCatID=$ProductCategory1&cCatID=$CategoryDefinition2 -> /Pages/ProductGridView/ProductGridView.aspx?N=126+4294959927+26

     

    /productgridview.aspx?pCatID=$ProductCategory2&cCatID=$CategoryDefinition3 -> /Pages/ProductGridView/ProductGridView.aspx?N=129+24+4294959919

     

    /productgridview.aspx?pCatID=$ProductCategory2&cCatID=$CategoryDefinition4 -> /Pages/ProductGridView/ProductGridView.aspx?N=129+36

     

     

    From this we could match $ProductCategory1 to 126, and $CategoryDefinition1 to 4294959927+20

     

    I'm not sure how complex this would get to create an N value from the incoming URI, honestly I would rather not do this.

     

     

    The /productgridview.aspx and /Pages/ProductGridView/ProductGridView.aspx are going to be the same though.

     

     

    Can we only do a compare/replace on incoming requests that start with /productgridview.aspx ?

     

     

    We have new BigIP 6400s, I'm hoping that they are powerful enough to do this in realtime.

     

     

  • This irule is missing a close bracket so I added it, and now my irule is:

    
    when HTTP_REQUEST {
      if { [matchclass [string tolower [HTTP::uri]] starts_with $::redirect] } {
        HTTP::respond 301 Location "http://www.mydomain.com/[findclass [string tolower [HTTP::uri]] $::redirect]"
      }
    }

    This was really just a guess at where the close bracket should go, and I'm not sure why there were the 2 trailing quotation marks, but I tried with and without them, to no avail.

    The irule loaded OK in the gui, and I have added it as a resource to my virtual server, but it does not seem to be doing anything. Examination of http traffic does not show a redirect taking place.

    I contacted support and they said they are unable to help me and that devcentral is the correct resource.

    Thank you for your help.

    Shawn
  • Deb_Allen_18's avatar
    Deb_Allen_18
    Historic F5 Account
    Hi Shawn --

    I think the best approach would be to rewrite the path and each query parameter separately, then redirect using the reconstructed URI.

    To re-write the query parameters, I'd start by creating 2 classes, one containing the map for pCatID and the other for cCatID:
    class pCatIDs {
      "$ProductCategory1     126"
      "$ProductCategory2     129"
    }
    class cCatIDs {
      "$CategoryDefinition1  4294959927+20"
      "$CategoryDefinition2  4294959927+26"
      "$CategoryDefinition3  24+4294959919"
      "$CategoryDefinition4  36"
    }

    The path is static, so no class required there. Here's the iRule:
    when HTTP_REQUEST {
       only process the desired requests
      if {[HTTP::path] == "/productgridview.aspx" }{
        set qstring [HTTP::query]
        log local0. "original qstring = >$qstring<"
         find match for pCatID, replace if found
        set pCatID1 [findstr "pCatID=" [HTTP::query] 7 "&"]
        if {$pCatID1 != ""}{
          set pCatID2 [findclass $pCatID1 $::pCatIDs " "]
          if {$pCatID2 != ""}{
            set qstring [string map [list $pCatID1 $pCatID2] $qstring]
            log local0. "qstring after pCatID relacement = >$qstring<"
          }
        }
         find match for cCatID, replace if found
        set cCatID1 [findstr "cCatID=" [HTTP::query] 7 "&"]
        if {$cCatID1 != ""}{
          set cCatID2 [findclass $cCatID1 $::cCatIDs " "]
          if {$cCatID2 != ""}{
            set qstring [string map [list $cCatID1 $cCatID2] $qstring]
            log local0. "qstring after cCatID relacement = >$qstring<"
          }
        }
        HTTP::respond 301 Location "http://[HTTP::host]/Pages/ProductGridView/ProductGridView.aspx$qstring"
      }
    }
    I think the string map/list construct will handle the "$" in the query parameters, if not you might have to do some special handling of that character -- added some logging to help out there, so look in the log for those values or runtime errors regarding non-existent variables.

    HTH

    /deb