use super::Dht;
use super::bencode::{Dict, bytes, entry};
use super::flight::Flight;
pub(crate) use super::frontier::Outcome;
use super::frontier::Walk;
use super::krpc::NodeId;
use std::net::SocketAddr;
impl Dht {
pub(crate) fn search(&mut self, target: NodeId, q: &str) -> Result<Outcome, String> {
if self.bootstrap.is_empty() {
return Err("no bootstrap node to ask".into());
}
let args = Dict::from([entry("target", bytes(&target.0))]);
let mut walk = Walk::new(target, self.config.k);
let mut flight = Flight::new();
let mut sent = self.door(&mut walk, &mut flight, &args);
loop {
let f = walk.frontier(&flight);
let under = sent < self.config.max_queries;
if let Some(addr) = f.next.filter(|_| under && f.walking < self.config.alpha) {
walk.asked.insert(addr);
sent += usize::from(self.ask(&mut flight, addr, false, q, args.clone()));
} else if f.waiting || f.door || (f.next.is_some() && under && f.walking > 0) {
if let Some((query, message)) = self.land(&mut flight)? {
walk.heard(query, message);
}
} else if f.next.is_none() && walk.dry() && under {
sent += self.door(&mut walk, &mut flight, &args);
} else {
break;
}
}
self.claims = walk.claims;
let mut out = walk.out;
if out.replies.is_empty() && out.errors.is_empty() {
return Err(format!("no DHT node answered {q} for {target}"));
}
out.replies.sort_by_key(|(n, _)| n.id.distance(&target));
Ok(out)
}
fn door(&mut self, walk: &mut Walk, flight: &mut Flight, args: &Dict) -> usize {
let mut sent = 0usize;
let knock: Vec<SocketAddr> = self
.bootstrap
.iter()
.copied()
.filter(|a| walk.knocks(*a))
.collect();
for addr in knock {
walk.asked.insert(addr);
sent += usize::from(self.ask(flight, addr, true, "find_node", args.clone()));
}
sent.max(1)
}
}