diff --git a/examples/helloworld.nim b/examples/helloworld.nim index 4c4aead6c..1844a96ea 100644 --- a/examples/helloworld.nim +++ b/examples/helloworld.nim @@ -20,7 +20,7 @@ proc new(T: typedesc[TestProto]): T = # We must close the connections ourselves when we're done with it await conn.close() - return T(codecs: @[TestCodec], handler: handle) + return T.new(codecs = @[TestCodec], handler = handle) ## # Helper to create a switch/node diff --git a/libp2p/multistream.nim b/libp2p/multistream.nim index 2cd9ce296..1c7449d6f 100644 --- a/libp2p/multistream.nim +++ b/libp2p/multistream.nim @@ -246,10 +246,14 @@ proc addHandler*(m: MultistreamSelect, matcher: Matcher = nil) = addHandler(m, @[codec], protocol, matcher) -proc addHandler*(m: MultistreamSelect, - codec: string, - handler: LPProtoHandler, - matcher: Matcher = nil) = +proc addHandler*[E]( + m: MultistreamSelect, + codec: string, + handler: LPProtoHandler | + proc ( + conn: Connection, + proto: string): InternalRaisesFuture[void, E], + matcher: Matcher = nil) = ## helper to allow registering pure handlers trace "registering proto handler", proto = codec let protocol = new LPProtocol diff --git a/libp2p/protocols/protocol.nim b/libp2p/protocols/protocol.nim index 1fd2eb9a1..2cc1ccf68 100644 --- a/libp2p/protocols/protocol.nim +++ b/libp2p/protocols/protocol.nim @@ -19,14 +19,12 @@ const type LPProtoHandler* = proc ( - conn: Connection, - proto: string): - Future[void] - {.gcsafe, raises: [].} + conn: Connection, + proto: string): Future[void] {.async.} LPProtocol* = ref object of RootObj codecs*: seq[string] - handler*: LPProtoHandler ## this handler gets invoked by the protocol negotiator + handlerImpl: LPProtoHandler ## invoked by the protocol negotiator started*: bool maxIncomingStreams: Opt[int] @@ -52,7 +50,7 @@ proc `maxIncomingStreams=`*(p: LPProtocol, val: int) = p.maxIncomingStreams = Opt.some(val) func codec*(p: LPProtocol): string = - assert(p.codecs.len > 0, "Codecs sequence was empty!") + doAssert(p.codecs.len > 0, "Codecs sequence was empty!") p.codecs[0] func `codec=`*(p: LPProtocol, codec: string) = @@ -60,15 +58,51 @@ func `codec=`*(p: LPProtocol, codec: string) = # if we use this abstraction p.codecs.insert(codec, 0) +template `handler`*(p: LPProtocol): LPProtoHandler = + p.handlerImpl + +template `handler`*( + p: LPProtocol, conn: Connection, proto: string): Future[void] = + p.handlerImpl(conn, proto) + +func `handler=`*(p: LPProtocol, handler: LPProtoHandler) = + p.handlerImpl = handler + +# Callbacks that are annotated with `{.async: (raises).}` explicitly +# document the types of errors that they may raise, but are not compatible +# with `LPProtoHandler` and need to use a custom `proc` type. +# They are internally wrapped into a `LPProtoHandler`, but still allow the +# compiler to check that their `{.async: (raises).}` annotation is correct. +# https://github.com/nim-lang/Nim/issues/23432 +func `handler=`*[E]( + p: LPProtocol, + handler: proc ( + conn: Connection, + proto: string): InternalRaisesFuture[void, E]) = + proc wrap(conn: Connection, proto: string): Future[void] {.async.} = + await handler(conn, proto) + p.handlerImpl = wrap + proc new*( - T: type LPProtocol, - codecs: seq[string], - handler: LPProtoHandler, - maxIncomingStreams: Opt[int] | int = Opt.none(int)): T = + T: type LPProtocol, + codecs: seq[string], + handler: LPProtoHandler, + maxIncomingStreams: Opt[int] | int = Opt.none(int)): T = T( codecs: codecs, - handler: handler, + handlerImpl: handler, maxIncomingStreams: when maxIncomingStreams is int: Opt.some(maxIncomingStreams) else: maxIncomingStreams ) + +proc new*[E]( + T: type LPProtocol, + codecs: seq[string], + handler: proc ( + conn: Connection, + proto: string): InternalRaisesFuture[void, E], + maxIncomingStreams: Opt[int] | int = Opt.none(int)): T = + proc wrap(conn: Connection, proto: string): Future[void] {.async.} = + await handler(conn, proto) + T.new(codec, wrap, maxIncomingStreams) diff --git a/tests/errorhelpers.nim b/tests/errorhelpers.nim index c7188f5dd..6c6a374b9 100644 --- a/tests/errorhelpers.nim +++ b/tests/errorhelpers.nim @@ -24,6 +24,6 @@ proc allFuturesThrowing*(args: varargs[FutureBase]): Future[void] = proc allFuturesThrowing*[T](futs: varargs[Future[T]]): Future[void] = allFuturesThrowing(futs.mapIt(FutureBase(it))) -proc allFuturesThrowing*[T, E]( +proc allFuturesThrowing*[T, E]( # https://github.com/nim-lang/Nim/issues/23432 futs: varargs[InternalRaisesFuture[T, E]]): Future[void] = allFuturesThrowing(futs.mapIt(FutureBase(it))) diff --git a/tests/testtortransport.nim b/tests/testtortransport.nim index aaf4011da..7b4af7ada 100644 --- a/tests/testtortransport.nim +++ b/tests/testtortransport.nim @@ -1,7 +1,7 @@ {.used.} # Nim-Libp2p -# Copyright (c) 2023 Status Research & Development GmbH +# 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)) @@ -88,7 +88,6 @@ suite "Tor transport": # every incoming connections will be in handled in this closure proc handle(conn: Connection, proto: string) {.async.} = - var resp: array[6, byte] await conn.readExactly(addr resp, 6) check string.fromBytes(resp) == "client" @@ -97,7 +96,7 @@ suite "Tor transport": # We must close the connections ourselves when we're done with it await conn.close() - return T(codecs: @[TestCodec], handler: handle) + return T.new(codecs = @[TestCodec], handler = handle) let rng = newRng()