rust-libp2p/libp2p-swarm
tomaka cd4075559e
Merge pull request #58 from tomaka/tokio-core-dep
Remove the tokio-core dependency where not necessary
2017-12-07 11:54:18 +01:00
..
2017-12-06 15:26:46 +01:00
2017-12-05 10:17:24 +01:00

Transport and protocol upgrade system of libp2p.

This crate contains all the core traits and mechanisms of the transport system of libp2p.

The Transport trait

The main trait that this crate provides is Transport, which provides the dial and listen_on methods and can be used to dial or listen on a multiaddress. This crate does not provide any concrete (non-dummy, non-adapter) implementation of this trait. It is implemented on structs such as TcpConfig, UdpConfig, WebsocketConfig that are provided by external crates.

Each implementation of Transport only supports some multiaddress protocols, for example the TcpConfig struct only supports multiaddresses that look like /ip*/*.*.*.*/tcp/*. Two implementations of Transport can be grouped together with the or_transport method, in order to obtain a single object that supports the protocols of both objects at once. This can be done multiple times in order to chain as many implementations as you want.

// TODO: right now only tcp-transport exists, but we need to add an example for chaining multiple // transports once that makes sense

Connection upgrades

Once a socket has been opened with a remote through a Transport, it can be upgraded. This consists in negotiating a protocol with the remote (through multistream-select), and applying that protocol on the socket.

A potential connection upgrade is represented with the ConnectionUpgrade trait. The trait consists in a protocol name plus a method that turns the socket into an Output object whose nature and type is specific to each upgrade.

There exists two kinds of connection upgrades: middlewares, and actual protocols.

Middlewares

Examples of middleware protocol upgrades include PlainTextConfig (dummy upgrade), SecioConfig (encyption layer), or MultiplexConfig.

The output of a middleware protocol upgrade must implement the AsyncRead and AsyncWrite traits, just like sockets do.

A middleware can be applied by using the with_upgrade method of the Transport trait. The return value of this method also implements the Transport trait, which means that you can call dial() and listen_on() on it in order to directly obtain an upgraded connection. An error is produced if the remote doesn't support the protocol that you are trying to negotiate.

extern crate libp2p_swarm;
extern crate libp2p_tcp_transport;
extern crate tokio_core;

use libp2p_swarm::Transport;

let tokio_core = tokio_core::reactor::Core::new().unwrap();
let tcp_transport = libp2p_tcp_transport::TcpConfig::new(tokio_core.handle());
let upgraded = tcp_transport.with_upgrade(libp2p_swarm::PlainText);

// upgraded.dial(...)   // automatically applies the plain text protocol on the socket

Actual protocols

Actual protocols work the same way as middlewares, except that their Output doesn't implement the AsyncRead and AsyncWrite traits. The consequence of this is that the return value of with_upgrade cannot doesn't implement the Transport trait and thus cannot be used as a transport.

However the value returned by with_upgrade still provides methods named dial and listen_on, which will yield you respectively a Future or a Stream, which you can use to obtain the Output. This Output can then be used in a protocol-specific way to use the protocol.

extern crate futures;
extern crate libp2p_ping;
extern crate libp2p_swarm;
extern crate libp2p_tcp_transport;
extern crate tokio_core;

use futures::Future;
use libp2p_ping::Ping;
use libp2p_swarm::Transport;

let mut core = tokio_core::reactor::Core::new().unwrap();

let ping_finished_future = libp2p_tcp_transport::TcpConfig::new(core.handle())
    // We have a `TcpConfig` struct that implements `Transport`, and apply a `Ping` upgrade on it.
    .with_upgrade(Ping)
    // TODO: right now the only available protocol is ping, but we want to replace it with
    //       something that is more simple to use
    .dial(libp2p_swarm::Multiaddr::new("127.0.0.1:12345").unwrap()).unwrap_or_else(|_| panic!())
    .and_then(|(mut pinger, service)| {
        pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!())
    });

// Runs until the ping arrives.
core.run(ping_finished_future).unwrap();

Grouping protocols

You can use the .or_upgrade() method to group multiple upgrades together. The return value also implements the ConnectionUpgrade trait and will choose one of the protocols amongst the ones supported.