mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-30 18:21:33 +00:00
misc/multistream-select: Interpretation of EOF as Failed negotiation (#1823)
Treat EOF error as [`NegotiationError::Failed`], not as [`NegotiationError::ProtocolError`], allowing dropping or closing an I/O stream as a permissible way to "gracefully" fail a negotiation. This is e.g. important when a listener rejects a protocol with [`Message::NotAvailable`] and the dialer does not have alternative protocols to propose. Then the dialer will stop the negotiation and drop the corresponding stream. As a listener this EOF should be interpreted as a failed negotiation.
This commit is contained in:
@ -22,7 +22,7 @@ libsecp256k1 = { version = "0.3.1", optional = true }
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
multiaddr = { package = "parity-multiaddr", version = "0.9.2", path = "../misc/multiaddr" }
|
multiaddr = { package = "parity-multiaddr", version = "0.9.2", path = "../misc/multiaddr" }
|
||||||
multihash = "0.11.3"
|
multihash = "0.11.3"
|
||||||
multistream-select = { version = "0.8.4", path = "../misc/multistream-select" }
|
multistream-select = { version = "0.8.5", path = "../misc/multistream-select" }
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
pin-project = "1.0.0"
|
pin-project = "1.0.0"
|
||||||
prost = "0.6.1"
|
prost = "0.6.1"
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
# 0.8.5 [unreleased]
|
||||||
|
|
||||||
|
- During negotiation do not interpret EOF error as an IO error, but instead as a
|
||||||
|
negotiation error. See https://github.com/libp2p/rust-libp2p/pull/1823.
|
||||||
|
|
||||||
# 0.8.4 [2020-10-20]
|
# 0.8.4 [2020-10-20]
|
||||||
|
|
||||||
- Temporarily disable the internal selection of "parallel" protocol
|
- Temporarily disable the internal selection of "parallel" protocol
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "multistream-select"
|
name = "multistream-select"
|
||||||
description = "Multistream-select negotiation protocol for libp2p"
|
description = "Multistream-select negotiation protocol for libp2p"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/libp2p/rust-libp2p"
|
repository = "https://github.com/libp2p/rust-libp2p"
|
||||||
|
@ -24,7 +24,7 @@ use crate::{Negotiated, NegotiationError};
|
|||||||
use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};
|
use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};
|
||||||
|
|
||||||
use futures::{future::Either, prelude::*};
|
use futures::{future::Either, prelude::*};
|
||||||
use std::{convert::TryFrom as _, io, iter, mem, pin::Pin, task::{Context, Poll}};
|
use std::{convert::TryFrom as _, iter, mem, pin::Pin, task::{Context, Poll}};
|
||||||
|
|
||||||
/// Returns a `Future` that negotiates a protocol on the given I/O stream
|
/// Returns a `Future` that negotiates a protocol on the given I/O stream
|
||||||
/// for a peer acting as the _dialer_ (or _initiator_).
|
/// for a peer acting as the _dialer_ (or _initiator_).
|
||||||
@ -235,9 +235,10 @@ where
|
|||||||
*this.state = SeqState::AwaitProtocol { io, protocol };
|
*this.state = SeqState::AwaitProtocol { io, protocol };
|
||||||
return Poll::Pending
|
return Poll::Pending
|
||||||
}
|
}
|
||||||
Poll::Ready(None) =>
|
// Treat EOF error as [`NegotiationError::Failed`], not as
|
||||||
return Poll::Ready(Err(NegotiationError::from(
|
// [`NegotiationError::ProtocolError`], allowing dropping or closing an I/O
|
||||||
io::Error::from(io::ErrorKind::UnexpectedEof)))),
|
// stream as a permissible way to "gracefully" fail a negotiation.
|
||||||
|
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
@ -358,9 +359,10 @@ where
|
|||||||
*this.state = ParState::RecvProtocols { io };
|
*this.state = ParState::RecvProtocols { io };
|
||||||
return Poll::Pending
|
return Poll::Pending
|
||||||
}
|
}
|
||||||
Poll::Ready(None) =>
|
// Treat EOF error as [`NegotiationError::Failed`], not as
|
||||||
return Poll::Ready(Err(NegotiationError::from(
|
// [`NegotiationError::ProtocolError`], allowing dropping or closing an I/O
|
||||||
io::Error::from(io::ErrorKind::UnexpectedEof)))),
|
// stream as a permissible way to "gracefully" fail a negotiation.
|
||||||
|
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match &msg {
|
match &msg {
|
||||||
|
@ -26,7 +26,7 @@ use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};
|
|||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{convert::TryFrom as _, io, iter::FromIterator, mem, pin::Pin, task::{Context, Poll}};
|
use std::{convert::TryFrom as _, iter::FromIterator, mem, pin::Pin, task::{Context, Poll}};
|
||||||
|
|
||||||
/// Returns a `Future` that negotiates a protocol on the given I/O stream
|
/// Returns a `Future` that negotiates a protocol on the given I/O stream
|
||||||
/// for a peer acting as the _listener_ (or _responder_).
|
/// for a peer acting as the _listener_ (or _responder_).
|
||||||
@ -118,10 +118,10 @@ where
|
|||||||
return Poll::Ready(Err(ProtocolError::InvalidMessage.into()))
|
return Poll::Ready(Err(ProtocolError::InvalidMessage.into()))
|
||||||
},
|
},
|
||||||
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(From::from(err))),
|
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(From::from(err))),
|
||||||
Poll::Ready(None) =>
|
// Treat EOF error as [`NegotiationError::Failed`], not as
|
||||||
return Poll::Ready(Err(NegotiationError::from(
|
// [`NegotiationError::ProtocolError`], allowing dropping or closing an I/O
|
||||||
ProtocolError::IoError(
|
// stream as a permissible way to "gracefully" fail a negotiation.
|
||||||
io::ErrorKind::UnexpectedEof.into())))),
|
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
*this.state = State::RecvHeader { io };
|
*this.state = State::RecvHeader { io };
|
||||||
return Poll::Pending
|
return Poll::Pending
|
||||||
@ -152,10 +152,16 @@ where
|
|||||||
State::RecvMessage { mut io } => {
|
State::RecvMessage { mut io } => {
|
||||||
let msg = match Pin::new(&mut io).poll_next(cx) {
|
let msg = match Pin::new(&mut io).poll_next(cx) {
|
||||||
Poll::Ready(Some(Ok(msg))) => msg,
|
Poll::Ready(Some(Ok(msg))) => msg,
|
||||||
Poll::Ready(None) =>
|
// Treat EOF error as [`NegotiationError::Failed`], not as
|
||||||
return Poll::Ready(Err(NegotiationError::from(
|
// [`NegotiationError::ProtocolError`], allowing dropping or closing an I/O
|
||||||
ProtocolError::IoError(
|
// stream as a permissible way to "gracefully" fail a negotiation.
|
||||||
io::ErrorKind::UnexpectedEof.into())))),
|
//
|
||||||
|
// This is e.g. important when a listener rejects a protocol with
|
||||||
|
// [`Message::NotAvailable`] and the dialer does not have alternative
|
||||||
|
// protocols to propose. Then the dialer will stop the negotiation and drop
|
||||||
|
// the corresponding stream. As a listener this EOF should be interpreted as
|
||||||
|
// a failed negotiation.
|
||||||
|
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
|
||||||
Poll::Pending => {
|
Poll::Pending => {
|
||||||
*this.state = State::RecvMessage { io };
|
*this.state = State::RecvMessage { io };
|
||||||
return Poll::Pending;
|
return Poll::Pending;
|
||||||
|
@ -85,9 +85,8 @@ fn no_protocol_found() {
|
|||||||
let protos = vec![b"/proto1", b"/proto2"];
|
let protos = vec![b"/proto1", b"/proto2"];
|
||||||
let io = match listener_select_proto(connec, protos).await {
|
let io = match listener_select_proto(connec, protos).await {
|
||||||
Ok((_, io)) => io,
|
Ok((_, io)) => io,
|
||||||
// We don't explicitly check for `Failed` because the client might close the connection when it
|
Err(NegotiationError::Failed) => return,
|
||||||
// realizes that we have no protocol in common.
|
Err(NegotiationError::ProtocolError(e)) => panic!("Unexpected protocol error {}", e),
|
||||||
Err(_) => return,
|
|
||||||
};
|
};
|
||||||
match io.complete().await {
|
match io.complete().await {
|
||||||
Err(NegotiationError::Failed) => {},
|
Err(NegotiationError::Failed) => {},
|
||||||
|
Reference in New Issue
Block a user