Skip to content

Commit

Permalink
refactor: extract torrent file service
Browse files Browse the repository at this point in the history
to generate random torrent files. It was moved from the handler.
  • Loading branch information
josecelano committed Jul 31, 2023
1 parent dfa260e commit b2870b9
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 42 deletions.
15 changes: 15 additions & 0 deletions src/models/torrent_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde_bytes::ByteBuf;
use sha1::{Digest, Sha1};

use crate::config::Configuration;
use crate::services::torrent_file::NewTorrentInfoRequest;
use crate::utils::hex::{from_bytes, into_bytes};

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -102,6 +103,20 @@ pub struct Torrent {
}

impl Torrent {
#[must_use]
pub fn from_new_torrent_info_request(new_torrent_info: NewTorrentInfoRequest) -> Self {
let torrent_info = DbTorrentInfo {
torrent_id: 1,
info_hash: String::new(),
name: new_torrent_info.name,
pieces: new_torrent_info.pieces,
piece_length: 16384,
private: None,
root_hash: 0,
};
Torrent::from_db_info_files_and_announce_urls(torrent_info, new_torrent_info.files, new_torrent_info.announce_urls)
}

/// It hydrates a `Torrent` struct from the database data.
///
/// # Panics
Expand Down
1 change: 1 addition & 0 deletions src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pub mod proxy;
pub mod settings;
pub mod tag;
pub mod torrent;
pub mod torrent_file;
pub mod user;
64 changes: 64 additions & 0 deletions src/services/torrent_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! This module contains the services related to torrent file management.
use sha1::{Digest, Sha1};
use uuid::Uuid;

use crate::models::torrent_file::{Torrent, TorrentFile};

pub struct NewTorrentInfoRequest {
pub name: String,
pub pieces: String,
pub piece_length: i64,
pub private: Option<i64>,
pub root_hash: i64,
pub files: Vec<TorrentFile>,
pub announce_urls: Vec<Vec<String>>,
}

/// It generates a random single-file torrent for testing purposes.
///
/// The torrent will contain a single text file with the UUID as its content.
///
/// # Panics
///
/// This function will panic if the sample file contents length in bytes is
/// greater than `i64::MAX`.
#[must_use]
pub fn generate_random_torrent(id: Uuid) -> Torrent {
// Content of the file from which the torrent will be generated.
// We use the UUID as the content of the file.
let file_contents = format!("{id}\n");

let torrent_files: Vec<TorrentFile> = vec![TorrentFile {
path: vec![String::new()],
length: i64::try_from(file_contents.len()).expect("file contents size in bytes cannot exceed i64::MAX"),
md5sum: None,
}];

let torrent_announce_urls: Vec<Vec<String>> = vec![];

let torrent_info_request = NewTorrentInfoRequest {
name: format!("file-{id}.txt"),
pieces: sha1(&file_contents),
piece_length: 16384,
private: None,
root_hash: 0,
files: torrent_files,
announce_urls: torrent_announce_urls,
};

Torrent::from_new_torrent_info_request(torrent_info_request)
}

fn sha1(data: &str) -> String {
// Create a Sha1 object
let mut hasher = Sha1::new();

// Write input message
hasher.update(data.as_bytes());

// Read hash digest and consume hasher
let result = hasher.finalize();

// Convert the hash (a byte array) to a string of hex characters
hex::encode(result)
}
44 changes: 2 additions & 42 deletions src/web/api/v1/contexts/torrent/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use axum::extract::{self, Multipart, Path, Query, State};
use axum::response::{IntoResponse, Response};
use axum::Json;
use serde::Deserialize;
use sha1::{Digest, Sha1};
use uuid::Uuid;

use super::forms::UpdateTorrentInfoForm;
Expand All @@ -17,9 +16,9 @@ use crate::common::AppData;
use crate::errors::ServiceError;
use crate::models::info_hash::InfoHash;
use crate::models::torrent::{AddTorrentRequest, Metadata};
use crate::models::torrent_file::{DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_tag::TagId;
use crate::services::torrent::ListingRequest;
use crate::services::torrent_file::generate_random_torrent;
use crate::utils::parse_torrent;
use crate::web::api::v1::auth::get_optional_logged_in_user;
use crate::web::api::v1::extractors::bearer_token::Extract;
Expand Down Expand Up @@ -226,7 +225,7 @@ impl UuidParam {
}
}

/// Returns a random torrent as a byte stream `application/x-bittorrent`.
/// Returns a random torrent file as a byte stream `application/x-bittorrent`.
///
/// This is useful for testing purposes.
///
Expand All @@ -248,45 +247,6 @@ pub async fn create_random_torrent_handler(State(_app_data): State<Arc<AppData>>
torrent_file_response(bytes, &format!("{}.torrent", torrent.info.name))
}

/// It generates a random single-file torrent for testing purposes.
fn generate_random_torrent(id: Uuid) -> Torrent {
let file_contents = format!("{id}\n");

let torrent_info = DbTorrentInfo {
torrent_id: 1,
info_hash: String::new(),
name: format!("file-{id}.txt"),
pieces: sha1(&file_contents),
piece_length: 16384,
private: None,
root_hash: 0,
};

let torrent_files: Vec<TorrentFile> = vec![TorrentFile {
path: vec![String::new()],
length: 37, // Number of bytes for the UUID plus one char for line break (`0a`).
md5sum: None,
}];

let torrent_announce_urls: Vec<Vec<String>> = vec![];

Torrent::from_db_info_files_and_announce_urls(torrent_info, torrent_files, torrent_announce_urls)
}

fn sha1(data: &str) -> String {
// Create a Sha1 object
let mut hasher = Sha1::new();

// Write input message
hasher.update(data.as_bytes());

// Read hash digest and consume hasher
let result = hasher.finalize();

// Convert the hash (a byte array) to a string of hex characters
hex::encode(result)
}

/// Extracts the [`TorrentRequest`] from the multipart form payload.
///
/// # Errors
Expand Down

0 comments on commit b2870b9

Please sign in to comment.