diff --git a/packages/primitives/src/announce_event.rs b/packages/primitives/src/announce_event.rs index 3bd56008..f6af4146 100644 --- a/packages/primitives/src/announce_event.rs +++ b/packages/primitives/src/announce_event.rs @@ -2,19 +2,24 @@ //! //! Distributed under Apache 2.0 license +use derive_more::Display; use serde::{Deserialize, Serialize}; /// Announce events. Described on the /// [BEP 3. The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html) -#[derive(Hash, Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Hash, Clone, Copy, Debug, Display, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum AnnounceEvent { /// The peer has started downloading the torrent. + #[display(fmt = "started")] Started, /// The peer has ceased downloading the torrent. + #[display(fmt = "stopped")] Stopped, /// The peer has completed downloading the torrent. + #[display(fmt = "completed")] Completed, /// This is one of the announcements done at regular intervals. + #[display(fmt = "")] None, } diff --git a/packages/primitives/src/info_hash.rs b/packages/primitives/src/info_hash.rs index 8059ba04..faa5236f 100644 --- a/packages/primitives/src/info_hash.rs +++ b/packages/primitives/src/info_hash.rs @@ -94,8 +94,8 @@ impl std::convert::From<&DefaultHasher> for InfoHash { } } -impl std::convert::From<&i32> for InfoHash { - fn from(n: &i32) -> InfoHash { +impl std::convert::From for InfoHash { + fn from(n: i32) -> InfoHash { let n = n.to_le_bytes(); InfoHash([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, n[0], n[1], n[2], n[3]]) } diff --git a/packages/primitives/src/peer.rs b/packages/primitives/src/peer.rs index f5b009f2..785818a0 100644 --- a/packages/primitives/src/peer.rs +++ b/packages/primitives/src/peer.rs @@ -392,13 +392,14 @@ pub mod fixture { #[allow(dead_code)] #[must_use] - pub fn build(self) -> Peer { - self.into() + pub fn with_event(mut self, event: AnnounceEvent) -> Self { + self.peer.event = event; + self } #[allow(dead_code)] #[must_use] - pub fn into(self) -> Peer { + pub fn build(self) -> Peer { self.peer } } diff --git a/packages/torrent-repository/tests/repository/mod.rs b/packages/torrent-repository/tests/repository/mod.rs index 7ffe17dd..ed48c062 100644 --- a/packages/torrent-repository/tests/repository/mod.rs +++ b/packages/torrent-repository/tests/repository/mod.rs @@ -115,7 +115,7 @@ fn many_out_of_order() -> Entries { let mut entry = EntrySingle::default(); entry.insert_or_update_peer(&a_started_peer(i)); - entries.insert((InfoHash::from(&i), entry)); + entries.insert((InfoHash::from(i), entry)); } // we keep the random order from the hashed set for the vector. diff --git a/src/console/clients/http/mod.rs b/src/console/clients/http/mod.rs index b70e7b60..8fda21f1 100644 --- a/src/console/clients/http/mod.rs +++ b/src/console/clients/http/mod.rs @@ -3,6 +3,7 @@ use std::time::Duration; use thiserror::Error; use torrust_tracker_primitives::info_hash::InfoHash; +use torrust_tracker_primitives::peer; use url::Url; use crate::shared::bit_torrent::tracker::http::client::requests::{announce, scrape}; @@ -35,11 +36,7 @@ pub async fn check_http_announce(url: &Url, timeout: Duration, info_hash: InfoHa let client = Client::new(url.clone(), timeout).map_err(|err| Error::HttpClientError { err })?; let response = client - .announce( - &announce::QueryBuilder::with_default_values() - .with_info_hash(&info_hash) - .build(), - ) + .announce(&announce::QueryBuilder::new(info_hash, peer::Id::from(1), 17548).build()) .await .map_err(|err| Error::HttpClientError { err })?; diff --git a/src/shared/bit_torrent/tracker/http/client/requests/announce.rs b/src/shared/bit_torrent/tracker/http/client/requests/announce.rs index 4fbbb617..3c8583ef 100644 --- a/src/shared/bit_torrent/tracker/http/client/requests/announce.rs +++ b/src/shared/bit_torrent/tracker/http/client/requests/announce.rs @@ -1,8 +1,8 @@ use std::fmt; -use std::net::{IpAddr, Ipv4Addr}; -use std::str::FromStr; +use std::net::IpAddr; use serde_repr::Serialize_repr; +use torrust_tracker_primitives::announce_event::AnnounceEvent; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; @@ -12,13 +12,13 @@ use crate::shared::bit_torrent::tracker::http::{percent_encode_byte_array, ByteA #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub(super) struct Query { pub info_hash: ByteArray20, - pub peer_addr: IpAddr, - pub downloaded: BaseTenASCII, - pub uploaded: BaseTenASCII, + pub peer_addr: Option, + pub downloaded: Option, + pub uploaded: Option, pub peer_id: ByteArray20, pub port: PortNumber, - pub left: BaseTenASCII, - pub event: Option, + pub left: Option, + pub event: Option, pub compact: Option, } @@ -31,23 +31,6 @@ impl fmt::Display for Query { pub type BaseTenASCII = u64; pub type PortNumber = u16; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Event { - //Started, - //Stopped, - Completed, -} - -impl fmt::Display for Event { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - //Event::Started => write!(f, "started"), - //Event::Stopped => write!(f, "stopped"), - Event::Completed => write!(f, "completed"), - } - } -} - #[derive(Serialize_repr, PartialEq, Eq, Debug, Clone, Copy)] #[repr(u8)] pub enum Compact { @@ -64,6 +47,7 @@ impl fmt::Display for Compact { } } +#[derive(Debug)] pub struct QueryBuilder { query: Query, } @@ -73,49 +57,43 @@ impl QueryBuilder { /// /// Will panic if the default info-hash value is not a valid info-hash. #[must_use] - pub fn with_default_values() -> QueryBuilder { + pub fn new(info_hash: InfoHash, peer_id: peer::Id, port: u16) -> QueryBuilder { Self { query: Query { - info_hash: InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap().0, // # DevSkim: ignore DS173237 - peer_addr: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 88)), - downloaded: 0, - uploaded: 0, - peer_id: peer::Id(*b"-qB00000000000000001").0, - port: 17548, - left: 0, - event: Some(Event::Completed), - compact: Some(Compact::NotAccepted), + info_hash: info_hash.0, + peer_addr: None, + downloaded: None, + uploaded: None, + peer_id: peer_id.0, + port, + left: None, + event: None, + compact: None, }, } } #[must_use] - pub fn with_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.query.info_hash = info_hash.0; + pub fn with_event(mut self, event: AnnounceEvent) -> Self { + self.query.event = Some(event); self } #[must_use] - pub fn with_peer_id(mut self, peer_id: &peer::Id) -> Self { - self.query.peer_id = peer_id.0; + pub fn with_compact(mut self) -> Self { + self.query.compact = Some(Compact::Accepted); self } #[must_use] - pub fn with_compact(mut self, compact: Compact) -> Self { - self.query.compact = Some(compact); + pub fn without_compact(mut self) -> Self { + self.query.compact = Some(Compact::NotAccepted); self } #[must_use] pub fn with_peer_addr(mut self, peer_addr: &IpAddr) -> Self { - self.query.peer_addr = *peer_addr; - self - } - - #[must_use] - pub fn without_compact(mut self) -> Self { - self.query.compact = None; + self.query.peer_addr = Some(*peer_addr); self } @@ -212,19 +190,16 @@ impl From<&Query> for QueryParams { fn from(value: &Query) -> Self { let query = value; - let event = query.event.as_ref().map(std::string::ToString::to_string); - let compact = query.compact.as_ref().map(std::string::ToString::to_string); - Self { info_hash: Some(percent_encode_byte_array(&query.info_hash)), - peer_addr: Some(query.peer_addr.to_string()), - downloaded: Some(query.downloaded.to_string()), - uploaded: Some(query.uploaded.to_string()), + peer_addr: query.peer_addr.as_ref().map(std::string::ToString::to_string), + downloaded: query.downloaded.as_ref().map(std::string::ToString::to_string), + uploaded: query.uploaded.as_ref().map(std::string::ToString::to_string), peer_id: Some(percent_encode_byte_array(&query.peer_id)), port: Some(query.port.to_string()), - left: Some(query.left.to_string()), - event, - compact, + left: query.left.as_ref().map(std::string::ToString::to_string), + event: query.event.as_ref().map(std::string::ToString::to_string), + compact: query.compact.as_ref().map(std::string::ToString::to_string), } } } diff --git a/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs b/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs index aa44ebd2..1c884cd0 100644 --- a/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs +++ b/src/shared/bit_torrent/tracker/http/client/requests/scrape.rs @@ -90,33 +90,37 @@ impl Query { #[derive(Default)] pub struct QueryBuilder { - scrape_query: Query, + query: Query, } impl FromIterator for QueryBuilder { fn from_iter>(iter: T) -> Self { Self { - scrape_query: Query::from_iter(iter), + query: Query::from_iter(iter), } } } -impl QueryBuilder { - #[must_use] - pub fn with_one_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.scrape_query.infohashes = [info_hash.0].to_vec(); - self +impl From<&InfoHash> for QueryBuilder { + fn from(value: &InfoHash) -> Self { + Self { + query: Query { + infohashes: [value.0].to_vec(), + }, + } } +} +impl QueryBuilder { #[must_use] pub fn add_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.scrape_query.infohashes.push(info_hash.0); + self.query.infohashes.push(info_hash.0); self } #[must_use] pub fn build(self) -> Scrape { - self.scrape_query.into() + self.query.into() } } diff --git a/src/shared/bit_torrent/tracker/http/client/responses/announce.rs b/src/shared/bit_torrent/tracker/http/client/responses/announce.rs index 5861df10..7955a82f 100644 --- a/src/shared/bit_torrent/tracker/http/client/responses/announce.rs +++ b/src/shared/bit_torrent/tracker/http/client/responses/announce.rs @@ -16,11 +16,35 @@ pub(super) struct Response { pub peers: Vec, // Peers using IPV4 and IPV6 } -#[derive(Default)] pub struct ResponseBuilder { response: Response, } +impl ResponseBuilder { + #[must_use] + pub fn new(policy: &torrust_tracker_configuration::AnnouncePolicy) -> Self { + Self { + response: Response { + interval: policy.interval, + min_interval: policy.interval_min, + ..Default::default() + }, + } + } + + #[must_use] + pub fn with_complete(mut self, complete: u32) -> Self { + self.response.complete = complete; + self + } + + #[must_use] + pub fn with_peers(mut self, peers: Vec) -> Self { + self.response.peers = peers; + self + } +} + impl TryFrom<&Bytes> for ResponseBuilder { type Error = BencodeParseError; diff --git a/src/shared/bit_torrent/tracker/http/client/responses/mod.rs b/src/shared/bit_torrent/tracker/http/client/responses/mod.rs index 64fe646a..0f66a54c 100644 --- a/src/shared/bit_torrent/tracker/http/client/responses/mod.rs +++ b/src/shared/bit_torrent/tracker/http/client/responses/mod.rs @@ -1,20 +1,22 @@ use std::sync::Arc; use derive_more::From; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use thiserror::Error; pub mod announce; pub mod error; pub mod scrape; -#[derive(Serialize, Debug, From, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Debug, From, PartialEq, Eq, Clone)] pub struct Announce { + #[serde(flatten)] response: announce::Response, } -#[derive(Serialize, Debug, From, PartialEq, Eq, Clone)] +#[derive(Serialize, Deserialize, Debug, From, PartialEq, Eq, Clone)] pub struct Scrape { + #[serde(flatten)] response: scrape::Response, } diff --git a/tests/servers/api/v1/contract/context/stats.rs b/tests/servers/api/v1/contract/context/stats.rs index af658767..5794c613 100644 --- a/tests/servers/api/v1/contract/context/stats.rs +++ b/tests/servers/api/v1/contract/context/stats.rs @@ -16,7 +16,7 @@ async fn should_allow_getting_tracker_statistics() { env.add_torrent_peer( &InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(), - &PeerBuilder::default().into(), + &PeerBuilder::default().build(), ) .await; diff --git a/tests/servers/api/v1/contract/context/torrent.rs b/tests/servers/api/v1/contract/context/torrent.rs index d54935f8..5e693b73 100644 --- a/tests/servers/api/v1/contract/context/torrent.rs +++ b/tests/servers/api/v1/contract/context/torrent.rs @@ -24,7 +24,7 @@ async fn should_allow_getting_all_torrents() { let info_hash = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); - env.add_torrent_peer(&info_hash, &PeerBuilder::default().into()).await; + env.add_torrent_peer(&info_hash, &PeerBuilder::default().build()).await; let response = Client::new(env.get_connection_info()).get_torrents(Query::empty()).await; @@ -50,8 +50,8 @@ async fn should_allow_limiting_the_torrents_in_the_result() { let info_hash_1 = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); let info_hash_2 = InfoHash::from_str("0b3aea4adc213ce32295be85d3883a63bca25446").unwrap(); - env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().into()).await; - env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().into()).await; + env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().build()).await; + env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().build()).await; let response = Client::new(env.get_connection_info()) .get_torrents(Query::params([QueryParam::new("limit", "1")].to_vec())) @@ -79,8 +79,8 @@ async fn should_allow_the_torrents_result_pagination() { let info_hash_1 = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); let info_hash_2 = InfoHash::from_str("0b3aea4adc213ce32295be85d3883a63bca25446").unwrap(); - env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().into()).await; - env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().into()).await; + env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().build()).await; + env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().build()).await; let response = Client::new(env.get_connection_info()) .get_torrents(Query::params([QueryParam::new("offset", "1")].to_vec())) @@ -107,8 +107,8 @@ async fn should_allow_getting_a_list_of_torrents_providing_infohashes() { let info_hash_1 = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); // DevSkim: ignore DS173237 let info_hash_2 = InfoHash::from_str("0b3aea4adc213ce32295be85d3883a63bca25446").unwrap(); // DevSkim: ignore DS173237 - env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().into()).await; - env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().into()).await; + env.add_torrent_peer(&info_hash_1, &PeerBuilder::default().build()).await; + env.add_torrent_peer(&info_hash_2, &PeerBuilder::default().build()).await; let response = Client::new(env.get_connection_info()) .get_torrents(Query::params( @@ -222,7 +222,7 @@ async fn should_allow_getting_a_torrent_info() { let info_hash = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); - let peer = PeerBuilder::default().into(); + let peer = PeerBuilder::default().build(); env.add_torrent_peer(&info_hash, &peer).await; @@ -285,7 +285,7 @@ async fn should_not_allow_getting_a_torrent_info_for_unauthenticated_users() { let info_hash = InfoHash::from_str("9e0217d0fa71c87332cd8bf9dbeabcb2c2cf3c4d").unwrap(); - env.add_torrent_peer(&info_hash, &PeerBuilder::default().into()).await; + env.add_torrent_peer(&info_hash, &PeerBuilder::default().build()).await; let response = Client::new(connection_with_invalid_token(env.get_connection_info().bind_address.as_str())) .get_torrent(&info_hash.to_string()) diff --git a/tests/servers/http/asserts.rs b/tests/servers/http/asserts.rs index 3a2e67bf..8dc09101 100644 --- a/tests/servers/http/asserts.rs +++ b/tests/servers/http/asserts.rs @@ -1,10 +1,9 @@ use std::panic::Location; use reqwest::Response; - -use super::responses::announce::{Announce, Compact, DeserializedCompact}; -use super::responses::scrape; -use crate::servers::http::responses::error::Error; +use torrust_tracker::shared::bit_torrent::tracker::http::client::responses; +use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::error::Error; +use torrust_tracker_configuration::AnnouncePolicy; pub fn assert_bencoded_error(response_text: &String, expected_failure_reason: &str, location: &'static Location<'static>) { let error_failure_reason = serde_bencode::from_str::(response_text) @@ -22,36 +21,36 @@ pub fn assert_bencoded_error(response_text: &String, expected_failure_reason: &s ); } -pub async fn assert_empty_announce_response(response: Response) { +pub async fn assert_empty_announce_response(response: Response, policy: &AnnouncePolicy) { assert_eq!(response.status(), 200); - let announce_response: Announce = serde_bencode::from_str(&response.text().await.unwrap()).unwrap(); - assert!(announce_response.peers.is_empty()); + let announce_response: responses::Announce = serde_bencode::from_str(&response.text().await.unwrap()).unwrap(); + assert_eq!(announce_response, responses::announce::ResponseBuilder::new(policy).build()); } -pub async fn assert_announce_response(response: Response, expected_announce_response: &Announce) { +pub async fn assert_announce_response(response: Response, expected_announce_response: &responses::Announce) { assert_eq!(response.status(), 200); let body = response.bytes().await.unwrap(); - let announce_response: Announce = serde_bencode::from_bytes(&body) + let announce_response: responses::Announce = serde_bencode::from_bytes(&body) .unwrap_or_else(|_| panic!("response body should be a valid announce response, got \"{:#?}\"", &body)); assert_eq!(announce_response, *expected_announce_response); } -pub async fn assert_compact_announce_response(response: Response, expected_response: &Compact) { +pub async fn assert_compact_announce_response(response: Response, expected_response: &responses::announce::Compact) { assert_eq!(response.status(), 200); let bytes = response.bytes().await.unwrap(); - let compact_announce = DeserializedCompact::from_bytes(&bytes).unwrap_or_else(|_| { + let compact_announce = responses::announce::DeserializedCompact::from_bytes(&bytes).unwrap_or_else(|_| { panic!( "response body should be a valid compact announce response, got \"{:?}\"", &bytes ) }); - let actual_response = Compact::from(compact_announce); + let actual_response = responses::announce::Compact::from(compact_announce); assert_eq!(actual_response, *expected_response); } @@ -61,10 +60,12 @@ pub async fn assert_compact_announce_response(response: Response, expected_respo /// ```text /// b"d5:filesd20:\x9c8B\"\x13\xe3\x0b\xff!+0\xc3`\xd2o\x9a\x02\x13d\"d8:completei1e10:downloadedi0e10:incompletei0eeee" /// ``` -pub async fn assert_scrape_response(response: Response, expected_response: &scrape::Response) { +pub async fn assert_scrape_response(response: Response, expected_response: &responses::Scrape) { assert_eq!(response.status(), 200); - let scrape_response = scrape::Response::try_from_bencoded(&response.bytes().await.unwrap()).unwrap(); + let scrape_response = responses::scrape::ResponseBuilder::try_from(&response.bytes().await.unwrap()) + .unwrap() + .build(); assert_eq!(scrape_response, *expected_response); } @@ -72,7 +73,7 @@ pub async fn assert_scrape_response(response: Response, expected_response: &scra pub async fn assert_is_announce_response(response: Response) { assert_eq!(response.status(), 200); let body = response.text().await.unwrap(); - let _announce_response: Announce = serde_bencode::from_str(&body) + let _announce_response: responses::Announce = serde_bencode::from_str(&body) .unwrap_or_else(|_| panic!("response body should be a valid announce response, got \"{}\"", &body)); } diff --git a/tests/servers/http/client.rs b/tests/servers/http/client.rs index 288987c5..eeaeddc6 100644 --- a/tests/servers/http/client.rs +++ b/tests/servers/http/client.rs @@ -2,9 +2,7 @@ use std::net::IpAddr; use reqwest::{Client as ReqwestClient, Response}; use torrust_tracker::core::auth::Key; - -use super::requests::announce::{self, Query}; -use super::requests::scrape; +use torrust_tracker::shared::bit_torrent::tracker::http::client::requests; /// HTTP Tracker Client pub struct Client { @@ -47,15 +45,15 @@ impl Client { } } - pub async fn announce(&self, query: &announce::Query) -> Response { + pub async fn announce(&self, query: &requests::Announce) -> Response { self.get(&self.build_announce_path_and_query(query)).await } - pub async fn scrape(&self, query: &scrape::Query) -> Response { + pub async fn scrape(&self, query: &requests::Scrape) -> Response { self.get(&self.build_scrape_path_and_query(query)).await } - pub async fn announce_with_header(&self, query: &Query, key: &str, value: &str) -> Response { + pub async fn announce_with_header(&self, query: &requests::Announce, key: &str, value: &str) -> Response { self.get_with_header(&self.build_announce_path_and_query(query), key, value) .await } @@ -77,11 +75,11 @@ impl Client { .unwrap() } - fn build_announce_path_and_query(&self, query: &announce::Query) -> String { + fn build_announce_path_and_query(&self, query: &requests::Announce) -> String { format!("{}?{query}", self.build_path("announce")) } - fn build_scrape_path_and_query(&self, query: &scrape::Query) -> String { + fn build_scrape_path_and_query(&self, query: &requests::Scrape) -> String { format!("{}?{query}", self.build_path("scrape")) } diff --git a/tests/servers/http/mod.rs b/tests/servers/http/mod.rs index 65affc43..7e23468b 100644 --- a/tests/servers/http/mod.rs +++ b/tests/servers/http/mod.rs @@ -1,31 +1,10 @@ pub mod asserts; pub mod client; pub mod environment; -pub mod requests; -pub mod responses; pub mod v1; -pub type Started = environment::Environment; - -use percent_encoding::NON_ALPHANUMERIC; use torrust_tracker::servers::http::server; -pub type ByteArray20 = [u8; 20]; - -pub fn percent_encode_byte_array(bytes: &ByteArray20) -> String { - percent_encoding::percent_encode(bytes, NON_ALPHANUMERIC).to_string() -} - -pub struct InfoHash(ByteArray20); - -impl InfoHash { - pub fn new(vec: &[u8]) -> Self { - let mut byte_array_20: ByteArray20 = Default::default(); - byte_array_20.clone_from_slice(vec); - Self(byte_array_20) - } +pub type Started = environment::Environment; - pub fn bytes(&self) -> ByteArray20 { - self.0 - } -} +//pub(crate) const TIMEOUT: Duration = Duration::from_secs(5); diff --git a/tests/servers/http/requests/announce.rs b/tests/servers/http/requests/announce.rs deleted file mode 100644 index 06199062..00000000 --- a/tests/servers/http/requests/announce.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::fmt; -use std::net::{IpAddr, Ipv4Addr}; -use std::str::FromStr; - -use serde_repr::Serialize_repr; -use torrust_tracker_primitives::info_hash::InfoHash; -use torrust_tracker_primitives::peer; - -use crate::servers::http::{percent_encode_byte_array, ByteArray20}; - -pub struct Query { - pub info_hash: ByteArray20, - pub peer_addr: IpAddr, - pub downloaded: BaseTenASCII, - pub uploaded: BaseTenASCII, - pub peer_id: ByteArray20, - pub port: PortNumber, - pub left: BaseTenASCII, - pub event: Option, - pub compact: Option, -} - -impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.build()) - } -} - -/// HTTP Tracker Announce Request: -/// -/// -/// -/// Some parameters in the specification are not implemented in this tracker yet. -impl Query { - /// It builds the URL query component for the announce request. - /// - /// This custom URL query params encoding is needed because `reqwest` does not allow - /// bytes arrays in query parameters. More info on this issue: - /// - /// - pub fn build(&self) -> String { - self.params().to_string() - } - - pub fn params(&self) -> QueryParams { - QueryParams::from(self) - } -} - -pub type BaseTenASCII = u64; -pub type PortNumber = u16; - -pub enum Event { - //Started, - //Stopped, - Completed, -} - -impl fmt::Display for Event { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - //Event::Started => write!(f, "started"), - //Event::Stopped => write!(f, "stopped"), - Event::Completed => write!(f, "completed"), - } - } -} - -#[derive(Serialize_repr, PartialEq, Debug)] -#[repr(u8)] -pub enum Compact { - Accepted = 1, - NotAccepted = 0, -} - -impl fmt::Display for Compact { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Compact::Accepted => write!(f, "1"), - Compact::NotAccepted => write!(f, "0"), - } - } -} - -pub struct QueryBuilder { - announce_query: Query, -} - -impl QueryBuilder { - pub fn default() -> QueryBuilder { - let default_announce_query = Query { - info_hash: InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap().0, - peer_addr: IpAddr::V4(Ipv4Addr::new(192, 168, 1, 88)), - downloaded: 0, - uploaded: 0, - peer_id: peer::Id(*b"-qB00000000000000001").0, - port: 17548, - left: 0, - event: Some(Event::Completed), - compact: Some(Compact::NotAccepted), - }; - Self { - announce_query: default_announce_query, - } - } - - pub fn with_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.announce_query.info_hash = info_hash.0; - self - } - - pub fn with_peer_id(mut self, peer_id: &peer::Id) -> Self { - self.announce_query.peer_id = peer_id.0; - self - } - - pub fn with_compact(mut self, compact: Compact) -> Self { - self.announce_query.compact = Some(compact); - self - } - - pub fn with_peer_addr(mut self, peer_addr: &IpAddr) -> Self { - self.announce_query.peer_addr = *peer_addr; - self - } - - pub fn without_compact(mut self) -> Self { - self.announce_query.compact = None; - self - } - - pub fn query(self) -> Query { - self.announce_query - } -} - -/// It contains all the GET parameters that can be used in a HTTP Announce request. -/// -/// Sample Announce URL with all the GET parameters (mandatory and optional): -/// -/// ```text -/// http://127.0.0.1:7070/announce? -/// info_hash=%9C8B%22%13%E3%0B%FF%21%2B0%C3%60%D2o%9A%02%13d%22 (mandatory) -/// peer_addr=192.168.1.88 -/// downloaded=0 -/// uploaded=0 -/// peer_id=%2DqB00000000000000000 (mandatory) -/// port=17548 (mandatory) -/// left=0 -/// event=completed -/// compact=0 -/// ``` -pub struct QueryParams { - pub info_hash: Option, - pub peer_addr: Option, - pub downloaded: Option, - pub uploaded: Option, - pub peer_id: Option, - pub port: Option, - pub left: Option, - pub event: Option, - pub compact: Option, -} - -impl std::fmt::Display for QueryParams { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut params = vec![]; - - if let Some(info_hash) = &self.info_hash { - params.push(("info_hash", info_hash)); - } - if let Some(peer_addr) = &self.peer_addr { - params.push(("peer_addr", peer_addr)); - } - if let Some(downloaded) = &self.downloaded { - params.push(("downloaded", downloaded)); - } - if let Some(uploaded) = &self.uploaded { - params.push(("uploaded", uploaded)); - } - if let Some(peer_id) = &self.peer_id { - params.push(("peer_id", peer_id)); - } - if let Some(port) = &self.port { - params.push(("port", port)); - } - if let Some(left) = &self.left { - params.push(("left", left)); - } - if let Some(event) = &self.event { - params.push(("event", event)); - } - if let Some(compact) = &self.compact { - params.push(("compact", compact)); - } - - let query = params - .iter() - .map(|param| format!("{}={}", param.0, param.1)) - .collect::>() - .join("&"); - - write!(f, "{query}") - } -} - -impl QueryParams { - pub fn from(announce_query: &Query) -> Self { - let event = announce_query.event.as_ref().map(std::string::ToString::to_string); - let compact = announce_query.compact.as_ref().map(std::string::ToString::to_string); - - Self { - info_hash: Some(percent_encode_byte_array(&announce_query.info_hash)), - peer_addr: Some(announce_query.peer_addr.to_string()), - downloaded: Some(announce_query.downloaded.to_string()), - uploaded: Some(announce_query.uploaded.to_string()), - peer_id: Some(percent_encode_byte_array(&announce_query.peer_id)), - port: Some(announce_query.port.to_string()), - left: Some(announce_query.left.to_string()), - event, - compact, - } - } - - pub fn remove_optional_params(&mut self) { - // todo: make them optional with the Option<...> in the AnnounceQuery struct - // if they are really optional. So that we can crete a minimal AnnounceQuery - // instead of removing the optional params afterwards. - // - // The original specification on: - // - // says only `ip` and `event` are optional. - // - // On - // says only `ip`, `numwant`, `key` and `trackerid` are optional. - // - // but the server is responding if all these params are not included. - self.peer_addr = None; - self.downloaded = None; - self.uploaded = None; - self.left = None; - self.event = None; - self.compact = None; - } - - pub fn set(&mut self, param_name: &str, param_value: &str) { - match param_name { - "info_hash" => self.info_hash = Some(param_value.to_string()), - "peer_addr" => self.peer_addr = Some(param_value.to_string()), - "downloaded" => self.downloaded = Some(param_value.to_string()), - "uploaded" => self.uploaded = Some(param_value.to_string()), - "peer_id" => self.peer_id = Some(param_value.to_string()), - "port" => self.port = Some(param_value.to_string()), - "left" => self.left = Some(param_value.to_string()), - "event" => self.event = Some(param_value.to_string()), - "compact" => self.compact = Some(param_value.to_string()), - &_ => panic!("Invalid param name for announce query"), - } - } -} diff --git a/tests/servers/http/requests/mod.rs b/tests/servers/http/requests/mod.rs deleted file mode 100644 index 776d2dfb..00000000 --- a/tests/servers/http/requests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod announce; -pub mod scrape; diff --git a/tests/servers/http/requests/scrape.rs b/tests/servers/http/requests/scrape.rs deleted file mode 100644 index f6660585..00000000 --- a/tests/servers/http/requests/scrape.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::fmt; -use std::str::FromStr; - -use torrust_tracker_primitives::info_hash::InfoHash; - -use crate::servers::http::{percent_encode_byte_array, ByteArray20}; - -pub struct Query { - pub info_hash: Vec, -} - -impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.build()) - } -} - -/// HTTP Tracker Scrape Request: -/// -/// -impl Query { - /// It builds the URL query component for the scrape request. - /// - /// This custom URL query params encoding is needed because `reqwest` does not allow - /// bytes arrays in query parameters. More info on this issue: - /// - /// - pub fn build(&self) -> String { - self.params().to_string() - } - - pub fn params(&self) -> QueryParams { - QueryParams::from(self) - } -} - -pub struct QueryBuilder { - scrape_query: Query, -} - -impl QueryBuilder { - pub fn default() -> QueryBuilder { - let default_scrape_query = Query { - info_hash: [InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap().0].to_vec(), - }; - Self { - scrape_query: default_scrape_query, - } - } - - pub fn with_one_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.scrape_query.info_hash = [info_hash.0].to_vec(); - self - } - - pub fn add_info_hash(mut self, info_hash: &InfoHash) -> Self { - self.scrape_query.info_hash.push(info_hash.0); - self - } - - pub fn query(self) -> Query { - self.scrape_query - } -} - -/// It contains all the GET parameters that can be used in a HTTP Scrape request. -/// -/// The `info_hash` param is the percent encoded of the the 20-byte array info hash. -/// -/// Sample Scrape URL with all the GET parameters: -/// -/// For `IpV4`: -/// -/// ```text -/// http://127.0.0.1:7070/scrape?info_hash=%9C8B%22%13%E3%0B%FF%21%2B0%C3%60%D2o%9A%02%13d%22 -/// ``` -/// -/// For `IpV6`: -/// -/// ```text -/// http://[::1]:7070/scrape?info_hash=%9C8B%22%13%E3%0B%FF%21%2B0%C3%60%D2o%9A%02%13d%22 -/// ``` -/// -/// You can add as many info hashes as you want, just adding the same param again. -pub struct QueryParams { - pub info_hash: Vec, -} - -impl QueryParams { - pub fn set_one_info_hash_param(&mut self, info_hash: &str) { - self.info_hash = vec![info_hash.to_string()]; - } -} - -impl std::fmt::Display for QueryParams { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let query = self - .info_hash - .iter() - .map(|info_hash| format!("info_hash={}", &info_hash)) - .collect::>() - .join("&"); - - write!(f, "{query}") - } -} - -impl QueryParams { - pub fn from(scrape_query: &Query) -> Self { - let info_hashes = scrape_query - .info_hash - .iter() - .map(percent_encode_byte_array) - .collect::>(); - - Self { info_hash: info_hashes } - } -} diff --git a/tests/servers/http/responses/announce.rs b/tests/servers/http/responses/announce.rs deleted file mode 100644 index 2b49b440..00000000 --- a/tests/servers/http/responses/announce.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - -use serde::{Deserialize, Serialize}; -use torrust_tracker_primitives::peer; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Announce { - pub complete: u32, - pub incomplete: u32, - pub interval: u32, - #[serde(rename = "min interval")] - pub min_interval: u32, - pub peers: Vec, // Peers using IPV4 and IPV6 -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct DictionaryPeer { - pub ip: String, - #[serde(rename = "peer id")] - #[serde(with = "serde_bytes")] - pub peer_id: Vec, - pub port: u16, -} - -impl From for DictionaryPeer { - fn from(peer: peer::Peer) -> Self { - DictionaryPeer { - peer_id: peer.peer_id.to_bytes().to_vec(), - ip: peer.peer_addr.ip().to_string(), - port: peer.peer_addr.port(), - } - } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct DeserializedCompact { - pub complete: u32, - pub incomplete: u32, - pub interval: u32, - #[serde(rename = "min interval")] - pub min_interval: u32, - #[serde(with = "serde_bytes")] - pub peers: Vec, -} - -impl DeserializedCompact { - pub fn from_bytes(bytes: &[u8]) -> Result { - serde_bencode::from_bytes::(bytes) - } -} - -#[derive(Debug, PartialEq)] -pub struct Compact { - // code-review: there could be a way to deserialize this struct directly - // by using serde instead of doing it manually. Or at least using a custom deserializer. - pub complete: u32, - pub incomplete: u32, - pub interval: u32, - pub min_interval: u32, - pub peers: CompactPeerList, -} - -#[derive(Debug, PartialEq)] -pub struct CompactPeerList { - peers: Vec, -} - -impl CompactPeerList { - pub fn new(peers: Vec) -> Self { - Self { peers } - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct CompactPeer { - ip: Ipv4Addr, - port: u16, -} - -impl CompactPeer { - pub fn new(socket_addr: &SocketAddr) -> Self { - match socket_addr.ip() { - IpAddr::V4(ip) => Self { - ip, - port: socket_addr.port(), - }, - IpAddr::V6(_ip) => panic!("IPV6 is not supported for compact peer"), - } - } - - pub fn new_from_bytes(bytes: &[u8]) -> Self { - Self { - ip: Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]), - port: u16::from_be_bytes([bytes[4], bytes[5]]), - } - } -} - -impl From for Compact { - fn from(compact_announce: DeserializedCompact) -> Self { - let mut peers = vec![]; - - for peer_bytes in compact_announce.peers.chunks_exact(6) { - peers.push(CompactPeer::new_from_bytes(peer_bytes)); - } - - Self { - complete: compact_announce.complete, - incomplete: compact_announce.incomplete, - interval: compact_announce.interval, - min_interval: compact_announce.min_interval, - peers: CompactPeerList::new(peers), - } - } -} diff --git a/tests/servers/http/responses/error.rs b/tests/servers/http/responses/error.rs deleted file mode 100644 index 00befdb5..00000000 --- a/tests/servers/http/responses/error.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Error { - #[serde(rename = "failure reason")] - pub failure_reason: String, -} diff --git a/tests/servers/http/responses/mod.rs b/tests/servers/http/responses/mod.rs deleted file mode 100644 index bdc68905..00000000 --- a/tests/servers/http/responses/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod announce; -pub mod error; -pub mod scrape; diff --git a/tests/servers/http/responses/scrape.rs b/tests/servers/http/responses/scrape.rs deleted file mode 100644 index fc741cbf..00000000 --- a/tests/servers/http/responses/scrape.rs +++ /dev/null @@ -1,200 +0,0 @@ -use std::collections::HashMap; -use std::str; - -use serde::{Deserialize, Serialize}; -use serde_bencode::value::Value; - -use crate::servers::http::{ByteArray20, InfoHash}; - -#[derive(Debug, PartialEq, Default)] -pub struct Response { - pub files: HashMap, -} - -impl Response { - pub fn with_one_file(info_hash_bytes: ByteArray20, file: File) -> Self { - let mut files: HashMap = HashMap::new(); - files.insert(info_hash_bytes, file); - Self { files } - } - - pub fn try_from_bencoded(bytes: &[u8]) -> Result { - let scrape_response: DeserializedResponse = serde_bencode::from_bytes(bytes).unwrap(); - Self::try_from(scrape_response) - } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] -pub struct File { - pub complete: i64, // The number of active peers that have completed downloading - pub downloaded: i64, // The number of peers that have ever completed downloading - pub incomplete: i64, // The number of active peers that have not completed downloading -} - -impl File { - pub fn zeroed() -> Self { - Self::default() - } -} - -impl TryFrom for Response { - type Error = BencodeParseError; - - fn try_from(scrape_response: DeserializedResponse) -> Result { - parse_bencoded_response(&scrape_response.files) - } -} - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -struct DeserializedResponse { - pub files: Value, -} - -pub struct ResponseBuilder { - response: Response, -} - -impl ResponseBuilder { - pub fn default() -> Self { - Self { - response: Response::default(), - } - } - - pub fn add_file(mut self, info_hash_bytes: ByteArray20, file: File) -> Self { - self.response.files.insert(info_hash_bytes, file); - self - } - - pub fn build(self) -> Response { - self.response - } -} - -#[derive(Debug)] -pub enum BencodeParseError { - #[allow(dead_code)] - InvalidValueExpectedDict { value: Value }, - #[allow(dead_code)] - InvalidValueExpectedInt { value: Value }, - #[allow(dead_code)] - InvalidFileField { value: Value }, - #[allow(dead_code)] - MissingFileField { field_name: String }, -} - -/// It parses a bencoded scrape response into a `Response` struct. -/// -/// For example: -/// -/// ```text -/// d5:filesd20:xxxxxxxxxxxxxxxxxxxxd8:completei11e10:downloadedi13772e10:incompletei19e -/// 20:yyyyyyyyyyyyyyyyyyyyd8:completei21e10:downloadedi206e10:incompletei20eee -/// ``` -/// -/// Response (JSON encoded for readability): -/// -/// ```text -/// { -/// 'files': { -/// 'xxxxxxxxxxxxxxxxxxxx': {'complete': 11, 'downloaded': 13772, 'incomplete': 19}, -/// 'yyyyyyyyyyyyyyyyyyyy': {'complete': 21, 'downloaded': 206, 'incomplete': 20} -/// } -/// } -fn parse_bencoded_response(value: &Value) -> Result { - let mut files: HashMap = HashMap::new(); - - match value { - Value::Dict(dict) => { - for file_element in dict { - let info_hash_byte_vec = file_element.0; - let file_value = file_element.1; - - let file = parse_bencoded_file(file_value).unwrap(); - - files.insert(InfoHash::new(info_hash_byte_vec).bytes(), file); - } - } - _ => return Err(BencodeParseError::InvalidValueExpectedDict { value: value.clone() }), - } - - Ok(Response { files }) -} - -/// It parses a bencoded dictionary into a `File` struct. -/// -/// For example: -/// -/// -/// ```text -/// d8:completei11e10:downloadedi13772e10:incompletei19ee -/// ``` -/// -/// into: -/// -/// ```text -/// File { -/// complete: 11, -/// downloaded: 13772, -/// incomplete: 19, -/// } -/// ``` -fn parse_bencoded_file(value: &Value) -> Result { - let file = match &value { - Value::Dict(dict) => { - let mut complete = None; - let mut downloaded = None; - let mut incomplete = None; - - for file_field in dict { - let field_name = file_field.0; - - let field_value = match file_field.1 { - Value::Int(number) => Ok(*number), - _ => Err(BencodeParseError::InvalidValueExpectedInt { - value: file_field.1.clone(), - }), - }?; - - if field_name == b"complete" { - complete = Some(field_value); - } else if field_name == b"downloaded" { - downloaded = Some(field_value); - } else if field_name == b"incomplete" { - incomplete = Some(field_value); - } else { - return Err(BencodeParseError::InvalidFileField { - value: file_field.1.clone(), - }); - } - } - - if complete.is_none() { - return Err(BencodeParseError::MissingFileField { - field_name: "complete".to_string(), - }); - } - - if downloaded.is_none() { - return Err(BencodeParseError::MissingFileField { - field_name: "downloaded".to_string(), - }); - } - - if incomplete.is_none() { - return Err(BencodeParseError::MissingFileField { - field_name: "incomplete".to_string(), - }); - } - - File { - complete: complete.unwrap(), - downloaded: downloaded.unwrap(), - incomplete: incomplete.unwrap(), - } - } - _ => return Err(BencodeParseError::InvalidValueExpectedDict { value: value.clone() }), - }; - - Ok(file) -} diff --git a/tests/servers/http/v1/contract.rs b/tests/servers/http/v1/contract.rs index a7962db0..d2c2f857 100644 --- a/tests/servers/http/v1/contract.rs +++ b/tests/servers/http/v1/contract.rs @@ -9,6 +9,8 @@ async fn environment_should_be_started_and_stopped() { env.stop().await; } +pub(crate) const PORT: u16 = 17548; + mod for_all_config_modes { use torrust_tracker::servers::http::v1::handlers::health_check::{Report, Status}; @@ -31,11 +33,15 @@ mod for_all_config_modes { } mod and_running_on_reverse_proxy { + + use torrust_tracker::shared::bit_torrent::tracker::http::client::requests; + use torrust_tracker_primitives::info_hash::InfoHash; + use torrust_tracker_primitives::peer; use torrust_tracker_test_helpers::configuration; use crate::servers::http::asserts::assert_could_not_find_remote_address_on_x_forwarded_for_header_error_response; use crate::servers::http::client::Client; - use crate::servers::http::requests::announce::QueryBuilder; + use crate::servers::http::v1::contract::PORT; use crate::servers::http::Started; #[tokio::test] @@ -45,7 +51,8 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_with_reverse_proxy().into()).await; - let params = QueryBuilder::default().query().params(); + let query = requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build(); + let params: requests::announce::QueryParams = (&query).into(); let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; @@ -58,7 +65,8 @@ mod for_all_config_modes { async fn should_fail_when_the_xff_http_request_header_contains_an_invalid_ip() { let env = Started::new(&configuration::ephemeral_with_reverse_proxy().into()).await; - let params = QueryBuilder::default().query().params(); + let query = requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build(); + let params: requests::announce::QueryParams = (&query).into(); let response = Client::new(*env.bind_address()) .get_with_header(&format!("announce?{params}"), "X-Forwarded-For", "INVALID IP") @@ -89,6 +97,8 @@ mod for_all_config_modes { use local_ip_address::local_ip; use reqwest::{Response, StatusCode}; use tokio::net::TcpListener; + use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, responses}; + use torrust_tracker_primitives::announce_event::AnnounceEvent; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; use torrust_tracker_primitives::peer::fixture::PeerBuilder; @@ -101,9 +111,20 @@ mod for_all_config_modes { assert_is_announce_response, assert_missing_query_params_for_announce_request_error_response, }; use crate::servers::http::client::Client; - use crate::servers::http::requests::announce::{Compact, QueryBuilder}; - use crate::servers::http::responses::announce::{Announce, CompactPeer, CompactPeerList, DictionaryPeer}; - use crate::servers::http::{responses, Started}; + use crate::servers::http::v1::contract::PORT; + use crate::servers::http::Started; + + pub(crate) fn default_query_builder() -> requests::announce::QueryBuilder { + requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT) + } + + pub(crate) fn default_prams() -> requests::announce::QueryParams { + requests::announce::QueryParams::from(&default_query_builder().build()) + } + + pub(crate) fn with_infohash(info_hash: &str) -> requests::Announce { + requests::announce::QueryBuilder::new(info_hash.parse().expect("invalid infohash"), peer::Id::from(1), PORT).build() + } #[tokio::test] async fn it_should_start_and_stop() { @@ -115,7 +136,7 @@ mod for_all_config_modes { async fn should_respond_if_only_the_mandatory_fields_are_provided() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); params.remove_optional_params(); @@ -158,7 +179,7 @@ mod for_all_config_modes { // Without `info_hash` param - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); params.info_hash = None; @@ -168,7 +189,7 @@ mod for_all_config_modes { // Without `peer_id` param - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); params.peer_id = None; @@ -178,7 +199,7 @@ mod for_all_config_modes { // Without `port` param - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); params.port = None; @@ -189,11 +210,24 @@ mod for_all_config_modes { env.stop().await; } + #[tokio::test] + async fn it_should_return_an_empty_response_when_announcing_a_stopped_peer() { + let env = Started::new(&configuration::ephemeral_mode_public().into()).await; + + let query = default_query_builder().with_event(AnnounceEvent::Stopped).build(); + + let response = Client::new(*env.bind_address()).announce(&query).await; + + assert_empty_announce_response(response, &env.tracker.get_announce_policy()).await; + + env.stop().await; + } + #[tokio::test] async fn should_fail_when_the_info_hash_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); for invalid_value in &invalid_info_hashes() { params.set("info_hash", invalid_value); @@ -215,7 +249,7 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); params.peer_addr = Some("INVALID-IP-ADDRESS".to_string()); @@ -230,7 +264,7 @@ mod for_all_config_modes { async fn should_fail_when_the_downloaded_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = ["-1", "1.1", "a"]; @@ -249,7 +283,7 @@ mod for_all_config_modes { async fn should_fail_when_the_uploaded_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = ["-1", "1.1", "a"]; @@ -268,7 +302,7 @@ mod for_all_config_modes { async fn should_fail_when_the_peer_id_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = [ "0", @@ -294,7 +328,7 @@ mod for_all_config_modes { async fn should_fail_when_the_port_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = ["-1", "1.1", "a"]; @@ -313,7 +347,7 @@ mod for_all_config_modes { async fn should_fail_when_the_left_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = ["-1", "1.1", "a"]; @@ -332,7 +366,7 @@ mod for_all_config_modes { async fn should_fail_when_the_event_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = [ "0", @@ -359,7 +393,7 @@ mod for_all_config_modes { async fn should_fail_when_the_compact_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params = default_prams(); let invalid_values = ["-1", "1.1", "a"]; @@ -378,27 +412,16 @@ mod for_all_config_modes { async fn should_return_no_peers_if_the_announced_peer_is_the_first_one() { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - let response = Client::new(*env.bind_address()) - .announce( - &QueryBuilder::default() - .with_info_hash(&InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap()) - .query(), - ) - .await; + let announce = with_infohash("9c38422213e30bff212b30c360d26f9a02136422"); - let announce_policy = env.tracker.get_announce_policy(); + let response = Client::new(*env.bind_address()).announce(&announce).await; - assert_announce_response( - response, - &Announce { - complete: 1, // the peer for this test - incomplete: 0, - interval: announce_policy.interval, - min_interval: announce_policy.interval_min, - peers: vec![], - }, - ) - .await; + let expected_response = responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) + // the peer for this test + .with_complete(1) + .build(); + + assert_announce_response(response, &expected_response).await; env.stop().await; } @@ -417,30 +440,19 @@ mod for_all_config_modes { // Add the Peer 1 env.add_torrent_peer(&info_hash, &previously_announced_peer).await; + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000002"), PORT).build(); + // Announce the new Peer 2. This new peer is non included on the response peer list - let response = Client::new(*env.bind_address()) - .announce( - &QueryBuilder::default() - .with_info_hash(&info_hash) - .with_peer_id(&peer::Id(*b"-qB00000000000000002")) - .query(), - ) - .await; + let response = Client::new(*env.bind_address()).announce(&query).await; - let announce_policy = env.tracker.get_announce_policy(); + let expected_response = responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) + // the peer for this test + .with_complete(2) + .with_peers(vec![responses::announce::DictionaryPeer::from(previously_announced_peer)]) + .build(); // It should only contain the previously announced peer - assert_announce_response( - response, - &Announce { - complete: 2, - incomplete: 0, - interval: announce_policy.interval, - min_interval: announce_policy.interval_min, - peers: vec![DictionaryPeer::from(previously_announced_peer)], - }, - ) - .await; + assert_announce_response(response, &expected_response).await; env.stop().await; } @@ -468,31 +480,22 @@ mod for_all_config_modes { .build(); env.add_torrent_peer(&info_hash, &peer_using_ipv6).await; - // Announce the new Peer. - let response = Client::new(*env.bind_address()) - .announce( - &QueryBuilder::default() - .with_info_hash(&info_hash) - .with_peer_id(&peer::Id(*b"-qB00000000000000003")) - .query(), - ) - .await; + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000003"), PORT).build(); - let announce_policy = env.tracker.get_announce_policy(); + // Announce the new Peer. + let response = Client::new(*env.bind_address()).announce(&query).await; // The newly announced peer is not included on the response peer list, // but all the previously announced peers should be included regardless the IP version they are using. - assert_announce_response( - response, - &Announce { - complete: 3, - incomplete: 0, - interval: announce_policy.interval, - min_interval: announce_policy.interval_min, - peers: vec![DictionaryPeer::from(peer_using_ipv4), DictionaryPeer::from(peer_using_ipv6)], - }, - ) - .await; + let expected_response = responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) + .with_complete(3) + .with_peers(vec![ + responses::announce::DictionaryPeer::from(peer_using_ipv4), + responses::announce::DictionaryPeer::from(peer_using_ipv6), + ]) + .build(); + + assert_announce_response(response, &expected_response).await; env.stop().await; } @@ -507,16 +510,17 @@ mod for_all_config_modes { // Add a peer env.add_torrent_peer(&info_hash, &peer).await; - let announce_query = QueryBuilder::default() - .with_info_hash(&info_hash) - .with_peer_id(&peer.peer_id) - .query(); + let query = requests::announce::QueryBuilder::new(info_hash, peer.peer_id, PORT).build(); - assert_ne!(peer.peer_addr.ip(), announce_query.peer_addr); + let response = Client::new(*env.bind_address()).announce(&query).await; - let response = Client::new(*env.bind_address()).announce(&announce_query).await; - - assert_empty_announce_response(response).await; + assert_announce_response( + response, + &responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) + .with_complete(1) + .build(), + ) + .await; env.stop().await; } @@ -538,23 +542,21 @@ mod for_all_config_modes { // Add the Peer 1 env.add_torrent_peer(&info_hash, &previously_announced_peer).await; + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000002"), PORT) + .with_compact() + .build(); + // Announce the new Peer 2 accepting compact responses - let response = Client::new(*env.bind_address()) - .announce( - &QueryBuilder::default() - .with_info_hash(&info_hash) - .with_peer_id(&peer::Id(*b"-qB00000000000000002")) - .with_compact(Compact::Accepted) - .query(), - ) - .await; + let response = Client::new(*env.bind_address()).announce(&query).await; let expected_response = responses::announce::Compact { complete: 2, incomplete: 0, interval: 120, min_interval: 120, - peers: CompactPeerList::new([CompactPeer::new(&previously_announced_peer.peer_addr)].to_vec()), + peers: responses::announce::CompactPeerList::new( + [responses::announce::CompactPeer::new(&previously_announced_peer.peer_addr)].to_vec(), + ), }; assert_compact_announce_response(response, &expected_response).await; @@ -582,15 +584,9 @@ mod for_all_config_modes { // Announce the new Peer 2 without passing the "compact" param // By default it should respond with the compact peer list // https://www.bittorrent.org/beps/bep_0023.html - let response = Client::new(*env.bind_address()) - .announce( - &QueryBuilder::default() - .with_info_hash(&info_hash) - .with_peer_id(&peer::Id(*b"-qB00000000000000002")) - .without_compact() - .query(), - ) - .await; + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000002"), PORT).build(); + + let response = Client::new(*env.bind_address()).announce(&query).await; assert!(!is_a_compact_announce_response(response).await); @@ -608,7 +604,7 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; Client::new(*env.bind_address()) - .announce(&QueryBuilder::default().query()) + .announce(&default_query_builder().build()) .await; let stats = env.tracker.get_stats().await; @@ -632,7 +628,7 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_ipv6().into()).await; Client::bind(*env.bind_address(), IpAddr::from_str("::1").unwrap()) - .announce(&QueryBuilder::default().query()) + .announce(&default_query_builder().build()) .await; let stats = env.tracker.get_stats().await; @@ -652,9 +648,9 @@ mod for_all_config_modes { Client::new(*env.bind_address()) .announce( - &QueryBuilder::default() + &default_query_builder() .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) - .query(), + .build(), ) .await; @@ -672,7 +668,7 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; Client::new(*env.bind_address()) - .announce(&QueryBuilder::default().query()) + .announce(&default_query_builder().build()) .await; let stats = env.tracker.get_stats().await; @@ -696,7 +692,7 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_ipv6().into()).await; Client::bind(*env.bind_address(), IpAddr::from_str("::1").unwrap()) - .announce(&QueryBuilder::default().query()) + .announce(&default_query_builder().build()) .await; let stats = env.tracker.get_stats().await; @@ -716,9 +712,9 @@ mod for_all_config_modes { Client::new(*env.bind_address()) .announce( - &QueryBuilder::default() + &default_query_builder() .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) - .query(), + .build(), ) .await; @@ -738,14 +734,13 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let client_ip = local_ip().unwrap(); - let announce_query = QueryBuilder::default() - .with_info_hash(&info_hash) + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) - .query(); + .build(); { let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&announce_query).await.status(); + let status = client.announce(&query).await.status(); assert_eq!(status, StatusCode::OK); } @@ -774,14 +769,13 @@ mod for_all_config_modes { let loopback_ip = IpAddr::from_str("127.0.0.1").unwrap(); let client_ip = loopback_ip; - let announce_query = QueryBuilder::default() - .with_info_hash(&info_hash) + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) - .query(); + .build(); { let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&announce_query).await.status(); + let status = client.announce(&query).await.status(); assert_eq!(status, StatusCode::OK); } @@ -814,14 +808,13 @@ mod for_all_config_modes { let loopback_ip = IpAddr::from_str("127.0.0.1").unwrap(); let client_ip = loopback_ip; - let announce_query = QueryBuilder::default() - .with_info_hash(&info_hash) + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) - .query(); + .build(); { let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&announce_query).await.status(); + let status = client.announce(&query).await.status(); assert_eq!(status, StatusCode::OK); } @@ -846,15 +839,15 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_with_reverse_proxy().into()).await; - let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); + let info_hash: InfoHash = "9c38422213e30bff212b30c360d26f9a02136422".parse().expect("it should parse"); - let announce_query = QueryBuilder::default().with_info_hash(&info_hash).query(); + let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build(); { let client = Client::new(*env.bind_address()); let status = client .announce_with_header( - &announce_query, + &query, "X-Forwarded-For", "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178", ) @@ -887,6 +880,8 @@ mod for_all_config_modes { use std::str::FromStr; use tokio::net::TcpListener; + use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::scrape::File; + use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, responses}; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; use torrust_tracker_primitives::peer::fixture::PeerBuilder; @@ -898,9 +893,7 @@ mod for_all_config_modes { assert_scrape_response, }; use crate::servers::http::client::Client; - use crate::servers::http::requests::scrape::QueryBuilder; - use crate::servers::http::responses::scrape::{self, File, ResponseBuilder}; - use crate::servers::http::{requests, Started}; + use crate::servers::http::Started; //#[tokio::test] #[allow(dead_code)] @@ -917,7 +910,7 @@ mod for_all_config_modes { async fn should_fail_when_the_info_hash_param_is_invalid() { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - let mut params = QueryBuilder::default().query().params(); + let mut params: requests::scrape::QueryParams = requests::scrape::QueryBuilder::default().build().into(); for invalid_value in &invalid_info_hashes() { params.set_one_info_hash_param(invalid_value); @@ -946,22 +939,18 @@ mod for_all_config_modes { .await; let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default() - .add_file( + let expected_scrape_response = responses::scrape::ResponseBuilder::default() + .add_file(( info_hash.bytes(), File { complete: 0, downloaded: 0, incomplete: 1, }, - ) + )) .build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -985,22 +974,18 @@ mod for_all_config_modes { .await; let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default() - .add_file( + let expected_scrape_response = responses::scrape::ResponseBuilder::default() + .add_file(( info_hash.bytes(), File { complete: 1, downloaded: 0, incomplete: 0, }, - ) + )) .build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1015,14 +1000,12 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - assert_scrape_response(response, &scrape::Response::with_one_file(info_hash.bytes(), File::zeroed())).await; + let expected_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); + + assert_scrape_response(response, &expected_response).await; env.stop().await; } @@ -1039,13 +1022,13 @@ mod for_all_config_modes { &requests::scrape::QueryBuilder::default() .add_info_hash(&info_hash1) .add_info_hash(&info_hash2) - .query(), + .build(), ) .await; - let expected_scrape_response = ResponseBuilder::default() - .add_file(info_hash1.bytes(), File::zeroed()) - .add_file(info_hash2.bytes(), File::zeroed()) + let expected_scrape_response = responses::scrape::ResponseBuilder::default() + .add_file((info_hash1.bytes(), File::zeroed())) + .add_file((info_hash2.bytes(), File::zeroed())) .build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1060,11 +1043,7 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; let stats = env.tracker.get_stats().await; @@ -1090,11 +1069,7 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); Client::bind(*env.bind_address(), IpAddr::from_str("::1").unwrap()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; let stats = env.tracker.get_stats().await; @@ -1113,12 +1088,14 @@ mod configured_as_whitelisted { mod and_receiving_an_announce_request { use std::str::FromStr; + use torrust_tracker::shared::bit_torrent::tracker::http::client::requests; use torrust_tracker_primitives::info_hash::InfoHash; + use torrust_tracker_primitives::peer; use torrust_tracker_test_helpers::configuration; use crate::servers::http::asserts::{assert_is_announce_response, assert_torrent_not_in_whitelist_error_response}; use crate::servers::http::client::Client; - use crate::servers::http::requests::announce::QueryBuilder; + use crate::servers::http::v1::contract::PORT; use crate::servers::http::Started; #[tokio::test] @@ -1128,7 +1105,7 @@ mod configured_as_whitelisted { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let response = Client::new(*env.bind_address()) - .announce(&QueryBuilder::default().with_info_hash(&info_hash).query()) + .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) .await; assert_torrent_not_in_whitelist_error_response(response).await; @@ -1148,7 +1125,7 @@ mod configured_as_whitelisted { .expect("should add the torrent to the whitelist"); let response = Client::new(*env.bind_address()) - .announce(&QueryBuilder::default().with_info_hash(&info_hash).query()) + .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) .await; assert_is_announce_response(response).await; @@ -1160,6 +1137,8 @@ mod configured_as_whitelisted { mod receiving_an_scrape_request { use std::str::FromStr; + use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::scrape::File; + use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, responses}; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; use torrust_tracker_primitives::peer::fixture::PeerBuilder; @@ -1167,8 +1146,7 @@ mod configured_as_whitelisted { use crate::servers::http::asserts::assert_scrape_response; use crate::servers::http::client::Client; - use crate::servers::http::responses::scrape::{File, ResponseBuilder}; - use crate::servers::http::{requests, Started}; + use crate::servers::http::Started; #[tokio::test] async fn should_return_the_zeroed_file_when_the_requested_file_is_not_whitelisted() { @@ -1186,14 +1164,10 @@ mod configured_as_whitelisted { .await; let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default().add_file(info_hash.bytes(), File::zeroed()).build(); + let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1221,23 +1195,18 @@ mod configured_as_whitelisted { .expect("should add the torrent to the whitelist"); let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default() - .add_file( - info_hash.bytes(), - File { - complete: 0, - downloaded: 0, - incomplete: 1, - }, - ) - .build(); + let expected_scrape_response = responses::scrape::ResponseBuilder::from(( + info_hash.bytes(), + File { + complete: 0, + downloaded: 0, + incomplete: 1, + }, + )) + .build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1253,12 +1222,14 @@ mod configured_as_private { use std::time::Duration; use torrust_tracker::core::auth::Key; + use torrust_tracker::shared::bit_torrent::tracker::http::client::requests; use torrust_tracker_primitives::info_hash::InfoHash; + use torrust_tracker_primitives::peer; use torrust_tracker_test_helpers::configuration; use crate::servers::http::asserts::{assert_authentication_error_response, assert_is_announce_response}; use crate::servers::http::client::Client; - use crate::servers::http::requests::announce::QueryBuilder; + use crate::servers::http::v1::contract::PORT; use crate::servers::http::Started; #[tokio::test] @@ -1268,7 +1239,7 @@ mod configured_as_private { let expiring_key = env.tracker.generate_auth_key(Duration::from_secs(60)).await.unwrap(); let response = Client::authenticated(*env.bind_address(), expiring_key.key()) - .announce(&QueryBuilder::default().query()) + .announce(&requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build()) .await; assert_is_announce_response(response).await; @@ -1283,7 +1254,7 @@ mod configured_as_private { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let response = Client::new(*env.bind_address()) - .announce(&QueryBuilder::default().with_info_hash(&info_hash).query()) + .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) .await; assert_authentication_error_response(response).await; @@ -1299,7 +1270,7 @@ mod configured_as_private { let response = Client::new(*env.bind_address()) .get(&format!( - "announce/{invalid_key}?info_hash=%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00&peer_addr=2.137.87.41&downloaded=0&uploaded=0&peer_id=-qB00000000000000001&port=17548&left=0&event=completed&compact=0" + "announce/{invalid_key}?info_hash=%81%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00&peer_addr=2.137.87.41&downloaded=0&uploaded=0&peer_id=-qB00000000000000001&port={PORT}&left=0&event=completed&compact=0" )) .await; @@ -1314,7 +1285,7 @@ mod configured_as_private { let unregistered_key = Key::from_str("YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ").unwrap(); let response = Client::authenticated(*env.bind_address(), unregistered_key) - .announce(&QueryBuilder::default().query()) + .announce(&requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build()) .await; assert_authentication_error_response(response).await; @@ -1329,6 +1300,8 @@ mod configured_as_private { use std::time::Duration; use torrust_tracker::core::auth::Key; + use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::scrape::File; + use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, responses}; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; use torrust_tracker_primitives::peer::fixture::PeerBuilder; @@ -1336,8 +1309,7 @@ mod configured_as_private { use crate::servers::http::asserts::{assert_authentication_error_response, assert_scrape_response}; use crate::servers::http::client::Client; - use crate::servers::http::responses::scrape::{File, ResponseBuilder}; - use crate::servers::http::{requests, Started}; + use crate::servers::http::Started; #[tokio::test] async fn should_fail_if_the_key_query_param_cannot_be_parsed() { @@ -1370,14 +1342,10 @@ mod configured_as_private { .await; let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default().add_file(info_hash.bytes(), File::zeroed()).build(); + let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1402,23 +1370,18 @@ mod configured_as_private { let expiring_key = env.tracker.generate_auth_key(Duration::from_secs(60)).await.unwrap(); let response = Client::authenticated(*env.bind_address(), expiring_key.key()) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default() - .add_file( - info_hash.bytes(), - File { - complete: 0, - downloaded: 0, - incomplete: 1, - }, - ) - .build(); + let expected_scrape_response = responses::scrape::ResponseBuilder::from(( + info_hash.bytes(), + File { + complete: 0, + downloaded: 0, + incomplete: 1, + }, + )) + .build(); assert_scrape_response(response, &expected_scrape_response).await; @@ -1446,14 +1409,10 @@ mod configured_as_private { let false_key: Key = "YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ".parse().unwrap(); let response = Client::authenticated(*env.bind_address(), false_key) - .scrape( - &requests::scrape::QueryBuilder::default() - .with_one_info_hash(&info_hash) - .query(), - ) + .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) .await; - let expected_scrape_response = ResponseBuilder::default().add_file(info_hash.bytes(), File::zeroed()).build(); + let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); assert_scrape_response(response, &expected_scrape_response).await;