mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 18:05:41 +00:00
Allow multiple substreams (#2379)
* Allow multiple substreams * Update core/network-libp2p/src/custom_proto/handler.rs Co-Authored-By: tomaka <pierre.krieger1708@gmail.com>
This commit is contained in:
committed by
Gavin Wood
parent
2b10d8e080
commit
48d53a35b7
@@ -26,7 +26,7 @@ use libp2p::core::{
|
|||||||
upgrade::{InboundUpgrade, OutboundUpgrade}
|
upgrade::{InboundUpgrade, OutboundUpgrade}
|
||||||
};
|
};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::{error, fmt, io, marker::PhantomData, mem, time::Duration, time::Instant};
|
use std::{error, fmt, io, marker::PhantomData, mem, time::Duration, time::Instant};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
use tokio_timer::{Delay, clock::Clock};
|
use tokio_timer::{Delay, clock::Clock};
|
||||||
@@ -73,13 +73,18 @@ use void::Void;
|
|||||||
/// `Disable` or an `Enable` message from the outer layer. At any time, the outer layer is free to
|
/// `Disable` or an `Enable` message from the outer layer. At any time, the outer layer is free to
|
||||||
/// toggle the handler between the disabled and enabled states.
|
/// toggle the handler between the disabled and enabled states.
|
||||||
///
|
///
|
||||||
/// When the handler is enabled for the first time, if it is the dialer of the connection, it tries
|
/// When the handler switches to "enabled", it opens a substream and negotiates the protocol named
|
||||||
/// to open a substream. The substream negotiates either a protocol named `/substrate/xxx`, where
|
/// `/substrate/xxx`, where `xxx` is chosen by the user and depends on the chain.
|
||||||
/// `xxx` is chosen by the user.
|
|
||||||
///
|
///
|
||||||
/// Then, we have one unique substream where bidirectional communications happen. If the remote
|
/// For backwards compatibility reasons, when we switch to "enabled" for the first time (while we
|
||||||
/// closes the substream, we consider that we are now disconnected. Re-enabling is performed by
|
/// are still in "init" mode) and we are the connection listener, we don't open a substream.
|
||||||
/// re-opening the substream (even if we are not the dialer of the connection).
|
///
|
||||||
|
/// In order the handle the situation where both the remote and us get enabled at the same time,
|
||||||
|
/// we tolerate multiple substreams open at the same time. Messages are transmitted on an arbitrary
|
||||||
|
/// substream. The endpoints don't try to agree on a single substream.
|
||||||
|
///
|
||||||
|
/// We consider that we are now "closed" if the remote closes all the existing substreams.
|
||||||
|
/// Re-opening it can then be performed by closing all active substream and re-opening one.
|
||||||
///
|
///
|
||||||
pub struct CustomProtoHandlerProto<TMessage, TSubstream> {
|
pub struct CustomProtoHandlerProto<TMessage, TSubstream> {
|
||||||
/// Configuration for the protocol upgrade to negotiate.
|
/// Configuration for the protocol upgrade to negotiate.
|
||||||
@@ -169,11 +174,11 @@ enum ProtocolState<TMessage, TSubstream> {
|
|||||||
deadline: Delay,
|
deadline: Delay,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Backwards-compatible mode. Contains the unique substream that is open.
|
/// Normal operating mode. Contains the substreams that are open.
|
||||||
/// If we are in this state, we have sent a `CustomProtocolOpen` message to the outside.
|
/// If we are in this state, we have sent a `CustomProtocolOpen` message to the outside.
|
||||||
Normal {
|
Normal {
|
||||||
/// The unique substream where bidirectional communications happen.
|
/// The substreams where bidirectional communications happen.
|
||||||
substream: RegisteredProtocolSubstream<TMessage, TSubstream>,
|
substreams: SmallVec<[RegisteredProtocolSubstream<TMessage, TSubstream>; 4]>,
|
||||||
/// Contains substreams which are being shut down.
|
/// Contains substreams which are being shut down.
|
||||||
shutdown: SmallVec<[RegisteredProtocolSubstream<TMessage, TSubstream>; 4]>,
|
shutdown: SmallVec<[RegisteredProtocolSubstream<TMessage, TSubstream>; 4]>,
|
||||||
},
|
},
|
||||||
@@ -284,8 +289,7 @@ where
|
|||||||
};
|
};
|
||||||
self.events_queue.push(ProtocolsHandlerEvent::Custom(event));
|
self.events_queue.push(ProtocolsHandlerEvent::Custom(event));
|
||||||
ProtocolState::Normal {
|
ProtocolState::Normal {
|
||||||
substream: incoming.into_iter().next()
|
substreams: incoming.into_iter().collect(),
|
||||||
.expect("We have a check above that incoming isn't empty; QED"),
|
|
||||||
shutdown: SmallVec::new()
|
shutdown: SmallVec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,9 +323,11 @@ where
|
|||||||
ProtocolState::Disabled { shutdown: SmallVec::new(), reenable: false }
|
ProtocolState::Disabled { shutdown: SmallVec::new(), reenable: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Normal { mut substream, mut shutdown } => {
|
ProtocolState::Normal { substreams, mut shutdown } => {
|
||||||
substream.shutdown();
|
for mut substream in substreams {
|
||||||
shutdown.push(substream);
|
substream.shutdown();
|
||||||
|
shutdown.push(substream);
|
||||||
|
}
|
||||||
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
||||||
result: Ok(())
|
result: Ok(())
|
||||||
};
|
};
|
||||||
@@ -341,13 +347,12 @@ where
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
fn poll_state(&mut self)
|
fn poll_state(&mut self)
|
||||||
-> Option<ProtocolsHandlerEvent<RegisteredProtocol<TMessage>, (), CustomProtoHandlerOut<TMessage>>> {
|
-> Option<ProtocolsHandlerEvent<RegisteredProtocol<TMessage>, (), CustomProtoHandlerOut<TMessage>>> {
|
||||||
let return_value;
|
match mem::replace(&mut self.state, ProtocolState::Poisoned) {
|
||||||
self.state = match mem::replace(&mut self.state, ProtocolState::Poisoned) {
|
|
||||||
ProtocolState::Poisoned => {
|
ProtocolState::Poisoned => {
|
||||||
error!(target: "sub-libp2p", "Handler with {:?} is in poisoned state",
|
error!(target: "sub-libp2p", "Handler with {:?} is in poisoned state",
|
||||||
self.remote_peer_id);
|
self.remote_peer_id);
|
||||||
return_value = None;
|
self.state = ProtocolState::Poisoned;
|
||||||
ProtocolState::Poisoned
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Init { substreams, mut init_deadline } => {
|
ProtocolState::Init { substreams, mut init_deadline } => {
|
||||||
@@ -361,8 +366,8 @@ where
|
|||||||
Err(_) => error!(target: "sub-libp2p", "Tokio timer has errored")
|
Err(_) => error!(target: "sub-libp2p", "Tokio timer has errored")
|
||||||
}
|
}
|
||||||
|
|
||||||
return_value = None;
|
self.state = ProtocolState::Init { substreams, init_deadline };
|
||||||
ProtocolState::Init { substreams, init_deadline }
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Opening { mut deadline } => {
|
ProtocolState::Opening { mut deadline } => {
|
||||||
@@ -373,63 +378,74 @@ where
|
|||||||
is_severe: true,
|
is_severe: true,
|
||||||
error: "Timeout when opening protocol".to_string().into(),
|
error: "Timeout when opening protocol".to_string().into(),
|
||||||
};
|
};
|
||||||
return_value = Some(ProtocolsHandlerEvent::Custom(event));
|
self.state = ProtocolState::Opening { deadline };
|
||||||
ProtocolState::Opening { deadline }
|
Some(ProtocolsHandlerEvent::Custom(event))
|
||||||
},
|
},
|
||||||
Ok(Async::NotReady) => {
|
Ok(Async::NotReady) => {
|
||||||
return_value = None;
|
self.state = ProtocolState::Opening { deadline };
|
||||||
ProtocolState::Opening { deadline }
|
None
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!(target: "sub-libp2p", "Tokio timer has errored");
|
error!(target: "sub-libp2p", "Tokio timer has errored");
|
||||||
deadline.reset(self.clock.now() + Duration::from_secs(60));
|
deadline.reset(self.clock.now() + Duration::from_secs(60));
|
||||||
return_value = None;
|
self.state = ProtocolState::Opening { deadline };
|
||||||
ProtocolState::Opening { deadline }
|
None
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Normal { mut substream, shutdown } => {
|
ProtocolState::Normal { mut substreams, shutdown } => {
|
||||||
match substream.poll() {
|
for n in (0..substreams.len()).rev() {
|
||||||
Ok(Async::Ready(Some(RegisteredProtocolEvent::Message(message)))) => {
|
let mut substream = substreams.swap_remove(n);
|
||||||
let event = CustomProtoHandlerOut::CustomMessage {
|
match substream.poll() {
|
||||||
message
|
Ok(Async::NotReady) => substreams.push(substream),
|
||||||
};
|
Ok(Async::Ready(Some(RegisteredProtocolEvent::Message(message)))) => {
|
||||||
return_value = Some(ProtocolsHandlerEvent::Custom(event));
|
let event = CustomProtoHandlerOut::CustomMessage {
|
||||||
ProtocolState::Normal { substream, shutdown }
|
message
|
||||||
},
|
};
|
||||||
Ok(Async::Ready(Some(RegisteredProtocolEvent::Clogged { messages }))) => {
|
substreams.push(substream);
|
||||||
let event = CustomProtoHandlerOut::Clogged {
|
self.state = ProtocolState::Normal { substreams, shutdown };
|
||||||
messages,
|
return Some(ProtocolsHandlerEvent::Custom(event));
|
||||||
};
|
},
|
||||||
return_value = Some(ProtocolsHandlerEvent::Custom(event));
|
Ok(Async::Ready(Some(RegisteredProtocolEvent::Clogged { messages }))) => {
|
||||||
ProtocolState::Normal { substream, shutdown }
|
let event = CustomProtoHandlerOut::Clogged {
|
||||||
}
|
messages,
|
||||||
Ok(Async::NotReady) => {
|
};
|
||||||
return_value = None;
|
substreams.push(substream);
|
||||||
ProtocolState::Normal { substream, shutdown }
|
self.state = ProtocolState::Normal { substreams, shutdown };
|
||||||
}
|
return Some(ProtocolsHandlerEvent::Custom(event));
|
||||||
Ok(Async::Ready(None)) => {
|
|
||||||
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
|
||||||
result: Ok(())
|
|
||||||
};
|
|
||||||
return_value = Some(ProtocolsHandlerEvent::Custom(event));
|
|
||||||
ProtocolState::Disabled {
|
|
||||||
shutdown: shutdown.into_iter().collect(),
|
|
||||||
reenable: true
|
|
||||||
}
|
}
|
||||||
}
|
Ok(Async::Ready(None)) => {
|
||||||
Err(err) => {
|
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
||||||
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
result: Ok(())
|
||||||
result: Err(err),
|
};
|
||||||
};
|
substreams.push(substream);
|
||||||
return_value = Some(ProtocolsHandlerEvent::Custom(event));
|
self.state = ProtocolState::Disabled {
|
||||||
ProtocolState::Disabled {
|
shutdown: shutdown.into_iter().collect(),
|
||||||
shutdown: shutdown.into_iter().collect(),
|
reenable: true
|
||||||
reenable: true
|
};
|
||||||
|
return Some(ProtocolsHandlerEvent::Custom(event));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if substreams.is_empty() {
|
||||||
|
let event = CustomProtoHandlerOut::CustomProtocolClosed {
|
||||||
|
result: Err(err),
|
||||||
|
};
|
||||||
|
self.state = ProtocolState::Disabled {
|
||||||
|
shutdown: shutdown.into_iter().collect(),
|
||||||
|
reenable: true
|
||||||
|
};
|
||||||
|
return Some(ProtocolsHandlerEvent::Custom(event));
|
||||||
|
} else {
|
||||||
|
debug!(target: "sub-libp2p", "Error on extra substream: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This code is reached is none if and only if none of the substreams are in a ready state.
|
||||||
|
self.state = ProtocolState::Normal { substreams, shutdown };
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Disabled { mut shutdown, reenable } => {
|
ProtocolState::Disabled { mut shutdown, reenable } => {
|
||||||
@@ -437,21 +453,19 @@ where
|
|||||||
// If `reenable` is `true`, that means we should open the substreams system again
|
// If `reenable` is `true`, that means we should open the substreams system again
|
||||||
// after all the substreams are closed.
|
// after all the substreams are closed.
|
||||||
if reenable && shutdown.is_empty() {
|
if reenable && shutdown.is_empty() {
|
||||||
return_value = Some(ProtocolsHandlerEvent::OutboundSubstreamRequest {
|
self.state = ProtocolState::Opening {
|
||||||
|
deadline: Delay::new(self.clock.now() + Duration::from_secs(60))
|
||||||
|
};
|
||||||
|
Some(ProtocolsHandlerEvent::OutboundSubstreamRequest {
|
||||||
protocol: SubstreamProtocol::new(self.protocol.clone()),
|
protocol: SubstreamProtocol::new(self.protocol.clone()),
|
||||||
info: (),
|
info: (),
|
||||||
});
|
})
|
||||||
ProtocolState::Opening {
|
|
||||||
deadline: Delay::new(self.clock.now() + Duration::from_secs(60))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return_value = None;
|
self.state = ProtocolState::Disabled { shutdown, reenable };
|
||||||
ProtocolState::Disabled { shutdown, reenable }
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return_value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called by `inject_fully_negotiated_inbound` and `inject_fully_negotiated_outbound`.
|
/// Called by `inject_fully_negotiated_inbound` and `inject_fully_negotiated_outbound`.
|
||||||
@@ -481,17 +495,14 @@ where
|
|||||||
};
|
};
|
||||||
self.events_queue.push(ProtocolsHandlerEvent::Custom(event));
|
self.events_queue.push(ProtocolsHandlerEvent::Custom(event));
|
||||||
ProtocolState::Normal {
|
ProtocolState::Normal {
|
||||||
substream,
|
substreams: smallvec![substream],
|
||||||
shutdown: SmallVec::new()
|
shutdown: SmallVec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Normal { substream: existing, mut shutdown } => {
|
ProtocolState::Normal { substreams: mut existing, shutdown } => {
|
||||||
debug!(target: "sub-libp2p", "Received extra substream after having already one \
|
existing.push(substream);
|
||||||
open in backwards-compatibility mode with {:?}", self.remote_peer_id);
|
ProtocolState::Normal { substreams: existing, shutdown }
|
||||||
substream.shutdown();
|
|
||||||
shutdown.push(substream);
|
|
||||||
ProtocolState::Normal { substream: existing, shutdown }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolState::Disabled { mut shutdown, .. } => {
|
ProtocolState::Disabled { mut shutdown, .. } => {
|
||||||
@@ -505,8 +516,8 @@ where
|
|||||||
/// Sends a message to the remote.
|
/// Sends a message to the remote.
|
||||||
fn send_message(&mut self, message: TMessage) {
|
fn send_message(&mut self, message: TMessage) {
|
||||||
match self.state {
|
match self.state {
|
||||||
ProtocolState::Normal { ref mut substream, .. } =>
|
ProtocolState::Normal { ref mut substreams, .. } =>
|
||||||
substream.send_message(message),
|
substreams[0].send_message(message),
|
||||||
|
|
||||||
_ => debug!(target: "sub-libp2p", "Tried to send message over closed protocol \
|
_ => debug!(target: "sub-libp2p", "Tried to send message over closed protocol \
|
||||||
with {:?}", self.remote_peer_id)
|
with {:?}", self.remote_peer_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user