Files
ubw-network/ubw-sward/src/http/integrated.rs

212 lines
6.3 KiB
Rust
Raw Normal View History

2025-09-04 00:57:59 +09:00
use crate::http::header_config::HeadersConfig;
use crate::http::simple::HttpSwardArray;
2025-09-04 01:21:44 +09:00
use crate::http::{RandomUrlGenerator, SimpleHttpRequest, SimpleHttpSward};
use crate::utils::multiplexed::{MultiplexedSward, MultiplexedSwardError};
2025-09-04 00:57:59 +09:00
use rand::Rng;
use reqwest::Method;
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use thiserror::Error;
2025-09-04 01:21:44 +09:00
use tower::ServiceExt;
use tower::Service;
2025-09-04 00:57:59 +09:00
use url::Url;
use wyrand::WyRand;
#[derive(Clone)]
enum AttackTarget {
Random(RandomUrlGenerator<WyRand>),
Fixed(Url),
}
pub struct IntegratedHttpSward {
attack_target: AttackTarget,
request_sender: MultiplexedSward<HttpSwardArray<Box<[SimpleHttpSward]>>>,
}
2025-09-04 01:21:44 +09:00
impl IntegratedHttpSward {
pub async fn oneshot(&mut self) -> Result<reqwest::Response, HttpSwardError> {
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!()
}
}
2025-09-04 00:57:59 +09:00
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<Item = Result<reqwest::Client, HttpSwardBuildError>>,
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<IntegratedHttpSward, HttpSwardBuildError> {
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::<Result<_, HttpSwardBuildError>>()?;
Ok(IntegratedHttpSward {
attack_target: AttackTarget::Fixed(url),
request_sender: MultiplexedSward::new(SimpleHttpSward::array(swards), capacity),
})
}
pub fn template(
self,
template: impl AsRef<str>,
) -> Result<IntegratedHttpSward, HttpSwardBuildError> {
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::<Result<_, HttpSwardBuildError>>()?;
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),
}
2025-09-04 01:21:44 +09:00
#[derive(Debug, Error)]
pub enum HttpSwardError {
#[error("Http Response: {0}")]
HttpCode(u8),
#[error(transparent)]
MultiplexError(#[from] MultiplexedSwardError<reqwest::Error>),
#[error(transparent)]
BadUrl(#[from] url::ParseError)
}