Skip to main content

api_scanner/scanner/common/
probe.rs

1use crate::{
2    error::CapturedError,
3    http_client::{HttpClient, HttpResponse},
4};
5use futures::stream::{self, StreamExt};
6
7pub struct BurstProbe {
8    pub count: usize,
9    pub headers: Option<Vec<(String, String)>>,
10}
11
12impl BurstProbe {
13    pub fn new(count: usize, headers: Option<Vec<(String, String)>>) -> Self {
14        Self { count, headers }
15    }
16
17    /// Execute concurrent burst requests.
18    ///
19    /// Captures owned values inside the async closures (`HttpClient` clone + owned URL)
20    /// to keep this burst path robust even if execution strategy is refactored later.
21    pub async fn execute(
22        &self,
23        client: &HttpClient,
24        url: &str,
25    ) -> Vec<Result<HttpResponse, CapturedError>> {
26        let headers = self.headers.clone();
27        let client = client.clone();
28        let url = url.to_string();
29
30        stream::iter(0..self.count)
31            .map(move |_| {
32                let headers = headers.clone();
33                let client = client.clone();
34                let url = url.clone();
35                async move {
36                    match headers.as_ref() {
37                        Some(h) => client.get_with_headers_burst(&url, h).await,
38                        None => client.get_burst(&url).await,
39                    }
40                }
41            })
42            .buffer_unordered(self.count.max(1))
43            .collect()
44            .await
45    }
46}