You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.9 KiB
Rust

use crate::torrent_manager::TorrentManager;
use crossbeam_channel::{unbounded, Receiver, Sender};
use reqwest::blocking::Client;
use reqwest::header::HeaderMap;
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::RwLock;
use std::thread::JoinHandle;
use torment_core::infohash::v1::U160;
use torment_core::peer_storage::PeerStorageHolder;
use torment_tracker::{
block_do_announce, Tracker, TrackerAnnounceRequest, TrackerAnnounceResponse,
};
use url::Url;
pub struct TrackerManager {
id: U160,
ip: IpAddr,
port: u16,
counter: AtomicUsize,
trackers: HashMap<usize, RwLock<Tracker>>,
url_index: HashMap<String, usize>,
peer_storage: PeerStorageHolder,
tracker_offshore_state: TrackerOffshoreState,
}
struct TrackerOffshoreState {
threads: Vec<JoinHandle<()>>,
tracker_request_sender: Sender<(usize, TrackerAnnounceRequest)>,
tracker_request_receiver: Receiver<(usize, TrackerAnnounceRequest)>,
tracker_response_sender: Sender<(usize, TrackerAnnounceResponse)>,
tracker_response_receiver: Receiver<(usize, TrackerAnnounceResponse)>,
}
impl TrackerOffshoreState {
fn new() -> TrackerOffshoreState {
let (req_send, req_recv) = unbounded();
let (resp_send, resp_recv) = unbounded();
TrackerOffshoreState {
threads: vec![],
tracker_request_sender: req_send,
tracker_request_receiver: req_recv,
tracker_response_sender: resp_send,
tracker_response_receiver: resp_recv,
}
}
}
pub type TrackerId = usize;
fn tracker_thread(
input: Receiver<(usize, TrackerAnnounceRequest)>,
output: Sender<(usize, TrackerAnnounceResponse)>,
) {
let mut map = HeaderMap::new();
map.insert(
reqwest::header::USER_AGENT,
format!("eater's Torment/{}", env!("CARGO_PKG_VERSION"))
.parse()
.unwrap(),
);
let client = Client::builder().default_headers(map).build().unwrap();
for (tracker_id, req) in input {
output
.send((tracker_id, block_do_announce(req, &client)))
.unwrap();
}
}
impl TrackerManager {
pub fn new(
ip: IpAddr,
port: u16,
peer_id: U160,
peer_storage: PeerStorageHolder,
) -> TrackerManager {
TrackerManager {
id: peer_id,
ip,
port,
counter: Default::default(),
trackers: Default::default(),
url_index: Default::default(),
peer_storage,
tracker_offshore_state: TrackerOffshoreState::new(),
}
}
pub fn get_tracker_id(&mut self, url: &Url) -> Option<TrackerId> {
// dht is not a tracker bye
if url.scheme() == "dht" {
return None;
}
let url_str = url.as_str();
if let Some(id) = self.url_index.get(url_str) {
return Some(*id);
}
let new_id = self.counter.fetch_add(1, Ordering::AcqRel);
self.url_index.insert(url_str.to_string(), new_id);
self.trackers.insert(
new_id,
RwLock::new(Tracker::new(url.clone(), self.peer_storage.clone())),
);
Some(new_id)
}
pub fn announce(&mut self, tracker_id: usize, torrent: &TorrentManager) {
let id = torrent.info_hash();
{
let tracker = &self.trackers[&tracker_id].read().unwrap();
if !tracker.needs_update(id) {
return;
}
}
let tracker = &mut self.trackers[&tracker_id].write().unwrap();
println!(
"Queueing announce {} on {}",
torrent.info_hash(),
tracker.url()
);
let req = tracker.create_update_request(
id,
self.id,
self.ip,
self.port,
torrent.uploaded(),
torrent.downloaded(),
torrent.bytes_left(),
);
self.tracker_offshore_state
.tracker_request_sender
.send((tracker_id, req))
.unwrap()
}
pub fn house_keeping(&mut self) {
while self.tracker_offshore_state.threads.len() < 5 {
let sender = self.tracker_offshore_state.tracker_response_sender.clone();
let receiver = self.tracker_offshore_state.tracker_request_receiver.clone();
self.tracker_offshore_state.threads.push(
std::thread::Builder::new()
.name(format!(
"tracker/{}",
self.tracker_offshore_state.threads.len()
))
.spawn(|| tracker_thread(receiver, sender))
.unwrap(),
);
}
while let Ok((tracker_id, resp)) = self
.tracker_offshore_state
.tracker_response_receiver
.try_recv()
{
self.trackers[&tracker_id].write().unwrap().process(resp);
}
}
}