Rotem ben
Rotem ben

Reputation: 176

send keep alive on long asynchronous request in spring server

I have a controller in spring which getting a POST request which is handling as asynchronous(using DeferredResult object as a return value).

The response for this request is writing bytes to the HTTP stream directly (HttpServletResponse.getWriter().print()) and when it's done writing it sets result on the DeferredResult object for close the connection.

I'm writing my response in stream chunks. I have an issue in this request handling because the client is closing the connection if I'm not writing to it for 1 minute. (I can write some chunks and then stop writing for 1 minute - therefore the connection will be closed in the middle of my procedure).

I want to control the closing connection procedure - I want to send keep alive when I'm not writing any data to the stream so that the connection won't be closed until I decided to close it from the server-side.

I didn't find out how should I get control of the connection from the controller in the server. Please assist. Thanks.

Upvotes: 2

Views: 4609

Answers (2)

Jose Quijada
Jose Quijada

Reputation: 692

I ran into similar need just recently. The server code executes a long running operation that can take as long as 30 minutes to return, and the client times out long before that. The solution was to have the long running operation send periodic "keep alive" packets of data to the client via a "callback" argument provided by the request handler method. The callback is nothing more than a function (think of Lambda in Java) that takes as parameter the "keep alive" data packet to send to client, and then writes that data packet to the client via the java.io.PrintWriter reference that you can get off of javax.servlet.http.HttpServletResponse. Below code is the handler method that does this. I had to refactor the code in the call hierarchy to accept this new "callback" parameter until the "callback" can reach the method that is performing the long running operation, and inside that code I invoke the "callback" every so often, for example every time 10 records are processed. Not that below is Groovy code (scripting code on top of Java that runs on the JVM) and the server-side framework is Spring,

  ...
  @Autowired
  DataImporter dataImporter

  @PostMapping("/my/endpoint")
  void importData(@RequestBody MyDto myDto, HttpServletResponse response) {
    // Callback to allow servant code deep in the call hierarchy to report back to client any arbitrary message
    Closure<Void> callback = { String str ->
      response.writer.print str
      response.writer.flush()
    }

    // This leads to the code that is performing a long running operation. Using
    // this "hook" that code has a direct connection to the client whereby
    // it can send packets of data to keep the connection from timing out.
    dataImporter.importData(myDto, callback)
  }
}

Upvotes: 1

Steffen Ullrich
Steffen Ullrich

Reputation: 123320

There is no such thing as a "keep alive" during an ongoing request or response in HTTP which can help with idle timeouts when receiving a request or response.

HTTP keep alive is only about keeping the TCP connection open after a response in order to process more requests on the same connection. TCP keep alive is instead used to detect connection loss without TCP shutdown and can also be used to prevent idle timeouts in stateful packet filters (as used in firewalls or NAT routers) in between client and server. It does not prevent idle timeouts at the application level though since it does not transport any data visible to the application level.

Note that the way you want to use HTTP is contrary to how HTTP was designed originally. It was designed for a client sending a full request and the server sending a full response immediately and not for the server sending some parts of the response, idling some time and then send some more. The proper way to implement such behavior would be by using WebSockets. With WebSockets both client and server can send new messages at any time (i.e. no request-response schema) and it also supports keep-alive messages. If WebSockets are not an option you can instead implement a polling client which regularly polls for new data from the server with a new request.

Upvotes: 5

Related Questions