Add expected connections to connmngr (#845)

Co-authored-by: diegomrsantos <diego@status.im>
This commit is contained in:
Tanguy 2023-01-23 23:28:39 +01:00 committed by GitHub
parent 7d9c43a5ce
commit 351bda2b56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 3 deletions

View File

@ -79,6 +79,7 @@ type
muxed: Table[Connection, MuxerHolder] muxed: Table[Connection, MuxerHolder]
connEvents: array[ConnEventKind, OrderedSet[ConnEventHandler]] connEvents: array[ConnEventKind, OrderedSet[ConnEventHandler]]
peerEvents: array[PeerEventKind, OrderedSet[PeerEventHandler]] peerEvents: array[PeerEventKind, OrderedSet[PeerEventHandler]]
expectedConnections: Table[PeerId, Future[Connection]]
peerStore*: PeerStore peerStore*: PeerStore
ConnectionSlot* = object ConnectionSlot* = object
@ -220,6 +221,19 @@ proc triggerPeerEvents*(c: ConnManager,
except CatchableError as exc: # handlers should not raise! except CatchableError as exc: # handlers should not raise!
warn "Exception in triggerPeerEvents", exc = exc.msg, peer = peerId warn "Exception in triggerPeerEvents", exc = exc.msg, peer = peerId
proc expectConnection*(c: ConnManager, p: PeerId): Future[Connection] {.async.} =
## Wait for a peer to connect to us. This will bypass the `MaxConnectionsPerPeer`
if p in c.expectedConnections:
raise LPError.newException("Already expecting a connection from that peer")
let future = newFuture[Connection]()
c.expectedConnections[p] = future
try:
return await future
finally:
c.expectedConnections.del(p)
proc contains*(c: ConnManager, conn: Connection): bool = proc contains*(c: ConnManager, conn: Connection): bool =
## checks if a connection is being tracked by the ## checks if a connection is being tracked by the
## connection manager ## connection manager
@ -396,7 +410,12 @@ proc storeConn*(c: ConnManager, conn: Connection)
raise newException(LPError, "Connection closed or EOF") raise newException(LPError, "Connection closed or EOF")
let peerId = conn.peerId let peerId = conn.peerId
if c.conns.getOrDefault(peerId).len > c.maxConnsPerPeer:
# we use getOrDefault in the if below instead of [] to avoid the KeyError
if peerId in c.expectedConnections and
not(c.expectedConnections.getOrDefault(peerId).finished):
c.expectedConnections.getOrDefault(peerId).complete(conn)
elif c.conns.getOrDefault(peerId).len > c.maxConnsPerPeer:
debug "Too many connections for peer", debug "Too many connections for peer",
conn, conns = c.conns.getOrDefault(peerId).len conn, conns = c.conns.getOrDefault(peerId).len
@ -536,6 +555,12 @@ proc close*(c: ConnManager) {.async.} =
let muxed = c.muxed let muxed = c.muxed
c.muxed.clear() c.muxed.clear()
let expected = c.expectedConnections
c.expectedConnections.clear()
for _, fut in expected:
await fut.cancelAndWait()
for _, muxer in muxed: for _, muxer in muxed:
await closeMuxerHolder(muxer) await closeMuxerHolder(muxer)

View File

@ -96,7 +96,8 @@ suite "Connection Manager":
await connMngr.close() await connMngr.close()
asyncTest "get conn with direction": asyncTest "get conn with direction":
let connMngr = ConnManager.new() # This would work with 1 as well cause of a bug in connmanager that will get fixed soon
let connMngr = ConnManager.new(maxConnsPerPeer = 2)
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet() let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
let conn1 = getConnection(peerId, Direction.Out) let conn1 = getConnection(peerId, Direction.Out)
let conn2 = getConnection(peerId) let conn2 = getConnection(peerId)
@ -176,7 +177,7 @@ suite "Connection Manager":
await stream.close() await stream.close()
asyncTest "should raise on too many connections": asyncTest "should raise on too many connections":
let connMngr = ConnManager.new(maxConnsPerPeer = 1) let connMngr = ConnManager.new(maxConnsPerPeer = 0)
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet() let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
connMngr.storeConn(getConnection(peerId)) connMngr.storeConn(getConnection(peerId))
@ -187,10 +188,46 @@ suite "Connection Manager":
expect TooManyConnectionsError: expect TooManyConnectionsError:
connMngr.storeConn(conns[0]) connMngr.storeConn(conns[0])
await connMngr.close()
await allFuturesThrowing(
allFutures(conns.mapIt( it.close() )))
asyncTest "expect connection from peer":
# FIXME This should be 1 instead of 0, it will get fixed soon
let connMngr = ConnManager.new(maxConnsPerPeer = 0)
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
connMngr.storeConn(getConnection(peerId))
let conns = @[
getConnection(peerId),
getConnection(peerId)]
expect TooManyConnectionsError:
connMngr.storeConn(conns[0])
let waitedConn1 = connMngr.expectConnection(peerId)
expect LPError:
discard await connMngr.expectConnection(peerId)
await waitedConn1.cancelAndWait()
let
waitedConn2 = connMngr.expectConnection(peerId)
waitedConn3 = connMngr.expectConnection(PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet())
conn = getConnection(peerId)
connMngr.storeConn(conn)
check (await waitedConn2) == conn
expect TooManyConnectionsError:
connMngr.storeConn(conns[1]) connMngr.storeConn(conns[1])
await connMngr.close() await connMngr.close()
checkExpiring: waitedConn3.cancelled()
await allFuturesThrowing( await allFuturesThrowing(
allFutures(conns.mapIt( it.close() ))) allFutures(conns.mapIt( it.close() )))