1
+ //! A peer list.
1
2
use std:: net:: SocketAddr ;
2
3
use std:: sync:: Arc ;
3
4
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.
5
11
6
12
#[ derive( Clone , Debug , Default , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
7
13
pub struct PeerList {
@@ -19,45 +25,48 @@ impl PeerList {
19
25
self . peers . is_empty ( )
20
26
}
21
27
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)
24
30
}
25
31
26
32
pub fn remove ( & mut self , key : & peer:: Id ) -> Option < Arc < peer:: Peer > > {
27
33
self . peers . remove ( key)
28
34
}
29
35
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) ;
35
39
}
36
40
37
41
#[ 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)
43
44
}
44
45
45
46
#[ 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 > > {
47
48
match limit {
48
49
Some ( limit) => self . peers . values ( ) . take ( limit) . cloned ( ) . collect ( ) ,
49
50
None => self . peers . values ( ) . cloned ( ) . collect ( ) ,
50
51
}
51
52
}
52
53
53
54
#[ 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 > > {
55
64
match limit {
56
65
Some ( limit) => self
57
66
. peers
58
67
. values ( )
59
68
// 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 )
61
70
// Limit the number of peers on the result
62
71
. take ( limit)
63
72
. cloned ( )
@@ -66,9 +75,215 @@ impl PeerList {
66
75
. peers
67
76
. values ( )
68
77
// 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 )
70
79
. cloned ( )
71
80
. collect ( ) ,
72
81
}
73
82
}
74
83
}
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