reqwest_proxy 0.1.7

Seamlessly proxy reqwest traffic through Shadowsocks, Hysteria2, and more. / 通过 Shadowsocks、Hysteria2 等多种协议无缝代理 reqwest 流量。
use std::{
  io,
  sync::Arc,
  task::{self, Poll},
};

use ::shadowsocks::{
  ProxyClientStream,
  config::{ServerConfig, ServerType},
  context::{Context, SharedContext},
  net::TcpStream,
  relay::Address,
};
use hyper::Uri;
use hyper_util::rt::TokioIo;
use tower::Service;

use crate::conn::shadowsocks::ConnFuture;
use crate::{middleware::ProxyMiddleware, stream::Stream};

pub const SCHEME: &str = "ss";
pub type StreamType = Stream<ProxyClientStream<TcpStream>>;
pub type StreamEnumType = Box<StreamType>;

#[derive(Clone)]
pub struct Conn {
  cfg: Arc<ServerConfig>,
  context: SharedContext,
}

impl Conn {
  pub fn new(url: &str) -> crate::Result<Self> {
    let cfg = ServerConfig::from_url(url)?;
    let context = Context::new(ServerType::Local);
    Ok(Self {
      cfg: Arc::new(cfg),
      context: Arc::new(context),
    })
  }
}

impl Service<Uri> for Conn {
  type Response = StreamType;
  type Error = io::Error;
  type Future = ConnFuture;

  fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
    Poll::Ready(Ok(()))
  }

  fn call(&mut self, uri: Uri) -> Self::Future {
    let context = self.context.clone();
    let cfg = self.cfg.clone();
    let fut = Box::pin(async move {
      let addr = uri_to_address(&uri)?;
      let stream = ProxyClientStream::connect(context, &cfg, &addr).await?;
      Ok(Stream(TokioIo::new(stream)))
    });
    ConnFuture { fut }
  }
}

fn uri_to_address(uri: &Uri) -> Result<Address, io::Error> {
  let (host, port) =
    uri_to_host_port::parse(uri).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
  Ok(Address::DomainNameAddress(host.to_string(), port))
}

pub fn from_url(url: &str) -> crate::Result<ProxyMiddleware<Conn>> {
  let connector = Conn::new(url)?;
  Ok(ProxyMiddleware::new(connector))
}