Skip to content

Commit e21e23b

Browse files
committed
fix(protocol): saturate scrape counters and add regression tests
1 parent 62f2d17 commit e21e23b

2 files changed

Lines changed: 37 additions & 7 deletions

File tree

packages/http-protocol/src/v1/responses/scrape.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,25 @@ mod tests {
131131
String::from_utf8(expected_bytes.to_vec()).unwrap()
132132
);
133133
}
134+
135+
#[test]
136+
fn should_saturate_large_download_counts() {
137+
let info_hash = InfoHash::from_bytes(&[0x69; 20]);
138+
let mut scrape_data = ScrapeData::empty();
139+
scrape_data.add_file(
140+
&info_hash,
141+
SwarmMetadata {
142+
complete: 1,
143+
downloaded: u32::MAX,
144+
incomplete: 3,
145+
},
146+
);
147+
148+
let response = Bencoded::from(scrape_data);
149+
let bytes = response.body();
150+
let body = String::from_utf8(bytes).unwrap();
151+
152+
assert!(body.contains(&format!("downloadedi{}e", i64::from(u32::MAX))));
153+
}
134154
}
135155
}

packages/udp-tracker-server/src/handlers/scrape.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,22 @@ pub async fn handle_scrape(
5353
Ok(build_response(request, &scrape_data))
5454
}
5555

56+
fn udp_counter_from_u32(value: u32) -> i32 {
57+
// Temporary saturation guard for UDP i32 counters. Proper type alignment across Rust and DB layers
58+
// will be addressed in docs/issues/1525-07-align-rust-and-db-types.md.
59+
i32::try_from(value).unwrap_or(i32::MAX)
60+
}
61+
5662
fn build_response(request: &ScrapeRequest, scrape_data: &ScrapeData) -> Response {
5763
let mut torrent_stats: Vec<TorrentScrapeStatistics> = Vec::new();
5864

5965
for file in &scrape_data.files {
6066
let swarm_metadata = file.1;
6167

62-
#[allow(clippy::cast_possible_truncation)]
63-
let scrape_entry = {
64-
TorrentScrapeStatistics {
65-
seeders: NumberOfPeers(I32::new(i64::from(swarm_metadata.complete) as i32)),
66-
completed: NumberOfDownloads(I32::new(i64::from(swarm_metadata.downloaded) as i32)),
67-
leechers: NumberOfPeers(I32::new(i64::from(swarm_metadata.incomplete) as i32)),
68-
}
68+
let scrape_entry = TorrentScrapeStatistics {
69+
seeders: NumberOfPeers(I32::new(udp_counter_from_u32(swarm_metadata.complete))),
70+
completed: NumberOfDownloads(I32::new(udp_counter_from_u32(swarm_metadata.downloaded))),
71+
leechers: NumberOfPeers(I32::new(udp_counter_from_u32(swarm_metadata.incomplete))),
6972
};
7073

7174
torrent_stats.push(scrape_entry);
@@ -458,4 +461,11 @@ mod tests {
458461
}
459462
}
460463
}
464+
465+
#[test]
466+
fn should_saturate_large_download_counts_for_udp_protocol() {
467+
assert_eq!(super::udp_counter_from_u32(u32::MAX), i32::MAX);
468+
assert_eq!(super::udp_counter_from_u32((i32::MAX as u32) + 1), i32::MAX);
469+
assert_eq!(super::udp_counter_from_u32(42), 42);
470+
}
461471
}

0 commit comments

Comments
 (0)