Skip to content

Commit aee258c

Browse files
kppmxindenAgeManningrubdosthomaseizinger
authored
Merge master (#18)
* protocols/gossipsub: Fix inconsistency in mesh peer tracking (libp2p#2189) Co-authored-by: Age Manning <[email protected]> * misc/metrics: Add auxiliary crate to record events as OpenMetrics (libp2p#2063) This commit adds an auxiliary crate recording protocol and Swarm events and exposing them as metrics in the OpenMetrics format. * README: Mention [email protected] * examples/: Add file sharing example (libp2p#2186) Basic file sharing application with peers either providing or locating and getting files by name. While obviously showcasing how to build a basic file sharing application, the actual goal of this example is **to show how to integrate rust-libp2p into a larger application**. Architectural properties - Clean clonable async/await interface ([`Client`]) to interact with the network layer. - Single task driving the network layer, no locks required. * examples/README: Give an overview over the many examples (libp2p#2194) * protocols/kad: Enable filtering of (provider) records (libp2p#2163) Introduce `KademliaStoreInserts` option, which allows to filter records. Co-authored-by: Max Inden <[email protected]> * swarm/src/protocols_handler: Impl ProtocolsHandler on either::Either (libp2p#2192) Implement ProtocolsHandler on either::Either representing either of two ProtocolsHandler implementations. Co-authored-by: Thomas Eizinger <[email protected]> * *: Make libp2p-core default features optional (libp2p#2181) Co-authored-by: Max Inden <[email protected]> * core/: Remove DisconnectedPeer::set_connected and Pool::add (libp2p#2195) This logic seems to be a leftover of libp2p#889 and unused today. * protocols/gossipsub: Use ed25519 in tests (libp2p#2197) With f2905c0 the secp256k1 feature is disabled by default. Instead of enabling it in the dev-dependency, simply use ed25519. * build(deps): Update minicbor requirement from 0.10 to 0.11 (libp2p#2200) Updates the requirements on [minicbor](https://gitlab.com/twittner/minicbor) to permit the latest version. - [Release notes](https://gitlab.com/twittner/minicbor/tags) - [Changelog](https://gitlab.com/twittner/minicbor/blob/master/CHANGELOG.md) - [Commits](https://gitlab.com/twittner/minicbor/compare/minicbor-v0.10.0...minicbor-v0.11.0) --- updated-dependencies: - dependency-name: minicbor dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): Update salsa20 requirement from 0.8 to 0.9 (libp2p#2206) * build(deps): Update salsa20 requirement from 0.8 to 0.9 Updates the requirements on [salsa20](https://github.com/RustCrypto/stream-ciphers) to permit the latest version. - [Release notes](https://github.com/RustCrypto/stream-ciphers/releases) - [Commits](RustCrypto/stream-ciphers@ctr-v0.8.0...salsa20-v0.9.0) --- updated-dependencies: - dependency-name: salsa20 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> * *: Bump pnet to v0.22 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Max Inden <[email protected]> * *: Dial with handler and return handler on error and closed (libp2p#2191) Require `NetworkBehaviourAction::{DialPeer,DialAddress}` to contain a `ProtocolsHandler`. This allows a behaviour to attach custom state to its handler. The behaviour would no longer need to track this state separately during connection establishment, thus reducing state required in a behaviour. E.g. in the case of `libp2p-kad` the behaviour can include a `GetRecord` request in its handler, or e.g. in the case of `libp2p-request-response` the behaviour can include the first request in the handler. Return `ProtocolsHandler` on connection error and close. This allows a behaviour to extract its custom state previously included in the handler on connection failure and connection closing. E.g. in the case of `libp2p-kad` the behaviour could extract the attached `GetRecord` from the handler of the failed connection and then start another connection attempt with a new handler with the same `GetRecord` or bubble up an error to the user. Co-authored-by: Thomas Eizinger <[email protected]> * core/: Remove deprecated read/write functions (libp2p#2213) Co-authored-by: Max Inden <[email protected]> * protocols/ping: Revise naming of symbols (libp2p#2215) Co-authored-by: Max Inden <[email protected]> * protocols/rendezvous: Implement protocol (libp2p#2107) Implement the libp2p rendezvous protocol. > A lightweight mechanism for generalized peer discovery. It can be used for bootstrap purposes, real time peer discovery, application specific routing, and so on. Co-authored-by: rishflab <[email protected]> Co-authored-by: Daniel Karzel <[email protected]> * core/src/network/event.rs: Fix typo (libp2p#2218) * protocols/mdns: Do not fire all timers at the same time. (libp2p#2212) Co-authored-by: Max Inden <[email protected]> * misc/metrics/src/kad: Set query_duration lowest bucket to 0.1 sec (libp2p#2219) Probability for a Kademlia query to return in less than 100 milliseconds is low, thus increasing the lower bucket to improve accuracy within the higher ranges. * misc/metrics/src/swarm: Expose role on connections_closed (libp2p#2220) Expose whether closed connection was a Dialer or Listener. * .github/workflows/ci.yml: Use clang 11 (libp2p#2233) * protocols/rendezvous: Update prost (libp2p#2226) Co-authored-by: Max Inden <[email protected]> * *: Fix clippy warnings (libp2p#2227) * swarm-derive/: Make event_process = false the default (libp2p#2214) Co-authored-by: Max Inden <[email protected]> Co-authored-by: Max Inden <[email protected]> Co-authored-by: Age Manning <[email protected]> Co-authored-by: Ruben De Smet <[email protected]> Co-authored-by: Thomas Eizinger <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: rishflab <[email protected]> Co-authored-by: Daniel Karzel <[email protected]> Co-authored-by: David Craven <[email protected]>
1 parent ee7a7f5 commit aee258c

File tree

146 files changed

+8475
-1128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

146 files changed

+8475
-1128
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
container:
4747
image: rust
4848
env:
49-
CC: clang-10
49+
CC: clang-11
5050
steps:
5151

5252
- name: Cancel Previous Runs
@@ -75,7 +75,7 @@ jobs:
7575
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
7676
echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main" >> /etc/apt/sources.list
7777
apt-get update
78-
apt-get install -y clang-10
78+
apt-get install -y clang-11
7979
8080
- name: Install CMake
8181
run: apt-get install -y cmake

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [`libp2p-ping` CHANGELOG](protocols/ping/CHANGELOG.md)
1717
- [`libp2p-relay` CHANGELOG](protocols/relay/CHANGELOG.md)
1818
- [`libp2p-request-response` CHANGELOG](protocols/request-response/CHANGELOG.md)
19+
- [`libp2p-rendezvous` CHANGELOG](protocols/rendezvous/CHANGELOG.md)
1920

2021
## Transport Protocols & Upgrades
2122

@@ -36,6 +37,7 @@
3637

3738
## Utilities
3839

40+
- [`libp2p-metrics` CHANGELOG](misc/metrics/CHANGELOG.md)
3941
- [`multistream-select` CHANGELOG](misc/multistream-select/CHANGELOG.md)
4042

4143
# `libp2p` facade crate
@@ -66,7 +68,9 @@
6668

6769
- Re-export the `wasm-bindgen` feature from `parking_lot`, so
6870
`libp2p` users can opt-in to that crate's Wasm support. See [PR 2180].
69-
71+
72+
- Add `libp2p-metrics`.
73+
7074
[PR 2180]: https://github.com/libp2p/rust-libp2p/pull/2180/
7175

7276
## Version 0.39.1 [2021-07-12]

Cargo.toml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ default = [
2626
"quic",
2727
"relay",
2828
"request-response",
29+
"rendezvous",
2930
"secp256k1",
3031
"tcp-async-io",
3132
"uds",
@@ -38,17 +39,19 @@ dns-async-std = ["libp2p-dns", "libp2p-dns/async-std"]
3839
dns-tokio = ["libp2p-dns", "libp2p-dns/tokio"]
3940
floodsub = ["libp2p-floodsub"]
4041
gossipsub = ["libp2p-gossipsub"]
41-
identify = ["libp2p-identify"]
42-
kad = ["libp2p-kad"]
42+
identify = ["libp2p-identify", "libp2p-metrics/identify"]
43+
kad = ["libp2p-kad", "libp2p-metrics/kad"]
44+
metrics = ["libp2p-metrics"]
4345
mdns = ["libp2p-mdns"]
4446
mplex = ["libp2p-mplex"]
4547
noise = ["libp2p-noise"]
46-
ping = ["libp2p-ping"]
48+
ping = ["libp2p-ping", "libp2p-metrics/ping"]
4749
plaintext = ["libp2p-plaintext"]
4850
pnet = ["libp2p-pnet"]
4951
quic = ["libp2p-quic"]
5052
relay = ["libp2p-relay"]
5153
request-response = ["libp2p-request-response"]
54+
rendezvous = ["libp2p-rendezvous"]
5255
tcp-async-io = ["libp2p-tcp", "libp2p-tcp/async-io"]
5356
tcp-tokio = ["libp2p-tcp", "libp2p-tcp/tokio"]
5457
uds = ["libp2p-uds"]
@@ -72,15 +75,17 @@ libp2p-floodsub = { version = "0.31.0", path = "protocols/floodsub", optional =
7275
libp2p-gossipsub = { version = "0.33.0", path = "./protocols/gossipsub", optional = true }
7376
libp2p-identify = { version = "0.31.0", path = "protocols/identify", optional = true }
7477
libp2p-kad = { version = "0.32.0", path = "protocols/kad", optional = true }
78+
libp2p-metrics = { version = "0.1.0", path = "misc/metrics", optional = true }
7579
libp2p-mplex = { version = "0.30.0", path = "muxers/mplex", optional = true }
7680
libp2p-noise = { version = "0.33.0", path = "transports/noise", optional = true }
7781
libp2p-ping = { version = "0.31.0", path = "protocols/ping", optional = true }
7882
libp2p-plaintext = { version = "0.30.0", path = "transports/plaintext", optional = true }
79-
libp2p-pnet = { version = "0.21.0", path = "transports/pnet", optional = true }
83+
libp2p-pnet = { version = "0.22.0", path = "transports/pnet", optional = true }
8084
libp2p-relay = { version = "0.4.0", path = "protocols/relay", optional = true }
85+
libp2p-rendezvous = { version = "0.1.0", path = "protocols/rendezvous", optional = true }
8186
libp2p-request-response = { version = "0.13.0", path = "protocols/request-response", optional = true }
8287
libp2p-swarm = { version = "0.31.0", path = "swarm" }
83-
libp2p-swarm-derive = { version = "0.24.0", path = "swarm-derive" }
88+
libp2p-swarm-derive = { version = "0.25.0", path = "swarm-derive" }
8489
libp2p-uds = { version = "0.30.0", path = "transports/uds", optional = true }
8590
libp2p-wasm-ext = { version = "0.30.0", path = "transports/wasm-ext", default-features = false, optional = true }
8691
libp2p-yamux = { version = "0.34.0", path = "muxers/yamux", optional = true }
@@ -100,19 +105,23 @@ libp2p-websocket = { version = "0.31.0", path = "transports/websocket", optional
100105

101106
[dev-dependencies]
102107
async-std = { version = "1.6.2", features = ["attributes"] }
108+
async-trait = "0.1"
103109
env_logger = "0.9.0"
110+
structopt = "0.3.21"
104111
tokio = { version = "1.0.1", features = ["io-util", "io-std", "macros", "rt", "rt-multi-thread"] }
105112

106113
[workspace]
107114
resolver = "2"
108115
members = [
109116
"core",
117+
"misc/metrics",
110118
"misc/multistream-select",
111119
"misc/peer-id-generator",
112120
"muxers/mplex",
113121
"muxers/yamux",
114122
"protocols/floodsub",
115123
"protocols/gossipsub",
124+
"protocols/rendezvous",
116125
"protocols/identify",
117126
"protocols/kad",
118127
"protocols/mdns",

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ This repository is the central place for Rust development of the [libp2p](https:
1212
- The **[examples](examples)** folder contains small binaries showcasing the
1313
many protocols in this repository.
1414

15-
- To **report bugs, suggest improvements or request new features** please open
15+
- For **security related issues** please reach out to [email protected]. Please
16+
do not file a public issue on GitHub.
17+
18+
- To **report bugs, suggest improvements or request new features** please open a
1619
GitHub issue on this repository.
1720

1821
- For **rust-libp2p specific questions** please use the GitHub _Discussions_

core/CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,32 @@
2020
- Require `ConnectionHandler::{InEvent,OutEvent,Error}` to implement `Debug`
2121
(see [PR 2183]).
2222

23+
- Remove `DisconnectedPeer::set_connected` and `Pool::add` (see [PR 2195]).
24+
25+
- Report `ConnectionLimit` error through `ConnectionError` and thus through
26+
`NetworkEvent::ConnectionClosed` instead of previously through
27+
`PendingConnectionError` and thus `NetworkEvent::{IncomingConnectionError,
28+
DialError}` (see [PR 2191]).
29+
30+
- Report abortion of pending connection through `DialError`,
31+
`UnknownPeerDialError` or `IncomingConnectionError` (see [PR 2191]).
32+
33+
- Remove deprecated functions `upgrade::write_one`, `upgrade::write_with_len_prefix`
34+
and `upgrade::read_one` (see [PR 2213]).
35+
36+
- Add `SignedEnvelope` and `PeerRecord` according to [RFC0002] and [RFC0003]
37+
(see [PR 2107]).
38+
2339
[PR 2145]: https://github.com/libp2p/rust-libp2p/pull/2145
40+
[PR 2213]: https://github.com/libp2p/rust-libp2p/pull/2213
2441
[PR 2142]: https://github.com/libp2p/rust-libp2p/pull/2142
2542
[PR 2137]: https://github.com/libp2p/rust-libp2p/pull/2137
2643
[PR 2183]: https://github.com/libp2p/rust-libp2p/pull/2183
44+
[PR 2191]: https://github.com/libp2p/rust-libp2p/pull/2191
45+
[PR 2195]: https://github.com/libp2p/rust-libp2p/pull/2195
46+
[PR 2107]: https://github.com/libp2p/rust-libp2p/pull/2107
47+
[RFC0002]: https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md
48+
[RFC0003]: https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md
2749

2850
# 0.29.0 [2021-07-12]
2951

core/build.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,13 @@
1919
// DEALINGS IN THE SOFTWARE.
2020

2121
fn main() {
22-
prost_build::compile_protos(&["src/keys.proto"], &["src"]).unwrap();
22+
prost_build::compile_protos(
23+
&[
24+
"src/keys.proto",
25+
"src/envelope.proto",
26+
"src/peer_record.proto",
27+
],
28+
&["src"],
29+
)
30+
.unwrap();
2331
}

core/src/connection.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ where
229229
self.handler.inject_event(event);
230230
}
231231

232-
/// Begins an orderly shutdown of the connection, returning a
233-
/// `Future` that resolves when connection shutdown is complete.
234-
pub fn close(self) -> Close<TMuxer> {
235-
self.muxing.close().0
232+
/// Begins an orderly shutdown of the connection, returning the connection
233+
/// handler and a `Future` that resolves when connection shutdown is complete.
234+
pub fn close(self) -> (THandler, Close<TMuxer>) {
235+
(self.handler, self.muxing.close().0)
236236
}
237237

238238
/// Polls the connection for events produced by the associated handler

core/src/connection/error.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ pub enum ConnectionError<THandlerErr> {
2929
// TODO: Eventually this should also be a custom error?
3030
IO(io::Error),
3131

32+
/// The connection was dropped because the connection limit
33+
/// for a peer has been reached.
34+
ConnectionLimit(ConnectionLimit),
35+
3236
/// The connection handler produced an error.
3337
Handler(THandlerErr),
3438
}
@@ -41,6 +45,9 @@ where
4145
match self {
4246
ConnectionError::IO(err) => write!(f, "Connection error: I/O error: {}", err),
4347
ConnectionError::Handler(err) => write!(f, "Connection error: Handler error: {}", err),
48+
ConnectionError::ConnectionLimit(l) => {
49+
write!(f, "Connection error: Connection limit: {}.", l)
50+
}
4451
}
4552
}
4653
}
@@ -53,6 +60,7 @@ where
5360
match self {
5461
ConnectionError::IO(err) => Some(err),
5562
ConnectionError::Handler(err) => Some(err),
63+
ConnectionError::ConnectionLimit(..) => None,
5664
}
5765
}
5866
}
@@ -63,14 +71,13 @@ pub enum PendingConnectionError<TTransErr> {
6371
/// An error occurred while negotiating the transport protocol(s).
6472
Transport(TransportError<TTransErr>),
6573

74+
/// Pending connection attempt has been aborted.
75+
Aborted,
76+
6677
/// The peer identity obtained on the connection did not
6778
/// match the one that was expected or is otherwise invalid.
6879
InvalidPeerId,
6980

70-
/// The connection was dropped because the connection limit
71-
/// for a peer has been reached.
72-
ConnectionLimit(ConnectionLimit),
73-
7481
/// An I/O error occurred on the connection.
7582
// TODO: Eventually this should also be a custom error?
7683
IO(io::Error),
@@ -83,15 +90,13 @@ where
8390
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8491
match self {
8592
PendingConnectionError::IO(err) => write!(f, "Pending connection: I/O error: {}", err),
93+
PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."),
8694
PendingConnectionError::Transport(err) => {
8795
write!(f, "Pending connection: Transport error: {}", err)
8896
}
8997
PendingConnectionError::InvalidPeerId => {
9098
write!(f, "Pending connection: Invalid peer ID.")
9199
}
92-
PendingConnectionError::ConnectionLimit(l) => {
93-
write!(f, "Connection error: Connection limit: {}.", l)
94-
}
95100
}
96101
}
97102
}
@@ -105,7 +110,7 @@ where
105110
PendingConnectionError::IO(err) => Some(err),
106111
PendingConnectionError::Transport(err) => Some(err),
107112
PendingConnectionError::InvalidPeerId => None,
108-
PendingConnectionError::ConnectionLimit(..) => None,
113+
PendingConnectionError::Aborted => None,
109114
}
110115
}
111116
}

