rqbit/crates/librqbit/src/torrent_manager.rs

353 lines
12 KiB
Rust
Raw Normal View History

2021-06-25 13:47:51 +01:00
use std::{
collections::HashSet,
2021-06-25 13:47:51 +01:00
fs::{File, OpenOptions},
2021-07-03 12:44:21 +01:00
net::SocketAddr,
2021-06-25 13:47:51 +01:00
path::{Path, PathBuf},
2021-07-04 14:38:44 +01:00
sync::Arc,
time::{Duration, Instant},
2021-06-25 13:47:51 +01:00
};
use anyhow::Context;
2021-07-03 19:10:59 +01:00
use bencode::from_bytes;
use buffers::ByteString;
use librqbit_core::{
lengths::Lengths, peer_id::generate_peer_id, speed_estimator::SpeedEstimator,
torrent_metainfo::TorrentMetaV1Info,
};
2021-07-03 12:44:21 +01:00
use log::{debug, info};
2021-07-04 11:05:20 +01:00
use parking_lot::Mutex;
2021-06-25 13:47:51 +01:00
use reqwest::Url;
2021-07-03 19:10:59 +01:00
use sha1w::Sha1;
2021-06-26 18:13:46 +01:00
use size_format::SizeFormatterBinary as SF;
2021-06-25 13:47:51 +01:00
use crate::{
chunk_tracker::ChunkTracker,
file_ops::FileOps,
2021-07-01 19:17:44 +01:00
spawn_utils::{spawn, BlockingSpawner},
2021-07-04 11:05:20 +01:00
torrent_state::TorrentState,
2021-06-30 18:42:16 +01:00
tracker_comms::{TrackerError, TrackerRequest, TrackerRequestEvent, TrackerResponse},
2021-06-25 13:47:51 +01:00
};
pub struct TorrentManagerBuilder {
2021-07-03 12:40:59 +01:00
info: TorrentMetaV1Info<ByteString>,
info_hash: [u8; 20],
2021-06-25 13:47:51 +01:00
overwrite: bool,
output_folder: PathBuf,
2021-06-26 16:43:36 +01:00
only_files: Option<Vec<usize>>,
2021-07-03 15:52:39 +01:00
peer_id: Option<[u8; 20]>,
2021-06-29 00:17:10 +01:00
force_tracker_interval: Option<Duration>,
2021-07-01 19:17:44 +01:00
spawner: Option<BlockingSpawner>,
2021-06-25 13:47:51 +01:00
}
impl TorrentManagerBuilder {
2021-07-03 12:40:59 +01:00
pub fn new<P: AsRef<Path>>(
info: TorrentMetaV1Info<ByteString>,
info_hash: [u8; 20],
output_folder: P,
) -> Self {
2021-06-25 13:47:51 +01:00
Self {
2021-07-03 12:40:59 +01:00
info,
info_hash,
2021-06-25 13:47:51 +01:00
overwrite: false,
output_folder: output_folder.as_ref().into(),
2021-06-26 16:43:36 +01:00
only_files: None,
2021-07-03 15:52:39 +01:00
peer_id: None,
2021-06-29 00:17:10 +01:00
force_tracker_interval: None,
2021-07-01 19:17:44 +01:00
spawner: None,
2021-06-25 13:47:51 +01:00
}
}
2021-06-26 16:43:36 +01:00
pub fn only_files(&mut self, only_files: Vec<usize>) -> &mut Self {
self.only_files = Some(only_files);
self
}
pub fn overwrite(&mut self, overwrite: bool) -> &mut Self {
2021-06-25 13:47:51 +01:00
self.overwrite = overwrite;
self
}
2021-06-29 00:17:10 +01:00
pub fn force_tracker_interval(&mut self, force_tracker_interval: Duration) -> &mut Self {
self.force_tracker_interval = Some(force_tracker_interval);
self
}
2021-07-01 19:17:44 +01:00
pub fn spawner(&mut self, spawner: BlockingSpawner) -> &mut Self {
self.spawner = Some(spawner);
self
}
2021-07-03 15:52:39 +01:00
pub fn peer_id(&mut self, peer_id: [u8; 20]) -> &mut Self {
self.peer_id = Some(peer_id);
self
}
2021-07-03 12:40:59 +01:00
pub fn start_manager(self) -> anyhow::Result<TorrentManagerHandle> {
2021-06-26 16:43:36 +01:00
TorrentManager::start(
2021-07-03 12:40:59 +01:00
self.info,
self.info_hash,
2021-06-26 16:43:36 +01:00
self.output_folder,
self.overwrite,
self.only_files,
2021-06-29 00:17:10 +01:00
self.force_tracker_interval,
2021-07-03 15:52:39 +01:00
self.peer_id,
2021-07-02 10:21:19 +01:00
self.spawner.unwrap_or_else(|| BlockingSpawner::new(true)),
2021-06-26 16:43:36 +01:00
)
2021-06-25 13:47:51 +01:00
}
}
#[derive(Clone)]
pub struct TorrentManagerHandle {
2021-07-03 12:40:59 +01:00
manager: Arc<TorrentManager>,
2021-06-25 13:47:51 +01:00
}
impl TorrentManagerHandle {
2021-07-03 12:40:59 +01:00
pub fn add_tracker(&self, url: Url) -> bool {
let mgr = self.manager.clone();
if mgr.trackers.lock().insert(url.clone()) {
spawn(format!("tracker monitor {}", url), async move {
mgr.single_tracker_monitor(url).await
});
true
} else {
false
}
}
2021-07-03 12:44:21 +01:00
pub fn add_peer(&self, addr: SocketAddr) -> bool {
self.manager.state.add_peer_if_not_seen(addr)
}
2021-07-04 14:38:44 +01:00
pub fn torrent_state(&self) -> &TorrentState {
&self.manager.state
}
2021-07-08 00:09:00 +01:00
pub fn speed_estimator(&self) -> &Arc<SpeedEstimator> {
&self.manager.speed_estimator
}
2021-06-25 13:47:51 +01:00
pub async fn cancel(&self) -> anyhow::Result<()> {
todo!()
}
pub async fn wait_until_completed(&self) -> anyhow::Result<()> {
loop {
tokio::time::sleep(Duration::from_secs(60)).await;
}
}
}
struct TorrentManager {
2021-06-29 00:17:10 +01:00
state: Arc<TorrentState>,
2021-07-03 15:52:39 +01:00
#[allow(dead_code)]
2021-06-30 23:26:22 +01:00
speed_estimator: Arc<SpeedEstimator>,
2021-07-03 12:40:59 +01:00
trackers: Mutex<HashSet<Url>>,
2021-06-29 00:17:10 +01:00
force_tracker_interval: Option<Duration>,
2021-06-25 13:47:51 +01:00
}
2021-07-04 18:01:58 +01:00
fn make_lengths<ByteBuf: AsRef<[u8]>>(
2021-07-03 12:40:59 +01:00
torrent: &TorrentMetaV1Info<ByteBuf>,
) -> anyhow::Result<Lengths> {
let total_length = torrent.iter_file_lengths().sum();
Lengths::new(total_length, torrent.piece_length, None)
2021-06-25 13:47:51 +01:00
}
impl TorrentManager {
2021-07-03 15:52:39 +01:00
#[allow(clippy::too_many_arguments)]
fn start<P: AsRef<Path>>(
2021-07-03 12:40:59 +01:00
info: TorrentMetaV1Info<ByteString>,
info_hash: [u8; 20],
2021-06-25 13:47:51 +01:00
out: P,
overwrite: bool,
2021-06-26 16:43:36 +01:00
only_files: Option<Vec<usize>>,
2021-06-29 00:17:10 +01:00
force_tracker_interval: Option<Duration>,
2021-07-03 15:52:39 +01:00
peer_id: Option<[u8; 20]>,
2021-07-01 19:17:44 +01:00
spawner: BlockingSpawner,
2021-06-25 13:47:51 +01:00
) -> anyhow::Result<TorrentManagerHandle> {
2021-06-26 17:29:59 +01:00
let files = {
2021-06-25 13:47:51 +01:00
let mut files =
2021-07-03 12:40:59 +01:00
Vec::<Arc<Mutex<File>>>::with_capacity(info.iter_file_lengths().count());
2021-06-25 13:47:51 +01:00
2021-07-03 12:40:59 +01:00
for (path_bits, _) in info.iter_filenames_and_lengths() {
2021-06-25 13:47:51 +01:00
let mut full_path = out.as_ref().to_owned();
for bit in path_bits.iter_components() {
full_path.push(
bit.as_ref()
.map(|b| std::str::from_utf8(b.as_ref()))
.unwrap_or(Ok("output"))?,
);
}
std::fs::create_dir_all(full_path.parent().unwrap())?;
let file = if overwrite {
OpenOptions::new()
.create(true)
.read(true)
.write(true)
.open(&full_path)?
} else {
// TODO: create_new does not seem to work with read(true), so calling this twice.
OpenOptions::new()
.create_new(true)
.write(true)
.open(&full_path)
.with_context(|| format!("error creating {:?}", &full_path))?;
OpenOptions::new().read(true).write(true).open(&full_path)?
};
files.push(Arc::new(Mutex::new(file)))
}
files
};
2021-07-03 15:52:39 +01:00
let peer_id = peer_id.unwrap_or_else(generate_peer_id);
2021-07-03 12:40:59 +01:00
let lengths = make_lengths(&info).context("unable to compute Lengths from torrent")?;
2021-06-25 13:47:51 +01:00
debug!("computed lengths: {:?}", &lengths);
2021-06-26 18:13:46 +01:00
info!("Doing initial checksum validation, this might take a while...");
2021-07-03 12:40:59 +01:00
let initial_check_results =
FileOps::<Sha1>::new(&info, &files, &lengths).initial_check(only_files.as_deref())?;
2021-06-26 18:13:46 +01:00
info!(
"Initial check results: have {}, needed {}",
SF::new(initial_check_results.have_bytes),
SF::new(initial_check_results.needed_bytes)
);
let chunk_tracker = ChunkTracker::new(
initial_check_results.needed_pieces,
initial_check_results.have_pieces,
lengths,
);
2021-06-25 13:47:51 +01:00
2021-07-04 11:36:16 +01:00
let state = TorrentState::new(
2021-07-04 11:05:20 +01:00
info,
2021-07-03 12:40:59 +01:00
info_hash,
2021-06-30 23:26:22 +01:00
peer_id,
files,
2021-07-04 11:05:20 +01:00
chunk_tracker,
2021-06-30 23:26:22 +01:00
lengths,
2021-07-04 11:05:20 +01:00
initial_check_results.have_bytes,
initial_check_results.needed_bytes,
2021-07-01 19:17:44 +01:00
spawner,
2021-07-04 11:36:16 +01:00
);
2021-07-04 11:05:20 +01:00
let estimator = Arc::new(SpeedEstimator::new(5));
2021-06-30 23:26:22 +01:00
2021-07-03 12:40:59 +01:00
let mgr = Arc::new(Self {
2021-06-30 23:26:22 +01:00
state,
speed_estimator: estimator.clone(),
2021-07-03 12:40:59 +01:00
trackers: Mutex::new(HashSet::new()),
2021-06-29 00:17:10 +01:00
force_tracker_interval,
2021-07-03 12:40:59 +01:00
});
2021-06-25 13:47:51 +01:00
2021-07-03 12:40:59 +01:00
spawn("stats printer", {
let this = mgr.clone();
async move { this.stats_printer().await }
});
spawn("speed estimator updater", {
let state = mgr.state.clone();
async move {
loop {
2021-07-04 11:36:16 +01:00
let downloaded = state.stats_snapshot().downloaded_and_checked_bytes;
2021-07-04 11:05:20 +01:00
let needed = state.initially_needed();
let remaining = needed - downloaded;
estimator.add_snapshot(downloaded, remaining, Instant::now());
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
});
2021-06-30 23:26:22 +01:00
2021-06-25 13:47:51 +01:00
Ok(mgr.into_handle())
}
2021-07-03 12:40:59 +01:00
async fn stats_printer(&self) -> anyhow::Result<()> {
2021-06-25 13:47:51 +01:00
loop {
2021-07-04 14:38:44 +01:00
let live_peer_stats = self.state.lock_read().peers.stats();
let seen_peers_count = self.state.lock_read().peers.seen().len();
2021-07-04 11:36:16 +01:00
let stats = self.state.stats_snapshot();
2021-07-04 11:05:20 +01:00
let needed = self.state.initially_needed();
2021-07-04 11:36:16 +01:00
let downloaded_pct = if stats.remaining_bytes == 0 {
2021-06-25 13:47:51 +01:00
100f64
} else {
2021-07-04 11:36:16 +01:00
(stats.downloaded_and_checked_bytes as f64 / needed as f64) * 100f64
2021-06-25 13:47:51 +01:00
};
info!(
2021-07-04 12:11:02 +01:00
"Stats: downloaded {:.2}% ({:.2}), peers {{live: {}, connecting: {}, queued: {}, seen: {}}}, fetched {}, remaining {:.2} out of {:.2}, uploaded {:.2}, total have {:.2}",
2021-06-26 18:13:46 +01:00
downloaded_pct,
2021-07-04 11:36:16 +01:00
SF::new(stats.downloaded_and_checked_bytes),
2021-06-28 22:48:14 +01:00
live_peer_stats.live,
live_peer_stats.connecting,
2021-07-04 12:11:02 +01:00
live_peer_stats.queued,
2021-06-28 22:48:14 +01:00
seen_peers_count,
2021-07-04 11:36:16 +01:00
SF::new(stats.fetched_bytes),
SF::new(stats.remaining_bytes),
2021-06-26 18:13:46 +01:00
SF::new(needed),
2021-07-04 11:36:16 +01:00
SF::new(stats.uploaded_bytes),
SF::new(stats.have_bytes)
2021-06-25 13:47:51 +01:00
);
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
2021-07-03 12:40:59 +01:00
fn into_handle(self: Arc<Self>) -> TorrentManagerHandle {
2021-06-25 13:47:51 +01:00
TorrentManagerHandle { manager: self }
}
2021-06-25 13:47:51 +01:00
async fn tracker_one_request(&self, tracker_url: Url) -> anyhow::Result<u64> {
let response: reqwest::Response = reqwest::get(tracker_url).await?;
2021-06-30 18:42:16 +01:00
if !response.status().is_success() {
anyhow::bail!("tracker responded with {:?}", response.status());
}
2021-06-25 13:47:51 +01:00
let bytes = response.bytes().await?;
2021-07-03 19:10:59 +01:00
if let Ok(error) = from_bytes::<TrackerError>(&bytes) {
2021-07-02 10:21:19 +01:00
anyhow::bail!(
2021-06-30 18:42:16 +01:00
"tracker returned failure. Failure reason: {}",
error.failure_reason
2021-07-02 10:21:19 +01:00
)
2021-06-30 18:42:16 +01:00
};
2021-07-03 19:10:59 +01:00
let response = from_bytes::<TrackerResponse>(&bytes)?;
2021-06-25 13:47:51 +01:00
for peer in response.peers.iter_sockaddrs() {
2021-06-30 10:14:33 +01:00
self.state.add_peer_if_not_seen(peer);
2021-06-25 13:47:51 +01:00
}
Ok(response.interval)
}
2021-06-27 08:44:25 +01:00
2021-07-03 12:40:59 +01:00
async fn single_tracker_monitor(&self, mut tracker_url: Url) -> anyhow::Result<()> {
2021-06-25 13:47:51 +01:00
let mut event = Some(TrackerRequestEvent::Started);
loop {
let request = TrackerRequest {
2021-07-04 11:05:20 +01:00
info_hash: self.state.info_hash(),
peer_id: self.state.peer_id(),
2021-06-25 13:47:51 +01:00
port: 6778,
2021-06-29 00:17:10 +01:00
uploaded: self.state.get_uploaded(),
downloaded: self.state.get_downloaded(),
left: self.state.get_left_to_download(),
2021-06-25 13:47:51 +01:00
compact: true,
no_peer_id: false,
event,
ip: None,
numwant: None,
key: None,
trackerid: None,
};
let request_query = request.as_querystring();
tracker_url.set_query(Some(&request_query));
2021-07-03 12:40:59 +01:00
match self.tracker_one_request(tracker_url.clone()).await {
2021-06-25 13:47:51 +01:00
Ok(interval) => {
event = None;
2021-06-29 00:17:10 +01:00
let interval = self
.force_tracker_interval
2021-07-02 10:21:19 +01:00
.unwrap_or_else(|| Duration::from_secs(interval));
2021-06-25 13:47:51 +01:00
debug!(
"sleeping for {:?} after calling tracker {}",
2021-06-29 00:17:10 +01:00
interval,
2021-06-25 13:47:51 +01:00
tracker_url.host().unwrap()
);
2021-06-29 00:17:10 +01:00
tokio::time::sleep(interval).await;
2021-06-25 13:47:51 +01:00
}
Err(e) => {
2021-06-28 21:06:00 +01:00
debug!("error calling the tracker {}: {:#}", tracker_url, e);
2021-06-25 13:47:51 +01:00
tokio::time::sleep(Duration::from_secs(60)).await;
}
};
}
}
}