Search code examples
rustipfuturelanreqwest

Send parallel requests and return the first 200 response in rust


I am trying to write a discovery function in Rust that discovers the device IP in LAN, this function should sends HTTP requests for all possible IPs in the IP space and once receives 200 status return the value and cancels all others requests (by dropping the future in case reqwest used)

async fn discover() -> Result<String, ()> {
    for i in 0..256 {
        tokio::spawn(async move {
            let url = format!("http://192.168.0.{i}:8000/ping");

            reqwest::get(url).await;
        });
    }
}

Edit 1

once receives 200 status return the value and cancels all others requests

We would like to also validate the response content to make sure that this is the correct device and not someone who is trying to spy on the network we will validate the response by verifying the JWT token returning from the /ping endpoint, how can we do this


Solution

  • This may be achievable with the futures-crate and select_ok().

    You can do the following:

    • convert the range in an Iterator and map each element i into a reqwest request. select_ok needs the Futures in the Vec to implement UnPin; that's why I used .boxed() here.

    • pass the Vec of futures into select_ok

    • the response will contain the first succesful future result

    • we can use the remaining_futures to cancel the remaining requests with drop (I am not sure if that works, you need to test this)

    use futures::{future::select_ok, FutureExt};
    
    async fn discover() {
        let futures = (0..256)
            .into_iter()
            .map(|i| {
                let url = format!("http://192.168.0.{i}:8000/ping");
    
                return reqwest::get(url).boxed();
            })
            .collect::<Vec<_>>();
    
        let (response, mut remaining_futures) = select_ok(futures).await.unwrap();
    
        remaining_futures.iter_mut().for_each(|f| drop(f));
    }