1
1
# Nim-LibP2P
2
- # Copyright (c) 2023 Status Research & Development GmbH
2
+ # Copyright (c) 2023-2024 Status Research & Development GmbH
3
3
# Licensed under either of
4
4
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
5
5
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
@@ -45,15 +45,18 @@ proc new*(T: typedesc[MultistreamSelect]): T =
45
45
)
46
46
47
47
template validateSuffix (str: string ): untyped =
48
- if str.endsWith (" \n " ):
49
- str.removeSuffix (" \n " )
50
- else :
51
- raise newException (MultiStreamError , " MultistreamSelect failed, malformed message" )
52
-
53
- proc select * (_: MultistreamSelect | type MultistreamSelect ,
54
- conn: Connection ,
55
- proto: seq [string ]):
56
- Future [string ] {.async .} =
48
+ if str.endsWith (" \n " ):
49
+ str.removeSuffix (" \n " )
50
+ else :
51
+ raise (ref MultiStreamError )(msg:
52
+ " MultistreamSelect failed, malformed message" )
53
+
54
+ proc select * (
55
+ _: MultistreamSelect | type MultistreamSelect ,
56
+ conn: Connection ,
57
+ proto: seq [string ]
58
+ ): Future [string ] {.async : (raises: [
59
+ CancelledError , LPStreamError , MultiStreamError ]).} =
57
60
trace " initiating handshake" , conn, codec = Codec
58
61
# # select a remote protocol
59
62
await conn.writeLp (Codec & " \n " ) # write handshake
@@ -66,7 +69,7 @@ proc select*(_: MultistreamSelect | type MultistreamSelect,
66
69
67
70
if s != Codec :
68
71
notice " handshake failed" , conn, codec = s
69
- raise newException ( MultiStreamError , " MultistreamSelect handshake failed" )
72
+ raise ( ref MultiStreamError )(msg: " MultistreamSelect handshake failed" )
70
73
else :
71
74
trace " multistream handshake success" , conn
72
75
@@ -98,19 +101,29 @@ proc select*(_: MultistreamSelect | type MultistreamSelect,
98
101
# No alternatives, fail
99
102
return " "
100
103
101
- proc select * (_: MultistreamSelect | type MultistreamSelect ,
102
- conn: Connection ,
103
- proto: string ): Future [bool ] {.async .} =
104
+ proc select * (
105
+ _: MultistreamSelect | type MultistreamSelect ,
106
+ conn: Connection ,
107
+ proto: string
108
+ ): Future [bool ] {.async : (raises: [
109
+ CancelledError , LPStreamError , MultiStreamError ]).} =
104
110
if proto.len > 0 :
105
- return (await MultistreamSelect .select (conn, @ [proto])) == proto
111
+ (await MultistreamSelect .select (conn, @ [proto])) == proto
106
112
else :
107
- return (await MultistreamSelect .select (conn, @ [])) == Codec
113
+ (await MultistreamSelect .select (conn, @ [])) == Codec
108
114
109
- proc select * (m: MultistreamSelect , conn: Connection ): Future [bool ] =
115
+ proc select * (
116
+ m: MultistreamSelect ,
117
+ conn: Connection
118
+ ): Future [bool ] {.async : (raises: [
119
+ CancelledError , LPStreamError , MultiStreamError ], raw: true ).} =
110
120
m.select (conn, " " )
111
121
112
- proc list * (m: MultistreamSelect ,
113
- conn: Connection ): Future [seq [string ]] {.async .} =
122
+ proc list * (
123
+ m: MultistreamSelect ,
124
+ conn: Connection
125
+ ): Future [seq [string ]] {.async : (raises: [
126
+ CancelledError , LPStreamError , MultiStreamError ]).} =
114
127
# # list remote protos requests on connection
115
128
if not await m.select (conn):
116
129
return
@@ -126,12 +139,13 @@ proc list*(m: MultistreamSelect,
126
139
result = list
127
140
128
141
proc handle * (
129
- _: type MultistreamSelect ,
130
- conn: Connection ,
131
- protos: seq [string ],
132
- matchers = newSeq [Matcher ](),
133
- active: bool = false ,
134
- ): Future [string ] {.async .} =
142
+ _: type MultistreamSelect ,
143
+ conn: Connection ,
144
+ protos: seq [string ],
145
+ matchers = newSeq [Matcher ](),
146
+ active: bool = false
147
+ ): Future [string ] {.async : (raises: [
148
+ CancelledError , LPStreamError , MultiStreamError ]).} =
135
149
trace " Starting multistream negotiation" , conn, handshaked = active
136
150
var handshaked = active
137
151
while not conn.atEof:
@@ -140,8 +154,8 @@ proc handle*(
140
154
141
155
if not handshaked and ms != Codec :
142
156
debug " expected handshake message" , conn, instead= ms
143
- raise newException ( CatchableError ,
144
- " MultistreamSelect handling failed, invalid first message" )
157
+ raise ( ref MultiStreamError )(msg:
158
+ " MultistreamSelect handling failed, invalid first message" )
145
159
146
160
trace " handle: got request" , conn, ms
147
161
if ms.len () <= 0 :
@@ -172,26 +186,30 @@ proc handle*(
172
186
trace " no handlers" , conn, protocol = ms
173
187
await conn.writeLp (Na )
174
188
175
- proc handle * (m: MultistreamSelect , conn: Connection , active: bool = false ) {.async .} =
189
+ proc handle * (
190
+ m: MultistreamSelect ,
191
+ conn: Connection ,
192
+ active: bool = false ) {.async : (raises: [CancelledError ]).} =
176
193
trace " Starting multistream handler" , conn, handshaked = active
177
194
var
178
195
protos: seq [string ]
179
196
matchers: seq [Matcher ]
180
197
for h in m.handlers:
181
- if not isNil ( h.match) :
198
+ if h.match != nil :
182
199
matchers.add (h.match)
183
200
for proto in h.protos:
184
201
protos.add (proto)
185
202
186
203
try :
187
204
let ms = await MultistreamSelect .handle (conn, protos, matchers, active)
188
205
for h in m.handlers:
189
- if (not isNil ( h.match) and h.match (ms)) or h.protos.contains (ms):
206
+ if (h.match != nil and h.match (ms)) or h.protos.contains (ms):
190
207
trace " found handler" , conn, protocol = ms
191
208
192
209
var protocolHolder = h
193
210
let maxIncomingStreams = protocolHolder.protocol.maxIncomingStreams
194
- if protocolHolder.openedStreams.getOrDefault (conn.peerId) >= maxIncomingStreams:
211
+ if protocolHolder.openedStreams.getOrDefault (conn.peerId) >=
212
+ maxIncomingStreams:
195
213
debug " Max streams for protocol reached, blocking new stream" ,
196
214
conn, protocol = ms, maxIncomingStreams
197
215
return
@@ -242,8 +260,32 @@ proc addHandler*(m: MultistreamSelect,
242
260
protocol: protocol,
243
261
match: matcher))
244
262
245
- proc start * (m: MultistreamSelect ) {.async .} =
246
- await allFutures (m.handlers.mapIt (it.protocol.start ()))
263
+ proc start * (m: MultistreamSelect ) {.async : (raises: [CancelledError ]).} =
264
+ let
265
+ handlers = m.handlers
266
+ futs = handlers.mapIt (it.protocol.start ())
267
+ try :
268
+ await allFutures (futs)
269
+ for fut in futs:
270
+ await fut
271
+ except CancelledError as exc:
272
+ var pending: seq [Future [void ].Raising ([])]
273
+ for i, fut in futs:
274
+ if not fut.finished:
275
+ pending.add noCancel fut.cancelAndWait ()
276
+ elif fut.completed:
277
+ pending.add handlers[i].protocol.stop ()
278
+ else :
279
+ static : doAssert typeof (fut).E is (CancelledError ,)
280
+ await noCancel allFutures (pending)
281
+ raise exc
282
+
247
283
248
- proc stop * (m: MultistreamSelect ) {.async .} =
249
- await allFutures (m.handlers.mapIt (it.protocol.stop ()))
284
+ proc stop * (m: MultistreamSelect ) {.async : (raises: []).} =
285
+ # Nim 1.6.18: Using `mapIt` results in a seq of `.Raising([CancelledError])`
286
+ var futs = newSeqOfCap [Future [void ].Raising ([])](m.handlers.len)
287
+ for it in m.handlers:
288
+ futs.add it.protocol.stop ()
289
+ await noCancel allFutures (futs)
290
+ for fut in futs:
291
+ await fut
0 commit comments