Simulating TCP Closes From Server When Using ASM
Problem this snippet solves:
BIG-IP ASM versions 9.2.x - 9.4.1 use a feature similar to OneConnect internally between the ASM module itself and the back-side server connections. This causes close isolation to occur, so server-initiated closures are not directly propagated to the client. This is usually a good thing for throughput as we will reuse open server connections for multiple client requests, independently of how the client-side connections are handled.
Some application-specific client/server combinations rely on layer 5 TCP session management where the server will initiate a close and the client relies on this close to continue processing. This is atypical behavior from established standards in that the client is seen to send a "Connection: keep-alive" to the server, but the client has been coded with prior knowledge that the server will disconnect after certain responses and will not respond gracefully if the connection does not close (whether through implementation issues, incomplete state handling or design). The typical symptom of such an arrangement would be a time-out error in the client (which typically is a dedicated client or running java application, not a typical web browser).
Any layer 7 proxy device between the client and server can run into trouble if it tries to maintain the connection to the client even after the server closes the connection. Arguably, if the client knows which requests should cause the server to close the connection, it should send "Connection: close" and BIG-IP ASM will duly honor the close after the response is relayed back, but examples in the wild have indicated otherwise.
Since this session/transaction handling is done outside of layer 7, there is no standard way to cope with this aberrant behavior. A workaround must be put into place to close the connection to the client so that the application works normally.
In order to work through this issue, logs, tcpdumps and other methods are necessary to determine if there is anything within the client's request that indicates a state in which the server will issue such a disconnect.
The following code example works for a collection of URIs that trigger the server closed connection behavior. It may be that a pattern in the query string, post data, or some other indication could be used, which is outside the scope of this example, but a similar strategy could be used. A client-side browser utility was used in this example to look for the last request sent by an embedded java application before it timed out.
If a specific URI is matched during the request, HTTP::close will be called during the response to force a BIG-IP ASM graceful disconnect to the client after it sends the response.
How to use this snippet:
This iRule requires ASM v9.2.x / 9.4.1 (or higher)
Code :
when RULE_INIT { # By default we do not force the connection closed set ::force_closed 0 # An array of URIs for which we will force a connection close # -- These are examples -- replace with your own list set ::closeURIs [list "/services/end_session" "/services/end_transaction" "/services/logout"] } when HTTP_REQUEST { if { [lsearch -exact $::closeURIs [HTTP::uri]] > -1 } { set ::force_closed 1 # local3 is the ASM log log local3. "Forcing close for [HTTP::uri]" } else { set ::force_closed 0 } } when HTTP_RESPONSE { if { $::force_closed == 1 } { HTTP::close # local3 is the ASM log log local3. "Forced close on response" } }