use crate::http::header_config::HeadersConfig; use crate::http::simple::HttpSwardArray; use crate::http::{RandomUrlGenerator, SimpleHttpRequest, SimpleHttpSward}; use crate::utils::multiplexed::{MultiplexedSward, MultiplexedSwardError}; use rand::Rng; use reqwest::Method; use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; use thiserror::Error; use tower::ServiceExt; use tower::Service; use url::Url; use wyrand::WyRand; #[derive(Clone)] enum AttackTarget { Random(RandomUrlGenerator), Fixed(Url), } pub struct IntegratedHttpSward { attack_target: AttackTarget, request_sender: MultiplexedSward>>, } impl IntegratedHttpSward { pub async fn oneshot(&mut self) -> Result { let url = match &mut self.attack_target { AttackTarget::Random(random) => random.generate_url()?, AttackTarget::Fixed(url) => url.clone() }; let request = SimpleHttpRequest { body: None, url }; let res = self.request_sender.ready()?.call(request); todo!() } } impl IntegratedHttpSward { pub fn builder() -> IntegratedHttpSwardBuilder { IntegratedHttpSwardBuilder::new() } } pub struct IntegratedHttpSwardBuilder { capacity: usize, headers_config: HeadersConfig, method: Method, ip_list: Box<[IpAddr]>, } impl Default for IntegratedHttpSwardBuilder { fn default() -> Self { Self { capacity: 32, method: Method::GET, headers_config: HeadersConfig::default(), ip_list: Box::new([]), } } } impl IntegratedHttpSwardBuilder { pub fn new() -> Self { Self::default() } pub fn set_capacity(mut self, cap: usize) -> Self { self.capacity = cap; self } pub fn set_method(mut self, method: Method) -> Self { self.method = method; self } pub fn set_headers_config(mut self, headers: HeadersConfig) -> Self { self.headers_config = headers; self } pub fn set_ip_list(mut self, ip: &[IpAddr]) -> Self { let cloned: Box<[_]> = ip.iter().copied().collect(); self.ip_list = cloned; self } fn build_clients( self, url_example: &Url, ) -> Result< impl Iterator>, HttpSwardBuildError, > { // check the ip list if self.ip_list.is_empty() { return Err(HttpSwardBuildError::EmptyIpResolve); } // resolve the IP let domain = url_example .host_str() .ok_or(HttpSwardBuildError::MissingHost)?; let port = url_example.port_or_known_default().unwrap_or(443); let socket_addrs = self .ip_list .into_iter() .map(move |ip| SocketAddr::new(ip, port)); // get the cookie let maybe_cookie = self .headers_config .get_cookie_jar(&url_example) .map(Arc::new); // build the clients let clients = socket_addrs .map(|addr| { reqwest::Client::builder() .resolve(domain, addr) .use_native_tls() }) .map(move |builder| self.headers_config.set_client_header(builder)) .map(move |builder| match &maybe_cookie { Some(jar) => builder.cookie_provider(jar.clone()).cookie_store(true), None => builder, }) .map(|builder| { builder .build() .map_err(HttpSwardBuildError::ReqwestBuildError) }); Ok(clients) } pub fn url(self, url: Url) -> Result { let request_time_headers = self.headers_config.other_headers.clone(); let method = self.method.clone(); let capacity = self.capacity; let clients = self.build_clients(&url)?; let swards: Box<[_]> = clients .map(|client_result| { client_result.map(|client| { SimpleHttpSward::new(client, method.clone(), request_time_headers.clone()) }) }) .collect::>()?; Ok(IntegratedHttpSward { attack_target: AttackTarget::Fixed(url), request_sender: MultiplexedSward::new(SimpleHttpSward::array(swards), capacity), }) } pub fn template( self, template: impl AsRef, ) -> Result { let request_time_headers = self.headers_config.other_headers.clone(); let method = self.method.clone(); let capacity = self.capacity; let random_core = WyRand::new(rand::rng().random()); let mut random_url_generator = RandomUrlGenerator::new(random_core, template.as_ref()) .map_err(|_| HttpSwardBuildError::BadTemplate)?; let example_url = random_url_generator .generate_url() .map_err(|_| HttpSwardBuildError::BadTemplate)?; let clients = self.build_clients(&example_url)?; let swards: Box<[_]> = clients .map(|client_result| { client_result.map(|client| { SimpleHttpSward::new(client, method.clone(), request_time_headers.clone()) }) }) .collect::>()?; Ok(IntegratedHttpSward { attack_target: AttackTarget::Random(random_url_generator), request_sender: MultiplexedSward::new(SimpleHttpSward::array(swards), capacity), }) } } #[derive(Debug, Error)] pub enum HttpSwardBuildError { #[error("Cannot parse domain or address in the url.")] MissingHost, #[error("The IP list is empty")] EmptyIpResolve, #[error("The template cannot generate a correct URL")] BadTemplate, #[error("Failed to build reqwest client {0}")] ReqwestBuildError(#[from] reqwest::Error), } #[derive(Debug, Error)] pub enum HttpSwardError { #[error("Http Response: {0}")] HttpCode(u8), #[error(transparent)] MultiplexError(#[from] MultiplexedSwardError), #[error(transparent)] BadUrl(#[from] url::ParseError) }