Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral


Questions and Answers

Loading... Loading...

How to disable iRule processing on keep-alive per request when using HTTP::respond


Okay, I have encountered a problem I cannot figure out how to get around.

Here is what I want to do:
1) First of all I want to keep the keep-alive connection open - I do not want to close it.
2) The web user connects to the IP for a web site and sends a request
3) the iRule catches something in the request, and send back a response via "HTTP::respond"
4) the iRule execute "event HTTP_REQUEST disable" to stop further iRule processing (important) of HTTP_REQUEST.
5) the keep-alive connection remains open
6) some how the iRule processing is enabled again for the HTTP_REQUEST event.
7) on the next web user's http request, the same iRule(s) is processed.

How do I do this?

*) I do not see how #6 can be possible, while keeping the keep-alive connection open, from everything I have read about how iRules work.
When we use HTTP::respond, the HTTP_RESPONSE event is never processed in the iRules (which is where it has been recommended to execute the "event enable" command), plus I do not see any other events being processed.

My question may be, how can I reenable the HTTP_REQUEST event for the next request on the same keep-alive connection?
But perhaps it should be, how do I turn off iRule processing on a per-request basis when using HTTP::respond?

My goal is to:
1) use the HTTP::respond - immediately returning the response to the web user
2) keep the keep-alive connection open
3) do not process any further iRules
4) allow the web user to send another request on the same keep-alive connection.
5) have the iRules process the new request coming across the same keep-alive connection from the web user.
6) be able to go back to #1 and continue this loop indefinitely.

