ninenines/cowboy

Is there a graceful way to drain a keep-alive connection server side?

eseres opened this issue · 1 comments

The use case is one of selective connection draining initiated server side. The server must continue to be able to receive new connections, processing new HTTP requests on those connections. We just need to be able to selectively drain a chosen connection without discarding HTTP requests on that connection while doing so. Suspending and subsequently stopping the ranch listener as described here is not a solution for us.

The obvious answer would be to add the Connection: close header to the response of a currently running HTTP 1.1 request. But what is the solution if there is no currently running HTTP 1.1 request and the connection is being kept alive by the client?

A naive solution would be to monitor the HTTP request processes and:

  • if one is running, signal it to include the Connection: close header,
  • if one is not running, send the connection process the {Closed, Socket} message.

However, this can create race conditions:

  • A running HTTP request process might already have sent the response by the time it was signaled to include the Connection: close header. In this case, the connection will not be closed as intended.
  • A connection not currently running any HTTP requests might receive one right after we have sent the connection process the {Close, Socket} message. In this case, the connection will close and discard the just received HTTP request.

Is there a clean and robust way built into Cowboy to handle the above scenarios? Perhaps half closing the connection so that inbound requests are blocked while outgoing responses can still be sent, then letting any current request run to completion and finally closing the connection by sending it the {Close, Socket} message? Or some other way?

Thank you.

You must have the pid of the connection process(es) you want to shut down, and call sys:terminate/2 using it. Cowboy will initiate a graceful connection closure. This works for all protocols (Connection: close is HTTP/1.1 only).

If there's no request ongoing, the connection gets closed immediately. Otherwise, the ongoing request(s) will be the last one(s). Note that for HTTP/1.1, pipelined requests that haven't started being processed will not be handled.