Forum Discussion

BlurredVision_1's avatar
BlurredVision_1
Icon for Nimbostratus rankNimbostratus
Mar 11, 2008

HTTP Double Dipping...

All,

I know this is a crazy corner case, and that there are probably 100 ways to do this with the app, the application guys are looking for some f5 lovin to get this working as a short term workaround (I have been talking up iRules for so long, I think they decided to make me put my money where my mouth is!!)

I have an application owner who needs to run extended QA n the new version of a server while maintaining the old version as the production system for a while. Long story short, it is a web services environment that they need to run 2 versions of the application side by side on the same data stream. The aim is to have the same WS transactions run against V1 and V2 one after the other, but for V1 to be the one that is actually carried back to the requesting server. (ie: to start with V1 is authorative, V2 is run, but the responses are ignored)

So far the config has a VS with a 2 pools of 1 server (v1 and v2).

Using an iRule, we have managed to get the following workflow:

iRule INIT

set loop 0

set lb_run 0

when HTTP_REQUEST

UNCHUNK

stuff HTTP::request into a var

if loop == 0

select pool V2

if loop == 1

select pool V1

when HTTP_RESPONSE

if loop == 0

incr loop

HTTP::retry $var

when LB_SELECTED

if loop == 0

if lb_run == 0

LB::reselect pool v2

set lb_run 1

else

LB::reselect pool v1

I know there is some weirdness in the logic of the flow, but I am still not 100% sure how the rule events hang topgether.

output of this rule runs correctly the first time, but as the key vars are established in irule init, it gets stuck in the second iteration as the deafult state.

I tried to reset the loop and lb_run counters in various places, but it ends up looping.

Where should I be looking to reset the state of the loops to get it to run properly again.

Full iRule as follows:


when RULE_INIT {
 outputs useful information to logs when debug is set to 1
    set ::debug 1
set ::first_lb_selected 0
set ::iteration 0
}
when HTTP_REQUEST {
 unchunk
   HTTP::version "1.0"
   if { $::iteration == 0} {
   set creq [HTTP::request]
   if { $::debug } { log local0. "HTTP_REQ at ::iteration 0" }
   pool Pool-V2
}
   if { $::iteration == 1} {
   if { $::debug } { log local0. "HTTP_REQ at ::iteration 1" }
   pool Pool-V1
}
}
when HTTP_RESPONSE { 
if { $::debug } { log local0. "HTTP_RES triggered" }
if { $::iteration == 0 } {
if { $::debug } { log local0. "HTTP_RES at ::iteration 0" }
if { $::debug } { log local0. "HTTP_RES lb detach:" }
LB::detach
if { $::debug } { log local0. "HTTP_RES set ::iteration to 1" }
set ::iteration 1
if { $::debug } { log local0. "HTTP_RES retrying" }
HTTP::retry $creq
}
if { $::debug } { log local0. "This is the last line!" }
if { $::first_lb_selected == 1 && $::iteration == 1 }{
set ::first_lb_selected 0
set ::iteration 0
}
}
when LB_SELECTED {
if { $::debug } { log local0. "LB::server is [LB::server addr]" }
  if { $::iteration == 0 } {
if { $::first_lb_selected == 0} {
   if { $::debug } { log local0. "LB_Selected at ::iteration 0 setting GP10" }
   LB::reselect pool V2
set ::first_lb_selected 1
}
} else {
if { $::debug } { log local0. "LB_Selected at ::iteration 0 setting GP7" }
   LB::reselect pool V1
}
}

