1+ //! A peer list.
12use std:: net:: SocketAddr ;
23use std:: sync:: Arc ;
34
4- use torrust_tracker_primitives:: peer;
5+ use torrust_tracker_primitives:: { peer, DurationSinceUnixEpoch } ;
6+
7+ // code-review: the current implementation uses the peer Id as the ``BTreeMap``
8+ // key. That would allow adding two identical peers except for the Id.
9+ // For example, two peers with the same socket address but a different peer Id
10+ // would be allowed. That would lead to duplicated peers in the tracker responses.
511
612#[ derive( Clone , Debug , Default , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
713pub struct PeerList {
@@ -19,45 +25,48 @@ impl PeerList {
1925 self . peers . is_empty ( )
2026 }
2127
22- pub fn insert ( & mut self , key : peer :: Id , value : Arc < peer:: Peer > ) -> Option < Arc < peer:: Peer > > {
23- self . peers . insert ( key , value)
28+ pub fn upsert ( & mut self , value : Arc < peer:: Peer > ) -> Option < Arc < peer:: Peer > > {
29+ self . peers . insert ( value . peer_id , value)
2430 }
2531
2632 pub fn remove ( & mut self , key : & peer:: Id ) -> Option < Arc < peer:: Peer > > {
2733 self . peers . remove ( key)
2834 }
2935
30- pub fn retain < F > ( & mut self , f : F )
31- where
32- F : FnMut ( & peer:: Id , & mut Arc < peer:: Peer > ) -> bool ,
33- {
34- self . peers . retain ( f) ;
36+ pub fn remove_inactive_peers ( & mut self , current_cutoff : DurationSinceUnixEpoch ) {
37+ self . peers
38+ . retain ( |_, peer| peer:: ReadInfo :: get_updated ( peer) > current_cutoff) ;
3539 }
3640
3741 #[ must_use]
38- pub fn seeders_and_leechers ( & self ) -> ( usize , usize ) {
39- let seeders = self . peers . values ( ) . filter ( |peer| peer. is_seeder ( ) ) . count ( ) ;
40- let leechers = self . len ( ) - seeders;
41-
42- ( seeders, leechers)
42+ pub fn get ( & self , peer_id : & peer:: Id ) -> Option < & Arc < peer:: Peer > > {
43+ self . peers . get ( peer_id)
4344 }
4445
4546 #[ must_use]
46- pub fn get_peers ( & self , limit : Option < usize > ) -> Vec < Arc < peer:: Peer > > {
47+ pub fn get_all ( & self , limit : Option < usize > ) -> Vec < Arc < peer:: Peer > > {
4748 match limit {
4849 Some ( limit) => self . peers . values ( ) . take ( limit) . cloned ( ) . collect ( ) ,
4950 None => self . peers . values ( ) . cloned ( ) . collect ( ) ,
5051 }
5152 }
5253
5354 #[ must_use]
54- pub fn get_peers_for_client ( & self , client : & SocketAddr , limit : Option < usize > ) -> Vec < Arc < peer:: Peer > > {
55+ pub fn seeders_and_leechers ( & self ) -> ( usize , usize ) {
56+ let seeders = self . peers . values ( ) . filter ( |peer| peer. is_seeder ( ) ) . count ( ) ;
57+ let leechers = self . len ( ) - seeders;
58+
59+ ( seeders, leechers)
60+ }
61+
62+ #[ must_use]
63+ pub fn get_peers_excluding_addr ( & self , peer_addr : & SocketAddr , limit : Option < usize > ) -> Vec < Arc < peer:: Peer > > {
5564 match limit {
5665 Some ( limit) => self
5766 . peers
5867 . values ( )
5968 // Take peers which are not the client peer
60- . filter ( |peer| peer:: ReadInfo :: get_address ( peer. as_ref ( ) ) != * client )
69+ . filter ( |peer| peer:: ReadInfo :: get_address ( peer. as_ref ( ) ) != * peer_addr )
6170 // Limit the number of peers on the result
6271 . take ( limit)
6372 . cloned ( )
@@ -66,9 +75,215 @@ impl PeerList {
6675 . peers
6776 . values ( )
6877 // Take peers which are not the client peer
69- . filter ( |peer| peer:: ReadInfo :: get_address ( peer. as_ref ( ) ) != * client )
78+ . filter ( |peer| peer:: ReadInfo :: get_address ( peer. as_ref ( ) ) != * peer_addr )
7079 . cloned ( )
7180 . collect ( ) ,
7281 }
7382 }
7483}
84+
85+ #[ cfg( test) ]
86+ mod tests {
87+
88+ mod it_should {
89+ use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
90+ use std:: sync:: Arc ;
91+
92+ use torrust_tracker_primitives:: peer:: fixture:: PeerBuilder ;
93+ use torrust_tracker_primitives:: peer:: { self } ;
94+ use torrust_tracker_primitives:: DurationSinceUnixEpoch ;
95+
96+ use crate :: entry:: peer_list:: PeerList ;
97+
98+ #[ test]
99+ fn be_empty_when_no_peers_have_been_inserted ( ) {
100+ let peer_list = PeerList :: default ( ) ;
101+
102+ assert ! ( peer_list. is_empty( ) ) ;
103+ }
104+
105+ #[ test]
106+ fn have_zero_length_when_no_peers_have_been_inserted ( ) {
107+ let peer_list = PeerList :: default ( ) ;
108+
109+ assert_eq ! ( peer_list. len( ) , 0 ) ;
110+ }
111+
112+ #[ test]
113+ fn allow_inserting_a_new_peer ( ) {
114+ let mut peer_list = PeerList :: default ( ) ;
115+
116+ let peer = PeerBuilder :: default ( ) . build ( ) ;
117+
118+ assert_eq ! ( peer_list. upsert( peer. into( ) ) , None ) ;
119+ }
120+
121+ #[ test]
122+ fn allow_updating_a_preexisting_peer ( ) {
123+ let mut peer_list = PeerList :: default ( ) ;
124+
125+ let peer = PeerBuilder :: default ( ) . build ( ) ;
126+
127+ peer_list. upsert ( peer. into ( ) ) ;
128+
129+ assert_eq ! ( peer_list. upsert( peer. into( ) ) , Some ( Arc :: new( peer) ) ) ;
130+ }
131+
132+ #[ test]
133+ fn allow_getting_all_peers ( ) {
134+ let mut peer_list = PeerList :: default ( ) ;
135+
136+ let peer = PeerBuilder :: default ( ) . build ( ) ;
137+
138+ peer_list. upsert ( peer. into ( ) ) ;
139+
140+ assert_eq ! ( peer_list. get_all( None ) , [ Arc :: new( peer) ] ) ;
141+ }
142+
143+ #[ test]
144+ fn allow_getting_one_peer_by_id ( ) {
145+ let mut peer_list = PeerList :: default ( ) ;
146+
147+ let peer = PeerBuilder :: default ( ) . build ( ) ;
148+
149+ peer_list. upsert ( peer. into ( ) ) ;
150+
151+ assert_eq ! ( peer_list. get( & peer. peer_id) , Some ( Arc :: new( peer) ) . as_ref( ) ) ;
152+ }
153+
154+ #[ test]
155+ fn increase_the_number_of_peers_after_inserting_a_new_one ( ) {
156+ let mut peer_list = PeerList :: default ( ) ;
157+
158+ let peer = PeerBuilder :: default ( ) . build ( ) ;
159+
160+ peer_list. upsert ( peer. into ( ) ) ;
161+
162+ assert_eq ! ( peer_list. len( ) , 1 ) ;
163+ }
164+
165+ #[ test]
166+ fn decrease_the_number_of_peers_after_removing_one ( ) {
167+ let mut peer_list = PeerList :: default ( ) ;
168+
169+ let peer = PeerBuilder :: default ( ) . build ( ) ;
170+
171+ peer_list. upsert ( peer. into ( ) ) ;
172+
173+ peer_list. remove ( & peer. peer_id ) ;
174+
175+ assert ! ( peer_list. is_empty( ) ) ;
176+ }
177+
178+ #[ test]
179+ fn allow_removing_an_existing_peer ( ) {
180+ let mut peer_list = PeerList :: default ( ) ;
181+
182+ let peer = PeerBuilder :: default ( ) . build ( ) ;
183+
184+ peer_list. upsert ( peer. into ( ) ) ;
185+
186+ peer_list. remove ( & peer. peer_id ) ;
187+
188+ assert_eq ! ( peer_list. get( & peer. peer_id) , None ) ;
189+ }
190+
191+ #[ test]
192+ fn allow_getting_all_peers_excluding_peers_with_a_given_address ( ) {
193+ let mut peer_list = PeerList :: default ( ) ;
194+
195+ let peer1 = PeerBuilder :: default ( )
196+ . with_peer_id ( & peer:: Id ( * b"-qB00000000000000001" ) )
197+ . with_peer_addr ( & SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) ) , 6969 ) )
198+ . build ( ) ;
199+ peer_list. upsert ( peer1. into ( ) ) ;
200+
201+ let peer2 = PeerBuilder :: default ( )
202+ . with_peer_id ( & peer:: Id ( * b"-qB00000000000000002" ) )
203+ . with_peer_addr ( & SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 127 , 0 , 0 , 2 ) ) , 6969 ) )
204+ . build ( ) ;
205+ peer_list. upsert ( peer2. into ( ) ) ;
206+
207+ assert_eq ! ( peer_list. get_peers_excluding_addr( & peer2. peer_addr, None ) , [ Arc :: new( peer1) ] ) ;
208+ }
209+
210+ #[ test]
211+ fn return_the_number_of_seeders_in_the_list ( ) {
212+ let mut peer_list = PeerList :: default ( ) ;
213+
214+ let seeder = PeerBuilder :: seeder ( ) . build ( ) ;
215+ let leecher = PeerBuilder :: leecher ( ) . build ( ) ;
216+
217+ peer_list. upsert ( seeder. into ( ) ) ;
218+ peer_list. upsert ( leecher. into ( ) ) ;
219+
220+ let ( seeders, _leechers) = peer_list. seeders_and_leechers ( ) ;
221+
222+ assert_eq ! ( seeders, 1 ) ;
223+ }
224+
225+ #[ test]
226+ fn return_the_number_of_leechers_in_the_list ( ) {
227+ let mut peer_list = PeerList :: default ( ) ;
228+
229+ let seeder = PeerBuilder :: seeder ( ) . build ( ) ;
230+ let leecher = PeerBuilder :: leecher ( ) . build ( ) ;
231+
232+ peer_list. upsert ( seeder. into ( ) ) ;
233+ peer_list. upsert ( leecher. into ( ) ) ;
234+
235+ let ( _seeders, leechers) = peer_list. seeders_and_leechers ( ) ;
236+
237+ assert_eq ! ( leechers, 1 ) ;
238+ }
239+
240+ #[ test]
241+ fn remove_inactive_peers ( ) {
242+ let mut peer_list = PeerList :: default ( ) ;
243+ let one_second = DurationSinceUnixEpoch :: new ( 1 , 0 ) ;
244+
245+ // Insert the peer
246+ let last_update_time = DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ;
247+ let peer = PeerBuilder :: default ( ) . last_updated_on ( last_update_time) . build ( ) ;
248+ peer_list. upsert ( peer. into ( ) ) ;
249+
250+ // Remove peers not updated since one second after inserting the peer
251+ peer_list. remove_inactive_peers ( last_update_time + one_second) ;
252+
253+ assert_eq ! ( peer_list. len( ) , 0 ) ;
254+ }
255+
256+ #[ test]
257+ fn not_remove_active_peers ( ) {
258+ let mut peer_list = PeerList :: default ( ) ;
259+ let one_second = DurationSinceUnixEpoch :: new ( 1 , 0 ) ;
260+
261+ // Insert the peer
262+ let last_update_time = DurationSinceUnixEpoch :: new ( 1_669_397_478_934 , 0 ) ;
263+ let peer = PeerBuilder :: default ( ) . last_updated_on ( last_update_time) . build ( ) ;
264+ peer_list. upsert ( peer. into ( ) ) ;
265+
266+ // Remove peers not updated since one second before inserting the peer.
267+ peer_list. remove_inactive_peers ( last_update_time - one_second) ;
268+
269+ assert_eq ! ( peer_list. len( ) , 1 ) ;
270+ }
271+
272+ #[ test]
273+ fn allow_inserting_two_identical_peers_except_for_the_id ( ) {
274+ let mut peer_list = PeerList :: default ( ) ;
275+
276+ let peer1 = PeerBuilder :: default ( )
277+ . with_peer_id ( & peer:: Id ( * b"-qB00000000000000001" ) )
278+ . build ( ) ;
279+ peer_list. upsert ( peer1. into ( ) ) ;
280+
281+ let peer2 = PeerBuilder :: default ( )
282+ . with_peer_id ( & peer:: Id ( * b"-qB00000000000000002" ) )
283+ . build ( ) ;
284+ peer_list. upsert ( peer2. into ( ) ) ;
285+
286+ assert_eq ! ( peer_list. len( ) , 2 ) ;
287+ }
288+ }
289+ }
0 commit comments