use tokio::net::TcpStream;
use crate::{ErrorKind, Proxy, ProxyError, ProxyResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProxyChain {
chain: Vec<Proxy>,
}
impl ProxyChain {
pub fn new() -> Self {
Self { chain: Vec::new() }
}
pub fn add_proxy(&mut self, proxy: impl Into<Proxy>) {
self.chain.push(proxy.into());
}
pub fn add_proxies(&mut self, proxies: Vec<impl Into<Proxy>>) {
let mut iter = Vec::new();
for proxy in proxies {
iter.push(proxy.into());
}
self.chain.extend(iter);
}
pub fn with_proxy(mut self, proxy: impl Into<Proxy>) -> Self {
self.chain.push(proxy.into());
self
}
pub fn with_proxies(mut self, proxies: Vec<impl Into<Proxy>>) -> Self {
let mut iter = Vec::new();
for proxy in proxies {
iter.push(proxy.into());
}
self.chain.extend(iter);
self
}
pub fn clear(&mut self) {
self.chain.clear();
}
pub fn get_chain(&self) -> &Vec<Proxy> {
&self.chain
}
pub async fn connect(&self, target_host: impl Into<String>, target_port: u16) -> ProxyResult<TcpStream> {
let first_proxy = &self.chain[0];
let first_addr = first_proxy.get_address();
let mut stream = TcpStream::connect(first_addr).await?;
for proxy in &self.chain[1..] {
let proxy_ip = proxy
.get_ip()
.ok_or(ProxyError::new(ErrorKind::InvalidData, "failed to obtain proxy IP address"))?;
let proxy_port = proxy
.get_port()
.ok_or(ProxyError::new(ErrorKind::InvalidData, "failed to obtain proxy port"))?;
stream = proxy.connect_with_stream(stream, proxy_ip, proxy_port).await?;
}
let last_proxy = &self.chain[self.chain.len() - 1];
stream = last_proxy.connect_with_stream(stream, target_host, target_port).await?;
Ok(stream)
}
}
impl From<Vec<String>> for ProxyChain {
fn from(value: Vec<String>) -> Self {
let mut chain = Vec::new();
for address in value {
chain.push(Proxy::from(address));
}
Self { chain }
}
}
impl From<Vec<&str>> for ProxyChain {
fn from(value: Vec<&str>) -> Self {
let mut chain = Vec::new();
for address in value {
chain.push(Proxy::from(address));
}
Self { chain }
}
}
impl From<&str> for ProxyChain {
fn from(value: &str) -> Self {
let pretty_value = value.replace(" ", "").replace("\n", "");
let chain: Vec<&str> = pretty_value.split(",").collect();
Self::from(chain)
}
}
impl From<String> for ProxyChain {
fn from(value: String) -> Self {
let pretty_value = value.replace(" ", "").replace("\n", "");
let chain: Vec<&str> = pretty_value.split(",").collect();
Self::from(chain)
}
}