mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-12 17:41:22 +00:00
[multistream] Make the lazy variant more interoperable. (#1855)
* Make the lazy variant interoperable. The remaining optimisation for `V1Lazy` for a listener in the negotiation, whereby the listener delays flushing of the multistream version header, is hereby removed. The remaining effect of `V1Lazy` is only on the side of the dialer, which delays flushing of its singular protocol proposal in order to send it together with the first application data (or an attempt is made to read from the negotiated stream, which similarly triggers a flush of the protocol proposal). This permits `V1Lazy` dialers to be interoperable with `V1` listeners. The remaining theoretical pitfall whereby application data gets misinterpreted as another protocol proposal by a listener remains, however unlikely. `V1` remains the default, but we may eventually risk just making this lazy dialer flush a part of the default `V1` implementation, removing the dedicated `V1Lazy` version identifier. * Update CHANGELOG * Separate versions from mere header lines. Every multistream-select version maps to a specific header line, but there may be different variants of the same multistream-select version using the same header line, i.e. the same wire protocol. * Cleanup * Update misc/multistream-select/CHANGELOG.md
This commit is contained in:
@ -20,8 +20,8 @@
|
||||
|
||||
//! Protocol negotiation strategies for the peer acting as the dialer.
|
||||
|
||||
use crate::{Negotiated, NegotiationError};
|
||||
use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};
|
||||
use crate::{Negotiated, NegotiationError, Version};
|
||||
use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, HeaderLine};
|
||||
|
||||
use futures::{future::Either, prelude::*};
|
||||
use std::{convert::TryFrom as _, iter, mem, pin::Pin, task::{Context, Poll}};
|
||||
@ -41,7 +41,7 @@ use std::{convert::TryFrom as _, iter, mem, pin::Pin, task::{Context, Poll}};
|
||||
/// thus an inaccurate size estimate may result in a suboptimal choice.
|
||||
///
|
||||
/// Within the scope of this library, a dialer always commits to a specific
|
||||
/// multistream-select protocol [`Version`], whereas a listener always supports
|
||||
/// multistream-select [`Version`], whereas a listener always supports
|
||||
/// all versions supported by this library. Frictionless multistream-select
|
||||
/// protocol upgrades may thus proceed by deployments with updated listeners,
|
||||
/// eventually followed by deployments of dialers choosing the newer protocol.
|
||||
@ -181,11 +181,15 @@ where
|
||||
},
|
||||
}
|
||||
|
||||
if let Err(err) = Pin::new(&mut io).start_send(Message::Header(*this.version)) {
|
||||
let h = HeaderLine::from(*this.version);
|
||||
if let Err(err) = Pin::new(&mut io).start_send(Message::Header(h)) {
|
||||
return Poll::Ready(Err(From::from(err)));
|
||||
}
|
||||
|
||||
let protocol = this.protocols.next().ok_or(NegotiationError::Failed)?;
|
||||
|
||||
// The dialer always sends the header and the first protocol
|
||||
// proposal in one go for efficiency.
|
||||
*this.state = SeqState::SendProtocol { io, protocol };
|
||||
}
|
||||
|
||||
@ -209,9 +213,14 @@ where
|
||||
} else {
|
||||
match this.version {
|
||||
Version::V1 => *this.state = SeqState::FlushProtocol { io, protocol },
|
||||
// This is the only effect that `V1Lazy` has compared to `V1`:
|
||||
// Optimistically settling on the only protocol that
|
||||
// the dialer supports for this negotiation. Notably,
|
||||
// the dialer expects a regular `V1` response.
|
||||
Version::V1Lazy => {
|
||||
log::debug!("Dialer: Expecting proposed protocol: {}", p);
|
||||
let io = Negotiated::expecting(io.into_reader(), p, *this.version);
|
||||
let hl = HeaderLine::from(Version::V1Lazy);
|
||||
let io = Negotiated::expecting(io.into_reader(), p, Some(hl));
|
||||
return Poll::Ready(Ok((protocol, io)))
|
||||
}
|
||||
}
|
||||
@ -242,7 +251,7 @@ where
|
||||
};
|
||||
|
||||
match msg {
|
||||
Message::Header(v) if v == *this.version => {
|
||||
Message::Header(v) if v == HeaderLine::from(*this.version) => {
|
||||
*this.state = SeqState::AwaitProtocol { io, protocol };
|
||||
}
|
||||
Message::Protocol(ref p) if p.as_ref() == protocol.as_ref() => {
|
||||
@ -318,7 +327,8 @@ where
|
||||
},
|
||||
}
|
||||
|
||||
if let Err(err) = Pin::new(&mut io).start_send(Message::Header(*this.version)) {
|
||||
let msg = Message::Header(HeaderLine::from(*this.version));
|
||||
if let Err(err) = Pin::new(&mut io).start_send(msg) {
|
||||
return Poll::Ready(Err(From::from(err)));
|
||||
}
|
||||
|
||||
@ -366,7 +376,7 @@ where
|
||||
};
|
||||
|
||||
match &msg {
|
||||
Message::Header(v) if v == this.version => {
|
||||
Message::Header(h) if h == &HeaderLine::from(*this.version) => {
|
||||
*this.state = ParState::RecvProtocols { io }
|
||||
}
|
||||
Message::Protocols(supported) => {
|
||||
@ -395,9 +405,10 @@ where
|
||||
if let Err(err) = Pin::new(&mut io).start_send(Message::Protocol(p.clone())) {
|
||||
return Poll::Ready(Err(From::from(err)));
|
||||
}
|
||||
log::debug!("Dialer: Expecting proposed protocol: {}", p);
|
||||
|
||||
let io = Negotiated::expecting(io.into_reader(), p, *this.version);
|
||||
log::debug!("Dialer: Expecting proposed protocol: {}", p);
|
||||
let io = Negotiated::expecting(io.into_reader(), p, None);
|
||||
|
||||
return Poll::Ready(Ok((protocol, io)))
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user