io: handle half-closed connection when peer closed first

This allows to send data back to the peer even if the peer already has sent
EOF/FIN as specified for TCP. To fully close a connection, the handler has
to close its side of the connection now actively once it is finished with
writing even if it received a `PeerClosed` message before.
This commit is contained in:
Johannes Rudolph 2013-04-02 15:17:48 +02:00
parent 632c310e9a
commit 1b95f65cf4
3 changed files with 77 additions and 23 deletions

View file

@ -389,6 +389,39 @@ class TcpConnectionSpec extends AkkaSpec("akka.io.tcp.register-timeout = 500ms")
selector.send(connectionActor, ChannelReadable)
connectionHandler.expectMsg(PeerClosed)
connectionHandler.send(connectionActor, Close)
assertThisConnectionActorTerminated()
}
"report when peer closed the connection but allow further writes and acknowledge normal close" in withEstablishedConnection() { setup
import setup._
closeServerSideAndWaitForClientReadable(fullClose = false) // send EOF (fin) from the server side
selector.send(connectionActor, ChannelReadable)
connectionHandler.expectMsg(PeerClosed)
object Ack
connectionHandler.send(connectionActor, writeCmd(Ack))
pullFromServerSide(TestSize)
connectionHandler.expectMsg(Ack)
connectionHandler.send(connectionActor, Close)
connectionHandler.expectMsg(Closed)
assertThisConnectionActorTerminated()
}
"report when peer closed the connection but allow further writes and acknowledge confirmed close" in withEstablishedConnection() { setup
import setup._
closeServerSideAndWaitForClientReadable(fullClose = false) // send EOF (fin) from the server side
selector.send(connectionActor, ChannelReadable)
connectionHandler.expectMsg(PeerClosed)
object Ack
connectionHandler.send(connectionActor, writeCmd(Ack))
pullFromServerSide(TestSize)
connectionHandler.expectMsg(Ack)
connectionHandler.send(connectionActor, ConfirmedClose)
connectionHandler.expectMsg(ConfirmedClosed)
assertThisConnectionActorTerminated()
}
@ -535,8 +568,8 @@ class TcpConnectionSpec extends AkkaSpec("akka.io.tcp.register-timeout = 500ms")
val clientSelectionKey = registerChannel(clientSideChannel, "client")
val serverSelectionKey = registerChannel(serverSideChannel, "server")
def closeServerSideAndWaitForClientReadable(): Unit = {
serverSideChannel.close()
def closeServerSideAndWaitForClientReadable(fullClose: Boolean = true): Unit = {
if (fullClose) serverSideChannel.close() else serverSideChannel.socket.shutdownOutput()
checkFor(clientSelectionKey, SelectionKey.OP_READ, 3.seconds.toMillis.toInt) must be(true)
}