In Resteasy, how to know if a client connection is alive or not, before sending a response

Consider the scenarios below (Using Rest easy to Rest):

  • The client sent a request to my RestService.
  • My rest service updated my DB associated with the request.
  • Finally my rest service posted some response.

If the client intercepts and closes its connection before sending a response from my rest service:

  • (I need to) revert the DB changes.

Question:

How to determine if a client has closed its connection or not?

+3


source to share


2 answers


Rest is statusless and you are trying to track the state of the request, which is not an acceptable RESTful strategy. However, we have solved a similar problem in our application. Timeout can be session driven and you can receive timeout events using listeners. But "closing" connections seems a little tricky. There are many hacks that you can get from the internet, but there is no generally accepted browser close event at the moment. While the problem looks simple, it is kind of a difficult problem. You can try the following hack

  • Create a unique identifier and send it to the server along with the request.

  • Before sending the response from the server schedule, the job is time-limited and register the job with the received ID.

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

  • Assign the role back in such a way that if it hasn't received any ping from the client with the registered ID within a limited amount of time, assume the role of the function.



You can take a look at sockets .

+1


source


As far as I know, neither RESTeasy nor the Servlet API provides a method to verify that the client has closed the connection. But as already answered here and here : the underlying servlet container may throw IOException

if the response is larger than the socket buffer. You can check this easily:

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 chars work for me with RESTeasy 3.0.6.Final on Wildlfy 8.1.0.

Because this exception is thrown too long after "your" code has been executed, you cannot catch the exception in the resource class or in the exception mapper. The only way is to execute 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 do not know what changes to the database should be reverted. You can enter for example. HttpHeaders

or HttpServletRequest

via @Context

here and maybe set and check your own header with transaction id or something.

0


source







All Articles