use tokio::net::TcpStream;
use crate::{ErrorKind, Proxy, ProxyError, ProxyResult};
pub struct ProxyChain {
chain: Vec<Proxy>,
}
impl ProxyChain {
pub fn new() -> Self {
Self { chain: Vec::new() }
}
pub fn add_proxy(&mut self, proxy: Proxy) {
self.chain.push(proxy);
}
pub fn add_proxies(&mut self, proxies: Vec<Proxy>) {
self.chain.extend(proxies);
}
pub fn with_proxy(mut self, proxy: Proxy) -> Self {
self.chain.push(proxy);
self
}
pub fn with_proxies(mut self, proxies: Vec<Proxy>) -> Self {
self.chain.extend(proxies);
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)
}
}
#[cfg(test)]
mod tests {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::{Proxy, ProxyChain, ProxyResult};
#[tokio::test]
async fn test_mini_chain() -> ProxyResult<()> {
let proxies = vec![
Proxy::from("socks4://98.181.137.83:4145"),
Proxy::from("socks4://98.170.57.249:4145"),
Proxy::from("socks5://212.58.132.5:1080"),
];
let chain = ProxyChain::new().with_proxies(proxies);
let mut stream = chain.connect("ipinfo.io", 80).await?;
stream.write_all(b"GET / HTTP/1.0\r\nHost: ipinfo.io\r\n\r\n").await?;
let mut resp = Vec::new();
stream.read_to_end(&mut resp).await?;
println!("{}", String::from_utf8_lossy(&resp));
Ok(())
}
}