Forum Discussion

Alan_Sha's avatar
Alan_Sha
Icon for Nimbostratus rankNimbostratus
Sep 02, 2005

How can I trigger a CLIENT_DATA event manually?

I have an iRule that breaks out the client transactions coming from TCP socket and forwards them to different servers based on the content of each request.

 

 

The TCP client is working in an asynchronous mode (i.e. the client has two threads, one for sending request, another one for receiving response from the same socket where the request is sent. Therefore, the client can send a whole bunch of requests at the same time without waiting on the response. ). Because it is impossible for iRule to open multiple server connections at the same time, I have to withhold the subsequent requests at the BIG IP while I am sending one request to the destination and waiting on the response. Once I get the response, a LB::detach is issued to release the server connection so that the next request can be re-load balanced.

 

 

The problem I am having right now is that because the requests are withheld at the BIG IP buffer, the CLIENT_DATA event (where the request is being analyzed and determined where to go.) will not be triggered immediately after the response is received and the server connection is detached.

 

 

For example, the TCP client sends two requests to BIG IP at the same time, BIG IP forwards one to the destination while keeping the other one in the buffer. When I receive the response and detach the server, the CLIENT_DATA event will not be triggered until almost 20 seconds later, which means the second request is waiting on the BIG IP for 20+ seconds later before it is being processed.

 

 

My question is: is there a way I can trigger a CLIENT_DATA event right away once the first request is completed? If no, what other ways I can use to delay the requests at the BIG IP?

 

 

Thanks

 

7 Replies

  • bl0ndie_127134's avatar
    bl0ndie_127134
    Historic F5 Account
    Currently the only way to raise events from rule is to use TCP::notify. This rule is 'undocumented' and some what cumbersome to use. Check out this posting for an example of this rule in action.

     

     

    Click here

     

     

    I think you should be able to modify the USER_RESPONSE event to flush the additional client requests that you may have pipelined.

     

     

  • Thanks! I have read that posting before and written similar codes in SERVER_DATA and USER_RESPONSE event. Below is what I am using:

     

     

    when SERVER_DATA {

     

    log local0.debug "Received SERVER response ... [TCP::payload]"

     

    if { [TCP::payload] ends_with $EOT } {

     

    set detachFromSvr 1

     

    } else {

     

    set detachFromSvr 0

     

    }

     

    TCP::release

     

    TCP::collect

     

    TCP::notify response

     

    }

     

     

    when USER_RESPONSE {

     

    log local0.debug "Received USER response ... "

     

    if { $detachFromSvr equals 1 } {

     

    LB::detach

     

    log local0.debug "Detaches server connection ... "

     

    set detachFromSvr 0

     

    }

     

    }

     

     

    I have tried to call TCP::notify response or TCP::notify request in USER_RESPONSE right after LB::detach is called, but it makes no difference.

     

     

    What actually happens when those two commands are called?
  • bl0ndie_127134's avatar
    bl0ndie_127134
    Historic F5 Account
    The purpose of 'TCP::notify response' is to raise the USER_RESPONSE on the client side so that you can detach the serverside connection after all the data has been delivered to the client. You only need to call it once when you have received all the response data.

    
    when SERVER_DATA {
       log local0.debug "Received SERVER response ... [TCP::payload]"
       if { [TCP::payload] ends_with $EOT } {
          TCP::notify response
       }
       TCP::release
       TCP::collect
    }
    when USER_RESPONSE {
       LB::detach
       log local0.debug "Detaches server connection ... "
       if {[TCP::payload length] > 0} {
          
           %TODO%
           Process additional client requests here ...
          
       }
    }

    Is your response length delimited? If so, I would suggest that you change your response logic to use length counter rather than look for the magic EOT string. The longer your magic sting the greater the chances of it being segmented into multiple packets.

  • No. The response is not length delimited. Since only one response is returned at a time and the end of response is always marked with EOT, even the response is segmented into several packets, the last one should always end with EOT, unless something happens and part of the response gets lost (rare case, right?)

     

     

    What should I put in the %TODO% section within the USER_RESPONSE event? Since the main logic of analyzing and routing the request is implemented in the CLIENT_DATA event, should I copy the same piece of codes here?

     

     

    Thx
  • Yes, when the terminator size is more than 1 byte, it is not guaranteed that the terminator will always come in one packet.

     

     

    I think I can use a n bytes buffer (where n is the size of the terminator.) to keep track of last n bytes of the received response so far, which gets refreshed accordingly when a packet arrives. This way we won't miss the terminator in case it doesn't come in one packet.

     

     

    Is this the correct approach?
  • drteeth_127330's avatar
    drteeth_127330
    Historic F5 Account
    I don't think you need an explicit buffer. The data is implicitly buffered by BIG-IP and is accessible with TCP::payload. If the terminator is split across two or more TCP segments, then you simply need to scan TCP::payload for the complete terminator on each CLIENT_DATA event. Your iRule is operating on the TCP payload. There's not much need to be concerned about packets and segmentation.
  • That's true. But, in my case, the response is quite large. And, it appears that Big IP does not wait until it receives the entire response before it raises the SERVER_DATA event. Therefore, the Big IP could raise the SERVER_DATA event several times for a single response. For each time that the SERVER_DATA event is raised, I simply release the TCP payload so that it can be forwarded back to the client right away. Once the TCP payload is released, the buffered data is gone. When the SERVER_DATA event is raised again, the TCP payload will only contain the latest response data received.

     

     

    If the size of the terminator is more than 1 byte, it is possible that it is split into 2 or more SERVER_DATA events. That's why I think I need to use an explicit data.