diff --git a/src/servers/http/mod.rs b/src/servers/http/mod.rs index b2d232fc6..08a59ef90 100644 --- a/src/servers/http/mod.rs +++ b/src/servers/http/mod.rs @@ -152,7 +152,7 @@ //! 000000f0: 65 e //! ``` //! -//! Refer to the [`NonCompact`](crate::servers::http::v1::responses::announce::NonCompact) +//! Refer to the [`Normal`](crate::servers::http::v1::responses::announce::Normal), i.e. `Non-Compact` //! response for more information about the response. //! //! **Sample compact response** diff --git a/src/servers/http/v1/handlers/announce.rs b/src/servers/http/v1/handlers/announce.rs index 0522042b1..23d8b2d6e 100644 --- a/src/servers/http/v1/handlers/announce.rs +++ b/src/servers/http/v1/handlers/announce.rs @@ -120,10 +120,10 @@ fn build_response(announce_request: &Announce, announce_data: AnnounceData) -> R match &announce_request.compact { Some(compact) => match compact { Compact::Accepted => announce::Compact::from(announce_data).into_response(), - Compact::NotAccepted => announce::NonCompact::from(announce_data).into_response(), + Compact::NotAccepted => announce::Normal::from(announce_data).into_response(), }, // Default response format non compact - None => announce::NonCompact::from(announce_data).into_response(), + None => announce::Normal::from(announce_data).into_response(), } } diff --git a/src/servers/http/v1/requests/announce.rs b/src/servers/http/v1/requests/announce.rs index 7f77f727d..08dd9da29 100644 --- a/src/servers/http/v1/requests/announce.rs +++ b/src/servers/http/v1/requests/announce.rs @@ -180,7 +180,7 @@ impl fmt::Display for Event { /// Depending on the value of this param, the tracker will return a different /// response: /// -/// - [`NonCompact`](crate::servers::http::v1::responses::announce::NonCompact) response. +/// - [`Normal`](crate::servers::http::v1::responses::announce::Normal), i.e. a `non-compact` response. /// - [`Compact`](crate::servers::http::v1::responses::announce::Compact) response. /// /// Refer to [BEP 23. Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) diff --git a/src/servers/http/v1/responses/announce.rs b/src/servers/http/v1/responses/announce.rs index 8a245476b..e23a5b122 100644 --- a/src/servers/http/v1/responses/announce.rs +++ b/src/servers/http/v1/responses/announce.rs @@ -2,40 +2,67 @@ //! //! Data structures and logic to build the `announce` response. use std::io::Write; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::panic::Location; use axum::http::StatusCode; -use axum::response::{IntoResponse, Response}; -use serde::{self, Deserialize, Serialize}; use thiserror::Error; use torrust_tracker_contrib_bencode::{ben_bytes, ben_int, ben_list, ben_map, BMutAccess, BencodeMut}; use crate::core::{self, AnnounceData}; use crate::servers::http::v1::responses; -/// Normal (non compact) `announce` response. +/// Trait that defines the Announce Response Format +pub trait Response: From + axum::response::IntoResponse { + /// Returns the Body of the Announce Response + /// + /// # Errors + /// + /// If unable to generate the response, it will return an error. + fn body(&self) -> Result, responses::error::Error>; +} + +/// Trait for the Encoding of a Response +pub trait Encoding { + /// The Peer Encoding should be convertible from `core::peer::Peer`. + type PeerEncoding: From; +} + +/// Use the Normal Non-Compact Encoding +pub struct EncodeNormal {} +impl Encoding for EncodeNormal { + type PeerEncoding = NormalPeer; +} + +/// Use the Compact Encoding +pub struct EncodeCompact {} +impl Encoding for EncodeCompact { + type PeerEncoding = CompactPeer; +} + +/// [`Normal`] (non compact) `announce` response. /// /// It's a bencoded dictionary. /// /// ```rust /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -/// use torrust_tracker::servers::http::v1::responses::announce::{NonCompact, Peer}; /// -/// let response = NonCompact { +/// use torrust_tracker::servers::http::v1::responses::announce::{Announce, Normal, NormalPeer, Response}; +/// +/// let response: Normal = Announce { /// interval: 111, /// interval_min: 222, /// complete: 333, /// incomplete: 444, /// peers: vec![ /// // IPV4 -/// Peer { +/// NormalPeer { /// peer_id: *b"-qB00000000000000001", /// ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 /// port: 0x7070, // 28784 /// }, /// // IPV6 -/// Peer { +/// NormalPeer { /// peer_id: *b"-qB00000000000000002", /// ip: IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)), /// port: 0x7070, // 28784 @@ -43,9 +70,8 @@ use crate::servers::http::v1::responses; /// ], /// }; /// -/// let bytes = response.body(); -/// -/// // The expected bencoded response. +/// let bytes = response.body().expect("it should encode without error"); +/// # // cspell:disable-next-line /// 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"; /// /// assert_eq!( @@ -56,131 +82,9 @@ use crate::servers::http::v1::responses; /// /// Refer to [BEP 03: The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html) /// for more information. -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct NonCompact { - /// Interval in seconds that the client should wait between sending regular - /// announce requests to the tracker. - /// - /// It's a **recommended** wait time between announcements. - /// - /// This is the standard amount of time that clients should wait between - /// sending consecutive announcements to the tracker. This value is set by - /// the tracker and is typically provided in the tracker's response to a - /// client's initial request. It serves as a guideline for clients to know - /// how often they should contact the tracker for updates on the peer list, - /// while ensuring that the tracker is not overwhelmed with requests. - pub interval: u32, - /// Minimum announce interval. Clients must not reannounce more frequently - /// than this. - /// - /// It establishes the shortest allowed wait time. - /// - /// This is an optional parameter in the protocol that the tracker may - /// provide in its response. It sets a lower limit on the frequency at which - /// clients are allowed to send announcements. Clients should respect this - /// value to prevent sending too many requests in a short period, which - /// could lead to excessive load on the tracker or even getting banned by - /// the tracker for not adhering to the rules. - #[serde(rename = "min interval")] - pub interval_min: u32, - /// Number of peers with the entire file, i.e. seeders. - pub complete: u32, - /// Number of non-seeder peers, aka "leechers". - pub incomplete: u32, - /// A list of peers. The value is a list of dictionaries. - pub peers: Vec, -} - -/// Peer information in the [`NonCompact`] -/// response. -/// -/// ```rust -/// use std::net::{IpAddr, Ipv4Addr}; -/// use torrust_tracker::servers::http::v1::responses::announce::{NonCompact, Peer}; -/// -/// let peer = Peer { -/// peer_id: *b"-qB00000000000000001", -/// ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 -/// port: 0x7070, // 28784 -/// }; -/// ``` -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Peer { - /// The peer's ID. - pub peer_id: [u8; 20], - /// The peer's IP address. - pub ip: IpAddr, - /// The peer's port number. - pub port: u16, -} - -impl Peer { - #[must_use] - pub fn ben_map(&self) -> BencodeMut<'_> { - ben_map! { - "peer id" => ben_bytes!(self.peer_id.clone().to_vec()), - "ip" => ben_bytes!(self.ip.to_string()), - "port" => ben_int!(i64::from(self.port)) - } - } -} - -impl From for Peer { - fn from(peer: core::peer::Peer) -> Self { - Peer { - peer_id: peer.peer_id.to_bytes(), - ip: peer.peer_addr.ip(), - port: peer.peer_addr.port(), - } - } -} +pub type Normal = Announce; -impl NonCompact { - /// Returns the bencoded body of the non-compact response. - /// - /// # Panics - /// - /// Will return an error if it can't access the bencode as a mutable `BListAccess`. - #[must_use] - pub fn body(&self) -> Vec { - let mut peers_list = ben_list!(); - let peers_list_mut = peers_list.list_mut().unwrap(); - for peer in &self.peers { - peers_list_mut.push(peer.ben_map()); - } - - (ben_map! { - "complete" => ben_int!(i64::from(self.complete)), - "incomplete" => ben_int!(i64::from(self.incomplete)), - "interval" => ben_int!(i64::from(self.interval)), - "min interval" => ben_int!(i64::from(self.interval_min)), - "peers" => peers_list.clone() - }) - .encode() - } -} - -impl IntoResponse for NonCompact { - fn into_response(self) -> Response { - (StatusCode::OK, self.body()).into_response() - } -} - -impl From for NonCompact { - fn from(domain_announce_response: AnnounceData) -> Self { - let peers: Vec = domain_announce_response.peers.iter().map(|peer| Peer::from(*peer)).collect(); - - Self { - interval: domain_announce_response.interval, - interval_min: domain_announce_response.interval_min, - complete: domain_announce_response.swarm_stats.seeders, - incomplete: domain_announce_response.swarm_stats.leechers, - peers, - } - } -} - -/// Compact `announce` response. +/// [`Compact`] `announce` response. /// /// _"To reduce the size of tracker responses and to reduce memory and /// computational requirements in trackers, trackers may return peers as a @@ -188,24 +92,24 @@ impl From for NonCompact { /// /// ```rust /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -/// use torrust_tracker::servers::http::v1::responses::announce::{Compact, CompactPeer}; +/// use torrust_tracker::servers::http::v1::responses::announce::{Announce, Compact, CompactPeer, CompactPeerData, Response}; /// -/// let response = Compact { +/// let response : Compact = Announce{ /// interval: 111, /// interval_min: 222, /// complete: 333, /// incomplete: 444, /// peers: vec![ /// // IPV4 -/// CompactPeer { -/// ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 -/// port: 0x7070, // 28784 -/// }, +/// CompactPeer::V4(CompactPeerData { +/// ip: Ipv4Addr::new(0x69, 0x69, 0x69, 0x69), +/// port: 0x7070, // 28784 +/// }), /// // IPV6 -/// CompactPeer { -/// ip: IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)), +/// CompactPeer::V6(CompactPeerData { +/// ip: Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969), /// port: 0x7070, // 28784 -/// }, +/// }), /// ], /// }; /// @@ -226,8 +130,15 @@ impl From for NonCompact { /// /// - [BEP 23: Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) /// - [BEP 07: IPv6 Tracker Extension](https://www.bittorrent.org/beps/bep_0007.html) -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct Compact { +pub type Compact = Announce; + +/// An [`Announce`] response, with either a [`Normal`] or [`Compact`] [`Encoding`]. +/// +#[derive(Debug, PartialEq)] +pub struct Announce +where + E: Encoding, +{ /// Interval in seconds that the client should wait between sending regular /// announce requests to the tracker. /// @@ -238,7 +149,7 @@ pub struct Compact { /// the tracker and is typically provided in the tracker's response to a /// client's initial request. It serves as a guideline for clients to know /// how often they should contact the tracker for updates on the peer list, - /// while ensuring that the tracker is not overwhelmed with requests. + /// while ensuring that the tracker is not overwhelmed with requests. pub interval: u32, /// Minimum announce interval. Clients must not reannounce more frequently /// than this. @@ -250,126 +161,226 @@ pub struct Compact { /// clients are allowed to send announcements. Clients should respect this /// value to prevent sending too many requests in a short period, which /// could lead to excessive load on the tracker or even getting banned by - /// the tracker for not adhering to the rules. - #[serde(rename = "min interval")] + /// the tracker for not adhering to the rules. + // #[serde(rename = "min interval")] pub interval_min: u32, - /// Number of seeders, aka "completed". + /// Number of peers with the entire file, i.e. seeders. pub complete: u32, - /// Number of non-seeder peers, aka "incomplete". + /// Number of non-seeder peers, aka "leechers". pub incomplete: u32, - /// Compact peer list. - pub peers: Vec, + /// A list of peers. The value is a list of dictionaries. + pub peers: Vec, } -/// Compact peer. It's used in the [`Compact`] -/// response. -/// -/// _"To reduce the size of tracker responses and to reduce memory and -/// computational requirements in trackers, trackers may return peers as a -/// packed string rather than as a bencoded list."_ +/// Implement the [`From`] trait. /// -/// A part from reducing the size of the response, this format does not contain -/// the peer's ID. -/// -/// ```rust -/// use std::net::{IpAddr, Ipv4Addr}; -/// use torrust_tracker::servers::http::v1::responses::announce::CompactPeer; -/// -/// let compact_peer = CompactPeer { -/// ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 -/// port: 0x7070 // 28784 -/// }; -/// ``` +/// To convert the [`AnnounceData`] into a [`Announce`] /// -/// Refer to [BEP 23: Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) -/// for more information. -#[derive(Serialize, Deserialize, Debug, PartialEq)] -pub struct CompactPeer { - /// The peer's IP address. - pub ip: IpAddr, - /// The peer's port number. - pub port: u16, -} +impl From for Announce +where + E: Encoding, +{ + fn from(domain_announce_response: AnnounceData) -> Self { + let peers: Vec = domain_announce_response + .peers + .iter() + .map(|peer| E::PeerEncoding::from(*peer)) + .collect(); -impl CompactPeer { - /// Returns the compact peer as a byte vector. - /// - /// # Errors - /// - /// Will return `Err` if internally interrupted. - pub fn bytes(&self) -> Result, Box> { - let mut bytes: Vec = Vec::new(); - match self.ip { - IpAddr::V4(ip) => { - bytes.write_all(&u32::from(ip).to_be_bytes())?; - } - IpAddr::V6(ip) => { - bytes.write_all(&u128::from(ip).to_be_bytes())?; - } + Self { + interval: domain_announce_response.interval, + interval_min: domain_announce_response.interval_min, + complete: domain_announce_response.swarm_stats.seeders, + incomplete: domain_announce_response.swarm_stats.leechers, + peers, } - bytes.write_all(&self.port.to_be_bytes())?; - Ok(bytes) } } -impl From for CompactPeer { - fn from(peer: core::peer::Peer) -> Self { - CompactPeer { - ip: peer.peer_addr.ip(), - port: peer.peer_addr.port(), - } +/// Implement the [`axum::response::IntoResponse`] trait. +/// +/// To convert the [`Announce`] into a [`axum::response::Response`] +/// +impl axum::response::IntoResponse for Announce +where + Encode: Encoding, + Announce: Response, +{ + fn into_response(self) -> axum::response::Response { + axum::response::IntoResponse::into_response(self.body().map(|bytes| (StatusCode::OK, bytes))) } } -impl Compact { - /// Returns the bencoded compact response as a byte vector. - /// - /// # Errors - /// - /// Will return `Err` if internally interrupted. - pub fn body(&self) -> Result, Box> { - let bytes = (ben_map! { +/// Implement the [`Response`] for the [`Announce`] with [`EncodeNormal`] [`Encoding`]. +/// +impl Response for Announce { + fn body(&self) -> Result, responses::error::Error> { + let mut peers_list = ben_list!(); + let peers_list_mut = peers_list.list_mut().unwrap(); + for peer in &self.peers { + peers_list_mut.push(peer.ben_map()); + } + + let encode = (ben_map! { "complete" => ben_int!(i64::from(self.complete)), "incomplete" => ben_int!(i64::from(self.incomplete)), "interval" => ben_int!(i64::from(self.interval)), "min interval" => ben_int!(i64::from(self.interval_min)), - "peers" => ben_bytes!(self.peers_v4_bytes()?), - "peers6" => ben_bytes!(self.peers_v6_bytes()?) + "peers" => peers_list.clone() }) .encode(); - Ok(bytes) + Ok(encode) } +} +/// Implement the [`Response`] for the [`Announce`] with [`EncodeCompact`] [`Encoding`]. +/// +impl Response for Announce { + fn body(&self) -> Result, responses::error::Error> { + let res: &dyn Fn() -> Result, Box> = &move || { + let bytes = (ben_map! { + "complete" => ben_int!(i64::from(self.complete)), + "incomplete" => ben_int!(i64::from(self.incomplete)), + "interval" => ben_int!(i64::from(self.interval)), + "min interval" => ben_int!(i64::from(self.interval_min)), + "peers" => ben_bytes!(self.peers_v4_bytes()?), + "peers6" => ben_bytes!(self.peers_v6_bytes()?) + }) + .encode(); + + Ok(bytes) + }; + + res().map_err(|err| { + responses::error::Error::from(CompactSerializationError::CannotWriteBytes { + location: Location::caller(), + inner_error: format!("{err}"), + }) + }) + } +} + +impl Announce { fn peers_v4_bytes(&self) -> Result, Box> { let mut bytes: Vec = Vec::new(); - for compact_peer in &self.peers { - match compact_peer.ip { - IpAddr::V4(_ip) => { - let peer_bytes = compact_peer.bytes()?; - bytes.write_all(&peer_bytes)?; - } - IpAddr::V6(_) => {} + + for peer in &self.peers { + if let CompactPeer::V4(data) = peer { + bytes.write_all(&u32::from(data.ip).to_be_bytes())?; + bytes.write_all(&data.port.to_be_bytes())?; } } + Ok(bytes) } fn peers_v6_bytes(&self) -> Result, Box> { let mut bytes: Vec = Vec::new(); - for compact_peer in &self.peers { - match compact_peer.ip { - IpAddr::V6(_ip) => { - let peer_bytes = compact_peer.bytes()?; - bytes.write_all(&peer_bytes)?; - } - IpAddr::V4(_) => {} + + for peer in &self.peers { + if let CompactPeer::V6(data) = peer { + bytes.write_all(&u128::from(data.ip).to_be_bytes())?; + bytes.write_all(&data.port.to_be_bytes())?; } } Ok(bytes) } } +/// A [`NormalPeer`], for the [`Normal`] [`Encoding`]. +/// +/// ```rust +/// use std::net::{IpAddr, Ipv4Addr}; +/// use torrust_tracker::servers::http::v1::responses::announce::{Normal, NormalPeer}; +/// +/// let peer = NormalPeer { +/// peer_id: *b"-qB00000000000000001", +/// ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 +/// port: 0x7070, // 28784 +/// }; +/// +/// ``` +#[derive(Debug, PartialEq)] +pub struct NormalPeer { + /// The peer's ID. + pub peer_id: [u8; 20], + /// The peer's IP address. + pub ip: IpAddr, + /// The peer's port number. + pub port: u16, +} + +impl NormalPeer { + #[must_use] + pub fn ben_map(&self) -> BencodeMut<'_> { + ben_map! { + "peer id" => ben_bytes!(self.peer_id.clone().to_vec()), + "ip" => ben_bytes!(self.ip.to_string()), + "port" => ben_int!(i64::from(self.port)) + } + } +} + +impl From for NormalPeer { + fn from(peer: core::peer::Peer) -> Self { + NormalPeer { + peer_id: peer.peer_id.to_bytes(), + ip: peer.peer_addr.ip(), + port: peer.peer_addr.port(), + } + } +} + +/// A [`CompactPeer`], for the [`Compact`] [`Encoding`]. +/// +/// _"To reduce the size of tracker responses and to reduce memory and +/// computational requirements in trackers, trackers may return peers as a +/// packed string rather than as a bencoded list."_ +/// +/// A part from reducing the size of the response, this format does not contain +/// the peer's ID. +/// +/// ```rust +/// use std::net::{IpAddr, Ipv4Addr}; +/// use torrust_tracker::servers::http::v1::responses::announce::{Compact, CompactPeer, CompactPeerData}; +/// +/// let peer = CompactPeer::V4(CompactPeerData { +/// ip: Ipv4Addr::new(0x69, 0x69, 0x69, 0x69), // 105.105.105.105 +/// port: 0x7070, // 28784 +/// }); +/// +/// ``` +/// +/// Refer to [BEP 23: Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) +/// for more information. +#[derive(Debug, PartialEq)] +pub enum CompactPeer { + /// The peer's IP address. + V4(CompactPeerData), + /// The peer's port number. + V6(CompactPeerData), +} + +/// The [`CompactPeerData`], that contains either a [`Ipv4Addr`], or [`Ipv6Addr`] along with a `port`. +/// +#[derive(Debug, PartialEq)] +pub struct CompactPeerData { + /// The peer's IP address. + pub ip: V, + /// The peer's port number. + pub port: u16, +} + +impl From for CompactPeer { + fn from(peer: core::peer::Peer) -> Self { + match (peer.peer_addr.ip(), peer.peer_addr.port()) { + (IpAddr::V4(ip), port) => Self::V4(CompactPeerData { ip, port }), + (IpAddr::V6(ip), port) => Self::V6(CompactPeerData { ip, port }), + } + } +} + /// `Compact` response serialization error. #[derive(Error, Debug)] pub enum CompactSerializationError { @@ -388,44 +399,14 @@ impl From for responses::error::Error { } } -impl IntoResponse for Compact { - fn into_response(self) -> Response { - match self.body() { - Ok(bytes) => (StatusCode::OK, bytes).into_response(), - Err(err) => responses::error::Error::from(CompactSerializationError::CannotWriteBytes { - location: Location::caller(), - inner_error: format!("{err}"), - }) - .into_response(), - } - } -} - -impl From for Compact { - fn from(domain_announce_response: AnnounceData) -> Self { - let peers: Vec = domain_announce_response - .peers - .iter() - .map(|peer| CompactPeer::from(*peer)) - .collect(); - - Self { - interval: domain_announce_response.interval, - interval_min: domain_announce_response.interval_min, - complete: domain_announce_response.swarm_stats.seeders, - incomplete: domain_announce_response.swarm_stats.leechers, - peers, - } - } -} - #[cfg(test)] mod tests { use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - use super::{NonCompact, Peer}; - use crate::servers::http::v1::responses::announce::{Compact, CompactPeer}; + use crate::servers::http::v1::responses::announce::{ + Announce, CompactPeer, CompactPeerData, EncodeCompact, Normal, NormalPeer, Response, + }; // Some ascii values used in tests: // @@ -441,20 +422,20 @@ mod tests { #[test] fn non_compact_announce_response_can_be_bencoded() { - let response = NonCompact { + let response: Normal = Announce { interval: 111, interval_min: 222, complete: 333, incomplete: 444, peers: vec![ // IPV4 - Peer { + NormalPeer { peer_id: *b"-qB00000000000000001", ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 port: 0x7070, // 28784 }, // IPV6 - Peer { + NormalPeer { peer_id: *b"-qB00000000000000002", ip: IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)), port: 0x7070, // 28784 @@ -462,7 +443,7 @@ mod tests { ], }; - let bytes = response.body(); + let bytes = response.body().expect("it should encode without error"); // cspell:disable-next-line 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"; @@ -475,26 +456,26 @@ mod tests { #[test] fn compact_announce_response_can_be_bencoded() { - let response = Compact { + let response: Announce = Announce { interval: 111, interval_min: 222, complete: 333, incomplete: 444, peers: vec![ // IPV4 - CompactPeer { - ip: IpAddr::V4(Ipv4Addr::new(0x69, 0x69, 0x69, 0x69)), // 105.105.105.105 - port: 0x7070, // 28784 - }, + CompactPeer::V4(CompactPeerData { + ip: Ipv4Addr::new(0x69, 0x69, 0x69, 0x69), + port: 0x7070, // 28784 + }), // IPV6 - CompactPeer { - ip: IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969)), + CompactPeer::V6(CompactPeerData { + ip: Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969), port: 0x7070, // 28784 - }, + }), ], }; - let bytes = response.body().unwrap(); + let bytes = response.body().expect("it should encode without error"); let expected_bytes = // cspell:disable-next-line