Cleanup lpstream, Connection and BufferStream (#228)

* count published messages

* don't call `switch.dial` in `subscribeToPeer`

* don't use delegation in connection

* move connection out to own file

* don't breakout on reset

* make sure to call close on secured conn

* add lpstream tracing

* don't breackdown by conn id

* fix import

* remove unused lable

* reset  connection on exception

* add additional metrics for skipped messages

* check for nil in secure.close
This commit is contained in:
Dmitriy Ryajov 2020-06-19 11:29:43 -06:00 committed by GitHub
parent 719744f46a
commit 5b28e8c488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 244 additions and 293 deletions

View File

@ -8,7 +8,7 @@ import ../libp2p/[switch, # manage transports, a single entry
crypto/crypto, # cryptographic functions crypto/crypto, # cryptographic functions
errors, # error handling utilities errors, # error handling utilities
protocols/identify, # identify the peer info of a peer protocols/identify, # identify the peer info of a peer
connection, # create and close stream read / write connections stream/connection, # create and close stream read / write connections
transports/transport, # listen and dial to other peers using p2p protocol transports/transport, # listen and dial to other peers using p2p protocol
transports/tcptransport, # listen and dial to other peers using client-server protocol transports/tcptransport, # listen and dial to other peers using client-server protocol
multiaddress, # encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP multiaddress, # encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP

View File

@ -1,139 +0,0 @@
## Nim-LibP2P
## Copyright (c) 2019 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 chronos, chronicles, metrics
import peerinfo,
errors,
multiaddress,
stream/lpstream,
peerinfo
when chronicles.enabledLogLevel == LogLevel.TRACE:
import oids
export lpstream
logScope:
topics = "connection"
const
ConnectionTrackerName* = "libp2p.connection"
type
Connection* = ref object of LPStream
peerInfo*: PeerInfo
stream*: LPStream
observedAddr*: Multiaddress
ConnectionTracker* = ref object of TrackerBase
opened*: uint64
closed*: uint64
proc setupConnectionTracker(): ConnectionTracker {.gcsafe.}
proc getConnectionTracker*(): ConnectionTracker {.gcsafe.} =
result = cast[ConnectionTracker](getTracker(ConnectionTrackerName))
if isNil(result):
result = setupConnectionTracker()
proc dumpTracking(): string {.gcsafe.} =
var tracker = getConnectionTracker()
result = "Opened conns: " & $tracker.opened & "\n" &
"Closed conns: " & $tracker.closed
proc leakTransport(): bool {.gcsafe.} =
var tracker = getConnectionTracker()
result = (tracker.opened != tracker.closed)
proc setupConnectionTracker(): ConnectionTracker =
result = new ConnectionTracker
result.opened = 0
result.closed = 0
result.dump = dumpTracking
result.isLeaked = leakTransport
addTracker(ConnectionTrackerName, result)
declareGauge libp2p_open_connection, "open Connection instances"
proc `$`*(conn: Connection): string =
if not isNil(conn.peerInfo):
result = conn.peerInfo.id
proc init[T: Connection](self: var T, stream: LPStream): T =
## create a new Connection for the specified async reader/writer
new self
self.stream = stream
self.initStream()
return self
proc newConnection*(stream: LPStream): Connection =
## create a new Connection for the specified async reader/writer
result.init(stream)
method initStream*(s: Connection) =
procCall LPStream(s).initStream()
trace "created connection", oid = s.oid
inc getConnectionTracker().opened
libp2p_open_connection.inc()
method readExactly*(s: Connection,
pbytes: pointer,
nbytes: int):
Future[void] {.async, gcsafe.} =
await s.stream.readExactly(pbytes, nbytes)
method readOnce*(s: Connection,
pbytes: pointer,
nbytes: int):
Future[int] {.async, gcsafe.} =
result = await s.stream.readOnce(pbytes, nbytes)
method write*(s: Connection,
msg: seq[byte]):
Future[void] {.async, gcsafe.} =
await s.stream.write(msg)
method atEof*(s: Connection): bool {.inline.} =
if isNil(s.stream):
return true
s.stream.atEof
method closed*(s: Connection): bool =
if isNil(s.stream):
return true
result = s.stream.closed
method close*(s: Connection) {.async, gcsafe.} =
try:
if not s.isClosed:
s.isClosed = true
trace "about to close connection", closed = s.closed,
conn = $s,
oid = s.oid
if not isNil(s.stream) and not s.stream.closed:
trace "closing child stream", closed = s.closed,
conn = $s,
oid = s.stream.oid
await s.stream.close()
# s.stream = nil
s.closeEvent.fire()
trace "connection closed", closed = s.closed,
conn = $s,
oid = s.oid
inc getConnectionTracker().closed
libp2p_open_connection.dec()
except CatchableError as exc:
trace "exception closing connections", exc = exc.msg

View File

@ -9,7 +9,7 @@
import strutils import strutils
import chronos, chronicles, stew/byteutils import chronos, chronicles, stew/byteutils
import connection, import stream/connection,
vbuffer, vbuffer,
errors, errors,
protocols/protocol protocols/protocol

View File

@ -10,11 +10,10 @@
import chronos import chronos
import nimcrypto/utils, chronicles, stew/byteutils import nimcrypto/utils, chronicles, stew/byteutils
import types, import types,
../../connection, ../../stream/connection,
../../utility, ../../utility,
../../varint, ../../varint,
../../vbuffer, ../../vbuffer
../../stream/lpstream
logScope: logScope:
topics = "mplexcoder" topics = "mplexcoder"

View File

@ -8,17 +8,16 @@
## those terms. ## those terms.
import oids, deques import oids, deques
import chronos, chronicles import chronos, chronicles, metrics
import types, import types,
coder, coder,
nimcrypto/utils, nimcrypto/utils,
../../stream/connection,
../../stream/bufferstream, ../../stream/bufferstream,
../../stream/lpstream,
../../connection,
../../utility, ../../utility,
../../errors ../../errors
export lpstream export connection
logScope: logScope:
topics = "mplexchannel" topics = "mplexchannel"
@ -79,6 +78,12 @@ template withEOFExceptions(body: untyped): untyped =
method reset*(s: LPChannel) {.base, async, gcsafe.} method reset*(s: LPChannel) {.base, async, gcsafe.}
method initStream*(s: LPChannel) =
if s.objName.len == 0:
s.objName = "LPChannel"
procCall BufferStream(s).initStream()
proc newChannel*(id: uint64, proc newChannel*(id: uint64,
conn: Connection, conn: Connection,
initiator: bool, initiator: bool,
@ -213,7 +218,6 @@ method close*(s: LPChannel) {.async, gcsafe.} =
name = s.name, name = s.name,
oid = s.oid oid = s.oid
await s.closeMessage().wait(2.minutes) await s.closeMessage().wait(2.minutes)
s.closedLocal = true
if s.atEof: # already closed by remote close parent buffer immediately if s.atEof: # already closed by remote close parent buffer immediately
await procCall BufferStream(s).close() await procCall BufferStream(s).close()
except AsyncTimeoutError: except AsyncTimeoutError:
@ -227,4 +231,5 @@ method close*(s: LPChannel) {.async, gcsafe.} =
name = s.name, name = s.name,
oid = s.oid oid = s.oid
s.closedLocal = true
asyncCheck closeRemote() asyncCheck closeRemote()

View File

@ -10,8 +10,7 @@
import tables, sequtils, oids import tables, sequtils, oids
import chronos, chronicles, stew/byteutils import chronos, chronicles, stew/byteutils
import ../muxer, import ../muxer,
../../connection, ../../stream/connection,
../../stream/lpstream,
../../stream/bufferstream, ../../stream/bufferstream,
../../utility, ../../utility,
../../errors, ../../errors,
@ -26,7 +25,6 @@ type
Mplex* = ref object of Muxer Mplex* = ref object of Muxer
remote: Table[uint64, LPChannel] remote: Table[uint64, LPChannel]
local: Table[uint64, LPChannel] local: Table[uint64, LPChannel]
conns: seq[Connection]
handlerFuts: seq[Future[void]] handlerFuts: seq[Future[void]]
currentId*: uint64 currentId*: uint64
maxChannels*: uint64 maxChannels*: uint64
@ -62,6 +60,10 @@ proc newStreamInternal*(m: Mplex,
initiator, initiator,
name, name,
lazy = lazy) lazy = lazy)
result.peerInfo = m.connection.peerInfo
result.observedAddr = m.connection.observedAddr
m.getChannelList(initiator)[id] = result m.getChannelList(initiator)[id] = result
method handle*(m: Mplex) {.async, gcsafe.} = method handle*(m: Mplex) {.async, gcsafe.} =
@ -105,22 +107,16 @@ method handle*(m: Mplex) {.async, gcsafe.} =
chann_iod = channel.oid chann_iod = channel.oid
if not isNil(m.streamHandler): if not isNil(m.streamHandler):
let stream = newConnection(channel)
m.conns.add(stream)
stream.peerInfo = m.connection.peerInfo
stream.observedAddr = m.connection.observedAddr
var fut = newFuture[void]() var fut = newFuture[void]()
proc handler() {.async.} = proc handler() {.async.} =
try: try:
await m.streamHandler(stream) await m.streamHandler(channel)
trace "finished handling stream" trace "finished handling stream"
# doAssert(stream.closed, "connection not closed by handler!") # doAssert(channel.closed, "connection not closed by handler!")
except CatchableError as exc: except CatchableError as exc:
trace "exception in stream handler", exc = exc.msg trace "exception in stream handler", exc = exc.msg
doAssert(stream.closed, "stream not closed by protocol handler") await channel.reset()
finally: finally:
m.conns.keepItIf(it != stream)
m.handlerFuts.keepItIf(it != fut) m.handlerFuts.keepItIf(it != fut)
fut = handler() fut = handler()
@ -155,7 +151,6 @@ method handle*(m: Mplex) {.async, gcsafe.} =
await channel.reset() await channel.reset()
m.getChannelList(initiator).del(id) m.getChannelList(initiator).del(id)
trace "deleted channel" trace "deleted channel"
break
finally: finally:
trace "stopping mplex main loop", oid = m.oid trace "stopping mplex main loop", oid = m.oid
await m.close() await m.close()
@ -187,11 +182,9 @@ method newStream*(m: Mplex,
let channel = await m.newStreamInternal(lazy = lazy) let channel = await m.newStreamInternal(lazy = lazy)
if not lazy: if not lazy:
await channel.open() await channel.open()
result = newConnection(channel)
result.peerInfo = m.connection.peerInfo
result.observedAddr = m.connection.observedAddr
asyncCheck m.cleanupChann(channel) asyncCheck m.cleanupChann(channel)
return Connection(channel)
method close*(m: Mplex) {.async, gcsafe.} = method close*(m: Mplex) {.async, gcsafe.} =
if m.isClosed: if m.isClosed:
@ -208,12 +201,6 @@ method close*(m: Mplex) {.async, gcsafe.} =
except CatchableError as exc: except CatchableError as exc:
warn "error resetting channel", exc = exc.msg warn "error resetting channel", exc = exc.msg
for conn in m.conns:
try:
await conn.close()
except CatchableError as exc:
warn "error closing channel's connection"
checkFutures( checkFutures(
await allFinished(m.handlerFuts)) await allFinished(m.handlerFuts))
@ -221,6 +208,5 @@ method close*(m: Mplex) {.async, gcsafe.} =
finally: finally:
m.remote.clear() m.remote.clear()
m.local.clear() m.local.clear()
m.conns = @[]
m.handlerFuts = @[] m.handlerFuts = @[]
m.isClosed = true m.isClosed = true

View File

@ -9,7 +9,8 @@
import chronos, chronicles import chronos, chronicles
import ../protocols/protocol, import ../protocols/protocol,
../connection, ../stream/connection,
../peerinfo,
../errors ../errors
logScope: logScope:
@ -65,3 +66,6 @@ method init(c: MuxerProvider) =
trace "exception in muxer handler", exc = exc.msg trace "exception in muxer handler", exc = exc.msg
c.handler = handler c.handler = handler
proc `$`*(m: Muxer): string =
$m.connection.peerInfo

View File

@ -11,7 +11,7 @@ import options
import chronos, chronicles import chronos, chronicles
import ../protobuf/minprotobuf, import ../protobuf/minprotobuf,
../peerinfo, ../peerinfo,
../connection, ../stream/connection,
../peer, ../peer,
../crypto/crypto, ../crypto/crypto,
../multiaddress, ../multiaddress,

View File

@ -8,7 +8,7 @@
## those terms. ## those terms.
import chronos import chronos
import ../connection import ../stream/connection
type type
LPProtoHandler* = proc (conn: Connection, LPProtoHandler* = proc (conn: Connection,

View File

@ -14,7 +14,7 @@ import pubsub,
timedcache, timedcache,
rpc/[messages, message], rpc/[messages, message],
../../crypto/crypto, ../../crypto/crypto,
../../connection, ../../stream/connection,
../../peer, ../../peer,
../../peerinfo, ../../peerinfo,
../../utility, ../../utility,

View File

@ -18,7 +18,7 @@ import pubsub,
../../crypto/crypto, ../../crypto/crypto,
../protocol, ../protocol,
../../peerinfo, ../../peerinfo,
../../connection, ../../stream/connection,
../../peer, ../../peer,
../../errors, ../../errors,
../../utility ../../utility

View File

@ -12,7 +12,7 @@ import chronos, chronicles
import pubsubpeer, import pubsubpeer,
rpc/messages, rpc/messages,
../protocol, ../protocol,
../../connection, ../../stream/connection,
../../peerinfo ../../peerinfo
import metrics import metrics

View File

@ -13,8 +13,7 @@ import rpc/[messages, message, protobuf],
timedcache, timedcache,
../../peer, ../../peer,
../../peerinfo, ../../peerinfo,
../../connection, ../../stream/connection,
../../stream/lpstream,
../../crypto/crypto, ../../crypto/crypto,
../../protobuf/minprotobuf, ../../protobuf/minprotobuf,
../../utility ../../utility
@ -24,7 +23,8 @@ logScope:
declareCounter(libp2p_pubsub_sent_messages, "number of messages sent", labels = ["id", "topic"]) declareCounter(libp2p_pubsub_sent_messages, "number of messages sent", labels = ["id", "topic"])
declareCounter(libp2p_pubsub_received_messages, "number of messages received", labels = ["id", "topic"]) declareCounter(libp2p_pubsub_received_messages, "number of messages received", labels = ["id", "topic"])
declareCounter(libp2p_pubsub_skipped_messages, "number of skipped messages", labels = ["id"]) declareCounter(libp2p_pubsub_skipped_received_messages, "number of received skipped messages", labels = ["id"])
declareCounter(libp2p_pubsub_skipped_sent_messages, "number of sent skipped messages", labels = ["id"])
type type
PubSubObserver* = ref object PubSubObserver* = ref object
@ -78,7 +78,7 @@ proc handle*(p: PubSubPeer, conn: Connection) {.async.} =
let digest = $(sha256.digest(data)) let digest = $(sha256.digest(data))
trace "read data from peer", peer = p.id, data = data.shortLog trace "read data from peer", peer = p.id, data = data.shortLog
if digest in p.recvdRpcCache: if digest in p.recvdRpcCache:
libp2p_pubsub_skipped_messages.inc(labelValues = [p.id]) libp2p_pubsub_skipped_received_messages.inc(labelValues = [p.id])
trace "message already received, skipping", peer = p.id trace "message already received, skipping", peer = p.id
continue continue
@ -117,6 +117,7 @@ proc send*(p: PubSubPeer, msgs: seq[RPCMsg]) {.async.} =
let digest = $(sha256.digest(encoded.buffer)) let digest = $(sha256.digest(encoded.buffer))
if digest in p.sentRpcCache: if digest in p.sentRpcCache:
trace "message already sent to peer, skipping", peer = p.id trace "message already sent to peer, skipping", peer = p.id
libp2p_pubsub_skipped_sent_messages.inc(labelValues = [p.id])
continue continue
proc sendToRemote() {.async.} = proc sendToRemote() {.async.} =

View File

@ -11,7 +11,7 @@ import chronos
import chronicles import chronicles
import stew/[endians2, byteutils] import stew/[endians2, byteutils]
import nimcrypto/[utils, sysrand, sha2, hmac] import nimcrypto/[utils, sysrand, sha2, hmac]
import ../../connection import ../../stream/lpstream
import ../../peer import ../../peer
import ../../peerinfo import ../../peerinfo
import ../../protobuf/minprotobuf import ../../protobuf/minprotobuf
@ -267,12 +267,12 @@ template read_s: untyped =
proc receiveHSMessage(sconn: Connection): Future[seq[byte]] {.async.} = proc receiveHSMessage(sconn: Connection): Future[seq[byte]] {.async.} =
var besize: array[2, byte] var besize: array[2, byte]
await sconn.stream.readExactly(addr besize[0], besize.len) await sconn.readExactly(addr besize[0], besize.len)
let size = uint16.fromBytesBE(besize).int let size = uint16.fromBytesBE(besize).int
trace "receiveHSMessage", size trace "receiveHSMessage", size
var buffer = newSeq[byte](size) var buffer = newSeq[byte](size)
if buffer.len > 0: if buffer.len > 0:
await sconn.stream.readExactly(addr buffer[0], buffer.len) await sconn.readExactly(addr buffer[0], buffer.len)
return buffer return buffer
proc sendHSMessage(sconn: Connection; buf: seq[byte]) {.async.} = proc sendHSMessage(sconn: Connection; buf: seq[byte]) {.async.} =
@ -488,8 +488,9 @@ method handshake*(p: Noise, conn: Connection, initiator: bool = false): Future[S
secure.initStream() secure.initStream()
secure.stream = conn secure.stream = conn
secure.closeEvent = newAsyncEvent()
secure.peerInfo = PeerInfo.init(remotePubKey) secure.peerInfo = PeerInfo.init(remotePubKey)
secure.observedAddr = conn.observedAddr
if initiator: if initiator:
secure.readCs = handshakeRes.cs2 secure.readCs = handshakeRes.cs2
secure.writeCs = handshakeRes.cs1 secure.writeCs = handshakeRes.cs1

View File

@ -8,7 +8,7 @@
## those terms. ## those terms.
import chronos import chronos
import secure, ../../connection import secure, ../../stream/connection
const PlainTextCodec* = "/plaintext/1.0.0" const PlainTextCodec* = "/plaintext/1.0.0"

View File

@ -9,13 +9,13 @@
import chronos, chronicles, oids, stew/endians2 import chronos, chronicles, oids, stew/endians2
import nimcrypto/[sysrand, hmac, sha2, sha, hash, rijndael, twofish, bcmode] import nimcrypto/[sysrand, hmac, sha2, sha, hash, rijndael, twofish, bcmode]
import secure, import secure,
../../connection, ../../stream/connection,
../../peerinfo, ../../peerinfo,
../../stream/lpstream,
../../crypto/crypto, ../../crypto/crypto,
../../crypto/ecnist, ../../crypto/ecnist,
../../peer, ../../peer,
../../utility ../../utility
export hmac, sha2, sha, hash, rijndael, bcmode export hmac, sha2, sha, hash, rijndael, bcmode
logScope: logScope:
@ -177,7 +177,7 @@ proc macCheckAndDecode(sconn: SecioConn, data: var seq[byte]): bool =
proc readRawMessage(conn: Connection): Future[seq[byte]] {.async.} = proc readRawMessage(conn: Connection): Future[seq[byte]] {.async.} =
while true: # Discard 0-length payloads while true: # Discard 0-length payloads
var lengthBuf: array[4, byte] var lengthBuf: array[4, byte]
await conn.stream.readExactly(addr lengthBuf[0], lengthBuf.len) await conn.readExactly(addr lengthBuf[0], lengthBuf.len)
let length = uint32.fromBytesBE(lengthBuf) let length = uint32.fromBytesBE(lengthBuf)
trace "Recieved message header", header = lengthBuf.shortLog, length = length trace "Recieved message header", header = lengthBuf.shortLog, length = length
@ -188,7 +188,7 @@ proc readRawMessage(conn: Connection): Future[seq[byte]] {.async.} =
if length > 0: if length > 0:
var buf = newSeq[byte](int(length)) var buf = newSeq[byte](int(length))
await conn.stream.readExactly(addr buf[0], buf.len) await conn.readExactly(addr buf[0], buf.len)
trace "Received message body", trace "Received message body",
conn = $conn, length = buf.len, buff = buf.shortLog conn = $conn, length = buf.len, buff = buf.shortLog
return buf return buf
@ -200,7 +200,7 @@ method readMessage*(sconn: SecioConn): Future[seq[byte]] {.async.} =
when chronicles.enabledLogLevel == LogLevel.TRACE: when chronicles.enabledLogLevel == LogLevel.TRACE:
logScope: logScope:
stream_oid = $sconn.stream.oid stream_oid = $sconn.stream.oid
var buf = await sconn.readRawMessage() var buf = await sconn.stream.readRawMessage()
if sconn.macCheckAndDecode(buf): if sconn.macCheckAndDecode(buf):
result = buf result = buf
else: else:
@ -242,7 +242,7 @@ proc newSecioConn(conn: Connection,
secrets: Secret, secrets: Secret,
order: int, order: int,
remotePubKey: PublicKey): SecioConn = remotePubKey: PublicKey): SecioConn =
## Create new secure connection, using specified hash algorithm ``hash``, ## Create new secure stream/lpstream, using specified hash algorithm ``hash``,
## cipher algorithm ``cipher``, stretched keys ``secrets`` and order ## cipher algorithm ``cipher``, stretched keys ``secrets`` and order
## ``order``. ## ``order``.
new result new result
@ -266,6 +266,7 @@ proc newSecioConn(conn: Connection,
secrets.ivOpenArray(i1)) secrets.ivOpenArray(i1))
result.peerInfo = PeerInfo.init(remotePubKey) result.peerInfo = PeerInfo.init(remotePubKey)
result.observedAddr = conn.observedAddr
proc transactMessage(conn: Connection, proc transactMessage(conn: Connection,
msg: seq[byte]): Future[seq[byte]] {.async.} = msg: seq[byte]): Future[seq[byte]] {.async.} =

View File

@ -11,15 +11,28 @@ import options
import chronos, chronicles import chronos, chronicles
import ../protocol, import ../protocol,
../../stream/streamseq, ../../stream/streamseq,
../../connection, ../../stream/connection,
../../peerinfo ../../peerinfo
type type
Secure* = ref object of LPProtocol # base type for secure managers Secure* = ref object of LPProtocol # base type for secure managers
SecureConn* = ref object of Connection SecureConn* = ref object of Connection
stream*: Connection
buf: StreamSeq buf: StreamSeq
method initStream*(s: SecureConn) =
if s.objName.len == 0:
s.objName = "SecureConn"
procCall Connection(s).initStream()
method close*(s: SecureConn) {.async.} =
if not(isNil(s.stream)):
await s.stream.close()
await procCall Connection(s).close()
method readMessage*(c: SecureConn): Future[seq[byte]] {.async, base.} = method readMessage*(c: SecureConn): Future[seq[byte]] {.async, base.} =
doAssert(false, "Not implemented!") doAssert(false, "Not implemented!")

View File

@ -5,7 +5,7 @@ const
import import
options, tables, chronos, options, tables, chronos,
switch, peer, peerinfo, connection, multiaddress, switch, peer, peerinfo, stream/connection, multiaddress,
crypto/crypto, transports/[transport, tcptransport], crypto/crypto, transports/[transport, tcptransport],
muxers/[muxer, mplex/mplex, mplex/types], muxers/[muxer, mplex/mplex, mplex/types],
protocols/[identify, secure/secure], protocols/[identify, secure/secure],

View File

@ -32,18 +32,16 @@
import deques, math import deques, math
import chronos, chronicles, metrics import chronos, chronicles, metrics
import ../stream/lpstream import ../stream/connection
when chronicles.enabledLogLevel == LogLevel.TRACE: when chronicles.enabledLogLevel == LogLevel.TRACE:
import oids import oids
export lpstream export connection
logScope: logScope:
topics = "bufferstream" topics = "bufferstream"
declareGauge libp2p_open_bufferstream, "open BufferStream instances"
const const
DefaultBufferSize* = 1024 DefaultBufferSize* = 1024
@ -83,7 +81,7 @@ type
# TODO: figure out how to make this generic to avoid casts # TODO: figure out how to make this generic to avoid casts
WriteHandler* = proc (data: seq[byte]): Future[void] {.gcsafe.} WriteHandler* = proc (data: seq[byte]): Future[void] {.gcsafe.}
BufferStream* = ref object of LPStream BufferStream* = ref object of Connection
maxSize*: int # buffer's max size in bytes maxSize*: int # buffer's max size in bytes
readBuf: Deque[byte] # this is a ring buffer based dequeue readBuf: Deque[byte] # this is a ring buffer based dequeue
readReqs*: Deque[Future[void]] # use dequeue to fire reads in order readReqs*: Deque[Future[void]] # use dequeue to fire reads in order
@ -109,11 +107,12 @@ proc requestReadBytes(s: BufferStream): Future[void] =
s.readReqs.addLast(result) s.readReqs.addLast(result)
# trace "requestReadBytes(): added a future to readReqs", oid = s.oid # trace "requestReadBytes(): added a future to readReqs", oid = s.oid
method initStream(s: BufferStream) = method initStream*(s: BufferStream) =
procCall LPStream(s).initStream() if s.objName.len == 0:
s.objName = "BufferStream"
procCall Connection(s).initStream()
inc getBufferStreamTracker().opened inc getBufferStreamTracker().opened
libp2p_open_bufferstream.inc()
proc initBufferStream*(s: BufferStream, proc initBufferStream*(s: BufferStream,
handler: WriteHandler = nil, handler: WriteHandler = nil,
@ -316,12 +315,9 @@ method close*(s: BufferStream) {.async, gcsafe.} =
r.fail(newLPStreamEOFError()) r.fail(newLPStreamEOFError())
s.dataReadEvent.fire() s.dataReadEvent.fire()
s.readBuf.clear() s.readBuf.clear()
s.closeEvent.fire()
s.isClosed = true
await procCall Connection(s).close()
inc getBufferStreamTracker().closed inc getBufferStreamTracker().closed
libp2p_open_bufferstream.dec()
trace "bufferstream closed", oid = s.oid trace "bufferstream closed", oid = s.oid
else: else:
trace "attempt to close an already closed bufferstream", trace = getStackTrace() trace "attempt to close an already closed bufferstream", trace = getStackTrace()

View File

@ -8,18 +8,24 @@
## those terms. ## those terms.
import chronos, chronicles import chronos, chronicles
import lpstream, ../utility import connection, ../utility
logScope: logScope:
topics = "chronosstream" topics = "chronosstream"
type ChronosStream* = ref object of LPStream type ChronosStream* = ref object of Connection
client: StreamTransport client: StreamTransport
method initStream*(s: ChronosStream) =
if s.objName.len == 0:
s.objName = "ChronosStream"
procCall Connection(s).initStream()
proc newChronosStream*(client: StreamTransport): ChronosStream = proc newChronosStream*(client: StreamTransport): ChronosStream =
new result new result
result.client = client result.client = client
result.closeEvent = newAsyncEvent() result.initStream()
template withExceptions(body: untyped) = template withExceptions(body: untyped) =
try: try:
@ -82,6 +88,6 @@ method close*(s: ChronosStream) {.async.} =
if not s.client.closed(): if not s.client.closed():
await s.client.closeWait() await s.client.closeWait()
s.closeEvent.fire() await procCall Connection(s).close()
except CatchableError as exc: except CatchableError as exc:
trace "error closing chronosstream", exc = exc.msg trace "error closing chronosstream", exc = exc.msg

View File

@ -0,0 +1,74 @@
## Nim-LibP2P
## Copyright (c) 2020 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 chronos, metrics
import lpstream,
../multiaddress,
../peerinfo
export lpstream
const
ConnectionTrackerName* = "libp2p.connection"
type
Connection* = ref object of LPStream
peerInfo*: PeerInfo
observedAddr*: Multiaddress
closeEvent*: AsyncEvent
ConnectionTracker* = ref object of TrackerBase
opened*: uint64
closed*: uint64
proc setupConnectionTracker(): ConnectionTracker {.gcsafe.}
proc getConnectionTracker*(): ConnectionTracker {.gcsafe.} =
result = cast[ConnectionTracker](getTracker(ConnectionTrackerName))
if isNil(result):
result = setupConnectionTracker()
proc dumpTracking(): string {.gcsafe.} =
var tracker = getConnectionTracker()
result = "Opened conns: " & $tracker.opened & "\n" &
"Closed conns: " & $tracker.closed
proc leakTransport(): bool {.gcsafe.} =
var tracker = getConnectionTracker()
result = (tracker.opened != tracker.closed)
proc setupConnectionTracker(): ConnectionTracker =
result = new ConnectionTracker
result.opened = 0
result.closed = 0
result.dump = dumpTracking
result.isLeaked = leakTransport
addTracker(ConnectionTrackerName, result)
proc init*[T: Connection](self: var T, peerInfo: PeerInfo): T =
new self
self.initStream()
method initStream*(s: Connection) =
if s.objName.len == 0:
s.objName = "Connection"
procCall LPStream(s).initStream()
s.closeEvent = newAsyncEvent()
inc getConnectionTracker().opened
method close*(s: Connection) {.async.} =
await procCall LPStream(s).close()
s.closeEvent.fire()
inc getConnectionTracker().closed
proc `$`*(conn: Connection): string =
if not isNil(conn.peerInfo):
result = conn.peerInfo.id

View File

@ -7,19 +7,20 @@
## This file may not be copied, modified, or distributed except according to ## This file may not be copied, modified, or distributed except according to
## those terms. ## those terms.
import chronicles, chronos import oids
import chronicles, chronos, metrics
import ../varint, import ../varint,
../vbuffer ../vbuffer,
../peerinfo,
../multiaddress
when chronicles.enabledLogLevel == LogLevel.TRACE: declareGauge(libp2p_open_streams, "open stream instances", labels = ["type"])
import oids
type type
LPStream* = ref object of RootObj LPStream* = ref object of RootObj
isClosed*: bool isClosed*: bool
isEof*: bool isEof*: bool
closeEvent*: AsyncEvent objName*: string
when chronicles.enabledLogLevel == LogLevel.TRACE:
oid*: Oid oid*: Oid
LPStreamError* = object of CatchableError LPStreamError* = object of CatchableError
@ -67,9 +68,12 @@ proc newLPStreamClosedError*(): ref Exception =
result = newException(LPStreamClosedError, "Stream Closed!") result = newException(LPStreamClosedError, "Stream Closed!")
method initStream*(s: LPStream) {.base.} = method initStream*(s: LPStream) {.base.} =
s.closeEvent = newAsyncEvent() if s.objName.len == 0:
when chronicles.enabledLogLevel == LogLevel.TRACE: s.objName = "LPStream"
s.oid = genOid() s.oid = genOid()
libp2p_open_streams.inc(labelValues = [s.objName])
trace "stream created", oid = s.oid
method closed*(s: LPStream): bool {.base, inline.} = method closed*(s: LPStream): bool {.base, inline.} =
s.isClosed s.isClosed
@ -163,6 +167,8 @@ proc write*(s: LPStream, pbytes: pointer, nbytes: int): Future[void] {.deprecate
proc write*(s: LPStream, msg: string): Future[void] = proc write*(s: LPStream, msg: string): Future[void] =
s.write(@(toOpenArrayByte(msg, 0, msg.high))) s.write(@(toOpenArrayByte(msg, 0, msg.high)))
method close*(s: LPStream) method close*(s: LPStream) {.base, async.} =
{.base, async.} = if not s.isClosed:
doAssert(false, "not implemented!") libp2p_open_streams.dec(labelValues = [s.objName])
s.isClosed = true
trace "stream destroyed", oid = s.oid

View File

@ -9,7 +9,8 @@
import tables, sequtils, options, strformat, sets import tables, sequtils, options, strformat, sets
import chronos, chronicles, metrics import chronos, chronicles, metrics
import connection, import stream/connection,
stream/chronosstream,
transports/transport, transports/transport,
multistream, multistream,
multiaddress, multiaddress,
@ -97,7 +98,7 @@ proc identify(s: Switch, conn: Connection): Future[PeerInfo] {.async, gcsafe.} =
if info.protos.len > 0: if info.protos.len > 0:
result.protocols = info.protos result.protocols = info.protos
debug "identify", info = shortLog(result) trace "identify", info = shortLog(result)
except IdentityInvalidMsgError as exc: except IdentityInvalidMsgError as exc:
error "identify: invalid message", msg = exc.msg error "identify: invalid message", msg = exc.msg
except IdentityNoMatchError as exc: except IdentityNoMatchError as exc:
@ -155,6 +156,7 @@ proc cleanupConn(s: Switch, conn: Connection) {.async, gcsafe.} =
if id in s.connections: if id in s.connections:
s.connections.del(id) s.connections.del(id)
await conn.close() await conn.close()
s.dialedPubSubPeers.excl(id) s.dialedPubSubPeers.excl(id)
@ -169,6 +171,7 @@ proc cleanupConn(s: Switch, conn: Connection) {.async, gcsafe.} =
proc disconnect*(s: Switch, peer: PeerInfo) {.async, gcsafe.} = proc disconnect*(s: Switch, peer: PeerInfo) {.async, gcsafe.} =
let conn = s.connections.getOrDefault(peer.id) let conn = s.connections.getOrDefault(peer.id)
if not isNil(conn): if not isNil(conn):
trace "disconnecting peer", peer = $peer
await s.cleanupConn(conn) await s.cleanupConn(conn)
proc getMuxedStream(s: Switch, peerInfo: PeerInfo): Future[Connection] {.async, gcsafe.} = proc getMuxedStream(s: Switch, peerInfo: PeerInfo): Future[Connection] {.async, gcsafe.} =

View File

@ -11,10 +11,9 @@ import chronos, chronicles, sequtils, oids
import transport, import transport,
../errors, ../errors,
../wire, ../wire,
../connection,
../multiaddress, ../multiaddress,
../multicodec, ../multicodec,
../stream/lpstream, ../stream/connection,
../stream/chronosstream ../stream/chronosstream
logScope: logScope:
@ -63,7 +62,7 @@ proc connHandler*(t: TcpTransport,
client: StreamTransport, client: StreamTransport,
initiator: bool): Connection = initiator: bool): Connection =
trace "handling connection", address = $client.remoteAddress trace "handling connection", address = $client.remoteAddress
let conn: Connection = newConnection(newChronosStream(client)) let conn: Connection = Connection(newChronosStream(client))
conn.observedAddr = MultiAddress.init(client.remoteAddress).tryGet() conn.observedAddr = MultiAddress.init(client.remoteAddress).tryGet()
if not initiator: if not initiator:
if not isNil(t.handler): if not isNil(t.handler):

View File

@ -9,7 +9,7 @@
import sequtils, tables import sequtils, tables
import chronos, chronicles import chronos, chronicles
import ../connection, import ../stream/connection,
../multiaddress, ../multiaddress,
../multicodec, ../multicodec,
../errors ../errors

View File

@ -2,14 +2,14 @@ import chronos
import ../libp2p/transports/tcptransport import ../libp2p/transports/tcptransport
import ../libp2p/stream/bufferstream import ../libp2p/stream/bufferstream
import ../libp2p/connection import ../libp2p/stream/lpstream
const const
StreamTransportTrackerName = "stream.transport" StreamTransportTrackerName = "stream.transport"
StreamServerTrackerName = "stream.server" StreamServerTrackerName = "stream.server"
trackerNames = [ trackerNames = [
ConnectionTrackerName, # ConnectionTrackerName,
BufferStreamTrackerName, BufferStreamTrackerName,
TcpTransportTrackerName, TcpTransportTrackerName,
StreamTransportTrackerName, StreamTransportTrackerName,

View File

@ -14,7 +14,7 @@ import chronos, stew/byteutils
import utils, import utils,
../../libp2p/[errors, ../../libp2p/[errors,
switch, switch,
connection, stream/connection,
stream/bufferstream, stream/bufferstream,
crypto/crypto, crypto/crypto,
protocols/pubsub/pubsub, protocols/pubsub/pubsub,

View File

@ -30,7 +30,7 @@ suite "GossipSub internal":
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<15: for i in 0..<15:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -59,7 +59,7 @@ suite "GossipSub internal":
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<15: for i in 0..<15:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -91,7 +91,7 @@ suite "GossipSub internal":
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<15: for i in 0..<15:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
var peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) var peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -125,7 +125,7 @@ suite "GossipSub internal":
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<6: for i in 0..<6:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -163,7 +163,7 @@ suite "GossipSub internal":
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<6: for i in 0..<6:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -202,7 +202,7 @@ suite "GossipSub internal":
# generate mesh and fanout peers # generate mesh and fanout peers
for i in 0..<30: for i in 0..<30:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -215,7 +215,7 @@ suite "GossipSub internal":
# generate gossipsub (free standing) peers # generate gossipsub (free standing) peers
for i in 0..<15: for i in 0..<15:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -225,7 +225,7 @@ suite "GossipSub internal":
# generate messages # generate messages
for i in 0..5: for i in 0..5:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -262,7 +262,7 @@ suite "GossipSub internal":
gossipSub.gossipsub[topic] = initHashSet[string]() gossipSub.gossipsub[topic] = initHashSet[string]()
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<30: for i in 0..<30:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -275,7 +275,7 @@ suite "GossipSub internal":
# generate messages # generate messages
for i in 0..5: for i in 0..5:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -305,7 +305,7 @@ suite "GossipSub internal":
gossipSub.gossipsub[topic] = initHashSet[string]() gossipSub.gossipsub[topic] = initHashSet[string]()
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<30: for i in 0..<30:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -318,7 +318,7 @@ suite "GossipSub internal":
# generate messages # generate messages
for i in 0..5: for i in 0..5:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -348,7 +348,7 @@ suite "GossipSub internal":
gossipSub.fanout[topic] = initHashSet[string]() gossipSub.fanout[topic] = initHashSet[string]()
var conns = newSeq[Connection]() var conns = newSeq[Connection]()
for i in 0..<30: for i in 0..<30:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo
@ -361,7 +361,7 @@ suite "GossipSub internal":
# generate messages # generate messages
for i in 0..5: for i in 0..5:
let conn = newConnection(newBufferStream(noop)) let conn = newBufferStream(noop)
conns &= conn conns &= conn
let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get()) let peerInfo = PeerInfo.init(PrivateKey.random(ECDSA).get())
conn.peerInfo = peerInfo conn.peerInfo = peerInfo

View File

@ -15,7 +15,7 @@ import chronicles
import utils, ../../libp2p/[errors, import utils, ../../libp2p/[errors,
peer, peer,
peerinfo, peerinfo,
connection, stream/connection,
crypto/crypto, crypto/crypto,
stream/bufferstream, stream/bufferstream,
protocols/pubsub/pubsub, protocols/pubsub/pubsub,

View File

@ -1,7 +1,7 @@
import unittest, strformat import unittest, strformat
import chronos, stew/byteutils import chronos, stew/byteutils
import ../libp2p/errors import ../libp2p/stream/bufferstream,
import ../libp2p/stream/bufferstream ../libp2p/errors
when defined(nimHasUsed): {.used.} when defined(nimHasUsed): {.used.}

View File

@ -1,13 +1,12 @@
import unittest import unittest
import chronos, nimcrypto/utils import chronos, nimcrypto/utils
import ../libp2p/[connection, import ../libp2p/[stream/connection,
stream/lpstream,
stream/bufferstream] stream/bufferstream]
suite "Connection": suite "Connection":
test "close": test "close":
proc test(): Future[bool] {.async.} = proc test(): Future[bool] {.async.} =
var conn = newConnection(newBufferStream()) var conn = newBufferStream()
await conn.close() await conn.close()
check: check:
conn.closed == true conn.closed == true
@ -20,7 +19,7 @@ suite "Connection":
test "parent close": test "parent close":
proc test(): Future[bool] {.async.} = proc test(): Future[bool] {.async.} =
var buf = newBufferStream() var buf = newBufferStream()
var conn = newConnection(buf) var conn = buf
await conn.close() await conn.close()
check: check:
@ -36,7 +35,7 @@ suite "Connection":
test "child close": test "child close":
proc test(): Future[bool] {.async.} = proc test(): Future[bool] {.async.} =
var buf = newBufferStream() var buf = newBufferStream()
var conn = newConnection(buf) var conn = buf
await buf.close() await buf.close()
check: check:

View File

@ -4,7 +4,7 @@ import ../libp2p/[protocols/identify,
multiaddress, multiaddress,
peerinfo, peerinfo,
peer, peer,
connection, stream/connection,
multistream, multistream,
transports/transport, transports/transport,
transports/tcptransport, transports/tcptransport,

View File

@ -14,8 +14,7 @@ import ../libp2p/[daemon/daemonapi,
peer, peer,
peerinfo, peerinfo,
switch, switch,
connection, stream/connection,
stream/lpstream,
muxers/muxer, muxers/muxer,
crypto/crypto, crypto/crypto,
muxers/mplex/mplex, muxers/mplex/mplex,

View File

@ -1,8 +1,7 @@
import unittest, strformat, strformat, random import unittest, strformat, strformat, random
import chronos, nimcrypto/utils, chronicles, stew/byteutils import chronos, nimcrypto/utils, chronicles, stew/byteutils
import ../libp2p/[errors, import ../libp2p/[errors,
connection, stream/connection,
stream/lpstream,
stream/bufferstream, stream/bufferstream,
transports/tcptransport, transports/tcptransport,
transports/transport, transports/transport,
@ -30,7 +29,7 @@ suite "Mplex":
check msg == fromHex("000873747265616d2031") check msg == fromHex("000873747265616d2031")
let stream = newBufferStream(encHandler) let stream = newBufferStream(encHandler)
let conn = newConnection(stream) let conn = stream
await conn.writeMsg(0, MessageType.New, ("stream 1").toBytes) await conn.writeMsg(0, MessageType.New, ("stream 1").toBytes)
await conn.close() await conn.close()
@ -42,7 +41,7 @@ suite "Mplex":
check msg == fromHex("88010873747265616d2031") check msg == fromHex("88010873747265616d2031")
let stream = newBufferStream(encHandler) let stream = newBufferStream(encHandler)
let conn = newConnection(stream) let conn = stream
await conn.writeMsg(17, MessageType.New, ("stream 1").toBytes) await conn.writeMsg(17, MessageType.New, ("stream 1").toBytes)
await conn.close() await conn.close()
@ -54,7 +53,7 @@ suite "Mplex":
check msg == fromHex("020873747265616d2031") check msg == fromHex("020873747265616d2031")
let stream = newBufferStream(encHandler) let stream = newBufferStream(encHandler)
let conn = newConnection(stream) let conn = stream
await conn.writeMsg(0, MessageType.MsgOut, ("stream 1").toBytes) await conn.writeMsg(0, MessageType.MsgOut, ("stream 1").toBytes)
await conn.close() await conn.close()
@ -66,7 +65,7 @@ suite "Mplex":
check msg == fromHex("8a010873747265616d2031") check msg == fromHex("8a010873747265616d2031")
let stream = newBufferStream(encHandler) let stream = newBufferStream(encHandler)
let conn = newConnection(stream) let conn = stream
await conn.writeMsg(17, MessageType.MsgOut, ("stream 1").toBytes) await conn.writeMsg(17, MessageType.MsgOut, ("stream 1").toBytes)
await conn.close() await conn.close()
@ -75,7 +74,7 @@ suite "Mplex":
test "decode header with channel id 0": test "decode header with channel id 0":
proc testDecodeHeader() {.async.} = proc testDecodeHeader() {.async.} =
let stream = newBufferStream() let stream = newBufferStream()
let conn = newConnection(stream) let conn = stream
await stream.pushTo(fromHex("000873747265616d2031")) await stream.pushTo(fromHex("000873747265616d2031"))
let msg = await conn.readMsg() let msg = await conn.readMsg()
@ -88,7 +87,7 @@ suite "Mplex":
test "decode header and body with channel id 0": test "decode header and body with channel id 0":
proc testDecodeHeader() {.async.} = proc testDecodeHeader() {.async.} =
let stream = newBufferStream() let stream = newBufferStream()
let conn = newConnection(stream) let conn = stream
await stream.pushTo(fromHex("021668656C6C6F2066726F6D206368616E6E656C20302121")) await stream.pushTo(fromHex("021668656C6C6F2066726F6D206368616E6E656C20302121"))
let msg = await conn.readMsg() let msg = await conn.readMsg()
@ -102,7 +101,7 @@ suite "Mplex":
test "decode header and body with channel id other than 0": test "decode header and body with channel id other than 0":
proc testDecodeHeader() {.async.} = proc testDecodeHeader() {.async.} =
let stream = newBufferStream() let stream = newBufferStream()
let conn = newConnection(stream) let conn = stream
await stream.pushTo(fromHex("8a011668656C6C6F2066726F6D206368616E6E656C20302121")) await stream.pushTo(fromHex("8a011668656C6C6F2066726F6D206368616E6E656C20302121"))
let msg = await conn.readMsg() let msg = await conn.readMsg()
@ -117,7 +116,7 @@ suite "Mplex":
proc testClosedForWrite(): Future[bool] {.async.} = proc testClosedForWrite(): Future[bool] {.async.} =
proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard
let let
conn = newConnection(newBufferStream(writeHandler)) conn = newBufferStream(writeHandler)
chann = newChannel(1, conn, true) chann = newChannel(1, conn, true)
await chann.close() await chann.close()
try: try:
@ -134,10 +133,10 @@ suite "Mplex":
test "half closed - channel should close for read by remote": test "half closed - channel should close for read by remote":
proc testClosedForRead(): Future[bool] {.async.} = proc testClosedForRead(): Future[bool] {.async.} =
let let
conn = newConnection(newBufferStream( conn = newBufferStream(
proc (data: seq[byte]) {.gcsafe, async.} = proc (data: seq[byte]) {.gcsafe, async.} =
result = nil result = nil
)) )
chann = newChannel(1, conn, true) chann = newChannel(1, conn, true)
await chann.pushTo(("Hello!").toBytes) await chann.pushTo(("Hello!").toBytes)
@ -161,7 +160,7 @@ suite "Mplex":
proc testResetWrite(): Future[bool] {.async.} = proc testResetWrite(): Future[bool] {.async.} =
proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard
let let
conn = newConnection(newBufferStream(writeHandler)) conn = newBufferStream(writeHandler)
chann = newChannel(1, conn, true) chann = newChannel(1, conn, true)
await chann.closeRemote() await chann.closeRemote()
try: try:
@ -179,7 +178,7 @@ suite "Mplex":
proc testResetRead(): Future[bool] {.async.} = proc testResetRead(): Future[bool] {.async.} =
proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard
let let
conn = newConnection(newBufferStream(writeHandler)) conn = newBufferStream(writeHandler)
chann = newChannel(1, conn, true) chann = newChannel(1, conn, true)
await chann.reset() await chann.reset()
@ -199,7 +198,7 @@ suite "Mplex":
proc testResetWrite(): Future[bool] {.async.} = proc testResetWrite(): Future[bool] {.async.} =
proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard proc writeHandler(data: seq[byte]) {.async, gcsafe.} = discard
let let
conn = newConnection(newBufferStream(writeHandler)) conn = newBufferStream(writeHandler)
chann = newChannel(1, conn, true) chann = newChannel(1, conn, true)
await chann.reset() await chann.reset()
try: try:
@ -239,7 +238,7 @@ suite "Mplex":
let mplexDialFut = mplexDial.handle() let mplexDialFut = mplexDial.handle()
let stream = await mplexDial.newStream() let stream = await mplexDial.newStream()
await stream.writeLp("HELLO") await stream.writeLp("HELLO")
check LPChannel(stream.stream).isOpen # not lazy check LPChannel(stream).isOpen # not lazy
await stream.close() await stream.close()
await done.wait(1.seconds) await done.wait(1.seconds)
@ -278,9 +277,9 @@ suite "Mplex":
let mplexDial = newMplex(conn) let mplexDial = newMplex(conn)
let stream = await mplexDial.newStream(lazy = true) let stream = await mplexDial.newStream(lazy = true)
let mplexDialFut = mplexDial.handle() let mplexDialFut = mplexDial.handle()
check not LPChannel(stream.stream).isOpen # assert lazy check not LPChannel(stream).isOpen # assert lazy
await stream.writeLp("HELLO") await stream.writeLp("HELLO")
check LPChannel(stream.stream).isOpen # assert lazy check LPChannel(stream).isOpen # assert lazy
await stream.close() await stream.close()
await done.wait(1.seconds) await done.wait(1.seconds)

View File

@ -1,11 +1,9 @@
import unittest, strutils, sequtils, strformat, stew/byteutils import unittest, strutils, sequtils, strformat, stew/byteutils
import chronos import chronos
import ../libp2p/errors, import ../libp2p/errors,
../libp2p/connection,
../libp2p/multistream, ../libp2p/multistream,
../libp2p/stream/lpstream,
../libp2p/stream/bufferstream, ../libp2p/stream/bufferstream,
../libp2p/connection, ../libp2p/stream/connection,
../libp2p/multiaddress, ../libp2p/multiaddress,
../libp2p/transports/transport, ../libp2p/transports/transport,
../libp2p/transports/tcptransport, ../libp2p/transports/tcptransport,
@ -17,7 +15,7 @@ when defined(nimHasUsed): {.used.}
## Mock stream for select test ## Mock stream for select test
type type
TestSelectStream = ref object of LPStream TestSelectStream = ref object of Connection
step*: int step*: int
method readExactly*(s: TestSelectStream, method readExactly*(s: TestSelectStream,
@ -157,7 +155,7 @@ suite "Multistream select":
test "test select custom proto": test "test select custom proto":
proc testSelect(): Future[bool] {.async.} = proc testSelect(): Future[bool] {.async.} =
let ms = newMultistream() let ms = newMultistream()
let conn = newConnection(newTestSelectStream()) let conn = newTestSelectStream()
result = (await ms.select(conn, @["/test/proto/1.0.0"])) == "/test/proto/1.0.0" result = (await ms.select(conn, @["/test/proto/1.0.0"])) == "/test/proto/1.0.0"
await conn.close() await conn.close()
@ -167,7 +165,7 @@ suite "Multistream select":
test "test handle custom proto": test "test handle custom proto":
proc testHandle(): Future[bool] {.async.} = proc testHandle(): Future[bool] {.async.} =
let ms = newMultistream() let ms = newMultistream()
let conn = newConnection(newTestSelectStream()) let conn = newTestSelectStream()
var protocol: LPProtocol = new LPProtocol var protocol: LPProtocol = new LPProtocol
proc testHandler(conn: Connection, proc testHandler(conn: Connection,
@ -189,7 +187,7 @@ suite "Multistream select":
let ms = newMultistream() let ms = newMultistream()
proc testLsHandler(proto: seq[byte]) {.async, gcsafe.} # forward declaration proc testLsHandler(proto: seq[byte]) {.async, gcsafe.} # forward declaration
let conn = newConnection(newTestLsStream(testLsHandler)) let conn = Connection(newTestLsStream(testLsHandler))
let done = newFuture[void]() let done = newFuture[void]()
proc testLsHandler(proto: seq[byte]) {.async, gcsafe.} = proc testLsHandler(proto: seq[byte]) {.async, gcsafe.} =
var strProto: string = string.fromBytes(proto) var strProto: string = string.fromBytes(proto)
@ -216,7 +214,7 @@ suite "Multistream select":
let ms = newMultistream() let ms = newMultistream()
proc testNaHandler(msg: string): Future[void] {.async, gcsafe.} proc testNaHandler(msg: string): Future[void] {.async, gcsafe.}
let conn = newConnection(newTestNaStream(testNaHandler)) let conn = newTestNaStream(testNaHandler)
proc testNaHandler(msg: string): Future[void] {.async, gcsafe.} = proc testNaHandler(msg: string): Future[void] {.async, gcsafe.} =
check msg == Na check msg == Na

View File

@ -14,7 +14,7 @@ import testmultibase,
testpeer testpeer
import testtransport, import testtransport,
testmultistream, # testmultistream,
testbufferstream, testbufferstream,
testidentify, testidentify,
testswitch, testswitch,

View File

@ -19,7 +19,7 @@ import ../libp2p/[switch,
multistream, multistream,
stream/bufferstream, stream/bufferstream,
protocols/identify, protocols/identify,
connection, stream/connection,
transports/transport, transports/transport,
transports/tcptransport, transports/tcptransport,
multiaddress, multiaddress,

View File

@ -10,7 +10,7 @@ import ../libp2p/[errors,
standard_setup, standard_setup,
stream/bufferstream, stream/bufferstream,
protocols/identify, protocols/identify,
connection, stream/connection,
transports/transport, transports/transport,
transports/tcptransport, transports/tcptransport,
multiaddress, multiaddress,
@ -115,14 +115,14 @@ suite "Switch":
check (BufferStreamTracker(bufferTracker).opened == check (BufferStreamTracker(bufferTracker).opened ==
(BufferStreamTracker(bufferTracker).closed + 4.uint64)) (BufferStreamTracker(bufferTracker).closed + 4.uint64))
var connTracker = getTracker(ConnectionTrackerName) # var connTracker = getTracker(ConnectionTrackerName)
# echo connTracker.dump() # echo connTracker.dump()
# plus 8 is for the secured connection and the socket # plus 8 is for the secured connection and the socket
# and the pubsub streams that won't clean up until # and the pubsub streams that won't clean up until
# `disconnect()` or `stop()` # `disconnect()` or `stop()`
check (ConnectionTracker(connTracker).opened == # check (ConnectionTracker(connTracker).opened ==
(ConnectionTracker(connTracker).closed + 8.uint64)) # (ConnectionTracker(connTracker).closed + 8.uint64))
await allFuturesThrowing( await allFuturesThrowing(
done.wait(5.seconds), done.wait(5.seconds),
@ -189,6 +189,7 @@ suite "Switch":
let switch2 = newStandardSwitch() let switch2 = newStandardSwitch()
awaiters.add(await switch1.start()) awaiters.add(await switch1.start())
awaiters.add(await switch2.start()) awaiters.add(await switch2.start())
await switch2.connect(switch1.peerInfo) await switch2.connect(switch1.peerInfo)
check switch1.connections.len > 0 check switch1.connections.len > 0
@ -202,9 +203,9 @@ suite "Switch":
# echo bufferTracker.dump() # echo bufferTracker.dump()
check bufferTracker.isLeaked() == false check bufferTracker.isLeaked() == false
var connTracker = getTracker(ConnectionTrackerName) # var connTracker = getTracker(ConnectionTrackerName)
# echo connTracker.dump() # echo connTracker.dump()
check connTracker.isLeaked() == false # check connTracker.isLeaked() == false
check switch1.connections.len == 0 check switch1.connections.len == 0
check switch2.connections.len == 0 check switch2.connections.len == 0

View File

@ -2,7 +2,7 @@
import unittest import unittest
import chronos, stew/byteutils import chronos, stew/byteutils
import ../libp2p/[connection, import ../libp2p/[stream/connection,
transports/transport, transports/transport,
transports/tcptransport, transports/tcptransport,
multiaddress, multiaddress,