569 lines
17 KiB
Nim
569 lines
17 KiB
Nim
{.used.}
|
|
|
|
import unittest, options
|
|
import chronos
|
|
import stew/byteutils
|
|
import nimcrypto/sysrand
|
|
import ../libp2p/[errors,
|
|
switch,
|
|
multistream,
|
|
standard_setup,
|
|
stream/bufferstream,
|
|
stream/connection,
|
|
multiaddress,
|
|
peerinfo,
|
|
crypto/crypto,
|
|
protocols/protocol,
|
|
muxers/muxer,
|
|
muxers/mplex/mplex,
|
|
stream/lpstream]
|
|
import ./helpers
|
|
|
|
const
|
|
TestCodec = "/test/proto/1.0.0"
|
|
|
|
type
|
|
TestProto = ref object of LPProtocol
|
|
|
|
suite "Switch":
|
|
teardown:
|
|
for tracker in testTrackers():
|
|
# echo tracker.dump()
|
|
check tracker.isLeaked() == false
|
|
|
|
test "e2e use switch dial proto string":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
var awaiters: seq[Future[void]]
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
let conn = await switch2.dial(switch1.peerInfo, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await allFuturesThrowing(
|
|
done.wait(5.seconds),
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
|
|
# this needs to go at end
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should not leak bufferstreams and connections on channel close":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
let done = newFuture[void]()
|
|
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.writeLp("Hello!")
|
|
finally:
|
|
await conn.close()
|
|
done.complete()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
var awaiters: seq[Future[void]]
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
let conn = await switch2.dial(switch1.peerInfo, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
await conn.close()
|
|
|
|
await sleepAsync(2.seconds) # wait a little for cleanup to happen
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
|
|
# plus 4 for the pubsub streams
|
|
check (BufferStreamTracker(bufferTracker).opened ==
|
|
(BufferStreamTracker(bufferTracker).closed))
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
|
|
# plus 8 is for the secured connection and the socket
|
|
# and the pubsub streams that won't clean up until
|
|
# `disconnect()` or `stop()`
|
|
check (ConnectionTracker(connTracker).opened ==
|
|
(ConnectionTracker(connTracker).closed + 4.uint64))
|
|
|
|
await allFuturesThrowing(
|
|
done.wait(5.seconds),
|
|
switch1.stop(),
|
|
switch2.stop(),
|
|
)
|
|
|
|
# this needs to go at end
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e use connect then dial":
|
|
proc testSwitch(): Future[bool] {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
proc handle(conn: Connection, proto: string) {.async, gcsafe.} =
|
|
try:
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
finally:
|
|
await conn.writeLp("Hello!")
|
|
await conn.close()
|
|
|
|
let testProto = new TestProto
|
|
testProto.codec = TestCodec
|
|
testProto.handler = handle
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
switch1.mount(testProto)
|
|
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Noise])
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
let conn = await switch2.dial(switch1.peerInfo, TestCodec)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
try:
|
|
await conn.writeLp("Hello!")
|
|
let msg = string.fromBytes(await conn.readLp(1024))
|
|
check "Hello!" == msg
|
|
result = true
|
|
except LPStreamError:
|
|
result = false
|
|
|
|
await allFuturesThrowing(
|
|
conn.close(),
|
|
switch1.stop(),
|
|
switch2.stop()
|
|
)
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
check:
|
|
waitFor(testSwitch()) == true
|
|
|
|
test "e2e should not leak on peer disconnect":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo)
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should trigger connection events (remote)":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
|
|
var step = 0
|
|
var kinds: set[ConnEventKind]
|
|
proc hook(peerId: PeerID, event: ConnEvent) {.async, gcsafe.} =
|
|
kinds = kinds + {event.kind}
|
|
case step:
|
|
of 0:
|
|
check:
|
|
event.kind == ConnEventKind.Connected
|
|
peerId == switch1.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == ConnEventKind.Disconnected
|
|
|
|
check peerId == switch1.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch2.addConnEventHandler(hook, ConnEventKind.Connected)
|
|
switch2.addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo)
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
check:
|
|
kinds == {
|
|
ConnEventKind.Connected,
|
|
ConnEventKind.Disconnected
|
|
}
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should trigger connection events (local)":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
|
|
var step = 0
|
|
var kinds: set[ConnEventKind]
|
|
proc hook(peerId: PeerID, event: ConnEvent) {.async, gcsafe.} =
|
|
kinds = kinds + {event.kind}
|
|
case step:
|
|
of 0:
|
|
check:
|
|
event.kind == ConnEventKind.Connected
|
|
peerId == switch2.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event.kind == ConnEventKind.Disconnected
|
|
|
|
check peerId == switch2.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch1.addConnEventHandler(hook, ConnEventKind.Connected)
|
|
switch1.addConnEventHandler(hook, ConnEventKind.Disconnected)
|
|
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo)
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
check:
|
|
kinds == {
|
|
ConnEventKind.Connected,
|
|
ConnEventKind.Disconnected
|
|
}
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should trigger peer events (remote)":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEvent]
|
|
proc handler(peerId: PeerID, event: PeerEvent) {.async, gcsafe.} =
|
|
kinds = kinds + {event}
|
|
case step:
|
|
of 0:
|
|
check:
|
|
event == PeerEvent.Joined
|
|
peerId == switch2.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event == PeerEvent.Left
|
|
peerId == switch2.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch1.addPeerEventHandler(handler, PeerEvent.Joined)
|
|
switch1.addPeerEventHandler(handler, PeerEvent.Left)
|
|
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo)
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
check:
|
|
kinds == {
|
|
PeerEvent.Joined,
|
|
PeerEvent.Left
|
|
}
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should trigger peer events (local)":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
let switch2 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEvent]
|
|
proc handler(peerId: PeerID, event: PeerEvent) {.async, gcsafe.} =
|
|
kinds = kinds + {event}
|
|
case step:
|
|
of 0:
|
|
check:
|
|
event == PeerEvent.Joined
|
|
peerId == switch1.peerInfo.peerId
|
|
of 1:
|
|
check:
|
|
event == PeerEvent.Left
|
|
peerId == switch1.peerInfo.peerId
|
|
else:
|
|
check false
|
|
|
|
step.inc()
|
|
|
|
switch2.addPeerEventHandler(handler, PeerEvent.Joined)
|
|
switch2.addPeerEventHandler(handler, PeerEvent.Left)
|
|
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
|
|
await switch2.connect(switch1.peerInfo)
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo)
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
check:
|
|
kinds == {
|
|
PeerEvent.Joined,
|
|
PeerEvent.Left
|
|
}
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|
|
|
|
test "e2e should trigger peer events only once per peer":
|
|
proc testSwitch() {.async, gcsafe.} =
|
|
var awaiters: seq[Future[void]]
|
|
|
|
let switch1 = newStandardSwitch(secureManagers = [SecureProtocol.Secio])
|
|
|
|
let rng = newRng()
|
|
# use same private keys to emulate two connection from same peer
|
|
let privKey = PrivateKey.random(rng[]).tryGet()
|
|
let switch2 = newStandardSwitch(
|
|
privKey = some(privKey),
|
|
rng = rng,
|
|
secureManagers = [SecureProtocol.Secio])
|
|
|
|
let switch3 = newStandardSwitch(
|
|
privKey = some(privKey),
|
|
rng = rng,
|
|
secureManagers = [SecureProtocol.Secio])
|
|
|
|
var step = 0
|
|
var kinds: set[PeerEvent]
|
|
proc handler(peerId: PeerID, event: PeerEvent) {.async, gcsafe.} =
|
|
kinds = kinds + {event}
|
|
case step:
|
|
of 0:
|
|
check:
|
|
event == PeerEvent.Joined
|
|
of 1:
|
|
check:
|
|
event == PeerEvent.Left
|
|
else:
|
|
check false # should not trigger this
|
|
|
|
step.inc()
|
|
|
|
switch1.addPeerEventHandler(handler, PeerEvent.Joined)
|
|
switch1.addPeerEventHandler(handler, PeerEvent.Left)
|
|
|
|
awaiters.add(await switch1.start())
|
|
awaiters.add(await switch2.start())
|
|
awaiters.add(await switch3.start())
|
|
|
|
await switch2.connect(switch1.peerInfo) # should trigger 1st Join event
|
|
await switch3.connect(switch1.peerInfo) # should trigger 2nd Join event
|
|
|
|
check switch1.isConnected(switch2.peerInfo)
|
|
check switch2.isConnected(switch1.peerInfo)
|
|
check switch3.isConnected(switch1.peerInfo)
|
|
|
|
await sleepAsync(100.millis)
|
|
await switch2.disconnect(switch1.peerInfo) # should trigger 1st Left event
|
|
await switch3.disconnect(switch1.peerInfo) # should trigger 2nd Left event
|
|
await sleepAsync(2.seconds)
|
|
|
|
check not switch1.isConnected(switch2.peerInfo)
|
|
check not switch2.isConnected(switch1.peerInfo)
|
|
check not switch3.isConnected(switch1.peerInfo)
|
|
|
|
var bufferTracker = getTracker(BufferStreamTrackerName)
|
|
# echo bufferTracker.dump()
|
|
check bufferTracker.isLeaked() == false
|
|
|
|
var connTracker = getTracker(ConnectionTrackerName)
|
|
# echo connTracker.dump()
|
|
check connTracker.isLeaked() == false
|
|
|
|
check:
|
|
kinds == {
|
|
PeerEvent.Joined,
|
|
PeerEvent.Left
|
|
}
|
|
|
|
await allFuturesThrowing(
|
|
switch1.stop(),
|
|
switch2.stop(),
|
|
switch3.stop())
|
|
await allFuturesThrowing(awaiters)
|
|
|
|
waitFor(testSwitch())
|