The server now doesnt read files by itself, client must do it

This commit is contained in:
Igor Katson 2023-11-22 18:06:09 +00:00
parent 7d6ed06166
commit 707d4be631
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
5 changed files with 70 additions and 40 deletions

View file

@ -6,7 +6,7 @@
use std::time::Duration; use std::time::Duration;
use anyhow::Context; use anyhow::Context;
use librqbit::session::{AddTorrentOptions, AddTorrentResponse, Session}; use librqbit::session::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Session};
use tracing::info; use tracing::info;
// This is ubuntu-21.04-live-server-amd64.iso.torrent // This is ubuntu-21.04-live-server-amd64.iso.torrent
@ -30,7 +30,7 @@ async fn main() -> Result<(), anyhow::Error> {
// Add the torrent to the session // Add the torrent to the session
let handle = match session let handle = match session
.add_torrent( .add_torrent(
MAGNET_LINK, AddTorrent::from_url(MAGNET_LINK),
Some(AddTorrentOptions { Some(AddTorrentOptions {
// Set this to true to allow writing on top of existing files. // Set this to true to allow writing on top of existing files.
// If the file is partially downloaded, librqbit will only download the // If the file is partially downloaded, librqbit will only download the

View file

@ -84,8 +84,8 @@ impl HttpApi {
) -> Result<impl IntoResponse> { ) -> Result<impl IntoResponse> {
let opts = params.into_add_torrent_options(); let opts = params.into_add_torrent_options();
let add = match String::from_utf8(data.to_vec()) { let add = match String::from_utf8(data.to_vec()) {
Ok(s) => AddTorrent::from(s), Ok(s) => AddTorrent::Url(s.into()),
Err(e) => AddTorrent::from(e.into_bytes()), Err(e) => AddTorrent::TorrentFileBytes(e.into_bytes().into()),
}; };
state.api_add_torrent(add, Some(opts)).await.map(axum::Json) state.api_add_torrent(add, Some(opts)).await.map(axum::Json)
} }

View file

@ -1,7 +1,10 @@
use anyhow::Context; use anyhow::Context;
use serde::Deserialize; use serde::Deserialize;
use crate::{http_api::TorrentAddQueryParams, session::AddTorrentOptions}; use crate::{
http_api::TorrentAddQueryParams,
session::{AddTorrent, AddTorrentOptions},
};
#[derive(Clone)] #[derive(Clone)]
pub struct HttpApiClient { pub struct HttpApiClient {
@ -77,7 +80,7 @@ impl HttpApiClient {
pub async fn add_torrent( pub async fn add_torrent(
&self, &self,
torrent: &str, torrent: AddTorrent<'_>,
opts: Option<AddTorrentOptions>, opts: Option<AddTorrentOptions>,
) -> anyhow::Result<crate::http_api::ApiAddTorrentResponse> { ) -> anyhow::Result<crate::http_api::ApiAddTorrentResponse> {
let opts = opts.unwrap_or_default(); let opts = opts.unwrap_or_default();
@ -94,7 +97,7 @@ impl HttpApiClient {
let response = check_response( let response = check_response(
self.client self.client
.post(&url) .post(&url)
.body(torrent.to_owned()) .body(torrent.into_bytes())
.send() .send()
.await?, .await?,
) )

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, fs::File, io::Read, net::SocketAddr, path::PathBuf, time::Duration}; use std::{borrow::Cow, io::Read, net::SocketAddr, path::PathBuf, time::Duration};
use anyhow::{bail, Context}; use anyhow::{bail, Context};
use buffers::ByteString; use buffers::ByteString;
@ -20,6 +20,8 @@ use crate::{
torrent_manager::{TorrentManagerBuilder, TorrentManagerHandle}, torrent_manager::{TorrentManagerBuilder, TorrentManagerHandle},
}; };
pub const SUPPORTED_SCHEMES: [&str; 3] = ["http:", "https:", "magnet:"];
#[derive(Clone)] #[derive(Clone)]
pub enum ManagedTorrentState { pub enum ManagedTorrentState {
Initializing, Initializing,
@ -83,21 +85,6 @@ async fn torrent_from_url(url: &str) -> anyhow::Result<TorrentMetaV1Owned> {
torrent_from_bytes(&b).context("error decoding torrent") torrent_from_bytes(&b).context("error decoding torrent")
} }
fn torrent_from_file(filename: &str) -> anyhow::Result<TorrentMetaV1Owned> {
let mut buf = Vec::new();
if filename == "-" {
std::io::stdin()
.read_to_end(&mut buf)
.context("error reading stdin")?;
} else {
File::open(filename)
.with_context(|| format!("error opening {filename}"))?
.read_to_end(&mut buf)
.with_context(|| format!("error reading {filename}"))?;
}
torrent_from_bytes(&buf).context("error decoding torrent")
}
fn compute_only_files<ByteBuf: AsRef<[u8]>>( fn compute_only_files<ByteBuf: AsRef<[u8]>>(
torrent: &TorrentMetaV1Info<ByteBuf>, torrent: &TorrentMetaV1Info<ByteBuf>,
filename_re: &str, filename_re: &str,
@ -142,26 +129,55 @@ pub enum AddTorrentResponse {
Added(TorrentManagerHandle), Added(TorrentManagerHandle),
} }
pub fn read_local_file_including_stdin(filename: &str) -> anyhow::Result<Vec<u8>> {
let mut buf = Vec::new();
if filename == "-" {
std::io::stdin()
.read_to_end(&mut buf)
.context("error reading stdin")?;
} else {
std::fs::File::open(filename)
.context("error opening")?
.read_to_end(&mut buf)
.context("error reading")?;
}
Ok(buf)
}
pub enum AddTorrent<'a> { pub enum AddTorrent<'a> {
Url(Cow<'a, str>), Url(Cow<'a, str>),
TorrentFileBytes(Vec<u8>), TorrentFileBytes(Cow<'a, [u8]>),
} }
impl<'a> From<&'a str> for AddTorrent<'a> { impl<'a> AddTorrent<'a> {
fn from(s: &'a str) -> Self { // Don't call this from HTTP API.
Self::Url(Cow::Borrowed(s)) pub fn from_cli_argument(path: &'a str) -> anyhow::Result<Self> {
if SUPPORTED_SCHEMES.iter().any(|s| path.starts_with(s)) {
return Ok(Self::Url(Cow::Borrowed(path)));
}
Self::from_local_filename(path)
} }
}
impl<'a> From<String> for AddTorrent<'a> { pub fn from_url(url: impl Into<Cow<'a, str>>) -> Self {
fn from(s: String) -> Self { Self::Url(url.into())
Self::Url(Cow::Owned(s))
} }
}
impl<'a> From<Vec<u8>> for AddTorrent<'a> { pub fn from_bytes(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
fn from(b: Vec<u8>) -> Self { Self::TorrentFileBytes(bytes.into())
Self::TorrentFileBytes(b) }
// Don't call this from HTTP API.
pub fn from_local_filename(filename: &str) -> anyhow::Result<Self> {
let file = read_local_file_including_stdin(filename)
.with_context(|| format!("error reading local file {filename:?}"))?;
Ok(Self::TorrentFileBytes(Cow::Owned(file)))
}
pub fn into_bytes(self) -> Vec<u8> {
match self {
Self::Url(s) => s.into_owned().into_bytes(),
Self::TorrentFileBytes(b) => b.into_owned(),
}
} }
} }
@ -270,7 +286,12 @@ impl Session {
{ {
torrent_from_url(&url).await? torrent_from_url(&url).await?
} }
AddTorrent::Url(filename) => torrent_from_file(&filename)?, AddTorrent::Url(url) => {
bail!(
"unsupported URL {:?}. Supporting magnet:, http:, and https",
url
)
}
AddTorrent::TorrentFileBytes(bytes) => { AddTorrent::TorrentFileBytes(bytes) => {
torrent_from_bytes(&bytes).context("error decoding torrent")? torrent_from_bytes(&bytes).context("error decoding torrent")?
} }

View file

@ -7,8 +7,8 @@ use librqbit::{
http_api_client, http_api_client,
peer_connection::PeerConnectionOptions, peer_connection::PeerConnectionOptions,
session::{ session::{
AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState, Session, AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState,
SessionOptions, Session, SessionOptions,
}, },
spawn_utils::{spawn, BlockingSpawner}, spawn_utils::{spawn, BlockingSpawner},
torrent_state::timeit, torrent_state::timeit,
@ -329,7 +329,10 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
if connect_to_existing { if connect_to_existing {
for torrent_url in &download_opts.torrent_path { for torrent_url in &download_opts.torrent_path {
match client match client
.add_torrent(torrent_url, Some(torrent_opts.clone())) .add_torrent(
AddTorrent::from_cli_argument(torrent_url)?,
Some(torrent_opts.clone()),
)
.await .await
{ {
Ok(ApiAddTorrentResponse { id, details }) => { Ok(ApiAddTorrentResponse { id, details }) => {
@ -382,7 +385,10 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
for path in &download_opts.torrent_path { for path in &download_opts.torrent_path {
let handle = match session let handle = match session
.add_torrent(path.as_str(), Some(torrent_opts.clone())) .add_torrent(
AddTorrent::from_cli_argument(path)?,
Some(torrent_opts.clone()),
)
.await .await
{ {
Ok(v) => match v { Ok(v) => match v {