Waiting for CLOSE_WAIT

227 views
Skip to first unread message

Bill Venners

unread,
Aug 14, 2014, 2:21:25 PM8/14/14
to spray...@googlegroups.com
Hi All,

I am trying to get spray to give me a CLOSE_WAIT state on a socket when I take a long, long time to handle a request in a route. My goal was to show that you get the CLOSE_WAIT when you do a long running action directly on the thread injected into the routing data structure instead of returning a future, then show the CLOSE_WAIT goes away when you return a future instead, letting the future do the long-running task. I'm happy if it is not possible, but I'd like to make sure that's the case, and if so, understand why.

Here's some code I used to produce a CLOSE_WAIT outside of spray:

package mightyleaf

// Server.scala (will sleep in CLOSE_WAIT)
import java.io._
import java.net._

object Server {
  def main(args: Array[String]): Unit = {
    val socket: Socket = new ServerSocket(12345).accept()
    val out: OutputStream = socket.getOutputStream()
    // out.write("Hello World\n".getBytes())
    // out.flush()
    println("Server got a connection; Time for a long nap...")
   
    // should now be in CLOSE_WAIT
    Thread.sleep(Integer.MAX_VALUE)
  }
}  

package mightyleaf

// Client.scala (receives some data and exits)
import java.io._
import java.net._

object Client {
  def main(args: Array[String]): Unit = {
    val socket: Socket = new Socket(InetAddress.getByName("localhost"), 12345)
    val in: InputStream = socket.getInputStream()
    println("Client got an input stream; Time for a short nap...")
    Thread.sleep(5000)
    println("Closing...")
    in.close()
  }
}

I run the server first, then the client. After about 5 seconds, the client closes his end and exits, and a netstat shows:

Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)   
tcp4       0      0  localhost.xxxxx        localhost.52856        CLOSE_WAIT
...

I have as yet been unable to get a CLOSE_WAIT when using spray routing, even when I hold onto the thread for hours and close the client. If this is as designed and not a problem with my code, can someone explain how spray is avoiding it?

Thanks.

Bill

Johannes Rudolph

unread,
Aug 15, 2014, 6:13:43 AM8/15/14
to spray...@googlegroups.com
Hi Bill,

can you explain on a higher level what you are trying to achieve?

IIUC the CLOSE_WAIT state represents a half-closed TCP connection
where the peer already has closed the connection (= won't send more
data) but may or may not still be able to receive data. On the Akka IO
level this state is supported by using
`Tcp.Register(keepOpenOnPeerClose = true)`.

For HTTP, however, it is not specified how a client-side connection
closure should be interpreted (e.g. see [1]). Rules wrt to closing the
connection in RFC 7230 [2] seem to imply that a server usually expects
client-side connection closure only after the response was received.
The rules leave room to interpret a "premature" client-side connection
closure as an abortion (e.g. because of timeout) by the client. Also,
SSL/TLS doesn't support half-closed connections at all.

For this reason we decided at some point that we don't support
client-side half-closed connections but react to a client-side
connection closure by immediately cancelling all the ongoing requests
on this connection and close the connection from the server-side as
well.

So, what is the solution? If you don't control the client-side I'm
afraid there's no way to make spray behave the way you want (= keeping
the connection open if the client has closed the connection) by
configuration. If you just want to support long-living HTTP
connections, then this is possible by disabling all the timeouts that
usually guard you from leaking connections too easily.

Btw. Akka IO and spray are asynchronous and Actor-based so that
holding to a thread won't usually block any concurrent processing like
handling network and timeout events (modulo thread-starvation).

HTH
Johannes

[1] https://www.ruby-forum.com/topic/165035
[2] http://tools.ietf.org/html/rfc7230#section-6.6
> --
> You received this message because you are subscribed to the Google Groups
> "spray.io User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to spray-user+...@googlegroups.com.
> Visit this group at http://groups.google.com/group/spray-user.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/spray-user/914c41d7-1206-4a61-83b3-88d3d2e708d4%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Bill Venners

unread,
Aug 22, 2014, 1:56:26 PM8/22/14
to spray...@googlegroups.com
Hi Johannes,

Sorry for the delay in responding. I don't get email notifications from this group and forgot to go back and check for a response.
Actually the behavior we really want is exactly what you described. And it is what I observed. That's even better than what I thought I would demonstrate, which was that CLOSE_WAIT would be observed unless a Future was used.
 
Btw. Akka IO and spray are asynchronous and Actor-based so that
holding to a thread won't usually block any concurrent processing like
handling network and timeout events (modulo thread-starvation).

Yes, but I didn't realize the thread injected into the routes was not the same thread that accepted the request from the server socket. But that's exactly what we want, so bravo.

Thanks.

Bill

 
Reply all
Reply to author
Forward
0 new messages