What does NOT work:
1) the "return" command - it returns, but further iRules are still processed.
2) the "event disable" command - it disables iRules for any future events on the same keep-alive connection, and if you use HTTP::respond, then you have no opportunity to execute "event enable" at the end of the request so iRules will process the next request.
3) the "reject", "TCP::close", or other commands that close the keep-alive connection - I do not want to close the connection.
4) using global variables as flags for determining it further iRules should be processed - according to this article ( https://devcentral.f5.com/Default.aspx?tabid=63&articleType=ArticleView&articleId=236 ) "a global variable ... [is] shared by all connections"

Would anyone know what I can do to resolve this?

-RG

16 Answer(s):

Hi RG,

Once you disable the HTTP_REQUEST event it will stay disabled on that TCP connection until it's re-enabled or the connection is closed. The only thing I can think of to allow you to bypass the logic of the other iRule(s) would be to set a local variable in the primary rule. The local variable is specific to that TCP connection so you don't have to worry about trampling with global variables between connections. The other rules can then check that local variable to see whether the code in the secondary rule(s) should be run.

Aaron
Using local variables - Can you provide an example, because I tried a local variable and it did not work.

Here is my test for using a local variable:

 
irule firstirule {
when HTTP_REQUEST priority 10 {
set localVar "somevalue"
}
}

irule secondirule {
when HTTP_REQUEST priority 100 {
if { not [info exists $localVar] } {
log local0.err "Variable localVar not available. Initialized in iRule firstirule."
}
}
}


When I run code like this, I get the log message every time.
When I try to access the localVar variable in secondirule anyway, I get an error message.

-RG
info exists expects the variable name without the $:

info exists localVar

or

info exists ::globalVar

I'd suggest setting it on every request though (either to 0 or 1) depending on whether you want the other iRules' HTTP_REQUEST event code to run. That ensures that the variable is in the correct state on each request on the same TCP connection. I suppose the other option would be to unset the variable instead of setting it to 0 and then checking to see if it's defined in other rules.

Aaron
Okay,

Is there any better way to do this without putting this code in for every iRule event:

 
rule any_irule {
when ?event? {
if { not $stop_processing_irules_flag } {
# normal iRule code
}
}
}

Not that I can think of. Anyone else have any ideas?

I tried disabling HTTP_REQUEST and then re-enabling the event in HTTP_REQUEST with a priority of 501, but the 501 instance never runs after the HTTP_REQUEST event is disabled:

 
when HTTP_REQUEST {
log local0. "500 disabling"
event HTTP_REQUEST disable
log local0. "500 disabled"
}
when HTTP_REQUEST priority 501 {
log local0. "501?"
}


Log output:

<HTTP_REQUEST>: 500 disabling
<HTTP_REQUEST>: 500 disabled

So the answer on that is no. I didn't think it would actually work, but that was the only other thing I could think of. It might be nice if you could do something like this:

event HTTP_REQUEST priority 500 disable

Aaron
I was actually thinking of a few new feature possibilities.

Currently the format for the event command is:
 
event [<name>] [enable|disable] | [enable all|disable all]


(1)
The first idea is to change this to:
 
event [<name>] [enable|disable] | [enable all|disable all] [forthisconnection|forthisrequest]


Adding the option to say:

event disable all forthisrequest
or
event HTTP_REQUEST disable forthisrequest

The default would be forthisconnection, which is the current action - to disable events for the remainder of the connection.


(2)
The second idea is to change it to:
 
event [<name>] [enable|skip|disable] | [enable all|disable all]


Add the option to say:

event skip all
or
event HTTP_REQUEST skip

which would skip the remainder of the event(s), until the event is completed.


Since I must conclude there is no good way to do what I am wanting, I would really like to add something like this as a feature request.

-RG
Actually the second suggestion is probably better as:

 
event [<name>] [enable|disable|end] | [enable all|disable all]


So when saying something like:

event HTTP_REQUEST end
or
event end all

Would cause the named event, or all events, to end immediately.
This would also mean that if you want to stop iRule processing, you have to add in the return command so you exit out of the current iRule.


Also, I like your suggestion, skipping ahead to a different priority level.
That is like a 'goto' command.


I have read how from one iRule we can cause the execution of another iRule.
That seems like a possibility to solve this issue, however, I hate the idea of chaining (stacked) iRules together from inside the code.

-RG
The only better thing I can think of is to do a return if that flag is set.
Something like:
   
rule first_rule {
when HTTP_REQUEST {
set stop_processing 0

if { $condition } {
HTTP::respond "foo"
set stop_processing 1
}
}
}
rule second_rule {
when HTTP_REQUEST {
if { $stop_processing } {
return
}
# do other stuff here
}
}

It looks a bit cleaner, IMO. I think that's about the best you can do.

Yes, but I'd like to avoid doing the "if" clause.

We have over 120 different web site, and they all use various iRules. I have tried hard to consolidate iRules so make everything uniform, and I think I have done a good job. But a lot of iRules are still sharing among virtual servers.

So using "if" is going to complicate a lot.

I am experimenting with different ways of stacking iRules now, using priority levels, to see if I can do anything similar.


I still prefer something better like:

event <skip the remainder of this event to the end>

Sot the event ends, but the event is not disabled.

-RG
Hi RG,

Spark is a developer with F5, so he can provide very good feedback. But if you want to create an official request for enhancement, you should open a case with F5 Support.

Aaron
The only other thing I can think of that even remotely has a chance of working is to add an HTTP class that matches every request, run "event disable" in HTTP_REQUEST like you were doing before, and then re-enable HTTP_REQUEST (via "event enable") in the
HTTP_CLASS_SELECTED event. I can't test this right now, but it just might work.
I have an open case already.
C498394
The support guy is fantastic!
He hinted I try over here for a possible solution first.
Now I am back at the case trying to write out a feature request.


Can someone tell me how the iRule events are execute during a connection? Yes, I thought about what spark is suggesting, but I did not know if there was an event fired in between each HTTP_REQUEST event on the same connection.

So what is the firing order of events?

CLIENT_CONNECT
HTTP_REQUEST
HTTP_RESPONSE (ONLY if the response comes from a node)
HTTP_REQUEST
HTTP_REQUEST
... etc..

Is there an event in between successive HTTP_REQUEST events?

If there is, then my problem is easily solvable by disabling the event in HTTP_REQUEST, and reenabling the HTTP_REQUEST event inside the execution of another in between event.

I would not have a problem right now if using HTTP::respond also caused the execution of HTTP_RESPONSE event - because I could then just reenable the HTTP_REQUEST event inside the HTTP_RESPONSE event when sending the response back to the client.

-RG
Okay, I submitted my feature request.

Here is my proposed changes:

I want to change the syntax for the "event" command.

Currently syntax is:
 
event [<name>] [enable|disable] | [enable all|disable all]


I propose that the new syntax would be:
 
event [<name>] [enable|disable|end] | [enable all|disable all|end all]


And basically, this will change the following scenario:

Current procedure for disabling an event for a single request:
1) CLIENT_CONNECT
2) HTTP_REQUEST event HTTP_REQUEST disable
3) HTTP_RESPONSE event HTTP_REQUEST enable
4) HTTP_REQUEST
5) HTTP_RESPONSE
6) <connection aborts>

New procedure to get the same exact results:
1) CLIENT_CONNECT
2) HTTP_REQUEST event HTTP_REQUEST end
3) HTTP_RESPONSE
4) HTTP_REQUEST
5) HTTP_RESPONSE
6) <connection aborts>

This is useful for functions like HTTP::respond which causes the BigIP to send a response back to the web client, but the HTTP_RESPOSE event is not being executed.

Also useful for HTTP::redirect

 
rule redirect {
when HTTP_REQUEST {
if { [HTTP::host] equals "yoursite.com" } {
HTTP::redirect http://mysite.com/
event end
return
}
elseif { HTTP::host equals "thatsite.com" } {
HTTP::response 200 content {
<html>
<META refresh="1;http://mysite.com" />
<body>We have moved to a new domain.</body>
</html>
} noserver
event end
return
}

pool mysite.com_pool
}
}


