| 
1 |  | -//! HTTP Tracker client:  | 
2 |  | -//!  | 
3 |  | -//! Examples:  | 
4 |  | -//!  | 
5 |  | -//! `Announce` request:  | 
6 |  | -//!  | 
7 |  | -//! ```text  | 
8 |  | -//! cargo run --bin http_tracker_client announce http://127.0.0.1:7070 9c38422213e30bff212b30c360d26f9a02136422 | jq  | 
9 |  | -//! ```  | 
10 |  | -//!  | 
11 |  | -//! `Scrape` request:  | 
12 |  | -//!  | 
13 |  | -//! ```text  | 
14 |  | -//! cargo run --bin http_tracker_client scrape http://127.0.0.1:7070 9c38422213e30bff212b30c360d26f9a02136422 | jq  | 
15 |  | -//! ```  | 
16 |  | -use std::str::FromStr;  | 
17 |  | - | 
18 |  | -use anyhow::Context;  | 
19 |  | -use clap::{Parser, Subcommand};  | 
20 |  | -use reqwest::Url;  | 
21 |  | -use torrust_tracker::shared::bit_torrent::info_hash::InfoHash;  | 
22 |  | -use torrust_tracker::shared::bit_torrent::tracker::http::client::requests::announce::QueryBuilder;  | 
23 |  | -use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::announce::Announce;  | 
24 |  | -use torrust_tracker::shared::bit_torrent::tracker::http::client::responses::scrape;  | 
25 |  | -use torrust_tracker::shared::bit_torrent::tracker::http::client::{requests, Client};  | 
26 |  | - | 
27 |  | -#[derive(Parser, Debug)]  | 
28 |  | -#[command(author, version, about, long_about = None)]  | 
29 |  | -struct Args {  | 
30 |  | -    #[command(subcommand)]  | 
31 |  | -    command: Command,  | 
32 |  | -}  | 
33 |  | - | 
34 |  | -#[derive(Subcommand, Debug)]  | 
35 |  | -enum Command {  | 
36 |  | -    Announce { tracker_url: String, info_hash: String },  | 
37 |  | -    Scrape { tracker_url: String, info_hashes: Vec<String> },  | 
38 |  | -}  | 
 | 1 | +//! Program to make request to HTTP trackers.  | 
 | 2 | +use torrust_tracker::console::clients::http::app;  | 
39 | 3 | 
 
  | 
40 | 4 | #[tokio::main]  | 
41 | 5 | async fn main() -> anyhow::Result<()> {  | 
42 |  | -    let args = Args::parse();  | 
43 |  | - | 
44 |  | -    match args.command {  | 
45 |  | -        Command::Announce { tracker_url, info_hash } => {  | 
46 |  | -            announce_command(tracker_url, info_hash).await?;  | 
47 |  | -        }  | 
48 |  | -        Command::Scrape {  | 
49 |  | -            tracker_url,  | 
50 |  | -            info_hashes,  | 
51 |  | -        } => {  | 
52 |  | -            scrape_command(&tracker_url, &info_hashes).await?;  | 
53 |  | -        }  | 
54 |  | -    }  | 
55 |  | -    Ok(())  | 
56 |  | -}  | 
57 |  | - | 
58 |  | -async fn announce_command(tracker_url: String, info_hash: String) -> anyhow::Result<()> {  | 
59 |  | -    let base_url = Url::parse(&tracker_url).context("failed to parse HTTP tracker base URL")?;  | 
60 |  | -    let info_hash =  | 
61 |  | -        InfoHash::from_str(&info_hash).expect("Invalid infohash. Example infohash: `9c38422213e30bff212b30c360d26f9a02136422`");  | 
62 |  | - | 
63 |  | -    let response = Client::new(base_url)  | 
64 |  | -        .announce(&QueryBuilder::with_default_values().with_info_hash(&info_hash).query())  | 
65 |  | -        .await;  | 
66 |  | - | 
67 |  | -    let body = response.bytes().await.unwrap();  | 
68 |  | - | 
69 |  | -    let announce_response: Announce = serde_bencode::from_bytes(&body)  | 
70 |  | -        .unwrap_or_else(|_| panic!("response body should be a valid announce response, got: \"{:#?}\"", &body));  | 
71 |  | - | 
72 |  | -    let json = serde_json::to_string(&announce_response).context("failed to serialize scrape response into JSON")?;  | 
73 |  | - | 
74 |  | -    println!("{json}");  | 
75 |  | - | 
76 |  | -    Ok(())  | 
77 |  | -}  | 
78 |  | - | 
79 |  | -async fn scrape_command(tracker_url: &str, info_hashes: &[String]) -> anyhow::Result<()> {  | 
80 |  | -    let base_url = Url::parse(tracker_url).context("failed to parse HTTP tracker base URL")?;  | 
81 |  | - | 
82 |  | -    let query = requests::scrape::Query::try_from(info_hashes).context("failed to parse infohashes")?;  | 
83 |  | - | 
84 |  | -    let response = Client::new(base_url).scrape(&query).await;  | 
85 |  | - | 
86 |  | -    let body = response.bytes().await.unwrap();  | 
87 |  | - | 
88 |  | -    let scrape_response = scrape::Response::try_from_bencoded(&body)  | 
89 |  | -        .unwrap_or_else(|_| panic!("response body should be a valid scrape response, got: \"{:#?}\"", &body));  | 
90 |  | - | 
91 |  | -    let json = serde_json::to_string(&scrape_response).context("failed to serialize scrape response into JSON")?;  | 
92 |  | - | 
93 |  | -    println!("{json}");  | 
94 |  | - | 
95 |  | -    Ok(())  | 
 | 6 | +    app::run().await  | 
96 | 7 | }  | 
0 commit comments