nim-libp2p/tests/testconnmngr.nim

393 lines
11 KiB
Nim
Raw Normal View History

{.used.}
# Nim-Libp2p
# Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import std/[sequtils,tables]
import stew/results
import chronos
import ../libp2p/[connmanager,
stream/connection,
crypto/crypto,
muxers/muxer,
peerinfo,
errors]
import helpers
2023-03-08 11:30:19 +00:00
proc getMuxer(peerId: PeerId, dir: Direction = Direction.In): Muxer =
return Muxer(connection: Connection.new(peerId, dir, Opt.none(MultiAddress)))
type
TestMuxer = ref object of Muxer
peerId: PeerId
method newStream*(
m: TestMuxer,
name: string = "",
lazy: bool = false
): Future[Connection] {.async: (raises: [
CancelledError, LPStreamError, MuxerError]).} =
Connection.new(m.peerId, Direction.Out, Opt.none(MultiAddress))
suite "Connection Manager":
teardown:
checkTrackers()
2023-03-08 11:30:19 +00:00
asyncTest "add and retrieve a muxer":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
let mux = getMuxer(peerId)
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(mux)
check mux in connMngr
2023-03-08 11:30:19 +00:00
let peerMux = connMngr.selectMuxer(peerId)
check peerMux == mux
check peerMux.connection.dir == Direction.In
await connMngr.close()
asyncTest "get all connections":
let connMngr = ConnManager.new()
let peers = toSeq(0..<2).mapIt(PeerId.random.tryGet())
let muxs = toSeq(0..<2).mapIt(getMuxer(peers[it]))
for mux in muxs: connMngr.storeMuxer(mux)
let conns = connMngr.getConnections()
let connsMux = toSeq(conns.values).mapIt(it[0])
check unorderedCompare(connsMux, muxs)
await connMngr.close()
asyncTest "shouldn't allow a closed connection":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
let mux = getMuxer(peerId)
await mux.connection.close()
expect CatchableError:
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(mux)
await connMngr.close()
asyncTest "shouldn't allow an EOFed connection":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
let mux = getMuxer(peerId)
mux.connection.isEof = true
expect CatchableError:
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(mux)
2023-03-08 11:30:19 +00:00
await mux.close()
await connMngr.close()
2023-03-08 11:30:19 +00:00
asyncTest "shouldn't allow a muxer with no connection":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
let muxer = getMuxer(peerId)
let conn = muxer.connection
muxer.connection = nil
expect CatchableError:
connMngr.storeMuxer(muxer)
await conn.close()
await muxer.close()
await connMngr.close()
asyncTest "get conn with direction":
# 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()
2023-03-08 11:30:19 +00:00
let mux1 = getMuxer(peerId, Direction.Out)
let mux2 = getMuxer(peerId)
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(mux1)
connMngr.storeMuxer(mux2)
check mux1 in connMngr
check mux2 in connMngr
2023-03-08 11:30:19 +00:00
let outMux = connMngr.selectMuxer(peerId, Direction.Out)
let inMux = connMngr.selectMuxer(peerId, Direction.In)
2023-03-08 11:30:19 +00:00
check outMux != inMux
check outMux == mux1
check inMux == mux2
check outMux.connection.dir == Direction.Out
check inMux.connection.dir == Direction.In
await connMngr.close()
asyncTest "get muxed stream for peer":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
let muxer = new TestMuxer
2023-03-08 11:30:19 +00:00
let connection = Connection.new(peerId, Direction.In, Opt.none(MultiAddress))
muxer.peerId = peerId
2023-03-08 11:30:19 +00:00
muxer.connection = connection
connMngr.storeMuxer(muxer)
check muxer in connMngr
let stream = await connMngr.getStream(peerId)
check not(isNil(stream))
check stream.peerId == peerId
await connMngr.close()
2023-03-08 11:30:19 +00:00
await connection.close()
await stream.close()
asyncTest "get stream from directed connection":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
let muxer = new TestMuxer
2023-03-08 11:30:19 +00:00
let connection = Connection.new(peerId, Direction.In, Opt.none(MultiAddress))
muxer.peerId = peerId
2023-03-08 11:30:19 +00:00
muxer.connection = connection
connMngr.storeMuxer(muxer)
check muxer in connMngr
let stream1 = await connMngr.getStream(peerId, Direction.In)
check not(isNil(stream1))
let stream2 = await connMngr.getStream(peerId, Direction.Out)
check isNil(stream2)
await connMngr.close()
await stream1.close()
2023-03-08 11:30:19 +00:00
await connection.close()
asyncTest "should raise on too many connections":
let connMngr = ConnManager.new(maxConnsPerPeer = 0)
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(getMuxer(peerId))
2023-03-08 11:30:19 +00:00
let muxs = @[getMuxer(peerId)]
expect TooManyConnectionsError:
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(muxs[0])
await connMngr.close()
await allFuturesThrowing(
2023-03-08 11:30:19 +00:00
allFutures(muxs.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()
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(getMuxer(peerId))
2023-03-08 11:30:19 +00:00
let muxs = @[
getMuxer(peerId),
getMuxer(peerId)]
Connection limits (#384) * master merge * wip * avoid deadlocks * tcp limits * expose client field in chronosstream * limit incoming connections * update with new listen api * fix release * don't override peerinfo in connection * rework transport with accept * use semaphore to track resource ussage * rework with new transport accept api * move events to conn manager (#373) * use semaphore to track resource ussage * merge master * expose api to acquire conn slots * don't fail expensive metrics * allow tracking and updating connections * set global connection limits to 80 * add per peer connection limits * make sure conn is closed if tracking failed * more descriptive naming for handle * rework with new transport accept api * add `getStream` hide `selectConn` * add TransportClosedError * make nil explicit * don't make unnecessary copies of message * logging * error handling * cleanup semaphore * track connections properly * throw `TooManyConnections` when tracking outgoing * use proper exception and handle conventions * check onCloseHandle for nil * revert internalConnect changes * adding upgraded flag * await stream before closing * simplify tracking * wip * logging * split connection limits into incoming and outgoing * further streamline connection limits split counts * don't use closeWithEOF * move peer and conn event triggers from switch * wip * wip * wip * merge master * handle nil connections properly * add clarifying comment * don't raise exc on nil * no finally * add proper min/max connections logic * rebase master * merge master * master merge * remove request timeout should be addressed in separate PR * merge master * share semaphore when in/out limits arent enforced * merge master * use import * pass semaphore to trackConn * don't close last conn * use storeConn * merge master * use storeConn
2021-01-21 04:00:24 +00:00
expect TooManyConnectionsError:
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(muxs[0])
let waitedConn1 = connMngr.expectConnection(peerId, In)
expect AlreadyExpectingConnectionError:
discard await connMngr.expectConnection(peerId, In)
await waitedConn1.cancelAndWait()
let
waitedConn2 = connMngr.expectConnection(peerId, In)
waitedConn3 = connMngr.expectConnection(PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet(), In)
2023-03-08 11:30:19 +00:00
conn = getMuxer(peerId)
connMngr.storeMuxer(conn)
check (await waitedConn2) == conn
expect TooManyConnectionsError:
2023-03-08 11:30:19 +00:00
connMngr.storeMuxer(muxs[1])
await connMngr.close()
checkUntilTimeout: waitedConn3.cancelled()
await allFuturesThrowing(
2023-03-08 11:30:19 +00:00
allFutures(muxs.mapIt( it.close() )))
asyncTest "cleanup on connection close":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
2023-03-08 11:30:19 +00:00
let muxer = getMuxer(peerId)
connMngr.storeMuxer(muxer)
check muxer in connMngr
2023-03-08 11:30:19 +00:00
await muxer.close()
checkUntilTimeout: muxer notin connMngr
await connMngr.close()
asyncTest "drop connections for peer":
let connMngr = ConnManager.new()
let peerId = PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet()
for i in 0..<2:
let dir = if i mod 2 == 0:
Direction.In else:
Direction.Out
2023-03-08 11:30:19 +00:00
let muxer = getMuxer(peerId, dir)
connMngr.storeMuxer(muxer)
check muxer in connMngr
2023-03-08 11:30:19 +00:00
check not(isNil(connMngr.selectMuxer(peerId, dir)))
check peerId in connMngr
await connMngr.dropPeer(peerId)
checkUntilTimeout: peerId notin connMngr
2023-03-08 11:30:19 +00:00
check isNil(connMngr.selectMuxer(peerId, Direction.In))
check isNil(connMngr.selectMuxer(peerId, Direction.Out))
await connMngr.close()
asyncTest "track total incoming connection limits":
let connMngr = ConnManager.new(maxConnections = 3)
for i in 0..<3:
check await connMngr.getIncomingSlot().withTimeout(10.millis)
# should timeout adding a connection over the limit
check not(await connMngr.getIncomingSlot().withTimeout(10.millis))
await connMngr.close()
asyncTest "track total outgoing connection limits":
let connMngr = ConnManager.new(maxConnections = 3)
for i in 0..<3:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
# should throw adding a connection over the limit
expect TooManyConnectionsError:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
await connMngr.close()
asyncTest "track both incoming and outgoing total connections limits - fail on incoming":
let connMngr = ConnManager.new(maxConnections = 3)
for i in 0..<3:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
# should timeout adding a connection over the limit
check not(await connMngr.getIncomingSlot().withTimeout(10.millis))
await connMngr.close()
asyncTest "track both incoming and outgoing total connections limits - fail on outgoing":
let connMngr = ConnManager.new(maxConnections = 3)
for i in 0..<3:
check await connMngr.getIncomingSlot().withTimeout(10.millis)
# should throw adding a connection over the limit
expect TooManyConnectionsError:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
await connMngr.close()
asyncTest "track max incoming connection limits":
let connMngr = ConnManager.new(maxIn = 3)
for i in 0..<3:
check await connMngr.getIncomingSlot().withTimeout(10.millis)
check not(await connMngr.getIncomingSlot().withTimeout(10.millis))
await connMngr.close()
asyncTest "track max outgoing connection limits":
let connMngr = ConnManager.new(maxOut = 3)
for i in 0..<3:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
# should throw adding a connection over the limit
expect TooManyConnectionsError:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
await connMngr.close()
asyncTest "track incoming max connections limits - fail on incoming":
let connMngr = ConnManager.new(maxOut = 3)
for i in 0..<3:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
# should timeout adding a connection over the limit
check not(await connMngr.getIncomingSlot().withTimeout(10.millis))
await connMngr.close()
asyncTest "track incoming max connections limits - fail on outgoing":
let connMngr = ConnManager.new(maxIn = 3)
for i in 0..<3:
check await connMngr.getIncomingSlot().withTimeout(10.millis)
# should throw adding a connection over the limit
expect TooManyConnectionsError:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot()
await connMngr.close()
2022-02-24 16:31:47 +00:00
asyncTest "allow force dial":
let connMngr = ConnManager.new(maxConnections = 2)
for i in 0..<3:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot(true)
2022-02-24 16:31:47 +00:00
# should throw adding a connection over the limit
expect TooManyConnectionsError:
2023-01-05 14:02:52 +00:00
discard connMngr.getOutgoingSlot(false)
2022-02-24 16:31:47 +00:00
await connMngr.close()
asyncTest "release slot on connection end":
let connMngr = ConnManager.new(maxConnections = 3)
2023-03-08 11:30:19 +00:00
var muxs: seq[Muxer]
for i in 0..<3:
2023-01-05 14:02:52 +00:00
let slot = connMngr.getOutgoingSlot()
2023-03-08 11:30:19 +00:00
let muxer =
getMuxer(
PeerId.init(PrivateKey.random(ECDSA, (newRng())[]).tryGet()).tryGet(),
Direction.In)
2023-03-08 11:30:19 +00:00
slot.trackMuxer(muxer)
muxs.add(muxer)
# should be full now
let incomingSlot = connMngr.getIncomingSlot()
check (await incomingSlot.withTimeout(10.millis)) == false
2022-02-24 16:31:47 +00:00
await allFuturesThrowing(
2023-03-08 11:30:19 +00:00
allFutures(muxs.mapIt( it.close() )))
check await incomingSlot.withTimeout(10.millis)
await connMngr.close()