diff --git a/Cargo.lock b/Cargo.lock index bcb27fb43..0bf1ad572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -616,6 +616,36 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "bittorrent-tracker-client" +version = "3.0.0-develop" +dependencies = [ + "anyhow", + "aquatic_udp_protocol", + "bittorrent-primitives", + "clap", + "derive_more", + "futures", + "hex-literal", + "hyper", + "percent-encoding", + "reqwest", + "serde", + "serde_bencode", + "serde_bytes", + "serde_json", + "serde_repr", + "thiserror", + "tokio", + "torrust-tracker-configuration", + "torrust-tracker-located-error", + "torrust-tracker-primitives", + "tracing", + "tracing-subscriber", + "url", + "zerocopy", +] + [[package]] name = "bitvec" version = "1.0.1" @@ -3818,6 +3848,7 @@ dependencies = [ "axum-extra", "axum-server", "bittorrent-primitives", + "bittorrent-tracker-client", "camino", "chrono", "clap", @@ -3827,7 +3858,6 @@ dependencies = [ "figment", "futures", "futures-util", - "hex-literal", "http-body", "hyper", "hyper-util", diff --git a/Cargo.toml b/Cargo.toml index e42702d06..a3d88be92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ axum-client-ip = "0" axum-extra = { version = "0", features = ["query"] } axum-server = { version = "0", features = ["tls-rustls"] } bittorrent-primitives = "0.1.0" +bittorrent-tracker-client = { version = "3.0.0-develop", path = "packages/tracker-client" } camino = { version = "1", features = ["serde", "serde1"] } chrono = { version = "0", default-features = false, features = ["clock"] } clap = { version = "4", features = ["derive", "env"] } @@ -46,7 +47,6 @@ derive_more = { version = "1", features = ["as_ref", "constructor", "from"] } figment = "0" futures = "0" futures-util = "0" -hex-literal = "0" http-body = "1" hyper = "1" hyper-util = { version = "0", features = ["http1", "http2", "tokio"] } @@ -100,6 +100,7 @@ members = [ "packages/primitives", "packages/test-helpers", "packages/torrent-repository", + "packages/tracker-client", ] [profile.dev] diff --git a/packages/tracker-client/Cargo.toml b/packages/tracker-client/Cargo.toml new file mode 100644 index 000000000..3334e7b47 --- /dev/null +++ b/packages/tracker-client/Cargo.toml @@ -0,0 +1,44 @@ +[package] +description = "A library with the primitive types shared by the Torrust tracker packages." +keywords = ["bittorrent", "client", "tracker"] +license = "LGPL-3.0" +name = "bittorrent-tracker-client" +readme = "README.md" + +authors.workspace = true +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +publish.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[dependencies] +anyhow = "1" +aquatic_udp_protocol = "0" +bittorrent-primitives = "0.1.0" +clap = { version = "4", features = ["derive", "env"] } +derive_more = { version = "1", features = ["as_ref", "constructor", "from"] } +futures = "0" +hex-literal = "0" +hyper = "1" +percent-encoding = "2" +reqwest = { version = "0", features = ["json"] } +serde = { version = "1", features = ["derive"] } +serde_bencode = "0" +serde_bytes = "0" +serde_json = { version = "1", features = ["preserve_order"] } +serde_repr = "0" +thiserror = "1" +tokio = { version = "1", features = ["macros", "net", "rt-multi-thread", "signal", "sync"] } +torrust-tracker-configuration = { version = "3.0.0-develop", path = "../configuration" } +torrust-tracker-located-error = { version = "3.0.0-develop", path = "../located-error" } +torrust-tracker-primitives = { version = "3.0.0-develop", path = "../primitives" } +tracing = "0" +tracing-subscriber = { version = "0", features = ["json"] } +url = { version = "2", features = ["serde"] } +zerocopy = "0.7" + +[package.metadata.cargo-machete] +ignored = ["serde_bytes"] diff --git a/packages/tracker-client/README.md b/packages/tracker-client/README.md new file mode 100644 index 000000000..1d12f9c86 --- /dev/null +++ b/packages/tracker-client/README.md @@ -0,0 +1,25 @@ +# BitTorrent Tracker Client + +A library an console applications to interact with a BitTorrent tracker. + +> **Disclaimer**: This project is actively under development. We’re currently extracting and refining common types from the ][Torrust Tracker](https://github.com/torrust/torrust-tracker) to make them available to the BitTorrent community in Rust. While these types are functional, they are not yet ready for use in production or third-party projects. + +## License + +**Copyright (c) 2024 The Torrust Developers.** + +This program is free software: you can redistribute it and/or modify it under the terms of the [GNU Lesser General Public License][LGPL_3_0] as published by the [Free Software Foundation][FSF], version 3. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU Lesser General Public License][LGPL_3_0] for more details. + +You should have received a copy of the *GNU Lesser General Public License* along with this program. If not, see . + +Some files include explicit copyright notices and/or license notices. + +### Legacy Exception + +For prosperity, versions of Torrust BitTorrent Tracker Client that are older than five years are automatically granted the [MIT-0][MIT_0] license in addition to the existing [LGPL-3.0-only][LGPL_3_0] license. + +[LGPL_3_0]: ./LICENSE +[MIT_0]: ./docs/licenses/LICENSE-MIT_0 +[FSF]: https://www.fsf.org/ diff --git a/packages/tracker-client/docs/licenses/LICENSE-MIT_0 b/packages/tracker-client/docs/licenses/LICENSE-MIT_0 new file mode 100644 index 000000000..fc06cc4fe --- /dev/null +++ b/packages/tracker-client/docs/licenses/LICENSE-MIT_0 @@ -0,0 +1,14 @@ +MIT No Attribution + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/bin/http_tracker_client.rs b/packages/tracker-client/src/bin/http_tracker_client.rs similarity index 67% rename from src/bin/http_tracker_client.rs rename to packages/tracker-client/src/bin/http_tracker_client.rs index 0de040549..8c2c0356d 100644 --- a/src/bin/http_tracker_client.rs +++ b/packages/tracker-client/src/bin/http_tracker_client.rs @@ -1,5 +1,5 @@ //! Program to make request to HTTP trackers. -use torrust_tracker::console::clients::http::app; +use bittorrent_tracker_client::console::clients::http::app; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/src/bin/tracker_checker.rs b/packages/tracker-client/src/bin/tracker_checker.rs similarity index 66% rename from src/bin/tracker_checker.rs rename to packages/tracker-client/src/bin/tracker_checker.rs index 87aeedeac..eb2a7d82c 100644 --- a/src/bin/tracker_checker.rs +++ b/packages/tracker-client/src/bin/tracker_checker.rs @@ -1,5 +1,5 @@ //! Program to check running trackers. -use torrust_tracker::console::clients::checker::app; +use bittorrent_tracker_client::console::clients::checker::app; #[tokio::main] async fn main() { diff --git a/src/bin/udp_tracker_client.rs b/packages/tracker-client/src/bin/udp_tracker_client.rs similarity index 67% rename from src/bin/udp_tracker_client.rs rename to packages/tracker-client/src/bin/udp_tracker_client.rs index 909b296ca..5f6b4f50d 100644 --- a/src/bin/udp_tracker_client.rs +++ b/packages/tracker-client/src/bin/udp_tracker_client.rs @@ -1,5 +1,5 @@ //! Program to make request to UDP trackers. -use torrust_tracker::console::clients::udp::app; +use bittorrent_tracker_client::console::clients::udp::app; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/src/console/clients/checker/app.rs b/packages/tracker-client/src/console/clients/checker/app.rs similarity index 100% rename from src/console/clients/checker/app.rs rename to packages/tracker-client/src/console/clients/checker/app.rs diff --git a/src/console/clients/checker/checks/health.rs b/packages/tracker-client/src/console/clients/checker/checks/health.rs similarity index 100% rename from src/console/clients/checker/checks/health.rs rename to packages/tracker-client/src/console/clients/checker/checks/health.rs diff --git a/src/console/clients/checker/checks/http.rs b/packages/tracker-client/src/console/clients/checker/checks/http.rs similarity index 92% rename from src/console/clients/checker/checks/http.rs rename to packages/tracker-client/src/console/clients/checker/checks/http.rs index b64297bed..48ce9678d 100644 --- a/src/console/clients/checker/checks/http.rs +++ b/packages/tracker-client/src/console/clients/checker/checks/http.rs @@ -6,9 +6,9 @@ use serde::Serialize; use url::Url; use crate::console::clients::http::Error; -use crate::shared::bit_torrent::tracker::http::client::responses::announce::Announce; -use crate::shared::bit_torrent::tracker::http::client::responses::scrape; -use crate::shared::bit_torrent::tracker::http::client::{requests, Client}; +use crate::http::client::responses::announce::Announce; +use crate::http::client::responses::scrape; +use crate::http::client::{requests, Client}; #[derive(Debug, Clone, Serialize)] pub struct Checks { diff --git a/src/console/clients/checker/checks/mod.rs b/packages/tracker-client/src/console/clients/checker/checks/mod.rs similarity index 100% rename from src/console/clients/checker/checks/mod.rs rename to packages/tracker-client/src/console/clients/checker/checks/mod.rs diff --git a/src/console/clients/checker/checks/structs.rs b/packages/tracker-client/src/console/clients/checker/checks/structs.rs similarity index 100% rename from src/console/clients/checker/checks/structs.rs rename to packages/tracker-client/src/console/clients/checker/checks/structs.rs diff --git a/src/console/clients/checker/checks/udp.rs b/packages/tracker-client/src/console/clients/checker/checks/udp.rs similarity index 100% rename from src/console/clients/checker/checks/udp.rs rename to packages/tracker-client/src/console/clients/checker/checks/udp.rs diff --git a/src/console/clients/checker/config.rs b/packages/tracker-client/src/console/clients/checker/config.rs similarity index 100% rename from src/console/clients/checker/config.rs rename to packages/tracker-client/src/console/clients/checker/config.rs diff --git a/src/console/clients/checker/console.rs b/packages/tracker-client/src/console/clients/checker/console.rs similarity index 100% rename from src/console/clients/checker/console.rs rename to packages/tracker-client/src/console/clients/checker/console.rs diff --git a/src/console/clients/checker/logger.rs b/packages/tracker-client/src/console/clients/checker/logger.rs similarity index 100% rename from src/console/clients/checker/logger.rs rename to packages/tracker-client/src/console/clients/checker/logger.rs diff --git a/src/console/clients/checker/mod.rs b/packages/tracker-client/src/console/clients/checker/mod.rs similarity index 100% rename from src/console/clients/checker/mod.rs rename to packages/tracker-client/src/console/clients/checker/mod.rs diff --git a/src/console/clients/checker/printer.rs b/packages/tracker-client/src/console/clients/checker/printer.rs similarity index 100% rename from src/console/clients/checker/printer.rs rename to packages/tracker-client/src/console/clients/checker/printer.rs diff --git a/src/console/clients/checker/service.rs b/packages/tracker-client/src/console/clients/checker/service.rs similarity index 100% rename from src/console/clients/checker/service.rs rename to packages/tracker-client/src/console/clients/checker/service.rs diff --git a/src/console/clients/http/app.rs b/packages/tracker-client/src/console/clients/http/app.rs similarity index 90% rename from src/console/clients/http/app.rs rename to packages/tracker-client/src/console/clients/http/app.rs index 6730c027d..8db6fe46d 100644 --- a/src/console/clients/http/app.rs +++ b/packages/tracker-client/src/console/clients/http/app.rs @@ -22,10 +22,10 @@ use clap::{Parser, Subcommand}; use reqwest::Url; use torrust_tracker_configuration::DEFAULT_TIMEOUT; -use crate::shared::bit_torrent::tracker::http::client::requests::announce::QueryBuilder; -use crate::shared::bit_torrent::tracker::http::client::responses::announce::Announce; -use crate::shared::bit_torrent::tracker::http::client::responses::scrape; -use crate::shared::bit_torrent::tracker::http::client::{requests, Client}; +use crate::http::client::requests::announce::QueryBuilder; +use crate::http::client::responses::announce::Announce; +use crate::http::client::responses::scrape; +use crate::http::client::{requests, Client}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] diff --git a/src/console/clients/http/mod.rs b/packages/tracker-client/src/console/clients/http/mod.rs similarity index 81% rename from src/console/clients/http/mod.rs rename to packages/tracker-client/src/console/clients/http/mod.rs index eaa71957f..e4b6fbe57 100644 --- a/src/console/clients/http/mod.rs +++ b/packages/tracker-client/src/console/clients/http/mod.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use serde::Serialize; use thiserror::Error; -use crate::shared::bit_torrent::tracker::http::client::responses::scrape::BencodeParseError; +use crate::http::client::responses::scrape::BencodeParseError; pub mod app; @@ -11,9 +11,7 @@ pub mod app; #[serde(into = "String")] pub enum Error { #[error("Http request did not receive a response within the timeout: {err:?}")] - HttpClientError { - err: crate::shared::bit_torrent::tracker::http::client::Error, - }, + HttpClientError { err: crate::http::client::Error }, #[error("Http failed to get a response at all: {err:?}")] ResponseError { err: Arc }, #[error("Failed to deserialize the bencoded response data with the error: \"{err:?}\"")] diff --git a/src/console/clients/mod.rs b/packages/tracker-client/src/console/clients/mod.rs similarity index 100% rename from src/console/clients/mod.rs rename to packages/tracker-client/src/console/clients/mod.rs diff --git a/src/console/clients/udp/app.rs b/packages/tracker-client/src/console/clients/udp/app.rs similarity index 100% rename from src/console/clients/udp/app.rs rename to packages/tracker-client/src/console/clients/udp/app.rs diff --git a/src/console/clients/udp/checker.rs b/packages/tracker-client/src/console/clients/udp/checker.rs similarity index 98% rename from src/console/clients/udp/checker.rs rename to packages/tracker-client/src/console/clients/udp/checker.rs index 14e94c132..b9fd3a729 100644 --- a/src/console/clients/udp/checker.rs +++ b/packages/tracker-client/src/console/clients/udp/checker.rs @@ -10,7 +10,7 @@ use aquatic_udp_protocol::{ use bittorrent_primitives::info_hash::InfoHash as TorrustInfoHash; use super::Error; -use crate::shared::bit_torrent::tracker::udp::client::UdpTrackerClient; +use crate::udp::client::UdpTrackerClient; /// A UDP Tracker client to make test requests (checks). #[derive(Debug)] diff --git a/src/console/clients/udp/mod.rs b/packages/tracker-client/src/console/clients/udp/mod.rs similarity index 97% rename from src/console/clients/udp/mod.rs rename to packages/tracker-client/src/console/clients/udp/mod.rs index b92bed096..ae6271a78 100644 --- a/src/console/clients/udp/mod.rs +++ b/packages/tracker-client/src/console/clients/udp/mod.rs @@ -4,7 +4,7 @@ use aquatic_udp_protocol::Response; use serde::Serialize; use thiserror::Error; -use crate::shared::bit_torrent::tracker::udp; +use crate::udp; pub mod app; pub mod checker; diff --git a/src/console/clients/udp/responses/dto.rs b/packages/tracker-client/src/console/clients/udp/responses/dto.rs similarity index 100% rename from src/console/clients/udp/responses/dto.rs rename to packages/tracker-client/src/console/clients/udp/responses/dto.rs diff --git a/src/console/clients/udp/responses/json.rs b/packages/tracker-client/src/console/clients/udp/responses/json.rs similarity index 100% rename from src/console/clients/udp/responses/json.rs rename to packages/tracker-client/src/console/clients/udp/responses/json.rs diff --git a/src/console/clients/udp/responses/mod.rs b/packages/tracker-client/src/console/clients/udp/responses/mod.rs similarity index 100% rename from src/console/clients/udp/responses/mod.rs rename to packages/tracker-client/src/console/clients/udp/responses/mod.rs diff --git a/packages/tracker-client/src/console/mod.rs b/packages/tracker-client/src/console/mod.rs new file mode 100644 index 000000000..4b4cb9de4 --- /dev/null +++ b/packages/tracker-client/src/console/mod.rs @@ -0,0 +1,2 @@ +//! Console apps. +pub mod clients; diff --git a/src/shared/bit_torrent/tracker/http/client/mod.rs b/packages/tracker-client/src/http/client/mod.rs similarity index 94% rename from src/shared/bit_torrent/tracker/http/client/mod.rs rename to packages/tracker-client/src/http/client/mod.rs index 4c70cd68b..3c904a7c9 100644 --- a/src/shared/bit_torrent/tracker/http/client/mod.rs +++ b/packages/tracker-client/src/http/client/mod.rs @@ -5,13 +5,13 @@ use std::net::IpAddr; use std::sync::Arc; use std::time::Duration; +use derive_more::Display; use hyper::StatusCode; use requests::{announce, scrape}; use reqwest::{Response, Url}; +use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::core::auth::Key; - #[derive(Debug, Clone, Error)] pub enum Error { #[error("Failed to Build a Http Client: {err:?}")] @@ -202,3 +202,19 @@ impl Client { self.base_url.to_string() } } + +/// A token used for authentication. +#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Display, Hash)] +pub struct Key(String); + +impl Key { + #[must_use] + pub fn new(value: &str) -> Self { + Self(value.to_owned()) + } + + #[must_use] + pub fn value(&self) -> &str { + &self.0 + } +} diff --git a/src/shared/bit_torrent/tracker/http/client/requests/announce.rs b/packages/tracker-client/src/http/client/requests/announce.rs similarity index 98% rename from src/shared/bit_torrent/tracker/http/client/requests/announce.rs rename to packages/tracker-client/src/http/client/requests/announce.rs index f3ce327ea..8f81cc80e 100644 --- a/src/shared/bit_torrent/tracker/http/client/requests/announce.rs +++ b/packages/tracker-client/src/http/client/requests/announce.rs @@ -6,7 +6,7 @@ use aquatic_udp_protocol::PeerId; use bittorrent_primitives::info_hash::InfoHash; use serde_repr::Serialize_repr; -use crate::shared::bit_torrent::tracker::http::{percent_encode_byte_array, ByteArray20}; +use crate::http::{percent_encode_byte_array, ByteArray20}; pub struct Query { pub info_hash: ByteArray20, diff --git a/src/shared/bit_torrent/tracker/http/client/requests/mod.rs b/packages/tracker-client/src/http/client/requests/mod.rs similarity index 100% rename from src/shared/bit_torrent/tracker/http/client/requests/mod.rs rename to packages/tracker-client/src/http/client/requests/mod.rs diff --git a/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs b/packages/tracker-client/src/http/client/requests/scrape.rs similarity index 98% rename from src/shared/bit_torrent/tracker/http/client/requests/scrape.rs rename to packages/tracker-client/src/http/client/requests/scrape.rs index 58b9e0dc7..1b423390b 100644 --- a/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs +++ b/packages/tracker-client/src/http/client/requests/scrape.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use bittorrent_primitives::info_hash::InfoHash; -use crate::shared::bit_torrent::tracker::http::{percent_encode_byte_array, ByteArray20}; +use crate::http::{percent_encode_byte_array, ByteArray20}; pub struct Query { pub info_hash: Vec, diff --git a/src/shared/bit_torrent/tracker/http/client/responses/announce.rs b/packages/tracker-client/src/http/client/responses/announce.rs similarity index 100% rename from src/shared/bit_torrent/tracker/http/client/responses/announce.rs rename to packages/tracker-client/src/http/client/responses/announce.rs diff --git a/src/shared/bit_torrent/tracker/http/client/responses/error.rs b/packages/tracker-client/src/http/client/responses/error.rs similarity index 100% rename from src/shared/bit_torrent/tracker/http/client/responses/error.rs rename to packages/tracker-client/src/http/client/responses/error.rs diff --git a/src/shared/bit_torrent/tracker/http/client/responses/mod.rs b/packages/tracker-client/src/http/client/responses/mod.rs similarity index 100% rename from src/shared/bit_torrent/tracker/http/client/responses/mod.rs rename to packages/tracker-client/src/http/client/responses/mod.rs diff --git a/src/shared/bit_torrent/tracker/http/client/responses/scrape.rs b/packages/tracker-client/src/http/client/responses/scrape.rs similarity index 98% rename from src/shared/bit_torrent/tracker/http/client/responses/scrape.rs rename to packages/tracker-client/src/http/client/responses/scrape.rs index 25a2f0a81..6c0e8800a 100644 --- a/src/shared/bit_torrent/tracker/http/client/responses/scrape.rs +++ b/packages/tracker-client/src/http/client/responses/scrape.rs @@ -6,7 +6,7 @@ use serde::ser::SerializeMap; use serde::{Deserialize, Serialize, Serializer}; use serde_bencode::value::Value; -use crate::shared::bit_torrent::tracker::http::{ByteArray20, InfoHash}; +use crate::http::{ByteArray20, InfoHash}; #[derive(Debug, PartialEq, Default, Deserialize)] pub struct Response { diff --git a/src/shared/bit_torrent/tracker/http/mod.rs b/packages/tracker-client/src/http/mod.rs similarity index 96% rename from src/shared/bit_torrent/tracker/http/mod.rs rename to packages/tracker-client/src/http/mod.rs index 15723c1b7..dc144814d 100644 --- a/src/shared/bit_torrent/tracker/http/mod.rs +++ b/packages/tracker-client/src/http/mod.rs @@ -1,4 +1,5 @@ pub mod client; +pub mod url_encoding; use percent_encoding::NON_ALPHANUMERIC; diff --git a/packages/tracker-client/src/http/url_encoding.rs b/packages/tracker-client/src/http/url_encoding.rs new file mode 100644 index 000000000..ee7ab166e --- /dev/null +++ b/packages/tracker-client/src/http/url_encoding.rs @@ -0,0 +1,132 @@ +//! This module contains functions for percent decoding infohashes and peer IDs. +//! +//! Percent encoding is an encoding format used to encode arbitrary data in a +//! format that is safe to use in URLs. It is used by the HTTP tracker protocol +//! to encode infohashes and peer ids in the URLs of requests. +//! +//! `BitTorrent` infohashes and peer ids are percent encoded like any other +//! arbitrary URL parameter. But they are encoded from binary data (byte arrays) +//! which may not be valid UTF-8. That makes hard to use the `percent_encoding` +//! crate to decode them because all of them expect a well-formed UTF-8 string. +//! However, percent encoding is not limited to UTF-8 strings. +//! +//! More information about "Percent Encoding" can be found here: +//! +//! - +//! - +//! - +use aquatic_udp_protocol::PeerId; +use bittorrent_primitives::info_hash::{self, InfoHash}; +use torrust_tracker_primitives::peer; + +/* code-review: this module is duplicated in torrust_tracker::servers::http::percent_encoding. + Should we move it to torrust_tracker_primitives? +*/ + +/// Percent decodes a percent encoded infohash. Internally an +/// [`InfoHash`] is a 20-byte array. +/// +/// For example, given the infohash `3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0`, +/// it's percent encoded representation is `%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0`. +/// +/// ```rust +/// use std::str::FromStr; +/// use torrust_tracker::servers::http::percent_encoding::percent_decode_info_hash; +/// use bittorrent_primitives::info_hash::InfoHash; +/// use torrust_tracker_primitives::peer; +/// +/// let encoded_infohash = "%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0"; +/// +/// let info_hash = percent_decode_info_hash(encoded_infohash).unwrap(); +/// +/// assert_eq!( +/// info_hash, +/// InfoHash::from_str("3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0").unwrap() +/// ); +/// ``` +/// +/// # Errors +/// +/// Will return `Err` if the decoded bytes do not represent a valid +/// [`InfoHash`]. +pub fn percent_decode_info_hash(raw_info_hash: &str) -> Result { + let bytes = percent_encoding::percent_decode_str(raw_info_hash).collect::>(); + InfoHash::try_from(bytes) +} + +/// Percent decodes a percent encoded peer id. Internally a peer [`Id`](PeerId) +/// is a 20-byte array. +/// +/// For example, given the peer id `*b"-qB00000000000000000"`, +/// it's percent encoded representation is `%2DqB00000000000000000`. +/// +/// ```rust +/// use std::str::FromStr; +/// +/// use aquatic_udp_protocol::PeerId; +/// use torrust_tracker::servers::http::percent_encoding::percent_decode_peer_id; +/// use bittorrent_primitives::info_hash::InfoHash; +/// +/// let encoded_peer_id = "%2DqB00000000000000000"; +/// +/// let peer_id = percent_decode_peer_id(encoded_peer_id).unwrap(); +/// +/// assert_eq!(peer_id, PeerId(*b"-qB00000000000000000")); +/// ``` +/// +/// # Errors +/// +/// Will return `Err` if if the decoded bytes do not represent a valid [`PeerId`]. +pub fn percent_decode_peer_id(raw_peer_id: &str) -> Result { + let bytes = percent_encoding::percent_decode_str(raw_peer_id).collect::>(); + Ok(*peer::Id::try_from(bytes)?) +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use aquatic_udp_protocol::PeerId; + use bittorrent_primitives::info_hash::InfoHash; + + use crate::http::url_encoding::{percent_decode_info_hash, percent_decode_peer_id}; + + #[test] + fn it_should_decode_a_percent_encoded_info_hash() { + let encoded_infohash = "%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0"; + + let info_hash = percent_decode_info_hash(encoded_infohash).unwrap(); + + assert_eq!( + info_hash, + InfoHash::from_str("3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0").unwrap() + ); + } + + #[test] + fn it_should_fail_decoding_an_invalid_percent_encoded_info_hash() { + let invalid_encoded_infohash = "invalid percent-encoded infohash"; + + let info_hash = percent_decode_info_hash(invalid_encoded_infohash); + + assert!(info_hash.is_err()); + } + + #[test] + fn it_should_decode_a_percent_encoded_peer_id() { + let encoded_peer_id = "%2DqB00000000000000000"; + + let peer_id = percent_decode_peer_id(encoded_peer_id).unwrap(); + + assert_eq!(peer_id, PeerId(*b"-qB00000000000000000")); + } + + #[test] + fn it_should_fail_decoding_an_invalid_percent_encoded_peer_id() { + let invalid_encoded_peer_id = "invalid percent-encoded peer id"; + + let peer_id = percent_decode_peer_id(invalid_encoded_peer_id); + + assert!(peer_id.is_err()); + } +} diff --git a/packages/tracker-client/src/lib.rs b/packages/tracker-client/src/lib.rs new file mode 100644 index 000000000..344e1b577 --- /dev/null +++ b/packages/tracker-client/src/lib.rs @@ -0,0 +1,3 @@ +pub mod console; +pub mod http; +pub mod udp; diff --git a/src/shared/bit_torrent/tracker/udp/client.rs b/packages/tracker-client/src/udp/client.rs similarity index 99% rename from src/shared/bit_torrent/tracker/udp/client.rs rename to packages/tracker-client/src/udp/client.rs index edb8adc85..facdfac38 100644 --- a/src/shared/bit_torrent/tracker/udp/client.rs +++ b/packages/tracker-client/src/udp/client.rs @@ -11,7 +11,7 @@ use torrust_tracker_configuration::DEFAULT_TIMEOUT; use zerocopy::network_endian::I32; use super::Error; -use crate::shared::bit_torrent::tracker::udp::MAX_PACKET_SIZE; +use crate::udp::MAX_PACKET_SIZE; pub const UDP_CLIENT_LOG_TARGET: &str = "UDP CLIENT"; diff --git a/packages/tracker-client/src/udp/mod.rs b/packages/tracker-client/src/udp/mod.rs new file mode 100644 index 000000000..b9d5f34f6 --- /dev/null +++ b/packages/tracker-client/src/udp/mod.rs @@ -0,0 +1,68 @@ +use std::net::SocketAddr; +use std::sync::Arc; + +use aquatic_udp_protocol::Request; +use thiserror::Error; +use torrust_tracker_located_error::DynError; + +pub mod client; + +/// The maximum number of bytes in a UDP packet. +pub const MAX_PACKET_SIZE: usize = 1496; +/// A magic 64-bit integer constant defined in the protocol that is used to +/// identify the protocol. +pub const PROTOCOL_ID: i64 = 0x0417_2710_1980; + +#[derive(Debug, Clone, Error)] +pub enum Error { + #[error("Timeout while waiting for socket to bind: {addr:?}")] + TimeoutWhileBindingToSocket { addr: SocketAddr }, + + #[error("Failed to bind to socket: {addr:?}, with error: {err:?}")] + UnableToBindToSocket { err: Arc, addr: SocketAddr }, + + #[error("Timeout while waiting for connection to remote: {remote_addr:?}")] + TimeoutWhileConnectingToRemote { remote_addr: SocketAddr }, + + #[error("Failed to connect to remote: {remote_addr:?}, with error: {err:?}")] + UnableToConnectToRemote { + err: Arc, + remote_addr: SocketAddr, + }, + + #[error("Timeout while waiting for the socket to become writable.")] + TimeoutWaitForWriteableSocket, + + #[error("Failed to get writable socket: {err:?}")] + UnableToGetWritableSocket { err: Arc }, + + #[error("Timeout while trying to send data: {data:?}")] + TimeoutWhileSendingData { data: Vec }, + + #[error("Failed to send data: {data:?}, with error: {err:?}")] + UnableToSendData { err: Arc, data: Vec }, + + #[error("Timeout while waiting for the socket to become readable.")] + TimeoutWaitForReadableSocket, + + #[error("Failed to get readable socket: {err:?}")] + UnableToGetReadableSocket { err: Arc }, + + #[error("Timeout while trying to receive data.")] + TimeoutWhileReceivingData, + + #[error("Failed to receive data: {err:?}")] + UnableToReceivingData { err: Arc }, + + #[error("Failed to get data from request: {request:?}, with error: {err:?}")] + UnableToWriteDataFromRequest { err: Arc, request: Request }, + + #[error("Failed to parse response: {response:?}, with error: {err:?}")] + UnableToParseResponse { err: Arc, response: Vec }, +} + +impl From for DynError { + fn from(e: Error) -> Self { + Arc::new(Box::new(e)) + } +} diff --git a/src/console/ci/e2e/tracker_checker.rs b/src/console/ci/e2e/tracker_checker.rs index 192795e61..b4c2544ee 100644 --- a/src/console/ci/e2e/tracker_checker.rs +++ b/src/console/ci/e2e/tracker_checker.rs @@ -7,12 +7,14 @@ use std::process::Command; /// /// Will return an error if the Tracker Checker fails. pub fn run(config_content: &str) -> io::Result<()> { - tracing::info!("Running Tracker Checker: TORRUST_CHECKER_CONFIG=[config] cargo run --bin tracker_checker"); + tracing::info!( + "Running Tracker Checker: TORRUST_CHECKER_CONFIG=[config] cargo run -p bittorrent-tracker-client --bin tracker_checker" + ); tracing::info!("Tracker Checker config:\n{config_content}"); let status = Command::new("cargo") .env("TORRUST_CHECKER_CONFIG", config_content) - .args(["run", "--bin", "tracker_checker"]) + .args(["run", "-p", "bittorrent-tracker-client", "--bin", "tracker_checker"]) .status()?; if status.success() { diff --git a/src/console/mod.rs b/src/console/mod.rs index dab338e4b..0e0da3fa2 100644 --- a/src/console/mod.rs +++ b/src/console/mod.rs @@ -1,4 +1,3 @@ //! Console apps. pub mod ci; -pub mod clients; pub mod profiling; diff --git a/src/servers/udp/server/launcher.rs b/src/servers/udp/server/launcher.rs index c9ad213f6..7f31d7739 100644 --- a/src/servers/udp/server/launcher.rs +++ b/src/servers/udp/server/launcher.rs @@ -2,6 +2,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; +use bittorrent_tracker_client::udp::client::check; use derive_more::Constructor; use futures_util::StreamExt; use tokio::select; @@ -18,7 +19,6 @@ use crate::servers::udp::server::bound_socket::BoundSocket; use crate::servers::udp::server::processor::Processor; use crate::servers::udp::server::receiver::Receiver; use crate::servers::udp::UDP_TRACKER_LOG_TARGET; -use crate::shared::bit_torrent::tracker::udp::client::check; /// A UDP server instance launcher. #[derive(Constructor)] diff --git a/src/shared/bit_torrent/tracker/mod.rs b/src/shared/bit_torrent/tracker/mod.rs index b08eaa622..7e5aaa137 100644 --- a/src/shared/bit_torrent/tracker/mod.rs +++ b/src/shared/bit_torrent/tracker/mod.rs @@ -1,2 +1 @@ -pub mod http; pub mod udp; diff --git a/src/shared/bit_torrent/tracker/udp/mod.rs b/src/shared/bit_torrent/tracker/udp/mod.rs index b9d5f34f6..1ceb8a08b 100644 --- a/src/shared/bit_torrent/tracker/udp/mod.rs +++ b/src/shared/bit_torrent/tracker/udp/mod.rs @@ -1,68 +1,6 @@ -use std::net::SocketAddr; -use std::sync::Arc; - -use aquatic_udp_protocol::Request; -use thiserror::Error; -use torrust_tracker_located_error::DynError; - -pub mod client; - /// The maximum number of bytes in a UDP packet. pub const MAX_PACKET_SIZE: usize = 1496; + /// A magic 64-bit integer constant defined in the protocol that is used to /// identify the protocol. pub const PROTOCOL_ID: i64 = 0x0417_2710_1980; - -#[derive(Debug, Clone, Error)] -pub enum Error { - #[error("Timeout while waiting for socket to bind: {addr:?}")] - TimeoutWhileBindingToSocket { addr: SocketAddr }, - - #[error("Failed to bind to socket: {addr:?}, with error: {err:?}")] - UnableToBindToSocket { err: Arc, addr: SocketAddr }, - - #[error("Timeout while waiting for connection to remote: {remote_addr:?}")] - TimeoutWhileConnectingToRemote { remote_addr: SocketAddr }, - - #[error("Failed to connect to remote: {remote_addr:?}, with error: {err:?}")] - UnableToConnectToRemote { - err: Arc, - remote_addr: SocketAddr, - }, - - #[error("Timeout while waiting for the socket to become writable.")] - TimeoutWaitForWriteableSocket, - - #[error("Failed to get writable socket: {err:?}")] - UnableToGetWritableSocket { err: Arc }, - - #[error("Timeout while trying to send data: {data:?}")] - TimeoutWhileSendingData { data: Vec }, - - #[error("Failed to send data: {data:?}, with error: {err:?}")] - UnableToSendData { err: Arc, data: Vec }, - - #[error("Timeout while waiting for the socket to become readable.")] - TimeoutWaitForReadableSocket, - - #[error("Failed to get readable socket: {err:?}")] - UnableToGetReadableSocket { err: Arc }, - - #[error("Timeout while trying to receive data.")] - TimeoutWhileReceivingData, - - #[error("Failed to receive data: {err:?}")] - UnableToReceivingData { err: Arc }, - - #[error("Failed to get data from request: {request:?}, with error: {err:?}")] - UnableToWriteDataFromRequest { err: Arc, request: Request }, - - #[error("Failed to parse response: {response:?}, with error: {err:?}")] - UnableToParseResponse { err: Arc, response: Vec }, -} - -impl From for DynError { - fn from(e: Error) -> Self { - Arc::new(Box::new(e)) - } -} diff --git a/tests/servers/udp/contract.rs b/tests/servers/udp/contract.rs index 1f9b71b62..73f7ce368 100644 --- a/tests/servers/udp/contract.rs +++ b/tests/servers/udp/contract.rs @@ -6,7 +6,7 @@ use core::panic; use aquatic_udp_protocol::{ConnectRequest, ConnectionId, Response, TransactionId}; -use torrust_tracker::shared::bit_torrent::tracker::udp::client::UdpTrackerClient; +use bittorrent_tracker_client::udp::client::UdpTrackerClient; use torrust_tracker::shared::bit_torrent::tracker::udp::MAX_PACKET_SIZE; use torrust_tracker_configuration::DEFAULT_TIMEOUT; use torrust_tracker_test_helpers::configuration; @@ -71,7 +71,7 @@ async fn should_return_a_bad_request_response_when_the_client_sends_an_empty_req mod receiving_a_connection_request { use aquatic_udp_protocol::{ConnectRequest, TransactionId}; - use torrust_tracker::shared::bit_torrent::tracker::udp::client::UdpTrackerClient; + use bittorrent_tracker_client::udp::client::UdpTrackerClient; use torrust_tracker_configuration::DEFAULT_TIMEOUT; use torrust_tracker_test_helpers::configuration; use tracing::level_filters::LevelFilter; @@ -120,7 +120,7 @@ mod receiving_an_announce_request { AnnounceActionPlaceholder, AnnounceEvent, AnnounceRequest, ConnectionId, InfoHash, NumberOfBytes, NumberOfPeers, PeerId, PeerKey, Port, TransactionId, }; - use torrust_tracker::shared::bit_torrent::tracker::udp::client::UdpTrackerClient; + use bittorrent_tracker_client::udp::client::UdpTrackerClient; use torrust_tracker_configuration::DEFAULT_TIMEOUT; use torrust_tracker_test_helpers::configuration; use tracing::level_filters::LevelFilter; @@ -214,7 +214,7 @@ mod receiving_an_announce_request { mod receiving_an_scrape_request { use aquatic_udp_protocol::{ConnectionId, InfoHash, ScrapeRequest, TransactionId}; - use torrust_tracker::shared::bit_torrent::tracker::udp::client::UdpTrackerClient; + use bittorrent_tracker_client::udp::client::UdpTrackerClient; use torrust_tracker_configuration::DEFAULT_TIMEOUT; use torrust_tracker_test_helpers::configuration; use tracing::level_filters::LevelFilter;