pub type PeerStreamSenders = std::sync::Arc<
tokio::sync::Mutex<std::collections::HashMap<(SelfID52, RemoteID52), StreamRequestSender>>,
>;
type Stream = (iroh::endpoint::SendStream, iroh::endpoint::RecvStream);
type StreamResult = eyre::Result<Stream>;
type ReplyChannel = tokio::sync::oneshot::Sender<StreamResult>;
type RemoteID52 = String;
type SelfID52 = String;
type StreamRequest = (crate::ProtocolHeader, ReplyChannel);
type StreamRequestSender = tokio::sync::mpsc::Sender<StreamRequest>;
type StreamRequestReceiver = tokio::sync::mpsc::Receiver<StreamRequest>;
#[tracing::instrument(skip_all)]
pub async fn get_stream(
self_endpoint: iroh::Endpoint,
header: crate::ProtocolHeader,
remote_node_id52: RemoteID52,
peer_stream_senders: PeerStreamSenders,
graceful: crate::Graceful,
) -> eyre::Result<(iroh::endpoint::SendStream, iroh::endpoint::RecvStream)> {
use eyre::WrapErr;
tracing::trace!("get_stream: {header:?}");
let stream_request_sender = get_stream_request_sender(
self_endpoint,
remote_node_id52,
peer_stream_senders,
graceful,
)
.await;
tracing::trace!("got stream_request_sender");
let (reply_channel, receiver) = tokio::sync::oneshot::channel();
stream_request_sender
.send((header, reply_channel))
.await
.wrap_err_with(|| "failed to send on stream_request_sender")?;
tracing::trace!("sent stream request");
let r = receiver.await?;
tracing::trace!("got stream request reply");
r
}
#[tracing::instrument(skip_all)]
async fn get_stream_request_sender(
self_endpoint: iroh::Endpoint,
remote_node_id52: RemoteID52,
peer_stream_senders: PeerStreamSenders,
graceful: crate::Graceful,
) -> StreamRequestSender {
let self_id52 = data_encoding::BASE32_DNSSEC.encode(self_endpoint.node_id().as_bytes());
let mut senders = peer_stream_senders.lock().await;
if let Some(sender) = senders.get(&(self_id52.clone(), remote_node_id52.clone())) {
return sender.clone();
}
let (sender, receiver) = tokio::sync::mpsc::channel(1);
senders.insert(
(self_id52.clone(), remote_node_id52.clone()),
sender.clone(),
);
drop(senders);
let graceful_for_connection_manager = graceful.clone();
graceful.spawn(async move {
connection_manager(
receiver,
self_endpoint,
remote_node_id52.clone(),
graceful_for_connection_manager,
)
.await;
let mut senders = peer_stream_senders.lock().await;
senders.remove(&(self_id52.clone(), remote_node_id52));
});
sender
}
async fn connection_manager(
mut receiver: StreamRequestReceiver,
self_endpoint: iroh::Endpoint,
remote_node_id52: RemoteID52,
graceful: crate::Graceful,
) {
let e = match connection_manager_(
&mut receiver,
self_endpoint,
remote_node_id52.clone(),
graceful,
)
.await
{
Ok(()) => {
tracing::info!("connection manager closed");
return;
}
Err(e) => e,
};
tracing::error!("connection manager worker error: {e:?}");
receiver.close();
while let Some((_protocol, reply_channel)) = receiver.recv().await {
if reply_channel
.send(Err(eyre::anyhow!("failed to create connection: {e:?}")))
.is_err()
{
tracing::error!("failed to send error reply: {e:?}");
}
}
}
#[tracing::instrument(skip_all)]
async fn connection_manager_(
receiver: &mut StreamRequestReceiver,
self_endpoint: iroh::Endpoint,
remote_node_id52: RemoteID52,
graceful: crate::Graceful,
) -> eyre::Result<()> {
let conn = match self_endpoint
.connect(
{
use std::str::FromStr;
let public_key = fastn_id52::PublicKey::from_str(&remote_node_id52)
.map_err(|e| eyre::anyhow!("{}", e))?;
iroh::NodeId::from(iroh::PublicKey::from_bytes(&public_key.to_bytes())?)
},
crate::APNS_IDENTITY,
)
.await
{
Ok(v) => v,
Err(e) => {
tracing::error!("failed to create connection: {e:?}");
return Err(eyre::anyhow!("failed to create connection: {e:?}"));
}
};
let timeout = std::time::Duration::from_secs(12);
let mut idle_counter = 0;
loop {
tracing::trace!("connection manager loop");
if idle_counter > 4 {
tracing::info!("connection idle timeout, returning");
break;
}
tokio::select! {
_ = graceful.cancelled() => {
tracing::info!("graceful shutdown");
break;
},
_ = tokio::time::sleep(timeout) => {
tracing::info!("woken up");
if let Err(e) = crate::ping(&conn).await {
tracing::error!("pinging failed: {e:?}");
break;
}
idle_counter += 1;
},
Some((header, reply_channel)) = receiver.recv() => {
tracing::info!("connection: {header:?}, idle counter: {idle_counter}");
idle_counter = 0;
if let Err(e) = handle_request(&conn, header, reply_channel).await {
tracing::error!("failed to handle request: {e:?}");
return Err(e);
}
tracing::info!("handled connection");
}
else => {
tracing::error!("failed to read from receiver");
break
},
}
}
Ok(())
}
async fn handle_request(
conn: &iroh::endpoint::Connection,
header: crate::ProtocolHeader,
reply_channel: ReplyChannel,
) -> eyre::Result<()> {
use eyre::WrapErr;
tracing::trace!("handling request: {header:?}");
let (mut send, mut recv) = match conn.open_bi().await {
Ok(v) => {
tracing::trace!("opened bi-stream");
v
}
Err(e) => {
tracing::error!("failed to open_bi: {e:?}");
return Err(eyre::anyhow!("failed to open_bi: {e:?}"));
}
};
send.write_all(
&serde_json::to_vec(&header.protocol)
.wrap_err_with(|| format!("failed to serialize protocol: {:?}", header.protocol))?,
)
.await?;
tracing::trace!("wrote protocol");
send.write(b"\n")
.await
.wrap_err_with(|| "failed to write newline")?;
tracing::trace!("wrote newline");
if let Some(extra) = header.extra {
send.write_all(extra.as_bytes()).await?;
tracing::trace!("wrote protocol");
send.write(b"\n")
.await
.wrap_err_with(|| "failed to write newline")?;
}
let msg = crate::next_string(&mut recv).await?;
if msg != crate::ACK {
tracing::error!("failed to read ack: {msg:?}");
return Err(eyre::anyhow!("failed to read ack: {msg:?}"));
}
tracing::trace!("received ack");
reply_channel.send(Ok((send, recv))).unwrap_or_else(|e| {
tracing::error!("failed to send reply: {e:?}");
});
tracing::trace!("handle_request done");
Ok(())
}