core/src/connection/manager.rs

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
use super::{
2222
handler::{THandlerError, THandlerInEvent, THandlerOutEvent},
23-
Connected, ConnectedPoint, Connection, ConnectionError, ConnectionHandler,
23+
Connected, ConnectedPoint, ConnectionError, ConnectionHandler, ConnectionLimit,
2424
IntoConnectionHandler, PendingConnectionError, Substream,
2525
};
2626
use crate::{muxing::StreamMuxer, Executor};
@@ -192,6 +192,7 @@ pub enum Event<'a, H: IntoConnectionHandler, TE> {
192192
/// The error that occurred, if any. If `None`, the connection
193193
/// has been actively closed.
194194
error: Option<ConnectionError<THandlerError<H>>>,
195+
handler: H::Handler,
195196
},
196197

197198
/// A connection has been established.
@@ -276,40 +277,6 @@ impl<H: IntoConnectionHandler, TE> Manager<H, TE> {
276277
ConnectionId(task_id)
277278
}
278279

279-
/// Adds an existing connection to the manager.
280-
pub fn add<M>(&mut self, conn: Connection<M, H::Handler>, info: Connected) -> ConnectionId
281-
where
282-
H: IntoConnectionHandler + Send + 'static,
283-
H::Handler: ConnectionHandler<Substream = Substream<M>> + Send + 'static,
284-
<H::Handler as ConnectionHandler>::OutboundOpenInfo: Send + 'static,
285-
TE: error::Error + Send + 'static,
286-
M: StreamMuxer + Send + Sync + 'static,
287-
M::OutboundSubstream: Send + 'static,
288-
{
289-
let task_id = self.next_task_id;
290-
self.next_task_id.0 += 1;
291-
292-
let (tx, rx) = mpsc::channel(self.task_command_buffer_size);
293-
self.tasks.insert(
294-
task_id,
295-
TaskInfo {
296-
sender: tx,
297-
state: TaskState::Established(info),
298-
},
299-
);
300-
301-
let task: Pin<Box<Task<Pin<Box<future::Pending<_>>>, _, _, _>>> =
302-
Box::pin(Task::established(task_id, self.events_tx.clone(), rx, conn));
303-
304-
if let Some(executor) = &mut self.executor {
305-
executor.exec(task);
306-
} else {
307-
self.local_spawns.push(task);
308-
}
309-
310-
ConnectionId(task_id)
311-
}
312-
313280
/// Gets an entry for a managed connection, if it exists.
314281
pub fn entry(&mut self, id: ConnectionId) -> Option<Entry<'_, THandlerInEvent<H>>> {
315282
if let hash_map::Entry::Occupied(task) = self.tasks.entry(id.0) {
@@ -384,14 +351,15 @@ impl<H: IntoConnectionHandler, TE> Manager<H, TE> {
384351
new_endpoint: new,
385352
}
386353
}
387-
task::Event::Closed { id, error } => {
354+
task::Event::Closed { id, error, handler } => {
388355
let id = ConnectionId(id);
389356
let task = task.remove();
390357
match task.state {
391358
TaskState::Established(connected) => Event::ConnectionClosed {
392359
id,
393360
connected,
394361
error,
362+
handler,
395363
},
396364
TaskState::Pending => unreachable!(
397365
"`Event::Closed` implies (2) occurred on that task and thus (3)."
@@ -471,15 +439,15 @@ impl<'a, I> EstablishedEntry<'a, I> {
471439
///
472440
/// When the connection is ultimately closed, [`Event::ConnectionClosed`]
473441
/// is emitted by [`Manager::poll`].
474-
pub fn start_close(mut self) {
442+
pub fn start_close(mut self, error: Option<ConnectionLimit>) {
475443
// Clone the sender so that we are guaranteed to have
476444
// capacity for the close command (every sender gets a slot).
477445
match self
478446
.task
479447
.get_mut()
480448
.sender
481449
.clone()
482-
.try_send(task::Command::Close)
450+
.try_send(task::Command::Close(error))
483451
{
484452
Ok(()) => {}
485453
Err(e) => assert!(e.is_disconnected(), "No capacity for close command."),
@@ -494,17 +462,6 @@ impl<'a, I> EstablishedEntry<'a, I> {
494462
}
495463
}
496464

497-
/// Instantly removes the entry from the manager, dropping
498-
/// the command channel to the background task of the connection,
499-
/// which will thus drop the connection asap without an orderly
500-
/// close or emitting another event.
501-
pub fn remove(self) -> Connected {
502-
match self.task.remove().state {
503-
TaskState::Established(c) => c,
504-
TaskState::Pending => unreachable!("By Entry::new()"),
505-
}
506-
}
507-
508465
/// Returns the connection ID.
509466
pub fn id(&self) -> ConnectionId {
510467
ConnectionId(*self.task.key())

0 commit comments

Comments
 (0)