[][src]Crate conec

COordinated NEtwork Channels: a network abstraction for communication among many Clients, facilitated by one Coordinator.

Clients are assumed to know (e.g., by configuration or service discovery) the hostname and port number of Coordinator. Coordinator is assumed to have a TLS certificate for this hostname issued by a CA that Clients trust.

The basic abstraction is a channel, which connects two entities (Client or Coordinator). A channel comprises one bi-directional control stream and zero or more bidirectional data streams. Every Client shares a channel with Coordinator: at startup, Client connects to Coordinator.

A data stream accepts a sequence of messages from its writer. The data stream's reader receives these messages in order. The stream handles all message framing: a read yields a full message or nothing. There is no support for out-of-order reads; use multiple data streams instead.

In this version of conec, the only supported data streams are proxied streams: Client sends data to Coordinator, who forwards to another Client. (In a future version, Coordinator will assist Clients in constructing Client-to-Client channels, including NAT traversal.)

Quickstart

A conec instance requires a Coordinator with a TLS certificate for its hostname and an IP address that Clients can reach. It is possible to use a self-signed certificate (generated, say, by rcgen) as long as the Clients trust it; see ClientConfig::set_ca.

See tests.rs for more complete usage examples than the ones below.

Coordinator

To start a Coordinator, first build a CoordConfig. For example,

This example is not tested
let mut coord_cfg = CoordConfig::new(cert_path, key_path).unwrap();
coord_cfg.enable_stateless_retry();
coord_cfg.set_port(1337);

Next, pass this configuration to the Coord constructor, which returns a future that returns the Coordiator plus a coord::IncomingStreams object when forced.

This example is not tested
let (coord, coord_istreams) = Coord::new(coord_cfg).await.unwrap();
coord.await

The Coord constructor launches driver threads in the background. These threads will run until the Coord struct is dropped and all clients have disconnected.

Coord is a future that returns only if an error occurs. It is not necessary to await this future for the coordinator to run, but you shouldn't drop the Coord object if you want the Coordinator to run!

Client

To start a Client, first build a ClientConfig. For example,

let mut client_cfg =
    ClientConfig::new("client1".to_string(), "coord.conec.example.com".to_string());
client_cfg.set_port(1337);

Next, pass this configuration to the Client constructor, which is a future that returns the Client plus a client::IncomingStreams object when forced:

This example is not tested
let (client, istreams) = Client::new(client_cfg).await.unwrap();

Streams

Once your Client has connected to its Coordinator, it can set up data streams with the Coordinator or other Clients, and send data on those streams:

This example is not tested
let (mut to_client2, _from_client2) = client
    .new_stream("client2".to_string())
    .await
    .unwrap();
to_client2.send(Bytes::from("hi there")).await.unwrap();

The receiving client first accepts the stream from its client::IncomingStreams and then reads data from it:

This example is not tested
let (peer, strmid, _to_client1, mut from_client1) = istreams
    .next()
    .await
    .unwrap();
println!("Got new stream with id {:?} from peer {}", strmid, peer);
let rec = from_client1
    .try_next()
    .await?
    .unwrap();

The first element of the returned 4-tuple will be Some(<name>) when the peer is a Client called <name>, or None when the peer is the Coordinator. To open a stream to the Coordinator, pass None to new_stream:

This example is not tested
let (mut to_coord, _from_coord) = client
    .new_stream(None)   // NOTE: None means open stream to Coordinator
    .await
    .unwrap();
to_coord.send(Bytes::from("hi coordinator")).await.unwrap();

The Coordinator receives this stream by taking the next element from its coord::IncomingStreams:

This example is not tested
let (peer, strmid, _to_client, mut from_client) = coord_istreams
    .next()
    .await
    .unwrap();

For the coordinator, the first element of the returned 4-tuple is a String rather than an Option, since all incoming streams must be from clients.

Re-exports

pub use client::Client;
pub use coord::Coord;

Modules

ca

This module defines convenience functions for generating certificates.

client

This module defines the Client entity and associated functionality.

coord

This module defines the Coordinator entity and associated functionality.

quinn

! Re-exports from quinn

Structs

ClientConfig

! Client configuration struct

CoordConfig

! Coordinator configuration struct

Type Definitions

InStream

! Receiving end of a data stream: a Stream of BytesMut.

OutStream

! Sending end of a data stream that accepts Bytes.