@@ -86,3 +86,257 @@ impl TorrentPeer {
8686 self . left . 0 <= 0 && self . event != AnnounceEvent :: Stopped
8787 }
8888}
89+
90+ #[ cfg( test) ]
91+ mod test {
92+ mod torrent_peer {
93+
94+ use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
95+
96+ use aquatic_udp_protocol:: { AnnounceEvent , NumberOfBytes } ;
97+
98+ use crate :: {
99+ peer:: TorrentPeer ,
100+ protocol:: clock:: { DefaultClock , Time } ,
101+ PeerId ,
102+ } ;
103+
104+ #[ test]
105+ fn it_should_be_serializable ( ) {
106+ let torrent_peer = TorrentPeer {
107+ peer_id : PeerId ( * b"-qB00000000000000000" ) ,
108+ peer_addr : SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) , 8080 ) ,
109+ updated : DefaultClock :: now ( ) ,
110+ uploaded : NumberOfBytes ( 0 ) ,
111+ downloaded : NumberOfBytes ( 0 ) ,
112+ left : NumberOfBytes ( 0 ) ,
113+ event : AnnounceEvent :: Started ,
114+ } ;
115+
116+ let json_serialized_value = serde_json:: to_string ( & torrent_peer) . unwrap ( ) ;
117+
118+ assert_eq ! (
119+ json_serialized_value,
120+ // todo: compare using pretty json format to improve readability
121+ r#"{"peer_id":{"id":"2d71423030303030303030303030303030303030","client":"qBittorrent"},"peer_addr":"126.0.0.1:8080","updated":0,"uploaded":0,"downloaded":0,"left":0,"event":"Started"}"#
122+ ) ;
123+ }
124+ }
125+
126+ mod torrent_peer_constructor_from_udp_requests {
127+
128+ use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
129+
130+ use aquatic_udp_protocol:: {
131+ AnnounceEvent , AnnounceRequest , NumberOfBytes , NumberOfPeers , PeerId as AquaticPeerId , PeerKey , Port , TransactionId ,
132+ } ;
133+
134+ use crate :: protocol:: utils:: get_connection_id;
135+
136+ use crate :: peer:: TorrentPeer ;
137+
138+ // todo: duplicate functions is PR 82. Remove duplication once both PR are merged.
139+
140+ fn sample_ipv4_remote_addr ( ) -> SocketAddr {
141+ sample_ipv4_socket_address ( )
142+ }
143+
144+ fn sample_ipv4_socket_address ( ) -> SocketAddr {
145+ SocketAddr :: new ( IpAddr :: V4 ( Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) ) , 8080 )
146+ }
147+
148+ struct AnnounceRequestBuilder {
149+ request : AnnounceRequest ,
150+ }
151+
152+ impl AnnounceRequestBuilder {
153+ pub fn default ( ) -> AnnounceRequestBuilder {
154+ let client_ip = Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ;
155+ let client_port = 8080 ;
156+ let info_hash_aquatic = aquatic_udp_protocol:: InfoHash ( [ 0u8 ; 20 ] ) ;
157+
158+ let default_request = AnnounceRequest {
159+ connection_id : get_connection_id ( & sample_ipv4_remote_addr ( ) ) ,
160+ transaction_id : TransactionId ( 0i32 ) ,
161+ info_hash : info_hash_aquatic,
162+ peer_id : AquaticPeerId ( * b"-qB00000000000000000" ) ,
163+ bytes_downloaded : NumberOfBytes ( 0i64 ) ,
164+ bytes_uploaded : NumberOfBytes ( 0i64 ) ,
165+ bytes_left : NumberOfBytes ( 0i64 ) ,
166+ event : AnnounceEvent :: Started ,
167+ ip_address : Some ( client_ip) ,
168+ key : PeerKey ( 0u32 ) ,
169+ peers_wanted : NumberOfPeers ( 1i32 ) ,
170+ port : Port ( client_port) ,
171+ } ;
172+ AnnounceRequestBuilder {
173+ request : default_request,
174+ }
175+ }
176+
177+ pub fn into ( self ) -> AnnounceRequest {
178+ self . request
179+ }
180+ }
181+
182+ #[ test]
183+ fn it_should_use_the_udp_source_ip_as_the_peer_ip_address_instead_of_the_ip_in_the_announce_request ( ) {
184+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) ;
185+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
186+
187+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, None ) ;
188+
189+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( remote_ip, announce_request. port. 0 ) ) ;
190+ }
191+
192+ #[ test]
193+ fn it_should_always_use_the_port_in_the_announce_request_for_the_peer_port ( ) {
194+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) ;
195+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
196+
197+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, None ) ;
198+
199+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( remote_ip, announce_request. port. 0 ) ) ;
200+ }
201+
202+ mod when_source_udp_ip_is_a_ipv_4_loopback_ip {
203+
204+ use std:: {
205+ net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ,
206+ str:: FromStr ,
207+ } ;
208+
209+ use crate :: peer:: { test:: torrent_peer_constructor_from_udp_requests:: AnnounceRequestBuilder , TorrentPeer } ;
210+
211+ #[ test]
212+ fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration ( ) {
213+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
214+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
215+
216+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, None ) ;
217+
218+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( remote_ip, announce_request. port. 0 ) ) ;
219+ }
220+
221+ #[ test]
222+ fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined ( ) {
223+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
224+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
225+
226+ let host_opt_ip = IpAddr :: V4 ( Ipv4Addr :: from_str ( "126.0.0.1" ) . unwrap ( ) ) ;
227+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, Some ( host_opt_ip) ) ;
228+
229+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( host_opt_ip, announce_request. port. 0 ) ) ;
230+ }
231+
232+ #[ test]
233+ fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv6_ip ( ) {
234+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) ;
235+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
236+
237+ let host_opt_ip = IpAddr :: V6 ( Ipv6Addr :: from_str ( "2345:0425:2CA1:0000:0000:0567:5673:23b5" ) . unwrap ( ) ) ;
238+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, Some ( host_opt_ip) ) ;
239+
240+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( host_opt_ip, announce_request. port. 0 ) ) ;
241+ }
242+ }
243+
244+ mod when_source_udp_ip_is_a_ipv6_loopback_ip {
245+
246+ use std:: {
247+ net:: { IpAddr , Ipv4Addr , Ipv6Addr , SocketAddr } ,
248+ str:: FromStr ,
249+ } ;
250+
251+ use crate :: peer:: { test:: torrent_peer_constructor_from_udp_requests:: AnnounceRequestBuilder , TorrentPeer } ;
252+
253+ #[ test]
254+ fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration ( ) {
255+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
256+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
257+
258+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, None ) ;
259+
260+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( remote_ip, announce_request. port. 0 ) ) ;
261+ }
262+
263+ #[ test]
264+ fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined ( ) {
265+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
266+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
267+
268+ let host_opt_ip = IpAddr :: V6 ( Ipv6Addr :: from_str ( "2345:0425:2CA1:0000:0000:0567:5673:23b5" ) . unwrap ( ) ) ;
269+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, Some ( host_opt_ip) ) ;
270+
271+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( host_opt_ip, announce_request. port. 0 ) ) ;
272+ }
273+
274+ #[ test]
275+ fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv4_ip ( ) {
276+ let remote_ip = IpAddr :: V6 ( Ipv6Addr :: LOCALHOST ) ;
277+ let announce_request = AnnounceRequestBuilder :: default ( ) . into ( ) ;
278+
279+ let host_opt_ip = IpAddr :: V4 ( Ipv4Addr :: from_str ( "126.0.0.1" ) . unwrap ( ) ) ;
280+ let torrent_peer = TorrentPeer :: from_udp_announce_request ( & announce_request, remote_ip, Some ( host_opt_ip) ) ;
281+
282+ assert_eq ! ( torrent_peer. peer_addr, SocketAddr :: new( host_opt_ip, announce_request. port. 0 ) ) ;
283+ }
284+ }
285+ }
286+
287+ mod torrent_peer_constructor_from_for_http_requests {
288+ use crate :: { http:: AnnounceRequest , peer:: TorrentPeer , InfoHash , PeerId } ;
289+
290+ use std:: net:: { IpAddr , Ipv4Addr } ;
291+
292+ fn sample_http_announce_request ( peer_addr : IpAddr , port : u16 ) -> AnnounceRequest {
293+ AnnounceRequest {
294+ info_hash : InfoHash ( [ 0u8 ; 20 ] ) ,
295+ peer_addr,
296+ downloaded : 0u64 ,
297+ uploaded : 0u64 ,
298+ peer_id : PeerId ( * b"-qB00000000000000000" ) ,
299+ port,
300+ left : 0u64 ,
301+ event : None ,
302+ compact : None ,
303+ }
304+ }
305+
306+ #[ test]
307+ fn it_should_use_the_source_ip_in_the_udp_heder_as_the_peer_ip_address_ignoring_the_peer_ip_in_the_announce_request ( ) {
308+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) ;
309+
310+ let ip_in_announce_request = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) ;
311+ let announce_request = sample_http_announce_request ( ip_in_announce_request, 8080 ) ;
312+
313+ let torrent_peer = TorrentPeer :: from_http_announce_request ( & announce_request, remote_ip, None ) ;
314+
315+ assert_eq ! ( torrent_peer. peer_addr. ip( ) , remote_ip) ;
316+ assert_ne ! ( torrent_peer. peer_addr. ip( ) , ip_in_announce_request) ;
317+ }
318+
319+ #[ test]
320+ fn it_should_always_use_the_port_in_the_announce_request_for_the_peer_port_ignoring_the_port_in_the_udp_header ( ) {
321+ let remote_ip = IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 2 ) ) ;
322+ let remote_port = 8080 ;
323+
324+ let port_in_announce_request = 8081 ;
325+ let announce_request =
326+ sample_http_announce_request ( IpAddr :: V4 ( Ipv4Addr :: new ( 126 , 0 , 0 , 1 ) ) , port_in_announce_request) ;
327+
328+ let torrent_peer = TorrentPeer :: from_http_announce_request ( & announce_request, remote_ip, None ) ;
329+
330+ assert_eq ! ( torrent_peer. peer_addr. port( ) , announce_request. port) ;
331+ assert_ne ! ( torrent_peer. peer_addr. port( ) , remote_port) ;
332+ }
333+
334+ // todo: other cases are already covered by UDP cases.
335+ // Code review:
336+ // We should extract the method "peer_addr_from_ip_and_port_and_opt_host_ip" from TorrentPeer.
337+ // It could be another service responsible for assigning the IP to the peer.
338+ // So we can test that behavior independently from where you use it.
339+ // We could also build the peer with the IP in the announce request and let the tracker decide
340+ // wether it has to change it or not depending on tracker configuration.
341+ }
342+ }
0 commit comments