3

Consider the below scenarios (Using Rest easy for Rest implementation):

  1. Client sent a request to my RestService.
  2. My rest service updated my DB related to the request.
  3. Finally my rest service sent some response.

If client timeouts and closes his connection before sending the response from my rest service:

  • (I need to) revert the DB changes.

Question is:

How to identify whether client closed its connection or not?

NeronLeVelu
  • 9,908
  • 1
  • 23
  • 43

2 Answers2

1

Rest is stateless and you are trying to keep track of the request state which is not an acceptable RESTful strategy. Any way this is how we have solved similar kind of problem in our application. Timeout can be managed by the session and you can get hold of timeout events using listeners. But 'closing' connections seems to be a bit complex. There are plenty of hacks that you can get it from internet but there is no universally accepted browser close event available as of now. Even though problem looks simple, it is a kind of tough problem. You can try following hack

  • Generate a Unique ID and send it to the server along with the request.

  • Just before sending the response from the server schedule a job with a time limit and register the job with the ID you got.

  • From the client side, in the callback you ping server with the same id again.

  • Schedule a role back in such a way that if it didn't receive any ping from the client with the registered ID within the limited time span, roleback the functionality.

You can have look at sockets also.

Bala
  • 1,295
  • 1
  • 12
  • 23
  • 1
    The OP is asking about a timeout of **one single request**. This has nothing to do with state or a session timeout. – lefloh Sep 23 '14 at 07:53
0

As far as I know neither RESTeasy nor the Servlet API provide a method to check wether a client has closed the connection. But as already answered here and here: The underlying servlet container might throw an IOException if the response is bigger then the socket buffer. You can easily test this:

Test Client

@Test
public void testTimeout() throws Exception {
    URLConnection connection = new URL("http://yourhost.com/foo").openConnection();
    connection.setReadTimeout(1000);
    connection.getContent();
}

Resource class

@Path("/foo")
public class SomeResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response test() throws InterruptedException {
        Thread.sleep(5000l);
        String response = RandomStringUtils.random(6000);
        return Response.ok(response).build();
    }

}

6000 characters is working for me with RESTeasy 3.0.6.Final on Wildlfy 8.1.0.

As this Exception is thrown long after "your" code is executed you can't catch the exception in a resource class or in an exception mapper. The only way is implementing a WriterInterceptor:

@Provider
public class ResponseInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
        try {
            context.proceed();
        } catch (IOException ex) {
            if (ex.getMessage().contains("Broken pipe")) { // ugly but seems like the only chance...
                // revert DB changes
            }
        }
    }

}

Here you have no idea which changes in the DB should be reverted. You can inject e.g. the HttpHeaders or the HttpServletRequest via @Context here and maybe set and check a custom header with a transaction id or something like that.

Community
  • 1
  • 1
lefloh
  • 10,653
  • 3
  • 28
  • 50