Forum Discussion

dubdub's avatar
dubdub
Icon for Nimbostratus rankNimbostratus
Jun 15, 2011

iRule to intercept traffic and then send on to original pool

Hi all,

 

 

I refuse to believe this is impossible, so I just need some advice on how to make it happen!

 

 

I am trying to create an iRule to intercept certain requests for additional processing before ultimately sending them on to their original target. The main application server tier is a data warehouse application. Users of the data warehouse app must be in a specific AD security group in order to access it. A separate web application (on a completely different environment) has been created that will handle this group processing - it will check if the user is already in the security group, and if not, it will add them. Once the user has established a session with the data warehouse, I can bypass sending any requests to the web application because I'll know that user has already been added. The session can be verified by looking for the jsessionid value.

 

 

So, this is the general flow I need:

 

 

if the jsessionid exists {

 

send to original pool, no special handling required

 

} else {

 

if the pool for the web application is up {

 

preserve the original URL somehow

 

invoke the web application

 

} else {

 

send to the original pool; the user may already be a member of the group and have access - we don't want to prevent them getting through just because the web app may be unavailable

 

}

 

}

 

 

I've tried a number of combinations of HTTP headers, cookies, redirects, responds, etc. in an attempt to get the intercept working. It seems that getting the flow back from the web application and sent onto the data warehouse within the same connection is not likely to happen, so I was hoping to send the original URL along to the web app, and have the web app actually do the redirection to the original data warehouse URL. However, the URLs can be unbelievably long, and I could seriously run into the URL limit boundary if I try to pass the original URL as a query parameter to the web application. I tried passing the URL as an HTTP header and as a cookie - neither seems to survive its transit into the web app (where right now we are dumping all header variables, cookies, etc. to the browser just as a gut check).

 

 

Any other suggestions on how I can get this URL down to the web app? Or is there a completely better way to approach this?

 

 

Thanks,

 

