mirror of https://github.com/vacp2p/nim-libp2p.git
move dialing logic to it's own proc to avoid try/finally bugs (#461)
* move dialing logic to it's own proc to avoid try/finally bugs * re-export transport * lint * add cancelation test * test remote conn close on dial
This commit is contained in:
parent
02b20440f2
commit
a8f5f7a8bb
|
@ -257,6 +257,45 @@ proc upgradeIncoming(s: Switch, incomingConn: Connection) {.async, gcsafe.} = #
|
||||||
finally:
|
finally:
|
||||||
await incomingConn.close()
|
await incomingConn.close()
|
||||||
|
|
||||||
|
proc dialAndUpgrade(s: Switch,
|
||||||
|
peerId: PeerID,
|
||||||
|
addrs: seq[MultiAddress]):
|
||||||
|
Future[Connection] {.async.} =
|
||||||
|
debug "Dialing peer", peerId
|
||||||
|
for t in s.transports: # for each transport
|
||||||
|
for a in addrs: # for each address
|
||||||
|
if t.handles(a): # check if it can dial it
|
||||||
|
trace "Dialing address", address = $a, peerId
|
||||||
|
let dialed = try:
|
||||||
|
await t.dial(a)
|
||||||
|
except CancelledError as exc:
|
||||||
|
debug "Dialing canceled", msg = exc.msg, peerId
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
debug "Dialing failed", msg = exc.msg, peerId
|
||||||
|
libp2p_failed_dials.inc()
|
||||||
|
continue # Try the next address
|
||||||
|
|
||||||
|
# make sure to assign the peer to the connection
|
||||||
|
dialed.peerInfo = PeerInfo.init(peerId, addrs)
|
||||||
|
|
||||||
|
libp2p_dialed_peers.inc()
|
||||||
|
|
||||||
|
let conn = try:
|
||||||
|
await s.upgradeOutgoing(dialed)
|
||||||
|
except CatchableError as exc:
|
||||||
|
# If we failed to establish the connection through one transport,
|
||||||
|
# we won't succeeded through another - no use in trying again
|
||||||
|
await dialed.close()
|
||||||
|
debug "Upgrade failed", msg = exc.msg, peerId
|
||||||
|
if exc isnot CancelledError:
|
||||||
|
libp2p_failed_upgrade.inc()
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
doAssert not isNil(conn), "connection died after upgradeOutgoing"
|
||||||
|
debug "Dial successful", conn, peerInfo = conn.peerInfo
|
||||||
|
return conn
|
||||||
|
|
||||||
proc internalConnect(s: Switch,
|
proc internalConnect(s: Switch,
|
||||||
peerId: PeerID,
|
peerId: PeerID,
|
||||||
addrs: seq[MultiAddress]): Future[Connection] {.async.} =
|
addrs: seq[MultiAddress]): Future[Connection] {.async.} =
|
||||||
|
@ -281,45 +320,9 @@ proc internalConnect(s: Switch,
|
||||||
raise newException(DialFailedError, "Zombie connection encountered")
|
raise newException(DialFailedError, "Zombie connection encountered")
|
||||||
|
|
||||||
trace "Reusing existing connection", conn, direction = $conn.dir
|
trace "Reusing existing connection", conn, direction = $conn.dir
|
||||||
|
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
debug "Dialing peer", peerId
|
conn = await s.dialAndUpgrade(peerId, addrs)
|
||||||
for t in s.transports: # for each transport
|
|
||||||
for a in addrs: # for each address
|
|
||||||
if t.handles(a): # check if it can dial it
|
|
||||||
trace "Dialing address", address = $a, peerId
|
|
||||||
let dialed = try:
|
|
||||||
await t.dial(a)
|
|
||||||
except CancelledError as exc:
|
|
||||||
debug "Dialing canceled", msg = exc.msg, peerId
|
|
||||||
raise exc
|
|
||||||
except CatchableError as exc:
|
|
||||||
debug "Dialing failed", msg = exc.msg, peerId
|
|
||||||
libp2p_failed_dials.inc()
|
|
||||||
continue # Try the next address
|
|
||||||
|
|
||||||
# make sure to assign the peer to the connection
|
|
||||||
dialed.peerInfo = PeerInfo.init(peerId, addrs)
|
|
||||||
|
|
||||||
libp2p_dialed_peers.inc()
|
|
||||||
|
|
||||||
let upgraded = try:
|
|
||||||
await s.upgradeOutgoing(dialed)
|
|
||||||
except CatchableError as exc:
|
|
||||||
# If we failed to establish the connection through one transport,
|
|
||||||
# we won't succeeded through another - no use in trying again
|
|
||||||
await dialed.close()
|
|
||||||
debug "Upgrade failed", msg = exc.msg, peerId
|
|
||||||
if exc isnot CancelledError:
|
|
||||||
libp2p_failed_upgrade.inc()
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
doAssert not isNil(upgraded), "connection died after upgradeOutgoing"
|
|
||||||
|
|
||||||
conn = upgraded
|
|
||||||
debug "Dial successful", conn, peerInfo = conn.peerInfo
|
|
||||||
break
|
|
||||||
finally:
|
finally:
|
||||||
if lock.locked():
|
if lock.locked():
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
|
@ -20,6 +20,8 @@ import transport,
|
||||||
logScope:
|
logScope:
|
||||||
topics = "tcptransport"
|
topics = "tcptransport"
|
||||||
|
|
||||||
|
export transport
|
||||||
|
|
||||||
const
|
const
|
||||||
TcpTransportTrackerName* = "libp2p.tcptransport"
|
TcpTransportTrackerName* = "libp2p.tcptransport"
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@ import ../libp2p/[errors,
|
||||||
protocols/secure/secure,
|
protocols/secure/secure,
|
||||||
muxers/muxer,
|
muxers/muxer,
|
||||||
muxers/mplex/lpchannel,
|
muxers/mplex/lpchannel,
|
||||||
stream/lpstream]
|
stream/lpstream,
|
||||||
|
stream/chronosstream,
|
||||||
|
transports/tcptransport]
|
||||||
import ./helpers
|
import ./helpers
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -624,6 +626,78 @@ suite "Switch":
|
||||||
switches.mapIt( it.stop() ))
|
switches.mapIt( it.stop() ))
|
||||||
await allFuturesThrowing(awaiters)
|
await allFuturesThrowing(awaiters)
|
||||||
|
|
||||||
|
# TODO: we should be able to test cancellation
|
||||||
|
# for most of the steps in the upgrade flow -
|
||||||
|
# this is just a basic test for dials
|
||||||
|
asyncTest "e2e canceling dial should not leak":
|
||||||
|
let ma: MultiAddress = Multiaddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
|
||||||
|
|
||||||
|
let transport = TcpTransport.init()
|
||||||
|
await transport.start(ma)
|
||||||
|
|
||||||
|
proc acceptHandler() {.async, gcsafe.} =
|
||||||
|
try:
|
||||||
|
let conn = await transport.accept()
|
||||||
|
discard await conn.readLp(100)
|
||||||
|
except CatchableError as exc:
|
||||||
|
discard
|
||||||
|
|
||||||
|
let handlerWait = acceptHandler()
|
||||||
|
let switch = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
||||||
|
|
||||||
|
var awaiters: seq[Future[void]]
|
||||||
|
awaiters.add(await switch.start())
|
||||||
|
|
||||||
|
var peerId = PeerID.init(PrivateKey.random(ECDSA, rng[]).get()).get()
|
||||||
|
let connectFut = switch.connect(peerId, @[transport.ma])
|
||||||
|
await sleepAsync(500.millis)
|
||||||
|
connectFut.cancel()
|
||||||
|
await handlerWait
|
||||||
|
|
||||||
|
checkTracker(LPChannelTrackerName)
|
||||||
|
checkTracker(SecureConnTrackerName)
|
||||||
|
checkTracker(ChronosStreamTrackerName)
|
||||||
|
|
||||||
|
await allFuturesThrowing(
|
||||||
|
transport.stop(),
|
||||||
|
switch.stop())
|
||||||
|
|
||||||
|
# this needs to go at end
|
||||||
|
await allFuturesThrowing(awaiters)
|
||||||
|
|
||||||
|
asyncTest "e2e closing remote conn should not leak":
|
||||||
|
let ma: MultiAddress = Multiaddress.init("/ip4/0.0.0.0/tcp/0").tryGet()
|
||||||
|
|
||||||
|
let transport = TcpTransport.init()
|
||||||
|
await transport.start(ma)
|
||||||
|
|
||||||
|
proc acceptHandler() {.async, gcsafe.} =
|
||||||
|
let conn = await transport.accept()
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
|
let handlerWait = acceptHandler()
|
||||||
|
let switch = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
||||||
|
|
||||||
|
var awaiters: seq[Future[void]]
|
||||||
|
awaiters.add(await switch.start())
|
||||||
|
|
||||||
|
var peerId = PeerID.init(PrivateKey.random(ECDSA, rng[]).get()).get()
|
||||||
|
expect LPStreamClosedError:
|
||||||
|
await switch.connect(peerId, @[transport.ma])
|
||||||
|
|
||||||
|
await handlerWait
|
||||||
|
|
||||||
|
checkTracker(LPChannelTrackerName)
|
||||||
|
checkTracker(SecureConnTrackerName)
|
||||||
|
checkTracker(ChronosStreamTrackerName)
|
||||||
|
|
||||||
|
await allFuturesThrowing(
|
||||||
|
transport.stop(),
|
||||||
|
switch.stop())
|
||||||
|
|
||||||
|
# this needs to go at end
|
||||||
|
await allFuturesThrowing(awaiters)
|
||||||
|
|
||||||
asyncTest "connect to inexistent peer":
|
asyncTest "connect to inexistent peer":
|
||||||
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
||||||
let sfut = await switch2.start()
|
let sfut = await switch2.start()
|
||||||
|
|
Loading…
Reference in New Issue