Forum Discussion

wlepkin_98758's avatar
wlepkin_98758
Icon for Nimbostratus rankNimbostratus
Nov 27, 2013

Redirect works but rewriting the URI does not

Hi, we have a perplexing problem that I hope someone can help with.

We have an app that's used by a number of customers. Unfortunately, new customers were added one at a time, and instead of using the same application stack for multiple customers, a new Apache/Tomcat stack was set up for each customer, along with a new URL and new F5 virtual server. So from a customer's perspective the URL is:

https://appname-customer1.company.com

or

https://appname-customer2.company.com

...etc, with no path after the hostname. The F5 virtual server for each customer uses the same iRule that adds a path (the same for each customer) by doing a redirect if the path is just "/", and when we then come back through the vip we send it off to the appropriate pool for that customer. The current iRule is:

if { [HTTP::path] eq "/" } then
      {
      HTTP::redirect https://[HTTP::host]/path-the-app-needs
      }

So pool1 gets:

http://appname-customer1.company.com:port1/path-the-app-needs

and pool2 gets

http://appname-customer2.company.com:port2/path-the-app-needs

...etc.

This is rather inefficient, even ignoring the separate server stack for each customer, since we need a separate external IP address for each customer, a separate internal IP address for each virtual server, and a separate ssl cert for each customer URL. It would be much better to do this:

https://appname.company.com/customername

...which uses a single external IP address, a single internal vip address, and a single ssl cert, and have an iRule determine where to send traffic based on the customer name in the initial request. (I do understand that we could continue to use different URL's and different certs but a single set of IP addresses and a single VIP by using TLS SNI, but since that solution is browser dependent we've chosen not to do that.)

I've written an iRule to do the above, which does this:

1) On the initial request, with the path being the customer name, do the following:

- attempt to identify the customer

- if we have a match, change the URI to remove the customer name and add the path the app needs

- choose the right pool:port for that customer

- add cookie persistence for that pool:port, since subsequent requests don't have the customer name

2) On subsequent requests:

- if the persistence cookie is there, let the request go through to the right pool based on persistence

3) Else if no customer match or no cookie, something's wrong and we discard

I'm also logging all the activity performed by the iRule, as well as logging both the request and response headers, so I can see what's going on. The rule appears to be working correctly in tests, that is:

- the customer is correctly matched

- the URI is correctly changed to remove the customer name and add the path the app needs

- the cookie gets correctly added in the response.

But it doesn't work -- the page is not completely rendered, if at all, and we get a mixture of 202, 404, and Transport errors.

If as a test I change the iRule to mimic current production -- instead of changing the URL, I just redirect to the same vip with the added path and without the customer name, it works fine. But that will only work for one customer. We still have to be able to identify the customer on subsequent requests (which the persistence does). But when you do a redirect any persistence information is lost. I thought about adding a header field identifying the customer and persisting on that, but when we do the redirect the header information I've added is lost, just like a persistence cookie is lost when you do a redirect.

So the basic problem is that a redirect works but rewriting the URL does not, even though the logs appear to show that the resulting requests are the same in both cases.

Does anyone have any thoughts about what might be different between rewriting the path (or URI) and doing a redirect, such that the application would behave differently in the two cases, or perhaps have ideas about some tests that could be run to try to figure out the difference?

Here's the iRule I'm using:


 This iRule handles requests for the NMT application.

 The same URL and IP address, and thus virtual server, is to be used for all
 NMT customers. The customer is identified by name as the path in the initial
 request, like so:  nmt.company.com/customername
 But the path must be changed to /stuff-the-app-needs.html before sending
 to the appropriate pool member, and there is a different server:port tuple for each
 customer.

 So this rule must do the following on the first request:
 - identify the customer
 - change the URI so the app will work
 - specify the correct pool for the customer
 - add cookie persistence for that pool

 On subsequent requests, the customer name will no longer be in the URI, but
 the persistence cookie should be there. If it is, just persist using that.
 If neither a valid customer name is in the URI nor a persistence cookie exists,
 then something is wrong and we must discard the request.

when HTTP_REQUEST {
   log local0. "URI = [HTTP::uri]"
    Change to lower case so we always match, and remove leading '/' from the URI
   set uri [string trimleft [string tolower [HTTP::uri]] "/"]
   if { [class match $uri equals nmt_customer_strings ] } then
      {
       URI consists of customer name, so must be new request
       Fix the URI so it has the right path and persist on the pool for that customer
      HTTP::uri /stuff-the-app-needs.html
      pool nmt-$uri
      persist cookie insert company_nmt 1800
      log local0. "New request for customer $uri, using pool nmt-$uri"
      log local0. "Changing URI to [HTTP::uri]"
      }
   elseif { [HTTP::cookie exists "company_nmt"] } then
      {
       No customer name in URI, so continue to persist on existing cookie 'company_nmt'
      log local0. "Persistence value [HTTP::cookie value "company_nmt"]"
      log local0. "URI = [HTTP::uri]"
      }
   else
      {
      log local0. "URI does not start with valid customer name but no cookie, dropping request"
      discard
      }
   unset uri
}

Many thanks, and I apologize for the long post. Happy Thanksgiving to all!

Regards,

Wayne

1 Reply

  • Have you looked at httpwatch logs or taken a tcpdump? Might be good to see which parts are failing and in what way.