2018-12-10 16:00:16 +01:00
|
|
|
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
|
|
// to deal in the Software without restriction, including without limitation
|
|
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
|
2021-12-29 19:02:20 +01:00
|
|
|
mod iface;
|
|
|
|
|
|
|
|
use self::iface::InterfaceState;
|
|
|
|
use crate::MdnsConfig;
|
|
|
|
use async_io::Timer;
|
2018-12-10 16:00:16 +01:00
|
|
|
use futures::prelude::*;
|
2021-03-02 10:18:24 +01:00
|
|
|
use if_watch::{IfEvent, IfWatcher};
|
2021-05-06 17:08:29 +02:00
|
|
|
use libp2p_core::connection::ListenerId;
|
2021-12-29 19:02:20 +01:00
|
|
|
use libp2p_core::{Multiaddr, PeerId};
|
2019-07-04 14:47:59 +02:00
|
|
|
use libp2p_swarm::{
|
2022-02-21 13:32:24 +01:00
|
|
|
handler::DummyConnectionHandler, ConnectionHandler, NetworkBehaviour, NetworkBehaviourAction,
|
|
|
|
PollParameters,
|
2019-07-04 14:47:59 +02:00
|
|
|
};
|
2018-12-10 16:00:16 +01:00
|
|
|
use smallvec::SmallVec;
|
2021-12-29 19:02:20 +01:00
|
|
|
use std::collections::hash_map::{Entry, HashMap};
|
|
|
|
use std::{cmp, fmt, io, net::IpAddr, pin::Pin, task::Context, task::Poll, time::Instant};
|
misc/mdns: Update to futures-preview (#1247)
* misc/mdns/service: Use async std with stack pinned futures
* misc/mdns: Define mdns broadcast address as lazy static
* misc/mdns: Drop future before borrowing their arguments again
* misc/mdns: Send queries on query socket, not socket
* misc/mdns: Use poll_next_unpin on query interval stream
* misc/mdns: Ensure underlying task is woken up on next interval tick
* misc/mdns: Wrap match expression in scope to drop future early
* misc/mdns: Adjust 'discovery_ourselves' test
* misc/mdns: Make query interval fire instantly on first tick
This is an optimization only important for short lived use cases, e.g.
unit tests. Instead of waiting for 20 seconds at first, the query
interval fires right away and thereby the service makes progress
instantly.
* misc/mdns: Adjust MdnsService documentation tests
* misc/mdns: Do not drop UDP socket send and reicv futures
Libp2p-mdns uses the async-std crate for network io. This crate only
offers async send and receive functions. In order to use this in non
async/await functions one needs to keep the future returned by the crate
functions around across `poll` invocations.
The future returned by the crate functions references the io resource.
Thus one has to keep both the io resource as well as the future
referencing it. This results in a self-referencing struct which is not
possible to create with safe Rust.
Instead, by having `MdnsService::next` (former `MdnsService::poll`) take
ownership of `self`, the Rust async magic takes care of the above (See
code comments for more details).
As a (negative) side effect, given that `MdnsService::next` takes
ownership of `self`, there is nothing to bind the lifetime of the
returned `MdnsPacket` to. With no better solution in mind, this patch
makes `MdnsPacket` static, not referencing the `MdnsService` receive
buffer.
* misc/mdns: Fix code comments and remove *if Free* TODO
* misc/mdns: Minor refactorings
* misc/mdns: Build responses in behaviour.rs directly
* misc/mdns: Move response ttl duration to constant
* misc/mdns: Remove optimization todo comment
* misc/mdns: Add query interval test
* misc/mdns: Move packet parsing into MdnPacket impl
* misc/mdns: Don't have receiving packets starve the query interval
When we 'await' on receiving a packet on the udp socket without
receiving a single packet we starve the remaining logic of the mdns
service, in this case the logic triggered on the receive interval.
* misc/mdns: Add debug_assert to MaybeBusyMdnsService check
* misc/mdns: Implement Debug for MaybeBusyMdnsService
* misc/mdns: Make ownership note a normal comment, not a doc comment
* misc/mdns: Have discovered_peers return an iterator
2019-11-20 13:25:12 +01:00
|
|
|
|
2018-12-10 16:00:16 +01:00
|
|
|
/// A `NetworkBehaviour` for mDNS. Automatically discovers peers on the local network and adds
|
|
|
|
/// them to the topology.
|
2021-03-02 10:18:24 +01:00
|
|
|
#[derive(Debug)]
|
2020-12-03 13:30:52 +01:00
|
|
|
pub struct Mdns {
|
2021-12-29 19:02:20 +01:00
|
|
|
/// InterfaceState config.
|
|
|
|
config: MdnsConfig,
|
2021-03-02 10:18:24 +01:00
|
|
|
|
|
|
|
/// Iface watcher.
|
|
|
|
if_watch: IfWatcher,
|
|
|
|
|
2021-12-29 19:02:20 +01:00
|
|
|
/// Mdns interface states.
|
|
|
|
iface_states: HashMap<IpAddr, InterfaceState>,
|
2018-12-10 16:00:16 +01:00
|
|
|
|
2019-01-26 23:57:53 +01:00
|
|
|
/// List of nodes that we have discovered, the address, and when their TTL expires.
|
|
|
|
///
|
|
|
|
/// Each combination of `PeerId` and `Multiaddr` can only appear once, but the same `PeerId`
|
|
|
|
/// can appear multiple times.
|
|
|
|
discovered_nodes: SmallVec<[(PeerId, Multiaddr, Instant); 8]>,
|
|
|
|
|
misc/mdns: Update to futures-preview (#1247)
* misc/mdns/service: Use async std with stack pinned futures
* misc/mdns: Define mdns broadcast address as lazy static
* misc/mdns: Drop future before borrowing their arguments again
* misc/mdns: Send queries on query socket, not socket
* misc/mdns: Use poll_next_unpin on query interval stream
* misc/mdns: Ensure underlying task is woken up on next interval tick
* misc/mdns: Wrap match expression in scope to drop future early
* misc/mdns: Adjust 'discovery_ourselves' test
* misc/mdns: Make query interval fire instantly on first tick
This is an optimization only important for short lived use cases, e.g.
unit tests. Instead of waiting for 20 seconds at first, the query
interval fires right away and thereby the service makes progress
instantly.
* misc/mdns: Adjust MdnsService documentation tests
* misc/mdns: Do not drop UDP socket send and reicv futures
Libp2p-mdns uses the async-std crate for network io. This crate only
offers async send and receive functions. In order to use this in non
async/await functions one needs to keep the future returned by the crate
functions around across `poll` invocations.
The future returned by the crate functions references the io resource.
Thus one has to keep both the io resource as well as the future
referencing it. This results in a self-referencing struct which is not
possible to create with safe Rust.
Instead, by having `MdnsService::next` (former `MdnsService::poll`) take
ownership of `self`, the Rust async magic takes care of the above (See
code comments for more details).
As a (negative) side effect, given that `MdnsService::next` takes
ownership of `self`, there is nothing to bind the lifetime of the
returned `MdnsPacket` to. With no better solution in mind, this patch
makes `MdnsPacket` static, not referencing the `MdnsService` receive
buffer.
* misc/mdns: Fix code comments and remove *if Free* TODO
* misc/mdns: Minor refactorings
* misc/mdns: Build responses in behaviour.rs directly
* misc/mdns: Move response ttl duration to constant
* misc/mdns: Remove optimization todo comment
* misc/mdns: Add query interval test
* misc/mdns: Move packet parsing into MdnPacket impl
* misc/mdns: Don't have receiving packets starve the query interval
When we 'await' on receiving a packet on the udp socket without
receiving a single packet we starve the remaining logic of the mdns
service, in this case the logic triggered on the receive interval.
* misc/mdns: Add debug_assert to MaybeBusyMdnsService check
* misc/mdns: Implement Debug for MaybeBusyMdnsService
* misc/mdns: Make ownership note a normal comment, not a doc comment
* misc/mdns: Have discovered_peers return an iterator
2019-11-20 13:25:12 +01:00
|
|
|
/// Future that fires when the TTL of at least one node in `discovered_nodes` expires.
|
2019-01-26 23:57:53 +01:00
|
|
|
///
|
|
|
|
/// `None` if `discovered_nodes` is empty.
|
2020-12-03 13:30:52 +01:00
|
|
|
closest_expiration: Option<Timer>,
|
misc/mdns: Update to futures-preview (#1247)
* misc/mdns/service: Use async std with stack pinned futures
* misc/mdns: Define mdns broadcast address as lazy static
* misc/mdns: Drop future before borrowing their arguments again
* misc/mdns: Send queries on query socket, not socket
* misc/mdns: Use poll_next_unpin on query interval stream
* misc/mdns: Ensure underlying task is woken up on next interval tick
* misc/mdns: Wrap match expression in scope to drop future early
* misc/mdns: Adjust 'discovery_ourselves' test
* misc/mdns: Make query interval fire instantly on first tick
This is an optimization only important for short lived use cases, e.g.
unit tests. Instead of waiting for 20 seconds at first, the query
interval fires right away and thereby the service makes progress
instantly.
* misc/mdns: Adjust MdnsService documentation tests
* misc/mdns: Do not drop UDP socket send and reicv futures
Libp2p-mdns uses the async-std crate for network io. This crate only
offers async send and receive functions. In order to use this in non
async/await functions one needs to keep the future returned by the crate
functions around across `poll` invocations.
The future returned by the crate functions references the io resource.
Thus one has to keep both the io resource as well as the future
referencing it. This results in a self-referencing struct which is not
possible to create with safe Rust.
Instead, by having `MdnsService::next` (former `MdnsService::poll`) take
ownership of `self`, the Rust async magic takes care of the above (See
code comments for more details).
As a (negative) side effect, given that `MdnsService::next` takes
ownership of `self`, there is nothing to bind the lifetime of the
returned `MdnsPacket` to. With no better solution in mind, this patch
makes `MdnsPacket` static, not referencing the `MdnsService` receive
buffer.
* misc/mdns: Fix code comments and remove *if Free* TODO
* misc/mdns: Minor refactorings
* misc/mdns: Build responses in behaviour.rs directly
* misc/mdns: Move response ttl duration to constant
* misc/mdns: Remove optimization todo comment
* misc/mdns: Add query interval test
* misc/mdns: Move packet parsing into MdnPacket impl
* misc/mdns: Don't have receiving packets starve the query interval
When we 'await' on receiving a packet on the udp socket without
receiving a single packet we starve the remaining logic of the mdns
service, in this case the logic triggered on the receive interval.
* misc/mdns: Add debug_assert to MaybeBusyMdnsService check
* misc/mdns: Implement Debug for MaybeBusyMdnsService
* misc/mdns: Make ownership note a normal comment, not a doc comment
* misc/mdns: Have discovered_peers return an iterator
2019-11-20 13:25:12 +01:00
|
|
|
}
|
|
|
|
|
2020-12-03 13:30:52 +01:00
|
|
|
impl Mdns {
|
2018-12-10 16:00:16 +01:00
|
|
|
/// Builds a new `Mdns` behaviour.
|
2021-03-02 10:18:24 +01:00
|
|
|
pub async fn new(config: MdnsConfig) -> io::Result<Self> {
|
|
|
|
let if_watch = if_watch::IfWatcher::new().await?;
|
2020-12-03 13:30:52 +01:00
|
|
|
Ok(Self {
|
2021-12-29 19:02:20 +01:00
|
|
|
config,
|
2021-03-02 10:18:24 +01:00
|
|
|
if_watch,
|
2021-12-29 19:02:20 +01:00
|
|
|
iface_states: Default::default(),
|
|
|
|
discovered_nodes: Default::default(),
|
|
|
|
closest_expiration: Default::default(),
|
2018-12-10 16:00:16 +01:00
|
|
|
})
|
|
|
|
}
|
2019-01-26 23:57:53 +01:00
|
|
|
|
|
|
|
/// Returns true if the given `PeerId` is in the list of nodes discovered through mDNS.
|
|
|
|
pub fn has_node(&self, peer_id: &PeerId) -> bool {
|
2020-03-02 17:11:28 +01:00
|
|
|
self.discovered_nodes().any(|p| p == peer_id)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the list of nodes that we have discovered through mDNS and that are not expired.
|
|
|
|
pub fn discovered_nodes(&self) -> impl ExactSizeIterator<Item = &PeerId> {
|
|
|
|
self.discovered_nodes.iter().map(|(p, _, _)| p)
|
2019-01-26 23:57:53 +01:00
|
|
|
}
|
2021-03-02 10:18:24 +01:00
|
|
|
|
2021-12-29 19:02:20 +01:00
|
|
|
/// Expires a node before the ttl.
|
|
|
|
pub fn expire_node(&mut self, peer_id: &PeerId) {
|
|
|
|
let now = Instant::now();
|
|
|
|
for (peer, _addr, expires) in &mut self.discovered_nodes {
|
|
|
|
if peer == peer_id {
|
|
|
|
*expires = now;
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
self.closest_expiration = Some(Timer::at(now));
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2018-12-10 16:00:16 +01:00
|
|
|
}
|
|
|
|
|
2020-12-03 13:30:52 +01:00
|
|
|
impl NetworkBehaviour for Mdns {
|
2022-02-21 13:32:24 +01:00
|
|
|
type ConnectionHandler = DummyConnectionHandler;
|
2019-01-26 23:57:53 +01:00
|
|
|
type OutEvent = MdnsEvent;
|
2018-12-10 16:00:16 +01:00
|
|
|
|
2022-02-21 13:32:24 +01:00
|
|
|
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
|
|
|
DummyConnectionHandler::default()
|
2018-12-10 16:00:16 +01:00
|
|
|
}
|
|
|
|
|
2019-01-30 14:55:39 +01:00
|
|
|
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
|
2019-01-26 23:57:53 +01:00
|
|
|
self.discovered_nodes
|
|
|
|
.iter()
|
2021-12-29 19:02:20 +01:00
|
|
|
.filter(|(peer, _, _)| peer == peer_id)
|
2019-01-26 23:57:53 +01:00
|
|
|
.map(|(_, addr, _)| addr.clone())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
Multiple connections per peer (#1440)
* Allow multiple connections per peer in libp2p-core.
Instead of trying to enforce a single connection per peer,
which involves quite a bit of additional complexity e.g.
to prioritise simultaneously opened connections and can
have other undesirable consequences [1], we now
make multiple connections per peer a feature.
The gist of these changes is as follows:
The concept of a "node" with an implicit 1-1 correspondence
to a connection has been replaced with the "first-class"
concept of a "connection". The code from `src/nodes` has moved
(with varying degrees of modification) to `src/connection`.
A `HandledNode` has become a `Connection`, a `NodeHandler` a
`ConnectionHandler`, the `CollectionStream` was the basis for
the new `connection::Pool`, and so forth.
Conceptually, a `Network` contains a `connection::Pool` which
in turn internally employs the `connection::Manager` for
handling the background `connection::manager::Task`s, one
per connection, as before. These are all considered implementation
details. On the public API, `Peer`s are managed as before through
the `Network`, except now the API has changed with the shift of focus
to (potentially multiple) connections per peer. The `NetworkEvent`s have
accordingly also undergone changes.
The Swarm APIs remain largely unchanged, except for the fact that
`inject_replaced` is no longer called. It may now practically happen
that multiple `ProtocolsHandler`s are associated with a single
`NetworkBehaviour`, one per connection. If implementations of
`NetworkBehaviour` rely somehow on communicating with exactly
one `ProtocolsHandler`, this may cause issues, but it is unlikely.
[1]: https://github.com/paritytech/substrate/issues/4272
* Fix intra-rustdoc links.
* Update core/src/connection/pool.rs
Co-Authored-By: Max Inden <mail@max-inden.de>
* Address some review feedback and fix doc links.
* Allow responses to be sent on the same connection.
* Remove unnecessary remainders of inject_replaced.
* Update swarm/src/behaviour.rs
Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>
* Update swarm/src/lib.rs
Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>
* Update core/src/connection/manager.rs
Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>
* Update core/src/connection/manager.rs
Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>
* Update core/src/connection/pool.rs
Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>
* Incorporate more review feedback.
* Move module declaration below imports.
* Update core/src/connection/manager.rs
Co-Authored-By: Toralf Wittner <tw@dtex.org>
* Update core/src/connection/manager.rs
Co-Authored-By: Toralf Wittner <tw@dtex.org>
* Simplify as per review.
* Fix rustoc link.
* Add try_notify_handler and simplify.
* Relocate DialingConnection and DialingAttempt.
For better visibility constraints.
* Small cleanup.
* Small cleanup. More robust EstablishedConnectionIter.
* Clarify semantics of `DialingPeer::connect`.
* Don't call inject_disconnected on InvalidPeerId.
To preserve the previous behavior and ensure calls to
`inject_disconnected` are always paired with calls to
`inject_connected`.
* Provide public ConnectionId constructor.
Mainly needed for testing purposes, e.g. in substrate.
* Move the established connection limit check to the right place.
* Clean up connection error handling.
Separate connection errors into those occuring during
connection setup or upon rejecting a newly established
connection (the `PendingConnectionError`) and those
errors occurring on previously established connections,
i.e. for which a `ConnectionEstablished` event has
been emitted by the connection pool earlier.
* Revert change in log level and clarify an invariant.
* Remove inject_replaced entirely.
* Allow notifying all connection handlers.
Thereby simplify by introducing a new enum `NotifyHandler`,
used with a single constructor `NetworkBehaviourAction::NotifyHandler`.
* Finishing touches.
Small API simplifications and code deduplication.
Some more useful debug logging.
Co-authored-by: Max Inden <mail@max-inden.de>
Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
Co-authored-by: Toralf Wittner <tw@dtex.org>
2020-03-04 13:49:25 +01:00
|
|
|
fn inject_event(
|
2018-12-10 16:00:16 +01:00
|
|
|
&mut self,
|
|
|
|
_: PeerId,
|
2021-07-31 03:48:32 +10:00
|
|
|
_: libp2p_core::connection::ConnectionId,
|
2022-02-21 13:32:24 +01:00
|
|
|
ev: <Self::ConnectionHandler as ConnectionHandler>::OutEvent,
|
2018-12-10 16:00:16 +01:00
|
|
|
) {
|
2020-12-03 13:30:52 +01:00
|
|
|
void::unreachable(ev)
|
2018-12-10 16:00:16 +01:00
|
|
|
}
|
|
|
|
|
2021-04-21 16:07:38 +02:00
|
|
|
fn inject_new_listen_addr(&mut self, _id: ListenerId, _addr: &Multiaddr) {
|
2021-12-29 19:02:20 +01:00
|
|
|
log::trace!("waking interface state because listening address changed");
|
|
|
|
for (_, iface) in &mut self.iface_states {
|
|
|
|
iface.fire_timer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-09 10:08:28 -05:00
|
|
|
fn inject_connection_closed(
|
|
|
|
&mut self,
|
|
|
|
peer: &PeerId,
|
|
|
|
_: &libp2p_core::connection::ConnectionId,
|
|
|
|
_: &libp2p_core::ConnectedPoint,
|
2022-02-21 13:32:24 +01:00
|
|
|
_: Self::ConnectionHandler,
|
2022-02-09 10:08:28 -05:00
|
|
|
remaining_established: usize,
|
|
|
|
) {
|
|
|
|
if remaining_established == 0 {
|
|
|
|
self.expire_node(peer);
|
|
|
|
}
|
2021-04-21 16:07:38 +02:00
|
|
|
}
|
|
|
|
|
2018-12-10 16:00:16 +01:00
|
|
|
fn poll(
|
|
|
|
&mut self,
|
2020-07-27 20:27:33 +00:00
|
|
|
cx: &mut Context<'_>,
|
2019-06-18 10:23:26 +02:00
|
|
|
params: &mut impl PollParameters,
|
2022-02-21 13:32:24 +01:00
|
|
|
) -> Poll<NetworkBehaviourAction<Self::OutEvent, DummyConnectionHandler>> {
|
2021-12-29 19:02:20 +01:00
|
|
|
// Poll ifwatch.
|
2021-03-02 10:18:24 +01:00
|
|
|
while let Poll::Ready(event) = Pin::new(&mut self.if_watch).poll(cx) {
|
|
|
|
match event {
|
|
|
|
Ok(IfEvent::Up(inet)) => {
|
2021-12-29 19:02:20 +01:00
|
|
|
let addr = inet.addr();
|
|
|
|
if addr.is_loopback() {
|
2021-03-02 10:18:24 +01:00
|
|
|
continue;
|
2019-01-26 23:57:53 +01:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
if addr.is_ipv4() && self.config.enable_ipv6
|
|
|
|
|| addr.is_ipv6() && !self.config.enable_ipv6
|
|
|
|
{
|
2021-03-02 10:18:24 +01:00
|
|
|
continue;
|
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
if let Entry::Vacant(e) = self.iface_states.entry(addr) {
|
|
|
|
match InterfaceState::new(addr, self.config.clone()) {
|
|
|
|
Ok(iface_state) => {
|
|
|
|
e.insert(iface_state);
|
2021-08-03 14:55:06 +02:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
Err(err) => log::error!("failed to create `InterfaceState`: {}", err),
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2019-01-26 23:57:53 +01:00
|
|
|
}
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
Ok(IfEvent::Down(inet)) => {
|
|
|
|
if self.iface_states.contains_key(&inet.addr()) {
|
|
|
|
log::info!("dropping instance {}", inet.addr());
|
|
|
|
self.iface_states.remove(&inet.addr());
|
misc/mdns: Update to futures-preview (#1247)
* misc/mdns/service: Use async std with stack pinned futures
* misc/mdns: Define mdns broadcast address as lazy static
* misc/mdns: Drop future before borrowing their arguments again
* misc/mdns: Send queries on query socket, not socket
* misc/mdns: Use poll_next_unpin on query interval stream
* misc/mdns: Ensure underlying task is woken up on next interval tick
* misc/mdns: Wrap match expression in scope to drop future early
* misc/mdns: Adjust 'discovery_ourselves' test
* misc/mdns: Make query interval fire instantly on first tick
This is an optimization only important for short lived use cases, e.g.
unit tests. Instead of waiting for 20 seconds at first, the query
interval fires right away and thereby the service makes progress
instantly.
* misc/mdns: Adjust MdnsService documentation tests
* misc/mdns: Do not drop UDP socket send and reicv futures
Libp2p-mdns uses the async-std crate for network io. This crate only
offers async send and receive functions. In order to use this in non
async/await functions one needs to keep the future returned by the crate
functions around across `poll` invocations.
The future returned by the crate functions references the io resource.
Thus one has to keep both the io resource as well as the future
referencing it. This results in a self-referencing struct which is not
possible to create with safe Rust.
Instead, by having `MdnsService::next` (former `MdnsService::poll`) take
ownership of `self`, the Rust async magic takes care of the above (See
code comments for more details).
As a (negative) side effect, given that `MdnsService::next` takes
ownership of `self`, there is nothing to bind the lifetime of the
returned `MdnsPacket` to. With no better solution in mind, this patch
makes `MdnsPacket` static, not referencing the `MdnsService` receive
buffer.
* misc/mdns: Fix code comments and remove *if Free* TODO
* misc/mdns: Minor refactorings
* misc/mdns: Build responses in behaviour.rs directly
* misc/mdns: Move response ttl duration to constant
* misc/mdns: Remove optimization todo comment
* misc/mdns: Add query interval test
* misc/mdns: Move packet parsing into MdnPacket impl
* misc/mdns: Don't have receiving packets starve the query interval
When we 'await' on receiving a packet on the udp socket without
receiving a single packet we starve the remaining logic of the mdns
service, in this case the logic triggered on the receive interval.
* misc/mdns: Add debug_assert to MaybeBusyMdnsService check
* misc/mdns: Implement Debug for MaybeBusyMdnsService
* misc/mdns: Make ownership note a normal comment, not a doc comment
* misc/mdns: Have discovered_peers return an iterator
2019-11-20 13:25:12 +01:00
|
|
|
}
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
Err(err) => log::error!("if watch returned an error: {}", err),
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
// Emit discovered event.
|
|
|
|
let mut discovered = SmallVec::<[(PeerId, Multiaddr); 4]>::new();
|
|
|
|
for (_, iface_state) in &mut self.iface_states {
|
|
|
|
while let Some((peer, addr, expiration)) = iface_state.poll(cx, params) {
|
|
|
|
if let Some((_, _, cur_expires)) = self
|
|
|
|
.discovered_nodes
|
|
|
|
.iter_mut()
|
|
|
|
.find(|(p, a, _)| *p == peer && *a == addr)
|
2021-05-06 17:08:29 +02:00
|
|
|
{
|
2021-12-29 19:02:20 +01:00
|
|
|
*cur_expires = cmp::max(*cur_expires, expiration);
|
|
|
|
} else {
|
|
|
|
log::info!("discovered: {} {}", peer, addr);
|
|
|
|
self.discovered_nodes.push((peer, addr.clone(), expiration));
|
|
|
|
discovered.push((peer, addr));
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2018-12-10 16:00:16 +01:00
|
|
|
}
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
if !discovered.is_empty() {
|
|
|
|
let event = MdnsEvent::Discovered(DiscoveredAddrsIter {
|
|
|
|
inner: discovered.into_iter(),
|
|
|
|
});
|
2021-03-02 10:18:24 +01:00
|
|
|
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event));
|
|
|
|
}
|
|
|
|
// Emit expired event.
|
2021-12-29 19:02:20 +01:00
|
|
|
let now = Instant::now();
|
|
|
|
let mut closest_expiration = None;
|
|
|
|
let mut expired = SmallVec::<[(PeerId, Multiaddr); 4]>::new();
|
|
|
|
self.discovered_nodes.retain(|(peer, addr, expiration)| {
|
|
|
|
if *expiration <= now {
|
|
|
|
log::info!("expired: {} {}", peer, addr);
|
|
|
|
expired.push((*peer, addr.clone()));
|
|
|
|
return false;
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
2021-12-29 19:02:20 +01:00
|
|
|
closest_expiration = Some(closest_expiration.unwrap_or(*expiration).min(*expiration));
|
|
|
|
true
|
|
|
|
});
|
|
|
|
if !expired.is_empty() {
|
|
|
|
let event = MdnsEvent::Expired(ExpiredAddrsIter {
|
|
|
|
inner: expired.into_iter(),
|
|
|
|
});
|
|
|
|
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event));
|
|
|
|
}
|
|
|
|
if let Some(closest_expiration) = closest_expiration {
|
|
|
|
let mut timer = Timer::at(closest_expiration);
|
|
|
|
let _ = Pin::new(&mut timer).poll(cx);
|
|
|
|
self.closest_expiration = Some(timer);
|
2021-03-02 10:18:24 +01:00
|
|
|
}
|
|
|
|
Poll::Pending
|
2018-12-10 16:00:16 +01:00
|
|
|
}
|
|
|
|
}
|
2020-08-18 14:51:03 +02:00
|
|
|
|
|
|
|
/// Event that can be produced by the `Mdns` behaviour.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum MdnsEvent {
|
|
|
|
/// Discovered nodes through mDNS.
|
|
|
|
Discovered(DiscoveredAddrsIter),
|
|
|
|
|
|
|
|
/// The given combinations of `PeerId` and `Multiaddr` have expired.
|
|
|
|
///
|
|
|
|
/// Each discovered record has a time-to-live. When this TTL expires and the address hasn't
|
|
|
|
/// been refreshed, we remove it from the list and emit it as an `Expired` event.
|
|
|
|
Expired(ExpiredAddrsIter),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterator that produces the list of addresses that have been discovered.
|
|
|
|
pub struct DiscoveredAddrsIter {
|
2021-03-02 10:18:24 +01:00
|
|
|
inner: smallvec::IntoIter<[(PeerId, Multiaddr); 4]>,
|
2020-08-18 14:51:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for DiscoveredAddrsIter {
|
|
|
|
type Item = (PeerId, Multiaddr);
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
self.inner.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.inner.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 10:18:24 +01:00
|
|
|
impl ExactSizeIterator for DiscoveredAddrsIter {}
|
2020-08-18 14:51:03 +02:00
|
|
|
|
|
|
|
impl fmt::Debug for DiscoveredAddrsIter {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2021-03-02 10:18:24 +01:00
|
|
|
fmt.debug_struct("DiscoveredAddrsIter").finish()
|
2020-08-18 14:51:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterator that produces the list of addresses that have expired.
|
|
|
|
pub struct ExpiredAddrsIter {
|
2021-03-02 10:18:24 +01:00
|
|
|
inner: smallvec::IntoIter<[(PeerId, Multiaddr); 4]>,
|
2020-08-18 14:51:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for ExpiredAddrsIter {
|
|
|
|
type Item = (PeerId, Multiaddr);
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
self.inner.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.inner.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 10:18:24 +01:00
|
|
|
impl ExactSizeIterator for ExpiredAddrsIter {}
|
2020-08-18 14:51:03 +02:00
|
|
|
|
|
|
|
impl fmt::Debug for ExpiredAddrsIter {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2021-03-02 10:18:24 +01:00
|
|
|
fmt.debug_struct("ExpiredAddrsIter").finish()
|
2020-08-18 14:51:03 +02:00
|
|
|
}
|
|
|
|
}
|