3 Replies

  • Hi,

     

     

    variables defined in RULE_INIT are considered global variables, which means you will have the same variable's value for ALL connections going through this iRule i.e they will share the same variable.

     

     

    if you need to have a variable which is unique for a connection then it must be a local variables. local variable will be reacheable from any event.

     

     

    So i would recommend to rewrite your code this way:

     

     

    when RULE_INIT {

     

    outputs useful information to logs when debug is set to 1

     

    set ::debug 1

     

    set ::first_lb_selected 0

     

    set ::iteration 0

     

    }

     

     

    should become:

     

     

    when RULE_INIT {

     

    outputs useful information to logs when debug is set to 1

     

    set ::debug 1

     

    }

     

     

    when CLIENT_ACCEPTED {

     

    set first_lb_selected 0

     

    set iteration 0

     

    }

     

     

    This way each connection will have its own iteration and first_lb_selected variable

     

     

     

     

    I did something similar where i had to do the following:

     

     

    -Send the request on poolV1

     

    -if poolv1 response a 200 then it's a valid event then forward it to poolv2 which is the real server. If the http status is something different then do reject:

     

     

    when CLIENT_ACCEPTED {

     

    set retrying 0

     

    }

     

     

    when HTTP_REQUEST {

     

    set uri [HTTP::uri]

     

    set request [HTTP::request]

     

    log local0. "HTTP request: $request"

     

    if {[HTTP::method] eq "POST"} {

     

    pool pool_v2

     

    }

     

    elseif { $retrying eq "1" } {

     

    pool pool_v2

     

    log local0. "sending request to [LB::server pool]"

     

    } else {

     

    pool pool_v1

     

    log local0. "sending request to [LB::server pool]"

     

    }

     

    }

     

     

     

     

    when HTTP_RESPONSE {

     

    log local0. "HTTP_RESPONSE: status [HTTP::status]"

     

    if { [LB::server pool] eq "poolv2" } {

     

    log local0. "Response coming from real server ok we reinit the variable"

     

    set retrying 0

     

    return

     

    } elseif { ([HTTP::status] starts_with "2") or ([HTTP::status] starts_with "3") } {

     

    log local0. " Content ok send it to the real pool..."

     

    set retrying 1

     

    HTTP::retry $request

     

    } else {

     

    log local0. "Response denied access, we discard the connection"

     

    reject

     

    }

     

    }

     

     

    I face an issue with v9.4.X where some specific request make the BIGIP hang for the response. I'll keep you in touch when i'll have the response from the support (looks like it doesn't work properly with POST request)
  • Worked a charm.. here is the final iRule for the annals of history:

    
    when RULE_INIT {
     HTTP Double Dip
     Turn logging on or off (0=off, 1=on)
    set ::debug 1
     if { $::debug } {log local0. "" } 
    }
    when CLIENT_ACCEPTED {
    set retrying 0
    if { $::debug } {log local0. "Resetting Iteration and Replay to 0"}
    } 
    when HTTP_REQUEST {
    if { $::debug } {log local0. "Entering HTTP_REQUEST" }
     Unchunk
    set HTTP::version "1.0"
    if { $::debug } {log local0. "Setting HTTP version 1.0" } 
     Store the request into a variable
    set request [HTTP::request]
    if { $::debug } {log local0. " HTTP Request: $request" } 
     check to see if replay trigger is set
    if { $retrying == 0 } {
     If replay trigger *IS NOT* set, play the connection to V2 first
    pool Pool-V2
    if { $::debug } {log local0. "Retrying = $retrying, sending request to [LB::server pool] " } 
    } elseif { $retrying == 1} {
     If replay trigger *IS* set, play the connection to V1
    pool Pool-V1
    if { $::debug } {log local0. "Retrying = $retrying, sending request to [LB::server pool] " } 
    }
    if { $::debug } {log local0. "Leaving HTTP_REQUEST" }
    }
    when HTTP_RESPONSE {
    if { $::debug } {log local0. "Enternig HTTP_RESPONSE" }
    if { $::debug } {log local0. "HTTP Response from [LB::server pool] of [HTTP::status]" } 
     Check to see if we are set to play to V1
    if { $::debug } {log local0. "Starting Retrying assesment." } 
    if { [ LB::server pool] eq "Pool-V1" } {
    if { $::debug } {log local0. "Response is from [ LB::server pool ] " }
    set retrying 0
    return
    } else {
    set retrying 1
    if { $::debug } {log local0. "Retrying = $retrying" } 
    if { $::debug } {log local0. "Initiating HTTP::retry" } 
    HTTP::retry $request
    }
    if { $::debug } {log local0. "Exiting HTTP_RESPONSE" 
    }
  • Hi,

     

     

    Just a warning,

     

     

    This won't work for POST request since you'll lose the parameters sent by the client