In the above code (assuming yoursite.com, thissite.com and mysite.com all resolve to the same IP number for the web browser) the keep-alive connection does not have to end in order to get sent to the mysite.com web site from one of the other URLs.

Of course this is a primitive example, this is best used in cases that have more advanced and complex uses that require the keep-alive connection to remain open.

-RG
Two things. First, I got this working using HTTP classes, but it was slightly different than I thought. My idea was also to get another event firing between two HTTP_REQUEST events, but there aren't any that occur naturally, so you have to induce one. There are a couple choices, like the NAME or AUTH stuff, but HTTP_CLASS_SELECTED sounded easiest. The problem is that if you do an HTTP::respond in HTTP_REQUEST, then HTTP_CLASS_SELECTED never fires. But you can do:
  
rule rule_one {
when HTTP_REQUEST {
set do_response 0
set condition 1

if { $condition } {
log local0. "Rule one, enabling response"
set do_response 1
event HTTP_REQUEST disable
}
}
}
rule rule_two {
when HTTP_REQUEST {
log local0. "Rule two, firing HTTP_REQUEST"
}
}
rule rule_respond {
when HTTP_CLASS_SELECTED {
log local0. "Got class match!"
if { $do_response } {
HTTP::respond 200 content { xyzabc123 }
event HTTP_REQUEST enable
}
}
}
virtual http_vip {
pool http_pool
destination 10.1.1.1:http
ip protocol tcp
rules {
rule_one
rule_two
rule_respond
}
httpclass httpclass
profiles {
http {}
tcp {}
}
}


In my tests, this will do the HTTP::respond and keep the connection open and the HTTP_REQUEST in rule_two will never fire. Obviously, you'll want something more complicated than "set condition 1" ;-).


On another note, while I understand "event [foo] end", and quite like the idea, what are you suggesting "event [foo] end all" to do? Skip all events for the remainder of the connection? Please keep in mind that "event" is a very general command that needs to apply to all the protocols that BIG-IP does or will handle, and is not limited to request/response protocols like HTTP, so "skip all events for the remainder of this request" is probably an unuseful concept.
On another note, while I understand "event [foo] end", and quite like the idea, what are you suggesting "event [foo] end all" to do? Skip all events for the remainder of the connection? Please keep in mind that "event" is a very general command that needs to apply to all the protocols that BIG-IP does or will handle, and is not limited to request/response protocols like HTTP, so "skip all events for the remainder of this request" is probably an unuseful concept.


I do not know the order of events for a request/connection, or what they are named, but bear with me while I give this example:

Say the events are this for the life of a connection and all requests on that connection:

01) CLIENT_CONNECT
02) CONNECTION_HANDSHAKE
03) EVENT_SOMETHING
04) HTTP_REQUEST
05) HTTP_RESPONSE
06) EVENT_BACKGROUND
07) EVENT_SOMETHING
08) HTTP_REQUEST
09) HTTP_RESPONSE
10) EVENT_BACKGROUND
11) CLIENT_DISCONNECT

If this would be the EXACT sequence of events, then issuing "event end all" would cause all events to end for one round of firing.

So if you issued "event end all" at event number #4 (HTTP_REQUEST), then event number 4,5,6,7 would all end immediately, and the cycle would continue at event number #8.

So in effect, you might do:
 
HTTP::redirect http://www.mysite.com/
event end all
return


Which keeps the keep-alive connection, and sets the next event to be HTTP_REQUEST again - or in other words jumps over the other events back to waiting for the client to provide an HTTP_REQUEST event.

Take for example the same events, but the client aborts the connection after getting a HTTP::redirect :

01) CLIENT_CONNECT
02) CONNECTION_HANDSHAKE
03) EVENT_SOMETHING
04) HTTP_REQUEST
05) HTTP_RESPONSE
06) EVENT_BACKGROUND
07) EVENT_SOMETHING
08) HTTP_REQUEST
... client aborts connection ...
11) CLIENT_DISCONNECT

This is how it might normally progress.
But if you know that the events EVENT_BACKGROUND and EVENT_SOMETHING are going to be executed and you don't want those events to be executed, you would need to tell the BigIP to complete this request cycle and begin at the next request cycle.


Perhaps this is not desired, or something.
Like I said, I do not know the events in between successive HTTP_REQUEST events.

But if there ARE in between events, and you want to skip through these cycle of other events that might otherwise get fired before the next HTTP_REQUEST event, then you essentially want to "even end all".

Or in other words, if I issue "event end all" in the HTTP_REQUEST event, then end every successive event until I get back to this same HTTP_REQUEST event again.


Probably "event end all" would be reserved to experts who know what they are doing.

-RG
The ability to perform "event <name> end" is all I need.
I do not need "event end all".

-RG

Your answer:

You must be logged in to reply. You can login here.