I'm tasked with solving a reported bug, where the logs show a
org.apache.catalina.connector.ClientAbortException: java.io.IOException
...
Caused by: java.io.IOException
at org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer(InternalAprOutputBuffer.java:205)
There are several questions here about the ClientAbortException, and my understanding from reading them, and also the Tomcat javadoc, is that the exception is thrown by Tomcat when the client aborts the HTTP request.
I'm having trouble reproducing the error. How can I simulate a client abort?
What I've tried
Adding a Thread.sleep(10000)
in the request handler, and then closing the browser while the request is running - but that doesn't do it.
Cancelling the HTTP request from the client side using this technique with angular.
Ok, with a bit of experimenting - I've found a way to do it.
What it looks like - is that if an http request is cancelled/timed out by the client while the server is writing/flushing the output then the error will be thrown. (NB. It appears as if the size of the response also matters - see my note at the end).
There are three things that can happen:
Condition 1: Server writes and flushes output before client timeouts.
Response is sent back to client.
Condition 2: Client times out before server writes and flushes output.
Client does not receive response, no server error.
Condition 3: Client times out while server is writing output.
Client does not receive response. Server throws ClientAbortException
(java.io.IOException
).
To simulate these three conditions, we play with three variables:
Here is the test code to simulate it:
Server side (This is a Spring MVC controller).
@RequestMapping(value = { "/debugGet" }, method = RequestMethod.GET)
@ResponseBody
public List<String> debugGet(@RequestParam int timeout, int numObjects) throws InterruptedException {
Thread.sleep(timeout);
List<String> l = new ArrayList<String>();
for (int i =0; i< numObjects; i++){
l.add(new String());
}
return l;
}
Client side (Angular)
this.debugGet = function(server, client, numObjects){
var httpProm = $http({
method: "GET",
url: "debugGet",
timeout: client,
params : {
timeout: server,
numObjects: numObjects}
});
httpProm.then(function(data){
console.log(data);
}, function(data){
console.log("error");
console.log(data);
});
};
Using this I can simulate the three conditions with the following params:
Client Timeout Server Burn Time Num Strings
Condition 1: 1000 900 10000
Condition 2: 1000 2000 100
Condition 3: 1000 950 10000
NB It appears as if the size of response also matters.
For example:
Client Timeout Server Burn Time Num Strings
Condition 2: 1000 2000 100
Condition 3: 1000 2000 10000
Here for the 10000 Strings, we get the java.io.IOException
even though the flush occurs well after the client has timed out, whereas it doesn't for the 100 Strings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With