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;