diff --git a/multistream-select/README.md b/multistream-select/README.md new file mode 100644 index 00000000..88d3ea81 --- /dev/null +++ b/multistream-select/README.md @@ -0,0 +1,92 @@ +# Multistream-select + +This crate implements the `multistream-select` protocol, which is the protocol used by libp2p +to negotiate which protocol to use with the remote. + +> **Note**: This crate is used by the internals of *libp2p*, and it is not required to +> understand it in order to use *libp2p*. + +Whenever a new connection or a new multiplexed substream is opened, libp2p uses +`multistream-select` to negotiate with the remote which protocol to use. After a protocol has +been successfully negotiated, the stream (ie. the connection or the multiplexed substream) +immediately stops using `multistream-select` and starts using the negotiated protocol. + +## Protocol explanation + +The dialer has two options available: either request the list of protocols that the listener +supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by +answering with the same protocol name) or refuse the choice (by answering "not available"). + +## Examples + +For a dialer: + +```rust +extern crate bytes; +extern crate futures; +extern crate multistream_select; +extern crate tokio_core; + +use bytes::Bytes; +use multistream_select::dialer_select_proto; +use futures::{Future, Sink, Stream}; +use tokio_core::net::TcpStream; +use tokio_core::reactor::Core; + +let mut core = Core::new().unwrap(); + +#[derive(Debug, Copy, Clone)] +enum MyProto { Echo, Hello } + +let client = TcpStream::connect(&"127.0.0.1:10333".parse().unwrap(), &core.handle()) + .from_err() + .and_then(move |connec| { + let protos = vec![ + (Bytes::from("/echo/1.0.0"), ::eq, MyProto::Echo), + (Bytes::from("/hello/2.5.0"), ::eq, MyProto::Hello), + ] + .into_iter(); + dialer_select_proto(connec, protos).map(|r| r.0) + }); + +let negotiated_protocol: MyProto = core.run(client).expect("failed to find a protocol"); +println!("negotiated: {:?}", negotiated_protocol); +``` + +For a listener: + +```rust +extern crate bytes; +extern crate futures; +extern crate multistream_select; +extern crate tokio_core; + +use bytes::Bytes; +use multistream_select::listener_select_proto; +use futures::{Future, Sink, Stream}; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; + +let mut core = Core::new().unwrap(); + +#[derive(Debug, Copy, Clone)] +enum MyProto { Echo, Hello } + +let server = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap() + .incoming() + .from_err() + .and_then(move |(connec, _)| { + let protos = vec![ + (Bytes::from("/echo/1.0.0"), ::eq, MyProto::Echo), + (Bytes::from("/hello/2.5.0"), ::eq, MyProto::Hello), + ] + .into_iter(); + listener_select_proto(connec, protos) + }) + .for_each(|(proto, _connec)| { + println!("new remote with {:?} negotiated", proto); + Ok(()) + }); + +core.run(server).expect("failed to run server"); +``` diff --git a/multistream-select/src/lib.rs b/multistream-select/src/lib.rs index 6de8d1e1..119b1a43 100644 --- a/multistream-select/src/lib.rs +++ b/multistream-select/src/lib.rs @@ -18,20 +18,105 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +// TODO: use this once stable ; for now we just copy-paste the content of the README.md +//#![doc(include = "../README.md")] + //! # Multistream-select //! -//! Multistream-select is the "main" protocol of libp2p. -//! Whenever a connection opens between two peers, it starts talking in `multistream-select`. +//! This crate implements the `multistream-select` protocol, which is the protocol used by libp2p +//! to negotiate which protocol to use with the remote. //! -//! The purpose of `multistream-select` is to choose which protocol we are going to use. As soon as -//! both sides agree on a given protocol, the socket immediately starts using it and multistream is -//! no longer relevant. +//! > **Note**: This crate is used by the internals of *libp2p*, and it is not required to +//! > understand it in order to use *libp2p*. //! -//! However note that `multistream-select` is also sometimes used on top of another protocol such -//! as secio or multiplex. For example, two hosts can use `multistream-select` to decide to use -//! secio, then use `multistream-select` again (wrapped inside `secio`) to decide to use -//! `multiplex`, then use `multistream-select` one more time (wrapped inside `secio` and -//! `multiplex`) to decide to use the final actual protocol. +//! Whenever a new connection or a new multiplexed substream is opened, libp2p uses +//! `multistream-select` to negotiate with the remote which protocol to use. After a protocol has +//! been successfully negotiated, the stream (ie. the connection or the multiplexed substream) +//! immediately stops using `multistream-select` and starts using the negotiated protocol. +//! +//! ## Protocol explanation +//! +//! The dialer has two options available: either request the list of protocols that the listener +//! supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by +//! answering with the same protocol name) or refuse the choice (by answering "not available"). +//! +//! ## Examples +//! +//! For a dialer: +//! +//! ```no_run +//! extern crate bytes; +//! extern crate futures; +//! extern crate multistream_select; +//! extern crate tokio_core; +//! +//! # fn main() { +//! use bytes::Bytes; +//! use multistream_select::dialer_select_proto; +//! use futures::{Future, Sink, Stream}; +//! use tokio_core::net::TcpStream; +//! use tokio_core::reactor::Core; +//! +//! let mut core = Core::new().unwrap(); +//! +//! #[derive(Debug, Copy, Clone)] +//! enum MyProto { Echo, Hello } +//! +//! let client = TcpStream::connect(&"127.0.0.1:10333".parse().unwrap(), &core.handle()) +//! .from_err() +//! .and_then(move |connec| { +//! let protos = vec![ +//! (Bytes::from("/echo/1.0.0"), ::eq, MyProto::Echo), +//! (Bytes::from("/hello/2.5.0"), ::eq, MyProto::Hello), +//! ] +//! .into_iter(); +//! dialer_select_proto(connec, protos).map(|r| r.0) +//! }); +//! +//! let negotiated_protocol: MyProto = core.run(client).expect("failed to find a protocol"); +//! println!("negotiated: {:?}", negotiated_protocol); +//! # } +//! ``` +//! +//! For a listener: +//! +//! ```no_run +//! extern crate bytes; +//! extern crate futures; +//! extern crate multistream_select; +//! extern crate tokio_core; +//! +//! # fn main() { +//! use bytes::Bytes; +//! use multistream_select::listener_select_proto; +//! use futures::{Future, Sink, Stream}; +//! use tokio_core::net::TcpListener; +//! use tokio_core::reactor::Core; +//! +//! let mut core = Core::new().unwrap(); +//! +//! #[derive(Debug, Copy, Clone)] +//! enum MyProto { Echo, Hello } +//! +//! let server = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap() +//! .incoming() +//! .from_err() +//! .and_then(move |(connec, _)| { +//! let protos = vec![ +//! (Bytes::from("/echo/1.0.0"), ::eq, MyProto::Echo), +//! (Bytes::from("/hello/2.5.0"), ::eq, MyProto::Hello), +//! ] +//! .into_iter(); +//! listener_select_proto(connec, protos) +//! }) +//! .for_each(|(proto, _connec)| { +//! println!("new remote with {:?} negotiated", proto); +//! Ok(()) +//! }); +//! +//! core.run(server).expect("failed to run server"); +//! # } +//! ``` extern crate bytes; extern crate futures;