diff --git a/src/shared/bit_torrent/tracker/http/client/mod.rs b/src/shared/bit_torrent/tracker/http/client/mod.rs index 50612642..a1d6090a 100644 --- a/src/shared/bit_torrent/tracker/http/client/mod.rs +++ b/src/shared/bit_torrent/tracker/http/client/mod.rs @@ -75,7 +75,7 @@ impl Client { /// # Errors /// /// This method fails if the client builder fails. - pub fn authenticated(base_url: Url, key: Key, timeout: Duration) -> Result { + pub fn authenticated(base_url: Url, timeout: Duration, key: Key) -> Result { let client = reqwest::Client::builder() .timeout(timeout) .build() diff --git a/tests/servers/http/client.rs b/tests/servers/http/client.rs deleted file mode 100644 index eeaeddc6..00000000 --- a/tests/servers/http/client.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::net::IpAddr; - -use reqwest::{Client as ReqwestClient, Response}; -use torrust_tracker::core::auth::Key; -use torrust_tracker::shared::bit_torrent::tracker::http::client::requests; - -/// HTTP Tracker Client -pub struct Client { - server_addr: std::net::SocketAddr, - reqwest: ReqwestClient, - key: Option, -} - -/// URL components in this context: -/// -/// ```text -/// http://127.0.0.1:62304/announce/YZ....rJ?info_hash=%9C8B%22%13%E3%0B%FF%21%2B0%C3%60%D2o%9A%02%13d%22 -/// \_____________________/\_______________/ \__________________________________________________________/ -/// | | | -/// base url path query -/// ``` -impl Client { - pub fn new(server_addr: std::net::SocketAddr) -> Self { - Self { - server_addr, - reqwest: reqwest::Client::builder().build().unwrap(), - key: None, - } - } - - /// Creates the new client binding it to an specific local address - pub fn bind(server_addr: std::net::SocketAddr, local_address: IpAddr) -> Self { - Self { - server_addr, - reqwest: reqwest::Client::builder().local_address(local_address).build().unwrap(), - key: None, - } - } - - pub fn authenticated(server_addr: std::net::SocketAddr, key: Key) -> Self { - Self { - server_addr, - reqwest: reqwest::Client::builder().build().unwrap(), - key: Some(key), - } - } - - 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: &requests::Scrape) -> Response { - self.get(&self.build_scrape_path_and_query(query)).await - } - - 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 - } - - pub async fn health_check(&self) -> Response { - self.get(&self.build_path("health_check")).await - } - - pub async fn get(&self, path: &str) -> Response { - self.reqwest.get(self.build_url(path)).send().await.unwrap() - } - - pub async fn get_with_header(&self, path: &str, key: &str, value: &str) -> Response { - self.reqwest - .get(self.build_url(path)) - .header(key, value) - .send() - .await - .unwrap() - } - - 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: &requests::Scrape) -> String { - format!("{}?{query}", self.build_path("scrape")) - } - - fn build_path(&self, path: &str) -> String { - match &self.key { - Some(key) => format!("{path}/{key}"), - None => path.to_string(), - } - } - - fn build_url(&self, path: &str) -> String { - let base_url = self.base_url(); - format!("{base_url}{path}") - } - - fn base_url(&self) -> String { - format!("http://{}/", &self.server_addr) - } -} diff --git a/tests/servers/http/mod.rs b/tests/servers/http/mod.rs index 7e23468b..5a40cfcd 100644 --- a/tests/servers/http/mod.rs +++ b/tests/servers/http/mod.rs @@ -1,10 +1,11 @@ pub mod asserts; -pub mod client; pub mod environment; pub mod v1; +use std::time::Duration; + use torrust_tracker::servers::http::server; pub type Started = environment::Environment; -//pub(crate) const TIMEOUT: Duration = Duration::from_secs(5); +pub(crate) const TIMEOUT: Duration = Duration::from_secs(5); diff --git a/tests/servers/http/v1/contract.rs b/tests/servers/http/v1/contract.rs index d2c2f857..5f71eaf9 100644 --- a/tests/servers/http/v1/contract.rs +++ b/tests/servers/http/v1/contract.rs @@ -9,21 +9,22 @@ 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}; use torrust_tracker_test_helpers::configuration; - use crate::servers::http::client::Client; + use crate::servers::http::v1::create_default_client; use crate::servers::http::Started; #[tokio::test] async fn health_check_endpoint_should_return_ok_if_the_http_tracker_is_running() { let env = Started::new(&configuration::ephemeral_with_reverse_proxy().into()).await; - let response = Client::new(*env.bind_address()).health_check().await; + let response = create_default_client(&env) + .health_check() + .await + .expect("it should get a response"); assert_eq!(response.status(), 200); assert_eq!(response.headers().get("content-type").unwrap(), "application/json"); @@ -35,13 +36,10 @@ 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::v1::contract::PORT; + use crate::servers::http::v1::{create_announce_query, create_client_response, create_default_client}; use crate::servers::http::Started; #[tokio::test] @@ -51,10 +49,10 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_with_reverse_proxy().into()).await; - let query = requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build(); + let query = create_announce_query(1, 1).build(); let params: requests::announce::QueryParams = (&query).into(); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_could_not_find_remote_address_on_x_forwarded_for_header_error_response(response).await; @@ -65,12 +63,13 @@ 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 query = requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build(); + let query = create_announce_query(1, 1).build(); let params: requests::announce::QueryParams = (&query).into(); - let response = Client::new(*env.bind_address()) + let response = create_default_client(&env) .get_with_header(&format!("announce?{params}"), "X-Forwarded-For", "INVALID IP") - .await; + .await + .expect("it should get a response"); assert_could_not_find_remote_address_on_x_forwarded_for_header_error_response(response).await; @@ -97,7 +96,7 @@ 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::shared::bit_torrent::tracker::http::client::responses; use torrust_tracker_primitives::announce_event::AnnounceEvent; use torrust_tracker_primitives::info_hash::InfoHash; use torrust_tracker_primitives::peer; @@ -110,22 +109,12 @@ mod for_all_config_modes { assert_cannot_parse_query_params_error_response, assert_compact_announce_response, assert_empty_announce_response, assert_is_announce_response, assert_missing_query_params_for_announce_request_error_response, }; - use crate::servers::http::client::Client; - use crate::servers::http::v1::contract::PORT; + use crate::servers::http::v1::{ + create_announce_query, create_bonded_client_announce_response, create_client_announce_response, + create_client_response, create_default_announce_prams, create_default_client, + }; 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() { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; @@ -136,11 +125,11 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); params.remove_optional_params(); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_is_announce_response(response).await; @@ -151,7 +140,7 @@ mod for_all_config_modes { async fn should_fail_when_the_url_query_component_is_empty() { let env = Started::new(&configuration::ephemeral().into()).await; - let response = Client::new(*env.bind_address()).get("announce").await; + let response = create_client_response(&env, "announce").await; assert_missing_query_params_for_announce_request_error_response(response).await; @@ -164,9 +153,7 @@ mod for_all_config_modes { let invalid_query_param = "a=b=c"; - let response = Client::new(*env.bind_address()) - .get(&format!("announce?{invalid_query_param}")) - .await; + let response = create_client_response(&env, &format!("announce?{invalid_query_param}")).await; assert_cannot_parse_query_param_error_response(response, "invalid param a=b=c").await; @@ -179,31 +166,31 @@ mod for_all_config_modes { // Without `info_hash` param - let mut params = default_prams(); + let mut params = create_default_announce_prams(); params.info_hash = None; - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "missing param info_hash").await; // Without `peer_id` param - let mut params = default_prams(); + let mut params = create_default_announce_prams(); params.peer_id = None; - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "missing param peer_id").await; // Without `port` param - let mut params = default_prams(); + let mut params = create_default_announce_prams(); params.port = None; - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "missing param port").await; @@ -214,9 +201,9 @@ mod for_all_config_modes { 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 query = create_announce_query(1, 1).with_event(AnnounceEvent::Stopped).build(); - let response = Client::new(*env.bind_address()).announce(&query).await; + let response = create_client_announce_response(&env, &query).await; assert_empty_announce_response(response, &env.tracker.get_announce_policy()).await; @@ -227,12 +214,12 @@ mod for_all_config_modes { async fn should_fail_when_the_info_hash_param_is_invalid() { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = default_prams(); + let mut params = create_default_announce_prams(); for invalid_value in &invalid_info_hashes() { params.set("info_hash", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_cannot_parse_query_params_error_response(response, "").await; } @@ -249,11 +236,11 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral().into()).await; - let mut params = default_prams(); + let mut params = create_default_announce_prams(); params.peer_addr = Some("INVALID-IP-ADDRESS".to_string()); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_is_announce_response(response).await; @@ -264,14 +251,14 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = ["-1", "1.1", "a"]; for invalid_value in invalid_values { params.set("downloaded", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -283,14 +270,14 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = ["-1", "1.1", "a"]; for invalid_value in invalid_values { params.set("uploaded", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -302,7 +289,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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = [ "0", @@ -316,7 +303,7 @@ mod for_all_config_modes { for invalid_value in invalid_values { params.set("peer_id", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -328,14 +315,14 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = ["-1", "1.1", "a"]; for invalid_value in invalid_values { params.set("port", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -347,14 +334,14 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = ["-1", "1.1", "a"]; for invalid_value in invalid_values { params.set("left", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -366,7 +353,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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = [ "0", @@ -381,7 +368,7 @@ mod for_all_config_modes { for invalid_value in invalid_values { params.set("event", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -393,14 +380,14 @@ 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 = default_prams(); + let mut params = create_default_announce_prams(); let invalid_values = ["-1", "1.1", "a"]; for invalid_value in invalid_values { params.set("compact", invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_bad_announce_request_error_response(response, "invalid param value").await; } @@ -412,9 +399,11 @@ 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 announce = with_infohash("9c38422213e30bff212b30c360d26f9a02136422"); + let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); + + let announce = create_announce_query(info_hash, 1).build(); - let response = Client::new(*env.bind_address()).announce(&announce).await; + let response = create_client_announce_response(&env, &announce).await; let expected_response = responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) // the peer for this test @@ -440,10 +429,10 @@ 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(); + let query = create_announce_query(info_hash, peer::Id(*b"-qB00000000000000002")).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(&query).await; + let response = create_client_announce_response(&env, &query).await; let expected_response = responses::announce::ResponseBuilder::new(&env.tracker.get_announce_policy()) // the peer for this test @@ -480,10 +469,10 @@ mod for_all_config_modes { .build(); env.add_torrent_peer(&info_hash, &peer_using_ipv6).await; - let query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000003"), PORT).build(); + let query = create_announce_query(info_hash, peer::Id(*b"-qB00000000000000003")).build(); // Announce the new Peer. - let response = Client::new(*env.bind_address()).announce(&query).await; + let response = create_client_announce_response(&env, &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. @@ -510,9 +499,9 @@ mod for_all_config_modes { // Add a peer env.add_torrent_peer(&info_hash, &peer).await; - let query = requests::announce::QueryBuilder::new(info_hash, peer.peer_id, PORT).build(); + let query = create_announce_query(info_hash, peer.peer_id).build(); - let response = Client::new(*env.bind_address()).announce(&query).await; + let response = create_client_announce_response(&env, &query).await; assert_announce_response( response, @@ -542,12 +531,12 @@ 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) + let query = create_announce_query(info_hash, peer::Id(*b"-qB00000000000000002")) .with_compact() .build(); // Announce the new Peer 2 accepting compact responses - let response = Client::new(*env.bind_address()).announce(&query).await; + let response = create_client_announce_response(&env, &query).await; let expected_response = responses::announce::Compact { complete: 2, @@ -584,9 +573,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 query = requests::announce::QueryBuilder::new(info_hash, peer::Id(*b"-qB00000000000000002"), PORT).build(); + let query = create_announce_query(info_hash, peer::Id(*b"-qB00000000000000002")).build(); - let response = Client::new(*env.bind_address()).announce(&query).await; + let response = create_client_announce_response(&env, &query).await; assert!(!is_a_compact_announce_response(response).await); @@ -603,37 +592,13 @@ mod for_all_config_modes { async fn should_increase_the_number_of_tcp4_connections_handled_in_statistics() { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - Client::new(*env.bind_address()) - .announce(&default_query_builder().build()) - .await; - - let stats = env.tracker.get_stats().await; - - assert_eq!(stats.tcp4_connections_handled, 1); - - drop(stats); - - env.stop().await; - } - - #[tokio::test] - async fn should_increase_the_number_of_tcp6_connections_handled_in_statistics() { - if TcpListener::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)) - .await - .is_err() - { - return; // we cannot bind to a ipv6 socket, so we will skip this test - } - - let env = Started::new(&configuration::ephemeral_ipv6().into()).await; + let query = create_announce_query(1, 1).build(); - Client::bind(*env.bind_address(), IpAddr::from_str("::1").unwrap()) - .announce(&default_query_builder().build()) - .await; + drop(create_client_announce_response(&env, &query).await); let stats = env.tracker.get_stats().await; - assert_eq!(stats.tcp6_connections_handled, 1); + assert_eq!(stats.tcp4_connections_handled, 1); drop(stats); @@ -646,34 +611,15 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - Client::new(*env.bind_address()) - .announce( - &default_query_builder() - .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) - .build(), - ) - .await; - - let stats = env.tracker.get_stats().await; - - assert_eq!(stats.tcp6_connections_handled, 0); - - drop(stats); - - env.stop().await; - } - - #[tokio::test] - async fn should_increase_the_number_of_tcp4_announce_requests_handled_in_statistics() { - let env = Started::new(&configuration::ephemeral_mode_public().into()).await; + let query = create_announce_query(1, 1) + .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) + .build(); - Client::new(*env.bind_address()) - .announce(&default_query_builder().build()) - .await; + drop(create_client_announce_response(&env, &query).await); let stats = env.tracker.get_stats().await; - assert_eq!(stats.tcp4_announces_handled, 1); + assert_eq!(stats.tcp6_connections_handled, 0); drop(stats); @@ -691,9 +637,9 @@ 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(&default_query_builder().build()) - .await; + let query = create_announce_query(1, 1).build(); + + drop(create_client_announce_response(&env, &query).await); let stats = env.tracker.get_stats().await; @@ -710,13 +656,11 @@ mod for_all_config_modes { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - Client::new(*env.bind_address()) - .announce( - &default_query_builder() - .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) - .build(), - ) - .await; + let query = create_announce_query(1, 1) + .with_peer_addr(&IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))) + .build(); + + drop(create_client_announce_response(&env, &query).await); let stats = env.tracker.get_stats().await; @@ -734,15 +678,13 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let client_ip = local_ip().unwrap(); - let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) + let query = create_announce_query(info_hash, 1) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) .build(); { - let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&query).await.status(); - - assert_eq!(status, StatusCode::OK); + let response = create_bonded_client_announce_response(&env, client_ip, &query).await; + assert_eq!(response.status(), StatusCode::OK); } let peers = env.tracker.get_torrent_peers(&info_hash); @@ -769,15 +711,13 @@ mod for_all_config_modes { let loopback_ip = IpAddr::from_str("127.0.0.1").unwrap(); let client_ip = loopback_ip; - let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) + let query = create_announce_query(info_hash, 1) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) .build(); { - let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&query).await.status(); - - assert_eq!(status, StatusCode::OK); + let response = create_bonded_client_announce_response(&env, client_ip, &query).await; + assert_eq!(response.status(), StatusCode::OK); } let peers = env.tracker.get_torrent_peers(&info_hash); @@ -808,15 +748,13 @@ mod for_all_config_modes { let loopback_ip = IpAddr::from_str("127.0.0.1").unwrap(); let client_ip = loopback_ip; - let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT) + let query = create_announce_query(info_hash, 1) .with_peer_addr(&IpAddr::from_str("2.2.2.2").unwrap()) .build(); { - let client = Client::bind(*env.bind_address(), client_ip); - let status = client.announce(&query).await.status(); - - assert_eq!(status, StatusCode::OK); + let response = create_bonded_client_announce_response(&env, client_ip, &query).await; + assert_eq!(response.status(), StatusCode::OK); } let peers = env.tracker.get_torrent_peers(&info_hash); @@ -841,20 +779,19 @@ mod for_all_config_modes { let info_hash: InfoHash = "9c38422213e30bff212b30c360d26f9a02136422".parse().expect("it should parse"); - let query = requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build(); + let query = create_announce_query(info_hash, 1).build(); { - let client = Client::new(*env.bind_address()); - let status = client + let client = create_default_client(&env) .announce_with_header( &query, "X-Forwarded-For", "203.0.113.195,2001:db8:85a3:8d3:1319:8a2e:370:7348,150.172.238.178", ) .await - .status(); + .expect("it should get a response"); - assert_eq!(status, StatusCode::OK); + assert_eq!(client.status(), StatusCode::OK); } let peers = env.tracker.get_torrent_peers(&info_hash); @@ -892,14 +829,16 @@ mod for_all_config_modes { assert_cannot_parse_query_params_error_response, assert_missing_query_params_for_scrape_request_error_response, assert_scrape_response, }; - use crate::servers::http::client::Client; + use crate::servers::http::v1::{ + create_bonded_client_scrape_response, create_client_response, create_client_scrape_response, create_scrape_query, + }; use crate::servers::http::Started; //#[tokio::test] #[allow(dead_code)] async fn should_fail_when_the_request_is_empty() { let env = Started::new(&configuration::ephemeral_mode_public().into()).await; - let response = Client::new(*env.bind_address()).get("scrape").await; + let response = create_client_response(&env, "scrape").await; assert_missing_query_params_for_scrape_request_error_response(response).await; @@ -910,12 +849,14 @@ 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: requests::scrape::QueryParams = requests::scrape::QueryBuilder::default().build().into(); + let query = create_scrape_query::<&InfoHash>(None).build(); + + let mut params = requests::scrape::QueryParams::from(query); for invalid_value in &invalid_info_hashes() { params.set_one_info_hash_param(invalid_value); - let response = Client::new(*env.bind_address()).get(&format!("announce?{params}")).await; + let response = create_client_response(&env, &format!("announce?{params}")).await; assert_cannot_parse_query_params_error_response(response, "").await; } @@ -938,9 +879,9 @@ mod for_all_config_modes { ) .await; - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::default() .add_file(( @@ -973,9 +914,9 @@ mod for_all_config_modes { ) .await; - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::default() .add_file(( @@ -999,9 +940,9 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); @@ -1017,14 +958,12 @@ mod for_all_config_modes { let info_hash1 = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); let info_hash2 = InfoHash::from_str("3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0").unwrap(); - let response = Client::new(*env.bind_address()) - .scrape( - &requests::scrape::QueryBuilder::default() - .add_info_hash(&info_hash1) - .add_info_hash(&info_hash2) - .build(), - ) - .await; + let query = requests::scrape::QueryBuilder::default() + .add_info_hash(&info_hash1) + .add_info_hash(&info_hash2) + .build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::default() .add_file((info_hash1.bytes(), File::zeroed())) @@ -1042,9 +981,9 @@ mod for_all_config_modes { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); - Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + drop(create_client_scrape_response(&env, &query).await); let stats = env.tracker.get_stats().await; @@ -1068,9 +1007,9 @@ 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::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + drop(create_bonded_client_scrape_response(&env, IpAddr::from_str("::1").unwrap(), &query).await); let stats = env.tracker.get_stats().await; @@ -1088,14 +1027,11 @@ 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::v1::contract::PORT; + use crate::servers::http::v1::{create_announce_query, create_client_announce_response}; use crate::servers::http::Started; #[tokio::test] @@ -1104,9 +1040,9 @@ mod configured_as_whitelisted { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); - let response = Client::new(*env.bind_address()) - .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) - .await; + let query = create_announce_query(info_hash, 1).build(); + + let response = create_client_announce_response(&env, &query).await; assert_torrent_not_in_whitelist_error_response(response).await; @@ -1124,9 +1060,9 @@ mod configured_as_whitelisted { .await .expect("should add the torrent to the whitelist"); - let response = Client::new(*env.bind_address()) - .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) - .await; + let query = create_announce_query(info_hash, 1).build(); + + let response = create_client_announce_response(&env, &query).await; assert_is_announce_response(response).await; @@ -1137,15 +1073,15 @@ mod configured_as_whitelisted { mod receiving_an_scrape_request { use std::str::FromStr; + use torrust_tracker::shared::bit_torrent::tracker::http::client::responses; 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; use torrust_tracker_test_helpers::configuration; use crate::servers::http::asserts::assert_scrape_response; - use crate::servers::http::client::Client; + use crate::servers::http::v1::{create_client_scrape_response, create_scrape_query}; use crate::servers::http::Started; #[tokio::test] @@ -1163,9 +1099,9 @@ mod configured_as_whitelisted { ) .await; - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); @@ -1194,9 +1130,9 @@ mod configured_as_whitelisted { .await .expect("should add the torrent to the whitelist"); - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::from(( info_hash.bytes(), @@ -1222,14 +1158,13 @@ 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::v1::contract::PORT; + use crate::servers::http::v1::{ + create_announce_query, create_authenticated_client, create_client_announce_response, create_client_response, + }; use crate::servers::http::Started; #[tokio::test] @@ -1238,9 +1173,12 @@ 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(&requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build()) - .await; + let query = create_announce_query(1, 1).build(); + + let response = create_authenticated_client(&env, expiring_key.key()) + .announce(&query) + .await + .expect("it should get a response"); assert_is_announce_response(response).await; @@ -1253,9 +1191,9 @@ mod configured_as_private { let info_hash = InfoHash::from_str("9c38422213e30bff212b30c360d26f9a02136422").unwrap(); - let response = Client::new(*env.bind_address()) - .announce(&requests::announce::QueryBuilder::new(info_hash, peer::Id::from(1), PORT).build()) - .await; + let query = create_announce_query(info_hash, 1).build(); + + let response = create_client_announce_response(&env, &query).await; assert_authentication_error_response(response).await; @@ -1268,11 +1206,12 @@ mod configured_as_private { let invalid_key = "INVALID_KEY"; - 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={PORT}&left=0&event=completed&compact=0" - )) - .await; + let path = 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={}&left=0&event=completed&compact=0", + crate::servers::http::v1::PORT + ); + + let response = create_client_response(&env, &path).await; assert_authentication_error_response(response).await; } @@ -1284,9 +1223,10 @@ mod configured_as_private { // The tracker does not have this key let unregistered_key = Key::from_str("YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ").unwrap(); - let response = Client::authenticated(*env.bind_address(), unregistered_key) - .announce(&requests::announce::QueryBuilder::new(InfoHash::from(1), peer::Id::from(1), PORT).build()) - .await; + let response = create_authenticated_client(&env, unregistered_key) + .announce(&create_announce_query(1, 1).build()) + .await + .expect("it should get a response"); assert_authentication_error_response(response).await; @@ -1300,15 +1240,17 @@ mod configured_as_private { use std::time::Duration; use torrust_tracker::core::auth::Key; + use torrust_tracker::shared::bit_torrent::tracker::http::client::responses; 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; use torrust_tracker_test_helpers::configuration; use crate::servers::http::asserts::{assert_authentication_error_response, assert_scrape_response}; - use crate::servers::http::client::Client; + use crate::servers::http::v1::{ + create_authenticated_client, create_client_response, create_client_scrape_response, create_scrape_query, + }; use crate::servers::http::Started; #[tokio::test] @@ -1317,11 +1259,9 @@ mod configured_as_private { let invalid_key = "INVALID_KEY"; - let response = Client::new(*env.bind_address()) - .get(&format!( - "scrape/{invalid_key}?info_hash=%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0" - )) - .await; + let path = &format!("scrape/{invalid_key}?info_hash=%3B%24U%04%CF%5F%11%BB%DB%E1%20%1C%EAjk%F4Z%EE%1B%C0"); + + let response = create_client_response(&env, path).await; assert_authentication_error_response(response).await; } @@ -1341,9 +1281,9 @@ mod configured_as_private { ) .await; - let response = Client::new(*env.bind_address()) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_client_scrape_response(&env, &query).await; let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); @@ -1369,9 +1309,12 @@ 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::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_authenticated_client(&env, expiring_key.key()) + .scrape(&query) + .await + .expect("it should get a response"); let expected_scrape_response = responses::scrape::ResponseBuilder::from(( info_hash.bytes(), @@ -1408,9 +1351,12 @@ mod configured_as_private { let false_key: Key = "YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ".parse().unwrap(); - let response = Client::authenticated(*env.bind_address(), false_key) - .scrape(&requests::scrape::QueryBuilder::from(&info_hash).build()) - .await; + let query = create_scrape_query(Some(&info_hash)).build(); + + let response = create_authenticated_client(&env, false_key) + .scrape(&query) + .await + .expect("it should get a response"); let expected_scrape_response = responses::scrape::ResponseBuilder::from((info_hash.bytes(), File::zeroed())).build(); diff --git a/tests/servers/http/v1/mod.rs b/tests/servers/http/v1/mod.rs index 2943dbb5..f70f72f5 100644 --- a/tests/servers/http/v1/mod.rs +++ b/tests/servers/http/v1/mod.rs @@ -1 +1,97 @@ +use std::net::IpAddr; + +use reqwest::Response; +use torrust_tracker::core::auth::Key; +use torrust_tracker::servers::http::server::Running; +use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, Client}; +use torrust_tracker_primitives::info_hash::InfoHash; +use torrust_tracker_primitives::peer; + +use super::environment::Environment; +use super::TIMEOUT; + pub mod contract; + +pub(crate) const PORT: u16 = 17548; + +pub(crate) fn create_default_client(env: &Environment) -> Client { + let url: url::Url = format!("http://{}/", &env.bind_address()) + .parse() + .expect("it should make a valid url"); + Client::new(url, TIMEOUT).expect("it should make a client") +} + +pub(crate) fn create_bonded_client(env: &Environment, local_address: IpAddr) -> Client { + let url: url::Url = format!("http://{}/", &env.bind_address()) + .parse() + .expect("it should make a valid url"); + Client::bind(url, TIMEOUT, local_address).expect("it should make a client") +} + +pub(crate) fn create_authenticated_client(env: &Environment, key: Key) -> Client { + let url: url::Url = format!("http://{}/", &env.bind_address()) + .parse() + .expect("it should make a valid url"); + Client::authenticated(url, TIMEOUT, key).expect("it should make a client") +} + +pub(crate) async fn create_client_response(env: &Environment, path: &str) -> Response { + create_default_client(env).get(path).await.expect("it should get a response") +} + +pub(crate) async fn create_client_announce_response(env: &Environment, query: &requests::Announce) -> Response { + create_default_client(env) + .announce(query) + .await + .expect("it should get a response") +} + +pub(crate) async fn create_client_scrape_response(env: &Environment, query: &requests::Scrape) -> Response { + create_default_client(env) + .scrape(query) + .await + .expect("it should get a response") +} + +pub(crate) async fn create_bonded_client_announce_response( + env: &Environment, + local_address: IpAddr, + query: &requests::Announce, +) -> Response { + create_bonded_client(env, local_address) + .announce(query) + .await + .expect("it should get a response") +} + +pub(crate) async fn create_bonded_client_scrape_response( + env: &Environment, + local_address: IpAddr, + query: &requests::Scrape, +) -> Response { + create_bonded_client(env, local_address) + .scrape(query) + .await + .expect("it should get a response") +} + +pub(crate) fn create_announce_query, P: Into>( + info_hash: I, + peer_id: P, +) -> requests::announce::QueryBuilder { + requests::announce::QueryBuilder::new(info_hash.into(), peer_id.into(), PORT) +} + +pub(crate) fn create_scrape_query>( + info_hash: Option, +) -> requests::scrape::QueryBuilder { + if let Some(info_hash) = info_hash { + info_hash.into() + } else { + requests::scrape::QueryBuilder::default() + } +} + +pub(crate) fn create_default_announce_prams() -> requests::announce::QueryParams { + (&create_announce_query(1, 1).build()).into() +}