torrust_tracker/servers/http/v1/responses/
announce.rs1use std::io::Write;
5use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
6
7use axum::http::StatusCode;
8use derive_more::{AsRef, Constructor, From};
9use torrust_tracker_contrib_bencode::{ben_bytes, ben_int, ben_list, ben_map, BMutAccess, BencodeMut};
10use torrust_tracker_primitives::peer;
11
12use super::Response;
13use crate::core::AnnounceData;
14use crate::servers::http::v1::responses;
15
16#[derive(Debug, AsRef, PartialEq, Constructor)]
34pub struct Announce<E>
35where
36 E: From<AnnounceData> + Into<Vec<u8>>,
37{
38 data: E,
39}
40
41impl<E: From<AnnounceData> + Into<Vec<u8>>> From<AnnounceData> for Announce<E> {
43 fn from(data: AnnounceData) -> Self {
44 Self::new(data.into())
45 }
46}
47
48impl<E: From<AnnounceData> + Into<Vec<u8>>> axum::response::IntoResponse for Announce<E>
50where
51 Announce<E>: Response,
52{
53 fn into_response(self) -> axum::response::Response {
54 axum::response::IntoResponse::into_response(self.body().map(|bytes| (StatusCode::OK, bytes)))
55 }
56}
57
58impl<E: From<AnnounceData> + Into<Vec<u8>>> Response for Announce<E> {
61 fn body(self) -> Result<Vec<u8>, responses::error::Error> {
62 Ok(self.data.into())
63 }
64}
65
66pub struct Normal {
68 complete: i64,
69 incomplete: i64,
70 interval: i64,
71 min_interval: i64,
72 peers: Vec<NormalPeer>,
73}
74
75impl From<AnnounceData> for Normal {
76 fn from(data: AnnounceData) -> Self {
77 Self {
78 complete: data.stats.complete.into(),
79 incomplete: data.stats.incomplete.into(),
80 interval: data.policy.interval.into(),
81 min_interval: data.policy.interval_min.into(),
82 peers: data.peers.iter().map(AsRef::as_ref).copied().collect(),
83 }
84 }
85}
86
87#[allow(clippy::from_over_into)]
88impl Into<Vec<u8>> for Normal {
89 fn into(self) -> Vec<u8> {
90 let mut peers_list = ben_list!();
91 let peers_list_mut = peers_list.list_mut().unwrap();
92 for peer in &self.peers {
93 peers_list_mut.push(peer.into());
94 }
95
96 (ben_map! {
97 "complete" => ben_int!(self.complete),
98 "incomplete" => ben_int!(self.incomplete),
99 "interval" => ben_int!(self.interval),
100 "min interval" => ben_int!(self.min_interval),
101 "peers" => peers_list.clone()
102 })
103 .encode()
104 }
105}
106
107pub struct Compact {
109 complete: i64,
110 incomplete: i64,
111 interval: i64,
112 min_interval: i64,
113 peers: Vec<u8>,
114 peers6: Vec<u8>,
115}
116
117impl From<AnnounceData> for Compact {
118 fn from(data: AnnounceData) -> Self {
119 let compact_peers: Vec<CompactPeer> = data.peers.iter().map(AsRef::as_ref).copied().collect();
120
121 let (peers, peers6): (Vec<CompactPeerData<Ipv4Addr>>, Vec<CompactPeerData<Ipv6Addr>>) =
122 compact_peers.into_iter().collect();
123
124 let peers_encoded: CompactPeersEncoded = peers.into_iter().collect();
125 let peers_encoded_6: CompactPeersEncoded = peers6.into_iter().collect();
126
127 Self {
128 complete: data.stats.complete.into(),
129 incomplete: data.stats.incomplete.into(),
130 interval: data.policy.interval.into(),
131 min_interval: data.policy.interval_min.into(),
132 peers: peers_encoded.0,
133 peers6: peers_encoded_6.0,
134 }
135 }
136}
137
138#[allow(clippy::from_over_into)]
139impl Into<Vec<u8>> for Compact {
140 fn into(self) -> Vec<u8> {
141 (ben_map! {
142 "complete" => ben_int!(self.complete),
143 "incomplete" => ben_int!(self.incomplete),
144 "interval" => ben_int!(self.interval),
145 "min interval" => ben_int!(self.min_interval),
146 "peers" => ben_bytes!(self.peers),
147 "peers6" => ben_bytes!(self.peers6)
148 })
149 .encode()
150 }
151}
152
153#[derive(Debug, PartialEq)]
167pub struct NormalPeer {
168 pub peer_id: [u8; 20],
170 pub ip: IpAddr,
172 pub port: u16,
174}
175
176impl peer::Encoding for NormalPeer {}
177
178impl From<peer::Peer> for NormalPeer {
179 fn from(peer: peer::Peer) -> Self {
180 NormalPeer {
181 peer_id: peer.peer_id.0,
182 ip: peer.peer_addr.ip(),
183 port: peer.peer_addr.port(),
184 }
185 }
186}
187
188impl From<&NormalPeer> for BencodeMut<'_> {
189 fn from(value: &NormalPeer) -> Self {
190 ben_map! {
191 "peer id" => ben_bytes!(value.peer_id.clone().to_vec()),
192 "ip" => ben_bytes!(value.ip.to_string()),
193 "port" => ben_int!(i64::from(value.port))
194 }
195 }
196}
197
198#[derive(Clone, Debug, PartialEq)]
221pub enum CompactPeer {
222 V4(CompactPeerData<Ipv4Addr>),
224 V6(CompactPeerData<Ipv6Addr>),
226}
227
228impl peer::Encoding for CompactPeer {}
229
230impl From<peer::Peer> for CompactPeer {
231 fn from(peer: peer::Peer) -> Self {
232 match (peer.peer_addr.ip(), peer.peer_addr.port()) {
233 (IpAddr::V4(ip), port) => Self::V4(CompactPeerData { ip, port }),
234 (IpAddr::V6(ip), port) => Self::V6(CompactPeerData { ip, port }),
235 }
236 }
237}
238
239#[derive(Clone, Debug, PartialEq)]
242pub struct CompactPeerData<V> {
243 pub ip: V,
245 pub port: u16,
247}
248
249impl FromIterator<CompactPeer> for (Vec<CompactPeerData<Ipv4Addr>>, Vec<CompactPeerData<Ipv6Addr>>) {
250 fn from_iter<T: IntoIterator<Item = CompactPeer>>(iter: T) -> Self {
251 let mut peers_v4: Vec<CompactPeerData<Ipv4Addr>> = vec![];
252 let mut peers_v6: Vec<CompactPeerData<Ipv6Addr>> = vec![];
253
254 for peer in iter {
255 match peer {
256 CompactPeer::V4(peer) => peers_v4.push(peer),
257 CompactPeer::V6(peer6) => peers_v6.push(peer6),
258 }
259 }
260
261 (peers_v4, peers_v6)
262 }
263}
264
265#[derive(From, PartialEq)]
266struct CompactPeersEncoded(Vec<u8>);
267
268impl FromIterator<CompactPeerData<Ipv4Addr>> for CompactPeersEncoded {
269 fn from_iter<T: IntoIterator<Item = CompactPeerData<Ipv4Addr>>>(iter: T) -> Self {
270 let mut bytes: Vec<u8> = vec![];
271
272 for peer in iter {
273 bytes
274 .write_all(&u32::from(peer.ip).to_be_bytes())
275 .expect("it should write peer ip");
276 bytes.write_all(&peer.port.to_be_bytes()).expect("it should write peer port");
277 }
278
279 bytes.into()
280 }
281}
282
283impl FromIterator<CompactPeerData<Ipv6Addr>> for CompactPeersEncoded {
284 fn from_iter<T: IntoIterator<Item = CompactPeerData<Ipv6Addr>>>(iter: T) -> Self {
285 let mut bytes: Vec<u8> = Vec::new();
286
287 for peer in iter {
288 bytes
289 .write_all(&u128::from(peer.ip).to_be_bytes())
290 .expect("it should write peer ip");
291 bytes.write_all(&peer.port.to_be_bytes()).expect("it should write peer port");
292 }
293 bytes.into()
294 }
295}
296
297#[cfg(test)]
298mod tests {
299
300 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
301 use std::sync::Arc;
302
303 use aquatic_udp_protocol::PeerId;
304 use torrust_tracker_configuration::AnnouncePolicy;
305 use torrust_tracker_primitives::peer::fixture::PeerBuilder;
306 use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
307
308 use crate::core::AnnounceData;
309 use crate::servers::http::v1::responses::announce::{Announce, Compact, Normal, Response};
310
311 fn setup_announce_data() -> AnnounceData {
324 let policy = AnnouncePolicy::new(111, 222);
325
326 let peer_ipv4 = PeerBuilder::default()
327 .with_peer_id(&PeerId(*b"-qB00000000000000001"))
328 .with_peer_addr(&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), 0x7070))
329 .build();
330
331 let peer_ipv6 = PeerBuilder::default()
332 .with_peer_id(&PeerId(*b"-qB00000000000000002"))
333 .with_peer_addr(&SocketAddr::new(
334 IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)),
335 0x7070,
336 ))
337 .build();
338
339 let peers = vec![Arc::new(peer_ipv4), Arc::new(peer_ipv6)];
340 let stats = SwarmMetadata::new(333, 333, 444);
341
342 AnnounceData::new(peers, stats, policy)
343 }
344
345 #[test]
346 fn non_compact_announce_response_can_be_bencoded() {
347 let response: Announce<Normal> = setup_announce_data().into();
348 let bytes = response.body().expect("it should encode the response");
349
350 let expected_bytes = b"d8:completei333e10:incompletei444e8:intervali111e12:min intervali222e5:peersld2:ip15:105.105.105.1057:peer id20:-qB000000000000000014:porti28784eed2:ip39:6969:6969:6969:6969:6969:6969:6969:69697:peer id20:-qB000000000000000024:porti28784eeee";
352
353 assert_eq!(
354 String::from_utf8(bytes).unwrap(),
355 String::from_utf8(expected_bytes.to_vec()).unwrap()
356 );
357 }
358
359 #[test]
360 fn compact_announce_response_can_be_bencoded() {
361 let response: Announce<Compact> = setup_announce_data().into();
362 let bytes = response.body().expect("it should encode the response");
363
364 let expected_bytes =
365 b"d8:completei333e10:incompletei444e8:intervali111e12:min intervali222e5:peers6:iiiipp6:peers618:iiiiiiiiiiiiiiiippe";
367
368 assert_eq!(
369 String::from_utf8(bytes).unwrap(),
370 String::from_utf8(expected_bytes.to_vec()).unwrap()
371 );
372 }
373}