Fix transport leaks + handle disconnects in message loop

This commit is contained in:
kdeme 2019-04-11 15:07:07 +02:00
parent 14513b3bd8
commit 6ba61488ff
1 changed files with 36 additions and 13 deletions

View File

@ -511,22 +511,34 @@ proc nextMsg*(peer: Peer, MsgType: type): Future[MsgType] =
# handled a layer lower for clarity (and consistency), as also the actual # handled a layer lower for clarity (and consistency), as also the actual
# message handler code as the TODO mentions already. # message handler code as the TODO mentions already.
proc dispatchMessages*(peer: Peer) {.async.} = proc dispatchMessages*(peer: Peer) {.async.} =
while true: while peer.connectionState notin {Disconnecting, Disconnected}:
var msgId: int var msgId: int
var msgData: Rlp var msgData: Rlp
try: try:
(msgId, msgData) = await peer.recvMsg() (msgId, msgData) = await peer.recvMsg()
except TransportIncompleteError: except TransportIncompleteError:
trace "Connection dropped, ending dispatchMessages loop", peer # Note: Could also "Transport is already closed!" error occur? Might have
# This can happen during the rlpx connection setup or at any point after. # to change here to the general TransportError.
# Because this code does not know, a disconnect needs to be done. case peer.connectionState
await peer.disconnect(ClientQuitting) of Connected:
# Dropped connection, still need to cleanup the peer.
# This could be seen as bad behaving peer.
trace "Dropped connection", peer
await peer.disconnect(ClientQuitting, false)
return
of Disconnecting, Disconnected:
# Graceful disconnect, can still cause TransportIncompleteError as it
# could be that this loop was waiting at recvMsg().
return
else:
# Connection dropped while `Connecting` (in rlpxConnect/rlpxAccept).
return
except PeerDisconnected:
return return
if msgId == 1: # p2p.disconnect if msgId == 1: # p2p.disconnect
await peer.transport.closeWait()
let reason = msgData.listElem(0).toInt(uint32).DisconnectionReason let reason = msgData.listElem(0).toInt(uint32).DisconnectionReason
await peer.disconnect(reason) await peer.disconnect(reason, false)
break break
try: try:
@ -1176,18 +1188,29 @@ macro handshake*(peer: Peer, timeout: untyped, sendCall: untyped): untyped =
proc disconnect*(peer: Peer, reason: DisconnectionReason, notifyOtherPeer = false) {.async.} = proc disconnect*(peer: Peer, reason: DisconnectionReason, notifyOtherPeer = false) {.async.} =
if peer.connectionState notin {Disconnecting, Disconnected}: if peer.connectionState notin {Disconnecting, Disconnected}:
peer.connectionState = Disconnecting peer.connectionState = Disconnecting
if notifyOtherPeer and not peer.transport.closed: # Do this first so sub-protocols have time to clean up and stop sending
var fut = peer.sendDisconnectMsg(reason) # before this node closes transport to remote peer
yield fut
if fut.failed:
debug "Failed to delived disconnect message", peer
if not peer.dispatcher.isNil: if not peer.dispatcher.isNil:
# In case of `CatchableError` in any of the handlers, this will be logged. # In case of `CatchableError` in any of the handlers, this will be logged.
# Other handlers will still execute. # Other handlers will still execute.
# In case of `Defect` in any of the handlers, program will quit. # In case of `Defect` in any of the handlers, program will quit.
traceAwaitErrors callDisconnectHandlers(peer, reason) traceAwaitErrors callDisconnectHandlers(peer, reason)
if notifyOtherPeer and not peer.transport.closed:
var fut = peer.sendDisconnectMsg(reason)
yield fut
if fut.failed:
debug "Failed to deliver disconnect message", peer
proc waitAndClose(peer: Peer, time: Duration) {.async.} =
await sleepAsync(time)
await peer.transport.closeWait()
# Give the peer a chance to disconnect
await peer.waitAndClose(2.seconds)
elif not peer.transport.closed:
await peer.transport.closeWait()
logDisconnectedPeer peer logDisconnectedPeer peer
peer.connectionState = Disconnected peer.connectionState = Disconnected
removePeer(peer.network, peer) removePeer(peer.network, peer)