Crates.io | ductile |
lib.rs | ductile |
version | 0.3.0 |
source | src |
created_at | 2020-08-02 18:31:52.007172 |
updated_at | 2022-12-29 16:41:18.510466 |
description | A channel implementation that allows both local in-memory channels and remote TCP-based channels with the same interface. |
homepage | https://github.com/edomora97/ductile |
repository | https://github.com/edomora97/ductile |
max_upload_size | |
id | 272291 |
size | 56,926 |
A channel implementation that allows both local in-memory channels and remote TCP-based/Unix channels with the same interface.
This crate exposes an interface similar to std::sync::mpsc
channels. It provides a multiple
producers, single consumer channel that can use under the hood local in-memory channels
(provided by crossbeam_channel
) but also network channels via TCP/Unix sockets. The remote
connection can also be encrypted using ChaCha20.
Like std::sync::mpsc
, there could be more ChannelSender
but there can be only one
ChannelReceiver
. The two ends of the channel are generic over the message type sent but the
type must match (this is checked at compile time only for local channels). If the types do not
match errors will be returned and possibly a panic can occur since the channel breaks.
The channels also offer a raw mode where the data is not serialized and send as-is,
improving drastically the performances for unstructured data. It should be noted that you can
mix the two modes in the same channel but you must be careful to always receive with the correct
mode (you cannot receive raw data with the normal recv
method). Extra care should be taken
when cloning the sender and using it from more threads.
With remote channels the messages are serialized using bincode
.
Here there are some simple examples of how you can use this crate:
let (tx, rx) = new_local_channel();
tx.send(42u64).unwrap();
tx.send_raw(&vec![1, 2, 3, 4]).unwrap();
let answer = rx.recv().unwrap();
assert_eq!(answer, 42u64);
let data = rx.recv_raw().unwrap();
assert_eq!(data, vec![1, 2, 3, 4]);
Note that your types must be Serialize
and Deserialize
.
#[derive(Serialize, Deserialize)]
struct Thing {
pub x: u32,
pub y: String,
}
let (tx, rx) = new_local_channel();
tx.send(Thing {
x: 42,
y: "foobar".into(),
})
.unwrap();
let thing: Thing = rx.recv().unwrap();
assert_eq!(thing.x, 42);
assert_eq!(thing.y, "foobar");
let port = 18452; // let's hope we can bind this port!
let mut server = ChannelServer::bind(("127.0.0.1", port)).unwrap();
// this examples need a second thread since the handshake cannot be done using a single thread
// only
let client_thread = std::thread::spawn(move || {
let (sender, receiver) = connect_channel(("127.0.0.1", port)).unwrap();
sender.send(vec![1, 2, 3, 4]).unwrap();
let data: Vec<i32> = receiver.recv().unwrap();
assert_eq!(data, vec![5, 6, 7, 8]);
sender.send(vec![9, 10, 11, 12]).unwrap();
sender.send_raw(&vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap();
});
let (sender, receiver, _addr) = server.next().unwrap();
let data: Vec<i32> = receiver.recv().unwrap();
assert_eq!(data, vec![1, 2, 3, 4]);
sender.send(vec![5, 6, 7, 8]).unwrap();
let data = receiver.recv().unwrap();
assert_eq!(data, vec![9, 10, 11, 12]);
let data = receiver.recv_raw().unwrap();
assert_eq!(data, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
let port = 18453;
let enc_key = [69u8; 32];
let mut server = ChannelServer::bind_with_enc(("127.0.0.1", port), enc_key).unwrap();
let client_thread = std::thread::spawn(move || {
let (sender, receiver) = connect_channel_with_enc(("127.0.0.1", port), &enc_key).unwrap();
sender.send(vec![1u8, 2, 3, 4]).unwrap();
let data: Vec<u8> = receiver.recv().unwrap();
assert_eq!(data, vec![5u8, 6, 7, 8]);
sender.send(vec![69u8; 12345]).unwrap();
sender.send_raw(&vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap();
});
let (sender, receiver, _addr) = server.next().unwrap();
let data: Vec<u8> = receiver.recv().unwrap();
assert_eq!(data, vec![1u8, 2, 3, 4]);
sender.send(vec![5u8, 6, 7, 8]).unwrap();
let data = receiver.recv().unwrap();
assert_eq!(data, vec![69u8; 12345]);
let file = receiver.recv_raw().unwrap();
assert_eq!(file, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
All the normal (non-raw) messages are encapsulated inside a ChannelMessage::Message
,
serialized and sent normally.
The messages in raw mode are sent differently depending if the channel is local or remote. If the channel is local there is no serialization penality so the data is simply sent into the channel. If the channel is removed to avoid serialization a small message with the data length is sent first, followed by the actual payload (that can be eventually encrypted).
Local channels do not need an handshake, therefore this section refers only to remote channels.
There are 2 kinds of handshake: one for encrypted channels and one for non-encrypted ones. The porpuse of the handshake is to share encryption information (like the nonce) and check if the encryption key is correct.
For encrypted channels 2 rounds of handshakes take place:
For unencrytpted channels the same handshake is done but with a static key and nonce and only the magic is encrypted. All the following messages will be sent unencrypted.
License: MIT