use std::collections::HashMap; use std::time::Duration; use anyhow::Result; use curl::easy::{Easy2, Handler, WriteError}; use curl::multi::{Easy2Handle, Multi}; const URLS: &[&str] = &[ "https://www.microsoft.com", "https://www.google.com", "https://www.amazon.com", "https://www.apple.com", ]; struct Collector(Vec); impl Handler for Collector { fn write(&mut self, data: &[u8]) -> Result { self.0.extend_from_slice(data); Ok(data.len()) } } fn download(multi: &mut Multi, token: usize, url: &str) -> Result> { let version = curl::Version::get(); let mut request = Easy2::new(Collector(Vec::new())); request.url(&url)?; request.useragent(&format!("curl/{}", version.version()))?; let mut handle = multi.add2(request)?; handle.set_token(token)?; Ok(handle) } fn main() -> Result<()> { let mut multi = Multi::new(); let mut handles = URLS .iter() .enumerate() .map(|(token, url)| Ok((token, download(&mut multi, token, url)?))) .collect::>>()?; let mut still_alive = true; while still_alive { // We still need to process the last messages when // `Multi::perform` returns "0". if multi.perform()? == 0 { still_alive = false; } multi.messages(|message| { let token = message.token().expect("failed to get the token"); let handle = handles .get_mut(&token) .expect("the download value should exist in the HashMap"); match message .result_for2(&handle) .expect("token mismatch with the `EasyHandle`") { Ok(()) => { let http_status = handle .response_code() .expect("HTTP request finished without status code"); println!( "R: Transfer succeeded (Status: {}) {} (Download length: {})", http_status, URLS[token], handle.get_ref().0.len() ); } Err(error) => { println!("E: {} - <{}>", error, URLS[token]); } } }); if still_alive { // The sleeping time could be reduced to allow other processing. // For instance, a thread could check a condition signalling the // thread shutdown. multi.wait(&mut [], Duration::from_secs(60))?; } } Ok(()) }