Jen
  • Hi Jen,

     

     

    Can you post the actual rule you've tested and debug logs showing the issue? If you need to save the original request you should be able to save the original request headers, send the first request to the pool for requests with no JSESSIONID and then use HTTP::retry to retry the original request once the prerequisite request is done.

     

     

    You can check the HTTP::retry wiki page and this article for details:

     

     

    http://devcentral.f5.com/wiki/default.aspx/iRules/http__retry

     

     

    Conditioning iRule Logic on External Information - 01 - HTTP::retry

     

    http://devcentral.f5.com/Default.aspx?tabid=63&articleType=ArticleView&articleId=105

     

     

    Aaron
  • dubdub's avatar
    dubdub
    Icon for Nimbostratus rankNimbostratus
    Hi Aaron,

     

     

    Thanks much for the tip on HTTP::retry, here is my latest effort:

     

     

    --------

     

    when RULE_INIT {

     

    set static::debug 1

     

    }

     

     

    when CLIENT_ACCEPTED {

     

    if { $static::debug != 0 } {

     

    log local0. "setting lookup to 1"

     

    }

     

    set lookup 1

     

    }

     

     

    when HTTP_REQUEST {

     

    if { [HTTP::cookie "JSESSIONID"] ne "" } {

     

    if { $static::debug != 0 } {

     

    log local0. "got jsession id, sending to DW"

     

    }

     

    pool pool_DW

     

    } else {

     

    if { $static::debug != 0 } {

     

    log local0. "no session id, sending to WF for url [HTTP::host][HTTP::uri]"

     

    }

     

    if { [active_members pool_WF] > 0 and ($lookup == 1) } {

     

    if { $static::debug != 0 } {

     

    log local0. "lookup is $lookup"

     

    log local0. "redirecting to WF, saving request first"

     

    }

     

    set myRequest [HTTP::request]

     

    HTTP::uri "/DW_add_user_app"

     

    pool pool_WF

     

    } else {

     

    if { $static::debug != 0 } {

     

    log local0. "WF pool unavailable or lookup is $lookup, sending to DW"

     

    }

     

    pool pool_DW

     

    }

     

    }

     

    }

     

     

    when HTTP_RESPONSE {

     

    if {[HTTP::header exists Content-Length] && \

     

    ([HTTP::header Content-Length] < 1048576)}{

     

    set clength [HTTP::header Content-Length]

     

    } else {

     

    set clength 1048576

     

    }

     

    if { $static::debug != 0 } {

     

    log local0. "collecting clength $clength"

     

    }

     

    HTTP::collect $clength

     

    }

     

     

    when HTTP_RESPONSE_DATA {

     

    if { $static::debug != 0 } {

     

    log local0. "got a reply back from web app, retrying request"

     

    }

     

    pool pool_DW

     

    HTTP::retry $myRequest

     

    set lookup 0

     

    }

     

    -----------

     

     

    And the output:

     

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : setting lookup to 1

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : no session id, sending to WF for url dwtestvip/

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : lookup is 1

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : redirecting to WF, saving request first

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : collecting clength 153

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : got a reply back from web app, retrying request

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : no session id, sending to WF for url dwtestvip/

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : WF pool unavailable or lookup is 0, sending to DW

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : collecting clength 1048576

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : no session id, sending to WF for url dwtestvip/InfoApp

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : WF pool unavailable or lookup is 0, sending to DW

     

    Jun 15 20:38:25 local/tmm1 info tmm1[6449]: Rule DWIntercept : collecting clength 1048576

     

     

    At this point the browser is just spinning. I'm not sure if it's waiting on collecting more data, maybe 1MB isn't enough? I tried increasing it to 10MB but I saw the documentation on HTTP::collect cautioned about stalling the connection. Hitting the root VIP normally results in a redirect to VIP/InfoApp, so there's some redirection going on at the data warehouse app as well, just to make things more interesting.

     

     

    Thanks,

     

    Jen
  • dubdub's avatar
    dubdub
    Icon for Nimbostratus rankNimbostratus
    Just to follow up... finally got this working, this is the final iRule, which is a variation on the excellent ideas suggested in http://devcentral.f5.com/Community/GroupDetails/tabid/1082223/asg/50/aft/14001/showtab/groupforums/Default.aspx.

     

     

    when RULE_INIT {

     

    set static::debug 1

     

    }

     

     

    when CLIENT_ACCEPTED {

     

    if { $static::debug != 0 } {

     

    log local0. "setting addingUser to 1"

     

    }

     

    set addingUser 1

     

    }

     

     

    when HTTP_REQUEST {

     

    save original request

     

    if { $addingUser == 1 } {

     

    if { $static::debug != 0 } {

     

    log local0. "saving request, redirecting to WebFarm"

     

    }

     

    set req [HTTP::request]

     

    inject lookup URI in place of original request

     

    HTTP::uri "/addDWUser"

     

    set pool to webfarm pool

     

    pool pool_WebFarm

     

    }

     

    }

     

     

    when HTTP_RESPONSE {

     

    if {$addingUser == 1 } {

     

    collect first response (from lookup server) only

     

    if {[HTTP::header exists Content-Length] && ([HTTP::header Content-Length] < 1048576) } {

     

    set clength [HTTP::header Content-Length]

     

    } else {

     

    set clength 1048576

     

    }

     

    if { $static::debug != 0 } {

     

    log local0. "collecting response presumably from WebFarm, length is $clength"

     

    }

     

    HTTP::collect $clength

     

    }

     

    }

     

     

    when HTTP_RESPONSE_DATA {

     

    Get poolname from server response

     

    Response would ideally be in the form of a pool name only.

     

    Otherwise parse or derive the poolname here

     

    set myPool pool_DataWarehouse

     

    verify pool exists and has members

     

    if { ![catch [pool $myPool]] }{

     

    if { $static::debug != 0 } {

     

    log local0. "retrying request"

     

    }

     

    pool pool_DataWarehouse

     

    HTTP::retry $req

     

    } else {

     

     

    insert dead/non-existent pool logic here

     

     

    }

     

    re-set flag so that subsequent response to re-tried

     

    request from real server is not processed as a lookup

     

    if { $static::debug != 0 } {

     

    log local0. "setting addingUser to 0"

     

    }

     

    set addingUser 0

     

    }

     

     

    Thanks!

     

    Jen
  • Nice work in figuring that out. Thanks for posting your rule.

     

     

    Aaron
  • Just a note on optimizations...I was curious about the efficiency of $static::debug != 0 versus $static::debug == 1 versus just $static::debug...here's how it worked out in my testing:

    
    1. 37166 cycles on average over 5000 requests from (ab -n 5000 -c 25 http://172.16.101.85/) 
    if { $static::debug != 0 } {} 
    2. 34341 cycles on average over 5000 requests from (ab -n 5000 -c 25 http://172.16.101.85/) if { $static::debug == 1 } {} 3. 29034 cycles on average over 5000 requests from (ab -n 5000 -c 25 http://172.16.101.85/): 
    if { $static::debug } {} 

    A 21.9% savings by using the 3rd option as opposed to the 1st!

  • The last one is the simplest, but it's great to hear that it's also the most efficient. Thanks for testing this.

     

     

    Aaron