Skip to content

Commit

Permalink
misc/multistream-select: Interpretation of EOF as Failed negotiation (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
mxinden authored Nov 9, 2020
1 parent 3859116 commit 70bb2d7
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 21 deletions.
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ libsecp256k1 = { version = "0.3.1", optional = true }
log = "0.4"
multiaddr = { package = "parity-multiaddr", version = "0.9.2", path = "../misc/multiaddr" }
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"
pin-project = "1.0.0"
prost = "0.6.1"
Expand Down
5 changes: 5 additions & 0 deletions misc/multistream-select/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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]

- Temporarily disable the internal selection of "parallel" protocol
Expand Down
2 changes: 1 addition & 1 deletion misc/multistream-select/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "multistream-select"
description = "Multistream-select negotiation protocol for libp2p"
version = "0.8.4"
version = "0.8.5"
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand Down
16 changes: 9 additions & 7 deletions misc/multistream-select/src/dialer_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{Negotiated, NegotiationError};
use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};

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
/// for a peer acting as the _dialer_ (or _initiator_).
Expand Down Expand Up @@ -235,9 +235,10 @@ where
*this.state = SeqState::AwaitProtocol { io, protocol };
return Poll::Pending
}
Poll::Ready(None) =>
return Poll::Ready(Err(NegotiationError::from(
io::Error::from(io::ErrorKind::UnexpectedEof)))),
// 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.
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
};

match msg {
Expand Down Expand Up @@ -358,9 +359,10 @@ where
*this.state = ParState::RecvProtocols { io };
return Poll::Pending
}
Poll::Ready(None) =>
return Poll::Ready(Err(NegotiationError::from(
io::Error::from(io::ErrorKind::UnexpectedEof)))),
// 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.
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
};

match &msg {
Expand Down
24 changes: 15 additions & 9 deletions misc/multistream-select/src/listener_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::protocol::{Protocol, ProtocolError, MessageIO, Message, Version};

use futures::prelude::*;
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
/// for a peer acting as the _listener_ (or _responder_).
Expand Down Expand Up @@ -118,10 +118,10 @@ where
return Poll::Ready(Err(ProtocolError::InvalidMessage.into()))
},
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(From::from(err))),
Poll::Ready(None) =>
return Poll::Ready(Err(NegotiationError::from(
ProtocolError::IoError(
io::ErrorKind::UnexpectedEof.into())))),
// 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.
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
Poll::Pending => {
*this.state = State::RecvHeader { io };
return Poll::Pending
Expand Down Expand Up @@ -152,10 +152,16 @@ where
State::RecvMessage { mut io } => {
let msg = match Pin::new(&mut io).poll_next(cx) {
Poll::Ready(Some(Ok(msg))) => msg,
Poll::Ready(None) =>
return Poll::Ready(Err(NegotiationError::from(
ProtocolError::IoError(
io::ErrorKind::UnexpectedEof.into())))),
// 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.
Poll::Ready(None) => return Poll::Ready(Err(NegotiationError::Failed)),
Poll::Pending => {
*this.state = State::RecvMessage { io };
return Poll::Pending;
Expand Down
5 changes: 2 additions & 3 deletions misc/multistream-select/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ fn no_protocol_found() {
let protos = vec![b"/proto1", b"/proto2"];
let io = match listener_select_proto(connec, protos).await {
Ok((_, io)) => io,
// We don't explicitly check for `Failed` because the client might close the connection when it
// realizes that we have no protocol in common.
Err(_) => return,
Err(NegotiationError::Failed) => return,
Err(NegotiationError::ProtocolError(e)) => panic!("Unexpected protocol error {}", e),
};
match io.complete().await {
Err(NegotiationError::Failed) => {},
Expand Down

0 comments on commit 70bb2d7

Please sign in to comment.