1use std::fmt;
3use std::net::{IpAddr, SocketAddr};
4use std::panic::Location;
5use std::sync::Arc;
6use std::time::Instant;
7
8use aquatic_udp_protocol::{
9 AnnounceInterval, AnnounceRequest, AnnounceResponse, AnnounceResponseFixedData, ConnectRequest, ConnectResponse,
10 ErrorResponse, Ipv4AddrBytes, Ipv6AddrBytes, NumberOfDownloads, NumberOfPeers, Port, Request, Response, ResponsePeer,
11 ScrapeRequest, ScrapeResponse, TorrentScrapeStatistics, TransactionId,
12};
13use torrust_tracker_located_error::DynError;
14use torrust_tracker_primitives::info_hash::InfoHash;
15use tracing::{instrument, Level};
16use uuid::Uuid;
17use zerocopy::network_endian::I32;
18
19use super::connection_cookie::{check, from_connection_id, into_connection_id, make};
20use super::RawRequest;
21use crate::core::{statistics, PeersWanted, ScrapeData, Tracker};
22use crate::servers::udp::error::Error;
23use crate::servers::udp::logging::{log_bad_request, log_error_response, log_request, log_response};
24use crate::servers::udp::peer_builder;
25use crate::shared::bit_torrent::common::MAX_SCRAPE_TORRENTS;
26
27#[instrument(skip(udp_request, tracker, local_addr), ret(level = Level::TRACE))]
36pub(crate) async fn handle_packet(udp_request: RawRequest, tracker: &Tracker, local_addr: SocketAddr) -> Response {
37 tracing::debug!("Handling Packets: {udp_request:?}");
38
39 let start_time = Instant::now();
40
41 let request_id = RequestId::make(&udp_request);
42
43 match Request::parse_bytes(&udp_request.payload[..udp_request.payload.len()], MAX_SCRAPE_TORRENTS).map_err(|e| {
44 Error::InternalServer {
45 message: format!("{e:?}"),
46 location: Location::caller(),
47 }
48 }) {
49 Ok(request) => {
50 log_request(&request, &request_id, &local_addr);
51
52 let transaction_id = match &request {
53 Request::Connect(connect_request) => connect_request.transaction_id,
54 Request::Announce(announce_request) => announce_request.transaction_id,
55 Request::Scrape(scrape_request) => scrape_request.transaction_id,
56 };
57
58 let response = match handle_request(request, udp_request.from, tracker).await {
59 Ok(response) => response,
60 Err(e) => handle_error(&e, transaction_id),
61 };
62
63 let latency = start_time.elapsed();
64
65 log_response(&response, &transaction_id, &request_id, &local_addr, latency);
66
67 response
68 }
69 Err(e) => {
70 log_bad_request(&request_id);
71
72 let response = handle_error(
73 &Error::BadRequest {
74 source: (Arc::new(e) as DynError).into(),
75 },
76 TransactionId(I32::new(0)),
77 );
78
79 log_error_response(&request_id);
80
81 response
82 }
83 }
84}
85
86#[instrument(skip(request, remote_addr, tracker))]
92pub async fn handle_request(request: Request, remote_addr: SocketAddr, tracker: &Tracker) -> Result<Response, Error> {
93 tracing::trace!("handle request");
94
95 match request {
96 Request::Connect(connect_request) => handle_connect(remote_addr, &connect_request, tracker).await,
97 Request::Announce(announce_request) => handle_announce(remote_addr, &announce_request, tracker).await,
98 Request::Scrape(scrape_request) => handle_scrape(remote_addr, &scrape_request, tracker).await,
99 }
100}
101
102#[instrument(skip(tracker), err, ret(level = Level::TRACE))]
109pub async fn handle_connect(remote_addr: SocketAddr, request: &ConnectRequest, tracker: &Tracker) -> Result<Response, Error> {
110 tracing::trace!("handle connect");
111
112 let connection_cookie = make(&remote_addr);
113 let connection_id = into_connection_id(&connection_cookie);
114
115 let response = ConnectResponse {
116 transaction_id: request.transaction_id,
117 connection_id,
118 };
119
120 match remote_addr {
122 SocketAddr::V4(_) => {
123 tracker.send_stats_event(statistics::Event::Udp4Connect).await;
124 }
125 SocketAddr::V6(_) => {
126 tracker.send_stats_event(statistics::Event::Udp6Connect).await;
127 }
128 }
129
130 Ok(Response::from(response))
131}
132
133#[instrument(skip(tracker), err, ret(level = Level::TRACE))]
140pub async fn handle_announce(
141 remote_addr: SocketAddr,
142 announce_request: &AnnounceRequest,
143 tracker: &Tracker,
144) -> Result<Response, Error> {
145 tracing::trace!("handle announce");
146
147 if tracker.requires_authentication() {
149 return Err(Error::TrackerAuthenticationRequired {
150 location: Location::caller(),
151 });
152 }
153
154 check(&remote_addr, &from_connection_id(&announce_request.connection_id))?;
155
156 let info_hash = announce_request.info_hash.into();
157 let remote_client_ip = remote_addr.ip();
158
159 tracker.authorize(&info_hash).await.map_err(|e| Error::TrackerError {
161 source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
162 })?;
163
164 let mut peer = peer_builder::from_request(announce_request, &remote_client_ip);
165 let peers_wanted: PeersWanted = i32::from(announce_request.peers_wanted.0).into();
166
167 let response = tracker.announce(&info_hash, &mut peer, &remote_client_ip, &peers_wanted);
168
169 match remote_client_ip {
170 IpAddr::V4(_) => {
171 tracker.send_stats_event(statistics::Event::Udp4Announce).await;
172 }
173 IpAddr::V6(_) => {
174 tracker.send_stats_event(statistics::Event::Udp6Announce).await;
175 }
176 }
177
178 #[allow(clippy::cast_possible_truncation)]
179 if remote_addr.is_ipv4() {
180 let announce_response = AnnounceResponse {
181 fixed: AnnounceResponseFixedData {
182 transaction_id: announce_request.transaction_id,
183 announce_interval: AnnounceInterval(I32::new(i64::from(tracker.get_announce_policy().interval) as i32)),
184 leechers: NumberOfPeers(I32::new(i64::from(response.stats.incomplete) as i32)),
185 seeders: NumberOfPeers(I32::new(i64::from(response.stats.complete) as i32)),
186 },
187 peers: response
188 .peers
189 .iter()
190 .filter_map(|peer| {
191 if let IpAddr::V4(ip) = peer.peer_addr.ip() {
192 Some(ResponsePeer::<Ipv4AddrBytes> {
193 ip_address: ip.into(),
194 port: Port(peer.peer_addr.port().into()),
195 })
196 } else {
197 None
198 }
199 })
200 .collect(),
201 };
202
203 Ok(Response::from(announce_response))
204 } else {
205 let announce_response = AnnounceResponse {
206 fixed: AnnounceResponseFixedData {
207 transaction_id: announce_request.transaction_id,
208 announce_interval: AnnounceInterval(I32::new(i64::from(tracker.get_announce_policy().interval) as i32)),
209 leechers: NumberOfPeers(I32::new(i64::from(response.stats.incomplete) as i32)),
210 seeders: NumberOfPeers(I32::new(i64::from(response.stats.complete) as i32)),
211 },
212 peers: response
213 .peers
214 .iter()
215 .filter_map(|peer| {
216 if let IpAddr::V6(ip) = peer.peer_addr.ip() {
217 Some(ResponsePeer::<Ipv6AddrBytes> {
218 ip_address: ip.into(),
219 port: Port(peer.peer_addr.port().into()),
220 })
221 } else {
222 None
223 }
224 })
225 .collect(),
226 };
227
228 Ok(Response::from(announce_response))
229 }
230}
231
232#[instrument(skip(tracker), err, ret(level = Level::TRACE))]
239pub async fn handle_scrape(remote_addr: SocketAddr, request: &ScrapeRequest, tracker: &Tracker) -> Result<Response, Error> {
240 tracing::trace!("handle scrape");
241
242 let mut info_hashes: Vec<InfoHash> = vec![];
244 for info_hash in &request.info_hashes {
245 info_hashes.push((*info_hash).into());
246 }
247
248 let scrape_data = if tracker.requires_authentication() {
249 ScrapeData::zeroed(&info_hashes)
250 } else {
251 tracker.scrape(&info_hashes).await
252 };
253
254 let mut torrent_stats: Vec<TorrentScrapeStatistics> = Vec::new();
255
256 for file in &scrape_data.files {
257 let swarm_metadata = file.1;
258
259 #[allow(clippy::cast_possible_truncation)]
260 let scrape_entry = {
261 TorrentScrapeStatistics {
262 seeders: NumberOfPeers(I32::new(i64::from(swarm_metadata.complete) as i32)),
263 completed: NumberOfDownloads(I32::new(i64::from(swarm_metadata.downloaded) as i32)),
264 leechers: NumberOfPeers(I32::new(i64::from(swarm_metadata.incomplete) as i32)),
265 }
266 };
267
268 torrent_stats.push(scrape_entry);
269 }
270
271 match remote_addr {
273 SocketAddr::V4(_) => {
274 tracker.send_stats_event(statistics::Event::Udp4Scrape).await;
275 }
276 SocketAddr::V6(_) => {
277 tracker.send_stats_event(statistics::Event::Udp6Scrape).await;
278 }
279 }
280
281 let response = ScrapeResponse {
282 transaction_id: request.transaction_id,
283 torrent_stats,
284 };
285
286 Ok(Response::from(response))
287}
288
289fn handle_error(e: &Error, transaction_id: TransactionId) -> Response {
290 let message = e.to_string();
291 Response::from(ErrorResponse {
292 transaction_id,
293 message: message.into(),
294 })
295}
296
297#[derive(Debug, Clone)]
299pub struct RequestId(Uuid);
300
301impl RequestId {
302 fn make(_request: &RawRequest) -> RequestId {
303 RequestId(Uuid::new_v4())
304 }
305}
306
307impl fmt::Display for RequestId {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 write!(f, "{}", self.0)
310 }
311}
312
313#[cfg(test)]
314mod tests {
315
316 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
317 use std::sync::Arc;
318
319 use aquatic_udp_protocol::{NumberOfBytes, PeerId};
320 use torrust_tracker_clock::clock::Time;
321 use torrust_tracker_configuration::Configuration;
322 use torrust_tracker_primitives::peer;
323 use torrust_tracker_test_helpers::configuration;
324
325 use crate::core::services::tracker_factory;
326 use crate::core::Tracker;
327 use crate::CurrentClock;
328
329 fn tracker_configuration() -> Configuration {
330 default_testing_tracker_configuration()
331 }
332
333 fn default_testing_tracker_configuration() -> Configuration {
334 configuration::ephemeral()
335 }
336
337 fn public_tracker() -> Arc<Tracker> {
338 initialized_tracker(&configuration::ephemeral_public())
339 }
340
341 fn private_tracker() -> Arc<Tracker> {
342 initialized_tracker(&configuration::ephemeral_private())
343 }
344
345 fn whitelisted_tracker() -> Arc<Tracker> {
346 initialized_tracker(&configuration::ephemeral_listed())
347 }
348
349 fn initialized_tracker(configuration: &Configuration) -> Arc<Tracker> {
350 tracker_factory(configuration).into()
351 }
352
353 fn sample_ipv4_remote_addr() -> SocketAddr {
354 sample_ipv4_socket_address()
355 }
356
357 fn sample_ipv6_remote_addr() -> SocketAddr {
358 sample_ipv6_socket_address()
359 }
360
361 fn sample_ipv4_socket_address() -> SocketAddr {
362 SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080)
363 }
364
365 fn sample_ipv6_socket_address() -> SocketAddr {
366 SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080)
367 }
368
369 #[derive(Debug, Default)]
370 pub struct TorrentPeerBuilder {
371 peer: peer::Peer,
372 }
373
374 impl TorrentPeerBuilder {
375 #[must_use]
376 pub fn new() -> Self {
377 Self {
378 peer: peer::Peer {
379 updated: CurrentClock::now(),
380 ..Default::default()
381 },
382 }
383 }
384
385 #[must_use]
386 pub fn with_peer_address(mut self, peer_addr: SocketAddr) -> Self {
387 self.peer.peer_addr = peer_addr;
388 self
389 }
390
391 #[must_use]
392 pub fn with_peer_id(mut self, peer_id: PeerId) -> Self {
393 self.peer.peer_id = peer_id;
394 self
395 }
396
397 #[must_use]
398 pub fn with_number_of_bytes_left(mut self, left: i64) -> Self {
399 self.peer.left = NumberOfBytes::new(left);
400 self
401 }
402
403 #[must_use]
404 pub fn into(self) -> peer::Peer {
405 self.peer
406 }
407 }
408
409 struct TrackerConfigurationBuilder {
410 configuration: Configuration,
411 }
412
413 impl TrackerConfigurationBuilder {
414 pub fn default() -> TrackerConfigurationBuilder {
415 let default_configuration = default_testing_tracker_configuration();
416 TrackerConfigurationBuilder {
417 configuration: default_configuration,
418 }
419 }
420
421 pub fn with_external_ip(mut self, external_ip: &str) -> Self {
422 self.configuration.core.net.external_ip = Some(external_ip.to_owned().parse().expect("valid IP address"));
423 self
424 }
425
426 pub fn into(self) -> Configuration {
427 self.configuration
428 }
429 }
430
431 mod connect_request {
432
433 use std::future;
434 use std::sync::Arc;
435
436 use aquatic_udp_protocol::{ConnectRequest, ConnectResponse, Response, TransactionId};
437 use mockall::predicate::eq;
438
439 use super::{sample_ipv4_socket_address, sample_ipv6_remote_addr, tracker_configuration};
440 use crate::core::{self, statistics};
441 use crate::servers::udp::connection_cookie::{into_connection_id, make};
442 use crate::servers::udp::handlers::handle_connect;
443 use crate::servers::udp::handlers::tests::{public_tracker, sample_ipv4_remote_addr};
444
445 fn sample_connect_request() -> ConnectRequest {
446 ConnectRequest {
447 transaction_id: TransactionId(0i32.into()),
448 }
449 }
450
451 #[tokio::test]
452 async fn a_connect_response_should_contain_the_same_transaction_id_as_the_connect_request() {
453 let request = ConnectRequest {
454 transaction_id: TransactionId(0i32.into()),
455 };
456
457 let response = handle_connect(sample_ipv4_remote_addr(), &request, &public_tracker())
458 .await
459 .unwrap();
460
461 assert_eq!(
462 response,
463 Response::Connect(ConnectResponse {
464 connection_id: into_connection_id(&make(&sample_ipv4_remote_addr())),
465 transaction_id: request.transaction_id
466 })
467 );
468 }
469
470 #[tokio::test]
471 async fn a_connect_response_should_contain_a_new_connection_id() {
472 let request = ConnectRequest {
473 transaction_id: TransactionId(0i32.into()),
474 };
475
476 let response = handle_connect(sample_ipv4_remote_addr(), &request, &public_tracker())
477 .await
478 .unwrap();
479
480 assert_eq!(
481 response,
482 Response::Connect(ConnectResponse {
483 connection_id: into_connection_id(&make(&sample_ipv4_remote_addr())),
484 transaction_id: request.transaction_id
485 })
486 );
487 }
488
489 #[tokio::test]
490 async fn it_should_send_the_upd4_connect_event_when_a_client_tries_to_connect_using_a_ip4_socket_address() {
491 let mut stats_event_sender_mock = statistics::MockEventSender::new();
492 stats_event_sender_mock
493 .expect_send_event()
494 .with(eq(statistics::Event::Udp4Connect))
495 .times(1)
496 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
497 let stats_event_sender = Box::new(stats_event_sender_mock);
498
499 let client_socket_address = sample_ipv4_socket_address();
500
501 let torrent_tracker = Arc::new(
502 core::Tracker::new(
503 &tracker_configuration().core,
504 Some(stats_event_sender),
505 statistics::Repo::new(),
506 )
507 .unwrap(),
508 );
509 handle_connect(client_socket_address, &sample_connect_request(), &torrent_tracker)
510 .await
511 .unwrap();
512 }
513
514 #[tokio::test]
515 async fn it_should_send_the_upd6_connect_event_when_a_client_tries_to_connect_using_a_ip6_socket_address() {
516 let mut stats_event_sender_mock = statistics::MockEventSender::new();
517 stats_event_sender_mock
518 .expect_send_event()
519 .with(eq(statistics::Event::Udp6Connect))
520 .times(1)
521 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
522 let stats_event_sender = Box::new(stats_event_sender_mock);
523
524 let torrent_tracker = Arc::new(
525 core::Tracker::new(
526 &tracker_configuration().core,
527 Some(stats_event_sender),
528 statistics::Repo::new(),
529 )
530 .unwrap(),
531 );
532 handle_connect(sample_ipv6_remote_addr(), &sample_connect_request(), &torrent_tracker)
533 .await
534 .unwrap();
535 }
536 }
537
538 mod announce_request {
539
540 use std::net::Ipv4Addr;
541 use std::num::NonZeroU16;
542
543 use aquatic_udp_protocol::{
544 AnnounceActionPlaceholder, AnnounceEvent, AnnounceRequest, ConnectionId, NumberOfBytes, NumberOfPeers,
545 PeerId as AquaticPeerId, PeerKey, Port, TransactionId,
546 };
547
548 use crate::servers::udp::connection_cookie::{into_connection_id, make};
549 use crate::servers::udp::handlers::tests::sample_ipv4_remote_addr;
550
551 struct AnnounceRequestBuilder {
552 request: AnnounceRequest,
553 }
554
555 impl AnnounceRequestBuilder {
556 pub fn default() -> AnnounceRequestBuilder {
557 let client_ip = Ipv4Addr::new(126, 0, 0, 1);
558 let client_port = 8080;
559 let info_hash_aquatic = aquatic_udp_protocol::InfoHash([0u8; 20]);
560
561 let default_request = AnnounceRequest {
562 connection_id: into_connection_id(&make(&sample_ipv4_remote_addr())),
563 action_placeholder: AnnounceActionPlaceholder::default(),
564 transaction_id: TransactionId(0i32.into()),
565 info_hash: info_hash_aquatic,
566 peer_id: AquaticPeerId([255u8; 20]),
567 bytes_downloaded: NumberOfBytes(0i64.into()),
568 bytes_uploaded: NumberOfBytes(0i64.into()),
569 bytes_left: NumberOfBytes(0i64.into()),
570 event: AnnounceEvent::Started.into(),
571 ip_address: client_ip.into(),
572 key: PeerKey::new(0i32),
573 peers_wanted: NumberOfPeers::new(1i32),
574 port: Port::new(NonZeroU16::new(client_port).expect("a non-zero client port")),
575 };
576 AnnounceRequestBuilder {
577 request: default_request,
578 }
579 }
580
581 pub fn with_connection_id(mut self, connection_id: ConnectionId) -> Self {
582 self.request.connection_id = connection_id;
583 self
584 }
585
586 pub fn with_info_hash(mut self, info_hash: aquatic_udp_protocol::InfoHash) -> Self {
587 self.request.info_hash = info_hash;
588 self
589 }
590
591 pub fn with_peer_id(mut self, peer_id: AquaticPeerId) -> Self {
592 self.request.peer_id = peer_id;
593 self
594 }
595
596 pub fn with_ip_address(mut self, ip_address: Ipv4Addr) -> Self {
597 self.request.ip_address = ip_address.into();
598 self
599 }
600
601 pub fn with_port(mut self, port: u16) -> Self {
602 self.request.port = Port(port.into());
603 self
604 }
605
606 pub fn into(self) -> AnnounceRequest {
607 self.request
608 }
609 }
610
611 mod using_ipv4 {
612
613 use std::future;
614 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
615 use std::sync::Arc;
616
617 use aquatic_udp_protocol::{
618 AnnounceInterval, AnnounceResponse, InfoHash as AquaticInfoHash, Ipv4AddrBytes, Ipv6AddrBytes, NumberOfPeers,
619 PeerId as AquaticPeerId, Response, ResponsePeer,
620 };
621 use mockall::predicate::eq;
622
623 use crate::core::{self, statistics};
624 use crate::servers::udp::connection_cookie::{into_connection_id, make};
625 use crate::servers::udp::handlers::tests::announce_request::AnnounceRequestBuilder;
626 use crate::servers::udp::handlers::tests::{
627 public_tracker, sample_ipv4_socket_address, tracker_configuration, TorrentPeerBuilder,
628 };
629 use crate::servers::udp::handlers::{handle_announce, AnnounceResponseFixedData};
630
631 #[tokio::test]
632 async fn an_announced_peer_should_be_added_to_the_tracker() {
633 let tracker = public_tracker();
634
635 let client_ip = Ipv4Addr::new(126, 0, 0, 1);
636 let client_port = 8080;
637 let info_hash = AquaticInfoHash([0u8; 20]);
638 let peer_id = AquaticPeerId([255u8; 20]);
639
640 let remote_addr = SocketAddr::new(IpAddr::V4(client_ip), client_port);
641
642 let request = AnnounceRequestBuilder::default()
643 .with_connection_id(into_connection_id(&make(&remote_addr)))
644 .with_info_hash(info_hash)
645 .with_peer_id(peer_id)
646 .with_ip_address(client_ip)
647 .with_port(client_port)
648 .into();
649
650 handle_announce(remote_addr, &request, &tracker).await.unwrap();
651
652 let peers = tracker.get_torrent_peers(&info_hash.0.into());
653
654 let expected_peer = TorrentPeerBuilder::new()
655 .with_peer_id(peer_id)
656 .with_peer_address(SocketAddr::new(IpAddr::V4(client_ip), client_port))
657 .into();
658
659 assert_eq!(peers[0], Arc::new(expected_peer));
660 }
661
662 #[tokio::test]
663 async fn the_announced_peer_should_not_be_included_in_the_response() {
664 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080);
665
666 let request = AnnounceRequestBuilder::default()
667 .with_connection_id(into_connection_id(&make(&remote_addr)))
668 .into();
669
670 let response = handle_announce(remote_addr, &request, &public_tracker()).await.unwrap();
671
672 let empty_peer_vector: Vec<ResponsePeer<Ipv4AddrBytes>> = vec![];
673 assert_eq!(
674 response,
675 Response::from(AnnounceResponse {
676 fixed: AnnounceResponseFixedData {
677 transaction_id: request.transaction_id,
678 announce_interval: AnnounceInterval(120i32.into()),
679 leechers: NumberOfPeers(0i32.into()),
680 seeders: NumberOfPeers(1i32.into()),
681 },
682 peers: empty_peer_vector
683 })
684 );
685 }
686
687 #[tokio::test]
688 async fn the_tracker_should_always_use_the_remote_client_ip_but_not_the_port_in_the_udp_request_header_instead_of_the_peer_address_in_the_announce_request(
689 ) {
690 let tracker = public_tracker();
694
695 let info_hash = AquaticInfoHash([0u8; 20]);
696 let peer_id = AquaticPeerId([255u8; 20]);
697 let client_port = 8080;
698
699 let remote_client_ip = Ipv4Addr::new(126, 0, 0, 1);
700 let remote_client_port = 8081;
701 let peer_address = Ipv4Addr::new(126, 0, 0, 2);
702
703 let remote_addr = SocketAddr::new(IpAddr::V4(remote_client_ip), remote_client_port);
704
705 let request = AnnounceRequestBuilder::default()
706 .with_connection_id(into_connection_id(&make(&remote_addr)))
707 .with_info_hash(info_hash)
708 .with_peer_id(peer_id)
709 .with_ip_address(peer_address)
710 .with_port(client_port)
711 .into();
712
713 handle_announce(remote_addr, &request, &tracker).await.unwrap();
714
715 let peers = tracker.get_torrent_peers(&info_hash.0.into());
716
717 assert_eq!(peers[0].peer_addr, SocketAddr::new(IpAddr::V4(remote_client_ip), client_port));
718 }
719
720 fn add_a_torrent_peer_using_ipv6(tracker: &Arc<core::Tracker>) {
721 let info_hash = AquaticInfoHash([0u8; 20]);
722
723 let client_ip_v4 = Ipv4Addr::new(126, 0, 0, 1);
724 let client_ip_v6 = client_ip_v4.to_ipv6_compatible();
725 let client_port = 8080;
726 let peer_id = AquaticPeerId([255u8; 20]);
727
728 let peer_using_ipv6 = TorrentPeerBuilder::new()
729 .with_peer_id(peer_id)
730 .with_peer_address(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port))
731 .into();
732
733 tracker.upsert_peer_and_get_stats(&info_hash.0.into(), &peer_using_ipv6);
734 }
735
736 async fn announce_a_new_peer_using_ipv4(tracker: Arc<core::Tracker>) -> Response {
737 let remote_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8080);
738 let request = AnnounceRequestBuilder::default()
739 .with_connection_id(into_connection_id(&make(&remote_addr)))
740 .into();
741
742 handle_announce(remote_addr, &request, &tracker).await.unwrap()
743 }
744
745 #[tokio::test]
746 async fn when_the_announce_request_comes_from_a_client_using_ipv4_the_response_should_not_include_peers_using_ipv6() {
747 let tracker = public_tracker();
748
749 add_a_torrent_peer_using_ipv6(&tracker);
750
751 let response = announce_a_new_peer_using_ipv4(tracker.clone()).await;
752
753 let peers: Option<Vec<ResponsePeer<Ipv6AddrBytes>>> = match response {
755 Response::AnnounceIpv6(announce_response) => Some(announce_response.peers),
756 _ => None,
757 };
758 let no_ipv6_peers = peers.is_none();
759 assert!(no_ipv6_peers);
760 }
761
762 #[tokio::test]
763 async fn should_send_the_upd4_announce_event() {
764 let mut stats_event_sender_mock = statistics::MockEventSender::new();
765 stats_event_sender_mock
766 .expect_send_event()
767 .with(eq(statistics::Event::Udp4Announce))
768 .times(1)
769 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
770 let stats_event_sender = Box::new(stats_event_sender_mock);
771
772 let tracker = Arc::new(
773 core::Tracker::new(
774 &tracker_configuration().core,
775 Some(stats_event_sender),
776 statistics::Repo::new(),
777 )
778 .unwrap(),
779 );
780
781 handle_announce(
782 sample_ipv4_socket_address(),
783 &AnnounceRequestBuilder::default().into(),
784 &tracker,
785 )
786 .await
787 .unwrap();
788 }
789
790 mod from_a_loopback_ip {
791 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
792 use std::sync::Arc;
793
794 use aquatic_udp_protocol::{InfoHash as AquaticInfoHash, PeerId as AquaticPeerId};
795
796 use crate::servers::udp::connection_cookie::{into_connection_id, make};
797 use crate::servers::udp::handlers::handle_announce;
798 use crate::servers::udp::handlers::tests::announce_request::AnnounceRequestBuilder;
799 use crate::servers::udp::handlers::tests::{public_tracker, TorrentPeerBuilder};
800
801 #[tokio::test]
802 async fn the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration_if_defined() {
803 let tracker = public_tracker();
804
805 let client_ip = Ipv4Addr::new(127, 0, 0, 1);
806 let client_port = 8080;
807 let info_hash = AquaticInfoHash([0u8; 20]);
808 let peer_id = AquaticPeerId([255u8; 20]);
809
810 let remote_addr = SocketAddr::new(IpAddr::V4(client_ip), client_port);
811
812 let request = AnnounceRequestBuilder::default()
813 .with_connection_id(into_connection_id(&make(&remote_addr)))
814 .with_info_hash(info_hash)
815 .with_peer_id(peer_id)
816 .with_ip_address(client_ip)
817 .with_port(client_port)
818 .into();
819
820 handle_announce(remote_addr, &request, &tracker).await.unwrap();
821
822 let peers = tracker.get_torrent_peers(&info_hash.0.into());
823
824 let external_ip_in_tracker_configuration = tracker.get_maybe_external_ip().unwrap();
825
826 let expected_peer = TorrentPeerBuilder::new()
827 .with_peer_id(peer_id)
828 .with_peer_address(SocketAddr::new(external_ip_in_tracker_configuration, client_port))
829 .into();
830
831 assert_eq!(peers[0], Arc::new(expected_peer));
832 }
833 }
834 }
835
836 mod using_ipv6 {
837
838 use std::future;
839 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
840 use std::sync::Arc;
841
842 use aquatic_udp_protocol::{
843 AnnounceInterval, AnnounceResponse, InfoHash as AquaticInfoHash, Ipv4AddrBytes, Ipv6AddrBytes, NumberOfPeers,
844 PeerId as AquaticPeerId, Response, ResponsePeer,
845 };
846 use mockall::predicate::eq;
847
848 use crate::core::{self, statistics};
849 use crate::servers::udp::connection_cookie::{into_connection_id, make};
850 use crate::servers::udp::handlers::tests::announce_request::AnnounceRequestBuilder;
851 use crate::servers::udp::handlers::tests::{
852 public_tracker, sample_ipv6_remote_addr, tracker_configuration, TorrentPeerBuilder,
853 };
854 use crate::servers::udp::handlers::{handle_announce, AnnounceResponseFixedData};
855
856 #[tokio::test]
857 async fn an_announced_peer_should_be_added_to_the_tracker() {
858 let tracker = public_tracker();
859
860 let client_ip_v4 = Ipv4Addr::new(126, 0, 0, 1);
861 let client_ip_v6 = client_ip_v4.to_ipv6_compatible();
862 let client_port = 8080;
863 let info_hash = AquaticInfoHash([0u8; 20]);
864 let peer_id = AquaticPeerId([255u8; 20]);
865
866 let remote_addr = SocketAddr::new(IpAddr::V6(client_ip_v6), client_port);
867
868 let request = AnnounceRequestBuilder::default()
869 .with_connection_id(into_connection_id(&make(&remote_addr)))
870 .with_info_hash(info_hash)
871 .with_peer_id(peer_id)
872 .with_ip_address(client_ip_v4)
873 .with_port(client_port)
874 .into();
875
876 handle_announce(remote_addr, &request, &tracker).await.unwrap();
877
878 let peers = tracker.get_torrent_peers(&info_hash.0.into());
879
880 let expected_peer = TorrentPeerBuilder::new()
881 .with_peer_id(peer_id)
882 .with_peer_address(SocketAddr::new(IpAddr::V6(client_ip_v6), client_port))
883 .into();
884
885 assert_eq!(peers[0], Arc::new(expected_peer));
886 }
887
888 #[tokio::test]
889 async fn the_announced_peer_should_not_be_included_in_the_response() {
890 let client_ip_v4 = Ipv4Addr::new(126, 0, 0, 1);
891 let client_ip_v6 = client_ip_v4.to_ipv6_compatible();
892
893 let remote_addr = SocketAddr::new(IpAddr::V6(client_ip_v6), 8080);
894
895 let request = AnnounceRequestBuilder::default()
896 .with_connection_id(into_connection_id(&make(&remote_addr)))
897 .into();
898
899 let response = handle_announce(remote_addr, &request, &public_tracker()).await.unwrap();
900
901 let empty_peer_vector: Vec<ResponsePeer<Ipv6AddrBytes>> = vec![];
902 assert_eq!(
903 response,
904 Response::from(AnnounceResponse {
905 fixed: AnnounceResponseFixedData {
906 transaction_id: request.transaction_id,
907 announce_interval: AnnounceInterval(120i32.into()),
908 leechers: NumberOfPeers(0i32.into()),
909 seeders: NumberOfPeers(1i32.into()),
910 },
911 peers: empty_peer_vector
912 })
913 );
914 }
915
916 #[tokio::test]
917 async fn the_tracker_should_always_use_the_remote_client_ip_but_not_the_port_in_the_udp_request_header_instead_of_the_peer_address_in_the_announce_request(
918 ) {
919 let tracker = public_tracker();
923
924 let info_hash = AquaticInfoHash([0u8; 20]);
925 let peer_id = AquaticPeerId([255u8; 20]);
926 let client_port = 8080;
927
928 let remote_client_ip = "::100".parse().unwrap(); let remote_client_port = 8081;
930 let peer_address = "126.0.0.1".parse().unwrap();
931
932 let remote_addr = SocketAddr::new(IpAddr::V6(remote_client_ip), remote_client_port);
933
934 let request = AnnounceRequestBuilder::default()
935 .with_connection_id(into_connection_id(&make(&remote_addr)))
936 .with_info_hash(info_hash)
937 .with_peer_id(peer_id)
938 .with_ip_address(peer_address)
939 .with_port(client_port)
940 .into();
941
942 handle_announce(remote_addr, &request, &tracker).await.unwrap();
943
944 let peers = tracker.get_torrent_peers(&info_hash.0.into());
945
946 assert_eq!(peers[0].peer_addr, SocketAddr::new(IpAddr::V6(remote_client_ip), client_port));
948 }
949
950 fn add_a_torrent_peer_using_ipv4(tracker: &Arc<core::Tracker>) {
951 let info_hash = AquaticInfoHash([0u8; 20]);
952
953 let client_ip_v4 = Ipv4Addr::new(126, 0, 0, 1);
954 let client_port = 8080;
955 let peer_id = AquaticPeerId([255u8; 20]);
956
957 let peer_using_ipv4 = TorrentPeerBuilder::new()
958 .with_peer_id(peer_id)
959 .with_peer_address(SocketAddr::new(IpAddr::V4(client_ip_v4), client_port))
960 .into();
961
962 tracker.upsert_peer_and_get_stats(&info_hash.0.into(), &peer_using_ipv4);
963 }
964
965 async fn announce_a_new_peer_using_ipv6(tracker: Arc<core::Tracker>) -> Response {
966 let client_ip_v4 = Ipv4Addr::new(126, 0, 0, 1);
967 let client_ip_v6 = client_ip_v4.to_ipv6_compatible();
968 let client_port = 8080;
969 let remote_addr = SocketAddr::new(IpAddr::V6(client_ip_v6), client_port);
970 let request = AnnounceRequestBuilder::default()
971 .with_connection_id(into_connection_id(&make(&remote_addr)))
972 .into();
973
974 handle_announce(remote_addr, &request, &tracker).await.unwrap()
975 }
976
977 #[tokio::test]
978 async fn when_the_announce_request_comes_from_a_client_using_ipv6_the_response_should_not_include_peers_using_ipv4() {
979 let tracker = public_tracker();
980
981 add_a_torrent_peer_using_ipv4(&tracker);
982
983 let response = announce_a_new_peer_using_ipv6(tracker.clone()).await;
984
985 let peers: Option<Vec<ResponsePeer<Ipv4AddrBytes>>> = match response {
987 Response::AnnounceIpv4(announce_response) => Some(announce_response.peers),
988 _ => None,
989 };
990 let no_ipv4_peers = peers.is_none();
991 assert!(no_ipv4_peers);
992 }
993
994 #[tokio::test]
995 async fn should_send_the_upd6_announce_event() {
996 let mut stats_event_sender_mock = statistics::MockEventSender::new();
997 stats_event_sender_mock
998 .expect_send_event()
999 .with(eq(statistics::Event::Udp6Announce))
1000 .times(1)
1001 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
1002 let stats_event_sender = Box::new(stats_event_sender_mock);
1003
1004 let tracker = Arc::new(
1005 core::Tracker::new(
1006 &tracker_configuration().core,
1007 Some(stats_event_sender),
1008 statistics::Repo::new(),
1009 )
1010 .unwrap(),
1011 );
1012
1013 let remote_addr = sample_ipv6_remote_addr();
1014
1015 let announce_request = AnnounceRequestBuilder::default()
1016 .with_connection_id(into_connection_id(&make(&remote_addr)))
1017 .into();
1018
1019 handle_announce(remote_addr, &announce_request, &tracker).await.unwrap();
1020 }
1021
1022 mod from_a_loopback_ip {
1023 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
1024 use std::sync::Arc;
1025
1026 use aquatic_udp_protocol::{InfoHash as AquaticInfoHash, PeerId as AquaticPeerId};
1027
1028 use crate::core;
1029 use crate::core::statistics::Keeper;
1030 use crate::servers::udp::connection_cookie::{into_connection_id, make};
1031 use crate::servers::udp::handlers::handle_announce;
1032 use crate::servers::udp::handlers::tests::announce_request::AnnounceRequestBuilder;
1033 use crate::servers::udp::handlers::tests::TrackerConfigurationBuilder;
1034
1035 #[tokio::test]
1036 async fn the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration() {
1037 let configuration = Arc::new(TrackerConfigurationBuilder::default().with_external_ip("::126.0.0.1").into());
1038 let (stats_event_sender, stats_repository) = Keeper::new_active_instance();
1039 let tracker =
1040 Arc::new(core::Tracker::new(&configuration.core, Some(stats_event_sender), stats_repository).unwrap());
1041
1042 let loopback_ipv4 = Ipv4Addr::new(127, 0, 0, 1);
1043 let loopback_ipv6 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1);
1044
1045 let client_ip_v4 = loopback_ipv4;
1046 let client_ip_v6 = loopback_ipv6;
1047 let client_port = 8080;
1048
1049 let info_hash = AquaticInfoHash([0u8; 20]);
1050 let peer_id = AquaticPeerId([255u8; 20]);
1051
1052 let remote_addr = SocketAddr::new(IpAddr::V6(client_ip_v6), client_port);
1053
1054 let request = AnnounceRequestBuilder::default()
1055 .with_connection_id(into_connection_id(&make(&remote_addr)))
1056 .with_info_hash(info_hash)
1057 .with_peer_id(peer_id)
1058 .with_ip_address(client_ip_v4)
1059 .with_port(client_port)
1060 .into();
1061
1062 handle_announce(remote_addr, &request, &tracker).await.unwrap();
1063
1064 let peers = tracker.get_torrent_peers(&info_hash.0.into());
1065
1066 let external_ip_in_tracker_configuration = tracker.get_maybe_external_ip().unwrap();
1067
1068 assert!(external_ip_in_tracker_configuration.is_ipv6());
1069
1070 assert_eq!(Ok(peers[0].peer_addr.ip()), "::126.0.0.1".parse());
1076 }
1077 }
1078 }
1079 }
1080
1081 mod scrape_request {
1082 use std::net::SocketAddr;
1083 use std::sync::Arc;
1084
1085 use aquatic_udp_protocol::{
1086 InfoHash, NumberOfDownloads, NumberOfPeers, PeerId, Response, ScrapeRequest, ScrapeResponse, TorrentScrapeStatistics,
1087 TransactionId,
1088 };
1089
1090 use super::TorrentPeerBuilder;
1091 use crate::core::{self};
1092 use crate::servers::udp::connection_cookie::{into_connection_id, make};
1093 use crate::servers::udp::handlers::handle_scrape;
1094 use crate::servers::udp::handlers::tests::{public_tracker, sample_ipv4_remote_addr};
1095
1096 fn zeroed_torrent_statistics() -> TorrentScrapeStatistics {
1097 TorrentScrapeStatistics {
1098 seeders: NumberOfPeers(0.into()),
1099 completed: NumberOfDownloads(0.into()),
1100 leechers: NumberOfPeers(0.into()),
1101 }
1102 }
1103
1104 #[tokio::test]
1105 async fn should_return_no_stats_when_the_tracker_does_not_have_any_torrent() {
1106 let remote_addr = sample_ipv4_remote_addr();
1107
1108 let info_hash = InfoHash([0u8; 20]);
1109 let info_hashes = vec![info_hash];
1110
1111 let request = ScrapeRequest {
1112 connection_id: into_connection_id(&make(&remote_addr)),
1113 transaction_id: TransactionId(0i32.into()),
1114 info_hashes,
1115 };
1116
1117 let response = handle_scrape(remote_addr, &request, &public_tracker()).await.unwrap();
1118
1119 let expected_torrent_stats = vec![zeroed_torrent_statistics()];
1120
1121 assert_eq!(
1122 response,
1123 Response::from(ScrapeResponse {
1124 transaction_id: request.transaction_id,
1125 torrent_stats: expected_torrent_stats
1126 })
1127 );
1128 }
1129
1130 async fn add_a_seeder(tracker: Arc<core::Tracker>, remote_addr: &SocketAddr, info_hash: &InfoHash) {
1131 let peer_id = PeerId([255u8; 20]);
1132
1133 let peer = TorrentPeerBuilder::new()
1134 .with_peer_id(peer_id)
1135 .with_peer_address(*remote_addr)
1136 .with_number_of_bytes_left(0)
1137 .into();
1138
1139 tracker.upsert_peer_and_get_stats(&info_hash.0.into(), &peer);
1140 }
1141
1142 fn build_scrape_request(remote_addr: &SocketAddr, info_hash: &InfoHash) -> ScrapeRequest {
1143 let info_hashes = vec![*info_hash];
1144
1145 ScrapeRequest {
1146 connection_id: into_connection_id(&make(remote_addr)),
1147 transaction_id: TransactionId::new(0i32),
1148 info_hashes,
1149 }
1150 }
1151
1152 async fn add_a_sample_seeder_and_scrape(tracker: Arc<core::Tracker>) -> Response {
1153 let remote_addr = sample_ipv4_remote_addr();
1154 let info_hash = InfoHash([0u8; 20]);
1155
1156 add_a_seeder(tracker.clone(), &remote_addr, &info_hash).await;
1157
1158 let request = build_scrape_request(&remote_addr, &info_hash);
1159
1160 handle_scrape(remote_addr, &request, &tracker).await.unwrap()
1161 }
1162
1163 fn match_scrape_response(response: Response) -> Option<ScrapeResponse> {
1164 match response {
1165 Response::Scrape(scrape_response) => Some(scrape_response),
1166 _ => None,
1167 }
1168 }
1169
1170 mod with_a_public_tracker {
1171 use aquatic_udp_protocol::{NumberOfDownloads, NumberOfPeers, TorrentScrapeStatistics};
1172
1173 use crate::servers::udp::handlers::tests::public_tracker;
1174 use crate::servers::udp::handlers::tests::scrape_request::{add_a_sample_seeder_and_scrape, match_scrape_response};
1175
1176 #[tokio::test]
1177 async fn should_return_torrent_statistics_when_the_tracker_has_the_requested_torrent() {
1178 let tracker = public_tracker();
1179
1180 let torrent_stats = match_scrape_response(add_a_sample_seeder_and_scrape(tracker.clone()).await);
1181
1182 let expected_torrent_stats = vec![TorrentScrapeStatistics {
1183 seeders: NumberOfPeers(1.into()),
1184 completed: NumberOfDownloads(0.into()),
1185 leechers: NumberOfPeers(0.into()),
1186 }];
1187
1188 assert_eq!(torrent_stats.unwrap().torrent_stats, expected_torrent_stats);
1189 }
1190 }
1191
1192 mod with_a_private_tracker {
1193
1194 use aquatic_udp_protocol::InfoHash;
1195
1196 use crate::servers::udp::handlers::handle_scrape;
1197 use crate::servers::udp::handlers::tests::scrape_request::{
1198 add_a_sample_seeder_and_scrape, build_scrape_request, match_scrape_response, zeroed_torrent_statistics,
1199 };
1200 use crate::servers::udp::handlers::tests::{private_tracker, sample_ipv4_remote_addr};
1201
1202 #[tokio::test]
1203 async fn should_return_zeroed_statistics_when_the_tracker_does_not_have_the_requested_torrent() {
1204 let tracker = private_tracker();
1205
1206 let remote_addr = sample_ipv4_remote_addr();
1207 let non_existing_info_hash = InfoHash([0u8; 20]);
1208
1209 let request = build_scrape_request(&remote_addr, &non_existing_info_hash);
1210
1211 let torrent_stats = match_scrape_response(handle_scrape(remote_addr, &request, &tracker).await.unwrap()).unwrap();
1212
1213 let expected_torrent_stats = vec![zeroed_torrent_statistics()];
1214
1215 assert_eq!(torrent_stats.torrent_stats, expected_torrent_stats);
1216 }
1217
1218 #[tokio::test]
1219 async fn should_return_zeroed_statistics_when_the_tracker_has_the_requested_torrent_because_authenticated_requests_are_not_supported_in_udp_tracker(
1220 ) {
1221 let tracker = private_tracker();
1222
1223 let torrent_stats = match_scrape_response(add_a_sample_seeder_and_scrape(tracker.clone()).await).unwrap();
1224
1225 let expected_torrent_stats = vec![zeroed_torrent_statistics()];
1226
1227 assert_eq!(torrent_stats.torrent_stats, expected_torrent_stats);
1228 }
1229 }
1230
1231 mod with_a_whitelisted_tracker {
1232 use aquatic_udp_protocol::{InfoHash, NumberOfDownloads, NumberOfPeers, TorrentScrapeStatistics};
1233
1234 use crate::servers::udp::handlers::handle_scrape;
1235 use crate::servers::udp::handlers::tests::scrape_request::{
1236 add_a_seeder, build_scrape_request, match_scrape_response, zeroed_torrent_statistics,
1237 };
1238 use crate::servers::udp::handlers::tests::{sample_ipv4_remote_addr, whitelisted_tracker};
1239
1240 #[tokio::test]
1241 async fn should_return_the_torrent_statistics_when_the_requested_torrent_is_whitelisted() {
1242 let tracker = whitelisted_tracker();
1243
1244 let remote_addr = sample_ipv4_remote_addr();
1245 let info_hash = InfoHash([0u8; 20]);
1246
1247 add_a_seeder(tracker.clone(), &remote_addr, &info_hash).await;
1248
1249 tracker.add_torrent_to_memory_whitelist(&info_hash.0.into()).await;
1250
1251 let request = build_scrape_request(&remote_addr, &info_hash);
1252
1253 let torrent_stats = match_scrape_response(handle_scrape(remote_addr, &request, &tracker).await.unwrap()).unwrap();
1254
1255 let expected_torrent_stats = vec![TorrentScrapeStatistics {
1256 seeders: NumberOfPeers(1.into()),
1257 completed: NumberOfDownloads(0.into()),
1258 leechers: NumberOfPeers(0.into()),
1259 }];
1260
1261 assert_eq!(torrent_stats.torrent_stats, expected_torrent_stats);
1262 }
1263
1264 #[tokio::test]
1265 async fn should_return_zeroed_statistics_when_the_requested_torrent_is_not_whitelisted() {
1266 let tracker = whitelisted_tracker();
1267
1268 let remote_addr = sample_ipv4_remote_addr();
1269 let info_hash = InfoHash([0u8; 20]);
1270
1271 add_a_seeder(tracker.clone(), &remote_addr, &info_hash).await;
1272
1273 let request = build_scrape_request(&remote_addr, &info_hash);
1274
1275 let torrent_stats = match_scrape_response(handle_scrape(remote_addr, &request, &tracker).await.unwrap()).unwrap();
1276
1277 let expected_torrent_stats = vec![zeroed_torrent_statistics()];
1278
1279 assert_eq!(torrent_stats.torrent_stats, expected_torrent_stats);
1280 }
1281 }
1282
1283 fn sample_scrape_request(remote_addr: &SocketAddr) -> ScrapeRequest {
1284 let info_hash = InfoHash([0u8; 20]);
1285 let info_hashes = vec![info_hash];
1286
1287 ScrapeRequest {
1288 connection_id: into_connection_id(&make(remote_addr)),
1289 transaction_id: TransactionId(0i32.into()),
1290 info_hashes,
1291 }
1292 }
1293
1294 mod using_ipv4 {
1295 use std::future;
1296 use std::sync::Arc;
1297
1298 use mockall::predicate::eq;
1299
1300 use super::sample_scrape_request;
1301 use crate::core::{self, statistics};
1302 use crate::servers::udp::handlers::handle_scrape;
1303 use crate::servers::udp::handlers::tests::{sample_ipv4_remote_addr, tracker_configuration};
1304
1305 #[tokio::test]
1306 async fn should_send_the_upd4_scrape_event() {
1307 let mut stats_event_sender_mock = statistics::MockEventSender::new();
1308 stats_event_sender_mock
1309 .expect_send_event()
1310 .with(eq(statistics::Event::Udp4Scrape))
1311 .times(1)
1312 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
1313 let stats_event_sender = Box::new(stats_event_sender_mock);
1314
1315 let remote_addr = sample_ipv4_remote_addr();
1316 let tracker = Arc::new(
1317 core::Tracker::new(
1318 &tracker_configuration().core,
1319 Some(stats_event_sender),
1320 statistics::Repo::new(),
1321 )
1322 .unwrap(),
1323 );
1324
1325 handle_scrape(remote_addr, &sample_scrape_request(&remote_addr), &tracker)
1326 .await
1327 .unwrap();
1328 }
1329 }
1330
1331 mod using_ipv6 {
1332 use std::future;
1333 use std::sync::Arc;
1334
1335 use mockall::predicate::eq;
1336
1337 use super::sample_scrape_request;
1338 use crate::core::{self, statistics};
1339 use crate::servers::udp::handlers::handle_scrape;
1340 use crate::servers::udp::handlers::tests::{sample_ipv6_remote_addr, tracker_configuration};
1341
1342 #[tokio::test]
1343 async fn should_send_the_upd6_scrape_event() {
1344 let mut stats_event_sender_mock = statistics::MockEventSender::new();
1345 stats_event_sender_mock
1346 .expect_send_event()
1347 .with(eq(statistics::Event::Udp6Scrape))
1348 .times(1)
1349 .returning(|_| Box::pin(future::ready(Some(Ok(())))));
1350 let stats_event_sender = Box::new(stats_event_sender_mock);
1351
1352 let remote_addr = sample_ipv6_remote_addr();
1353 let tracker = Arc::new(
1354 core::Tracker::new(
1355 &tracker_configuration().core,
1356 Some(stats_event_sender),
1357 statistics::Repo::new(),
1358 )
1359 .unwrap(),
1360 );
1361
1362 handle_scrape(remote_addr, &sample_scrape_request(&remote_addr), &tracker)
1363 .await
1364 .unwrap();
1365 }
1366 }
1367 }
1368}