1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
//! Rust Implementation of Erlang Distribution Protocol.
//!
//! Distribution protocol is used to communicate with distributed erlang nodes.
//!
//! Reference: [Distribution Protocol](http://erlang.org/doc/apps/erts/erl_dist_protocol.html)
//!
//! # Examples
//!
//! Gets a node entry from EPMD:
//! ```no_run
//! # use smol::net::TcpStream;
//! use erl_dist::epmd::{DEFAULT_EPMD_PORT, EpmdClient};
//!
//! # fn main() -> anyhow::Result<()> {
//! # smol::block_on(async {
//! // Connect to the local EPMD.
//! let connection = TcpStream::connect(("localhost", DEFAULT_EPMD_PORT)).await?;
//! let client = EpmdClient::new(connection);
//!
//! // Get the information of a node.
//! let node_name = "foo";
//! if let Some(node) = client.get_node(node_name).await? {
//! println!("Found: {:?}", node);
//! } else {
//! println!("Not found");
//! }
//! # Ok(())
//! # })
//! # }
//! ```
//!
//! Sends a message to an Erlang node:
//! ```no_run
//! # use smol::net::TcpStream;
//! use erl_dist::LOWEST_DISTRIBUTION_PROTOCOL_VERSION;
//! use erl_dist::node::{Creation, LocalNode};
//! use erl_dist::handshake::ClientSideHandshake;
//! use erl_dist::term::{Atom, Pid};
//! use erl_dist::message::{channel, Message};
//!
//! # fn main() -> anyhow::Result<()> {
//! # smol::block_on(async {
//! // Connect to a peer node.
//! let peer_host = "localhost";
//! let peer_port = 7483; // NOTE: Usually, port number is retrieved from EPMD.
//! let connection = TcpStream::connect((peer_host, peer_port)).await?;
//!
//! // Local node information.
//! let creation = Creation::random();
//! let local_node = LocalNode::new("foo@localhost".parse()?, creation);
//!
//! // Do handshake.
//! let mut handshake = ClientSideHandshake::new(connection, local_node.clone(), "cookie");
//! let _status = handshake.execute_send_name(LOWEST_DISTRIBUTION_PROTOCOL_VERSION).await?;
//! let (connection, peer_node) = handshake.execute_rest(true).await?;
//!
//! // Create a channel.
//! let capability_flags = local_node.flags & peer_node.flags;
//! let (mut tx, _) = channel(connection, capability_flags);
//!
//! // Send a message.
//! let from_pid = Pid::new(local_node.name.to_string(), 0, 0, local_node.creation.get());
//! let to_name = Atom::from("bar");
//! let msg = Message::reg_send(from_pid, to_name, Atom::from("hello").into());
//! tx.send(msg).await?;
//! # Ok(())
//! # })
//! # }
//! ```
//!
//! Example commands:
//! - EPMD Client Example: [send_msg.rs](https://github.com/sile/erl_dist/blob/master/examples/epmd_cli.rs)
//! - Client Node Example: [send_msg.rs](https://github.com/sile/erl_dist/blob/master/examples/send_msg.rs)
//! - Server Node Example: [recv_msg.rs](https://github.com/sile/erl_dist/blob/master/examples/recv_msg.rs)
#![warn(missing_docs)]
pub mod epmd;
pub mod handshake;
pub mod message;
pub mod node;
pub mod term;
mod channel;
mod eetf_ext;
mod flags;
mod io;
pub use self::flags::DistributionFlags;
/// The lowest distribution protocol version this crate can handle.
pub const LOWEST_DISTRIBUTION_PROTOCOL_VERSION: u16 = 6;
/// The highest distribution protocol version this crate can handle.
pub const HIGHEST_DISTRIBUTION_PROTOCOL_VERSION: u16 = 6;
#[cfg(test)]
mod tests {
use std::process::{Child, Command};
pub const COOKIE: &str = "test-cookie";
#[derive(Debug)]
pub struct TestErlangNode {
child: Child,
}
impl TestErlangNode {
pub async fn new(name: &str) -> anyhow::Result<Self> {
let child = Command::new("erl")
.args(&["-sname", name, "-noshell", "-setcookie", COOKIE])
.spawn()?;
let start = std::time::Instant::now();
loop {
if let Ok(client) = try_epmd_client().await {
if client.get_node(name).await?.is_some() {
break;
}
}
std::thread::sleep(std::time::Duration::from_millis(500));
if start.elapsed() > std::time::Duration::from_secs(10) {
break;
}
}
Ok(Self { child })
}
}
impl Drop for TestErlangNode {
fn drop(&mut self) {
let _ = self.child.kill();
}
}
pub async fn try_epmd_client() -> anyhow::Result<crate::epmd::EpmdClient<smol::net::TcpStream>>
{
let client = smol::net::TcpStream::connect(("127.0.0.1", crate::epmd::DEFAULT_EPMD_PORT))
.await
.map(crate::epmd::EpmdClient::new)?;
Ok(client)
}
pub async fn epmd_client() -> crate::epmd::EpmdClient<smol::net::TcpStream> {
try_epmd_client().await.unwrap()
}
}