use alloc::{boxed::Box, sync::Arc};
use core::{error::Error, result::Result};
use iroh::{
Endpoint, EndpointAddr,
endpoint::Connection,
protocol::{AcceptError, ProtocolHandler},
};
use iroh_metrics::{Counter, MetricsGroup};
use tokio::time::{Duration, Instant};
pub const PING_ALPN: &[u8] = b"asimov/ping";
#[derive(Debug, Default, MetricsGroup)]
#[metrics(name = "ping")]
pub struct PingMetrics {
pub pings_sent: Counter,
pub pings_recv: Counter,
}
#[derive(Debug, Clone)]
pub struct PingProtocol {
metrics: Arc<PingMetrics>,
}
impl Default for PingProtocol {
fn default() -> Self {
Self::new()
}
}
impl PingProtocol {
pub fn new() -> Self {
Self {
metrics: Arc::new(PingMetrics::default()),
}
}
pub fn metrics(&self) -> &Arc<PingMetrics> {
&self.metrics
}
pub async fn ping(
&self,
endpoint: &Endpoint,
addr: EndpointAddr,
) -> Result<Duration, Box<dyn Error>> {
let conn = endpoint.connect(addr, PING_ALPN).await?;
let (mut send, mut recv) = conn.open_bi().await?;
let start = Instant::now();
send.write_all(b"PING").await?;
send.finish()?;
let response = recv.read_to_end(4).await?;
assert_eq!(&response, b"PONG");
let duration = start.elapsed();
self.metrics.pings_sent.inc();
conn.close(0u32.into(), b"BYE!");
Ok(duration)
}
}
impl ProtocolHandler for PingProtocol {
async fn accept(&self, connection: Connection) -> n0_error::Result<(), AcceptError> {
let node_id = connection.remote_id();
#[cfg(feature = "std")]
std::eprintln!("Accepted a connection from node {node_id}");
let (mut send, mut recv) = connection.accept_bi().await?;
let req = recv.read_to_end(4).await.map_err(AcceptError::from_err)?;
assert_eq!(&req, b"PING");
self.metrics.pings_recv.inc();
send.write_all(b"PONG")
.await
.map_err(AcceptError::from_err)?;
send.finish()?;
connection.closed().await;
Ok(())
}
}