Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I simulate a client aborting request?

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.

like image 559
dwjohnston Avatar asked Aug 02 '16 23:08

dwjohnston


1 Answers

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:

  1. The time client takes to timeout
  2. Time server burns getting its result.
  3. The size of the server response.

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.

like image 122
dwjohnston Avatar answered Oct 12 '22 02:10

dwjohnston