rqbit/crates/librqbit/src/dht_utils.rs

106 lines
3.1 KiB
Rust
Raw Normal View History

use std::{collections::HashSet, net::SocketAddr};
2021-07-13 13:16:59 +01:00
use anyhow::Context;
2021-07-03 19:10:59 +01:00
use buffers::ByteString;
use futures::{stream::FuturesUnordered, StreamExt};
2021-07-03 19:10:59 +01:00
use librqbit_core::torrent_metainfo::TorrentMetaV1Info;
2021-07-03 15:52:39 +01:00
use log::debug;
2021-07-03 19:10:59 +01:00
use crate::peer_info_reader;
2021-07-12 21:59:08 +01:00
use librqbit_core::id20::Id20;
#[derive(Debug)]
pub enum ReadMetainfoResult<Rx> {
Found {
info: TorrentMetaV1Info<ByteString>,
rx: Rx,
seen: HashSet<SocketAddr>,
},
ChannelClosed {
seen: HashSet<SocketAddr>,
},
}
pub async fn read_metainfo_from_peer_receiver<A: StreamExt<Item = SocketAddr> + Unpin>(
2021-07-12 21:59:08 +01:00
peer_id: Id20,
info_hash: Id20,
mut addrs: A,
) -> ReadMetainfoResult<A> {
let mut seen = HashSet::<SocketAddr>::new();
2021-07-12 19:42:48 +01:00
let first_addr = match addrs.next().await {
Some(addr) => addr,
None => return ReadMetainfoResult::ChannelClosed { seen },
};
seen.insert(first_addr);
2021-07-13 13:16:59 +01:00
let semaphore = tokio::sync::Semaphore::new(128);
let read_info_guarded = |addr| {
let semaphore = &semaphore;
async move {
let token = semaphore.acquire().await?;
let ret = peer_info_reader::read_metainfo_from_peer(addr, peer_id, info_hash)
.await
.with_context(|| format!("error reading metainfo from {}", addr));
drop(token);
ret
}
};
let mut unordered = FuturesUnordered::new();
2021-07-13 13:16:59 +01:00
unordered.push(read_info_guarded(first_addr));
loop {
tokio::select! {
2021-07-12 19:42:48 +01:00
next_addr = addrs.next() => {
match next_addr {
Some(addr) => {
if seen.insert(addr) {
2021-07-13 13:16:59 +01:00
unordered.push(read_info_guarded(addr));
}
},
None => return ReadMetainfoResult::ChannelClosed { seen },
}
},
done = unordered.next(), if !unordered.is_empty() => {
match done {
Some(Ok(info)) => return ReadMetainfoResult::Found { info, seen, rx: addrs },
Some(Err(e)) => {
2021-07-13 13:16:59 +01:00
debug!("{:#}", e);
},
None => unreachable!()
}
}
};
}
}
#[cfg(test)]
mod tests {
2021-07-12 21:59:08 +01:00
use dht::{Dht, Id20};
use librqbit_core::peer_id::generate_peer_id;
2021-07-03 19:10:59 +01:00
use super::*;
2021-07-12 21:59:08 +01:00
use std::{str::FromStr, sync::Once};
static LOG_INIT: Once = Once::new();
fn init_logging() {
LOG_INIT.call_once(pretty_env_logger::init)
}
#[tokio::test]
async fn read_metainfo_from_dht() {
init_logging();
2021-07-12 21:59:08 +01:00
let info_hash = Id20::from_str("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
let dht = Dht::new().await.unwrap();
let peer_rx = dht.get_peers(info_hash).await;
let peer_id = generate_peer_id();
2021-07-12 21:59:08 +01:00
match read_metainfo_from_peer_receiver(peer_id, info_hash, peer_rx).await {
2021-07-13 13:16:59 +01:00
ReadMetainfoResult::Found { info, .. } => dbg!(info),
ReadMetainfoResult::ChannelClosed { .. } => todo!("should not have happened"),
2021-07-12 21:59:08 +01:00
};
}
}