Forum Discussion

BigIP_Support_9's avatar
BigIP_Support_9
Icon for Nimbostratus rankNimbostratus
Nov 17, 2005

forward URI with manipulation

We have an application with several modules for each dept. Each module is handled by a dedicated server. The application use URI to identify the module:

http://www.abc.com/sales/func1/index.jsp (will forward to a pool with member 10.1.1.1:7780)

http://www.abc.com/cs/func1/index.jsp (will forward to a pool with member 10.1.1.1:7790)

http://www.abc.com/it/func1/index.jsp (will forward to a pool with member 10.1.1.1:7800)

http://www.abc.com/logistics/func1/index.jsp (will forward to a pool with member 10.1.1.1:7810)

I use below to identify the modules

when HTTP_REQUEST {
  if { [HTTP::uri] starts_with "/sales" } {
     pool sales_pool
  } else {
    if { [HTTP::uri] starts_with "/cs" } {
       pool cs_pool
    } else {
       ...
    }
  }
}

However all the servers are running Oracle 9ias with context root func1. They don't have a root path begins with "sales", "cs", "it", or "logistics". They only accept

http://10.1.1.1:7780/func1/index.jsp

http://10.1.1.1:7790/func1/index.jsp

http://10.1.1.1:7800/func1/index.jsp

http://10.1.1.1:7810/func1/index.jsp

When I use the above irule to forward to the server pool, the URI is kept unchanged (eg GET /sales/func1/index.jsp HTTP/1.1), which is "page not found" on the servers. I need to remove the first level folder in the URI.

Is there a way to manipulate the URI before I forward it to corresponding pools?
  • This is actually easier than it sounds. You can use the standard TCL string command (Click here) to extract everything including and past the second slash. Then use the HTTP::uri command to change the URI. I'm sure there are other ways to do this but this should work for you.

     

     

    Also, In 9.x we support elseif's which should make your code a bit cleaner.

     

     

    when HTTP_REQUEST {
       strip first directory from uri.
       ie. /foo/bar/foobar.ext -> /bar/foobar.ext
      set sub_uri [string range [HTTP::uri] [string first {/} [HTTP::uri] 1] end]
      if { [HTTP::uri] starts_with "/sales" } {
         pool sales_pool
         HTTP::uri $sub_uri
      } elseif { [HTTP::uri] starts_with "/cs" } {
         pool cs_pool
         HTTP::uri $sub_uri
      } else {
          do something else
      }
    }

     

     

    Here are the docs for the string commands I'm using.

     

     

    string first string1 string2 ?startIndex?

     

    Search string2 for a sequence of characters that exactly match the characters in string1. If found, return the index of the first character in the first such match within string2. If not found, return -1. If startIndex is specified (in any of the forms accepted by the index method), then the search is constrained to start with the character in string2 specified by the index.

     

     

    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.

     

     

    Here's a break down of the command

     

     

    set sub_uri [string range [HTTP::uri] [string first {/} [HTTP::uri] 1] end]

     

     

    is equivalent to:

     

     Find index of first slash searching from position 1 (2nd char)
    set idx [string first {/} [HTTP::uri] 1]
     Get substring from uri starting at location of second slash to the end
    set sub_uri [string range [HTTP::uri] $idx end]

     

     

    just without the overhead of the temporary variable.

     

     

    It shouldn't be necessary to check the return value of the string first command because it will return -1 if the uri doesn't have a second slash and then the subsequent string range command will return the entire string.

     

     

    I'll leave it to others to post alternate implementations.

     

     

    Good luck!

     

     

    -Joe
  • Thanks Joe. There is a minor issue with the URI manipulation. Below both URL should have the first directory removed, but only the second works.

    http://www.abc.com/sales

    http://www.abc.com/sales/

    More, if the server page has absolute path eg

    ....
    ....

    the "ink href" and "img src" will not be accessible, since user need to access with "/sales" pre-pended. Is there any way to modify the server page data to insert the first directory?
  • Ok, you didn't mention that you needed the directory truncated if there wasn't a second slash. My previous example assumed that if there was only one directory, then it would remain.

     

     

    Here's some code that should do the trick

     

     

    when HTTP_REQUEST {
      set idx [string first {/} [HTTP::uri] 1]
      if { $idx != -1 } {
        set sub_uri [string range [HTTP::uri] $idx end]
      } else {
        set sub_uri {/}
      }
      ...
      HTTP::uri $sub_uri
    }

     

     

    This should result in the following transformations

     

     

    /foo -> /

     

    /foo/ -> /

     

    /foo/bar -> /bar

     

    /foo/bar.html -> /bar.html

     

     

     

    Now, if you need to modify the response content to "re-add" your prefixes, we've covered this before. Check out my iRules entries on my blog

     

     

    http://devcentral.f5.com/weblogs/joe/category/27.aspx?Show=All

     

    Click here

     

     

    Check out the "modify uri and rewrite" entry. I think that has what you are looking for.

     

     

    Also, with 9.2 you can use the stream profile to accomplish the same thing in a more efficient manner.

     

     

    -Joe
  • Sorry that I can't find the item "modify uri and rewrite" in the list. Can you give me more details? Thansk.
  • I think the one we can use is the last one with "RESPONSE_DATA".

    However as you can see that the http data doesn't contain the FQDN, it just contain a root, like "link href" and "img src" below

    ........

    it is too risky to replace all "/ to "/sales/

    more, by search and replace we may miss some cases like composed in javascript ...

    any idea?
  • The example I provided does replace the FQDN and not the URI, but the concept is the same. Basically all we can do within the response data is do straight string replacement. You can do some smart parsing with the content looking for link, img, a, etc elements and then look for the corresponding href's or src's. Then extract the contents within. Basically if you get to this point you are building your own HTML parser within iRules which will more than likely be a big performance hog.

     

     

    Unless you know a full list of strings to look for that you are going to change, it's going to be difficult to make it fool proof.

     

     

    Good luck and let us know how it goes...

     

     

    -Joe