reqwest_proxy 0.1.7

Seamlessly proxy reqwest traffic through Shadowsocks, Hysteria2, and more. / 通过 Shadowsocks、Hysteria2 等多种协议无缝代理 reqwest 流量。
#[macro_export]
macro_rules! enums {

  ($($name:ident, $name_str:literal);+) => {
    pastey::paste!{
      $crate::enums!(
        $(
          $name, $name_str, [< $name:camel >]
        );+
      );
    }
  };

  ($($name:ident, $name_str:literal, $name_camel:ident);+) => {
    use std::{io, pin::Pin, task, task::Poll};
    use async_trait::async_trait;
    use hyper::Uri;
    use hyper_util::client::legacy::connect::Connection;
    use reqwest::{Request, Response};
    use reqwest_middleware::{Error, Middleware, Next};
    use tower::Service;
    use $crate::{Error::UnsupportedProtocol, ProxyMiddleware};

    $(
      #[cfg(feature = $name_str)]
      use $crate::$name;
    )+

    pub enum StreamEnum {
      $(
        #[cfg(feature = $name_str)]
        $name_camel($name::StreamEnumType)
      ),+
    }

    $(
      #[cfg(feature = $name_str)]
      impl From<$name::StreamType> for StreamEnum {
        fn from(s: $name::StreamType) -> Self {
          StreamEnum::$name_camel(s.into())
        }
      }
    )+

      #[derive(Clone)]
      pub enum ProxyConn {
        $(
          #[cfg(feature = $name_str)]
          $name_camel($name::Conn),
        )+
      }

    impl ProxyConn {
      pub fn from_url(url: &str) -> $crate::Result<Self> {
        if let Some(scheme) = url.split(':').next() {
          $(
            #[cfg(feature = $name_str)]
            if scheme == $name::SCHEME {
              let connector = $name::Conn::new(url)?;
              return Ok(ProxyConn::$name_camel(connector));
            }
          )+
        }
        Err(UnsupportedProtocol(url.into()))
      }
    }

    impl hyper::rt::Read for StreamEnum {
      fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: hyper::rt::ReadBufCursor<'_>,
      ) -> Poll<io::Result<()>> {
          match self.get_mut() {
            $(
              #[cfg(feature = $name_str)]
              StreamEnum::$name_camel(s) => Pin::new(s).poll_read(cx, buf),
            )+
          }
      }
    }

    impl hyper::rt::Write for StreamEnum {
      fn poll_write(
        self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
        buf: &[u8],
      ) -> Poll<Result<usize, io::Error>> {
          match self.get_mut() {
            $(
              #[cfg(feature = $name_str)]
              StreamEnum::$name_camel(s) => Pin::new(s).poll_write(cx, buf),
            )+
          }
      }

      fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Result<(), io::Error>> {
          match self.get_mut() {
            $(
              #[cfg(feature = $name_str)]
              StreamEnum::$name_camel(s) => Pin::new(s).poll_flush(cx),
            )+
          }
      }

      fn poll_shutdown(
        self: Pin<&mut Self>,
        cx: &mut task::Context<'_>,
      ) -> Poll<Result<(), io::Error>> {
          match self.get_mut() {
            $(
              #[cfg(feature = $name_str)]
              StreamEnum::$name_camel(s) => Pin::new(s).poll_shutdown(cx),
            )+
          }
      }
    }

    impl Connection for StreamEnum {
      fn connected(&self) -> hyper_util::client::legacy::connect::Connected {
          match self {
            $(
              #[cfg(feature = $name_str)]
              StreamEnum::$name_camel(s) => s.connected(),
            )+
          }
      }
    }

    pub enum ConnFuture {
      $(
        #[cfg(feature = $name_str)]
        $name_camel($crate::conn::$name::ConnFuture),
      )+
    }

    impl Future for ConnFuture {
      type Output = Result<StreamEnum, io::Error>;

      fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
        match self.get_mut() {
          $(
            #[cfg(feature = $name_str)]
            ConnFuture::$name_camel(f) => Pin::new(f).poll(cx).map(|r| r.map(|s| s.into())),
          )+
        }
      }
    }
    impl Service<Uri> for ProxyConn {
      type Response = StreamEnum;
      type Error = io::Error;
      type Future = ConnFuture;

      fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        match self {
          $(
            #[cfg(feature = $name_str)]
            ProxyConn::$name_camel(c) => c.poll_ready(cx)
          ),+
        }
      }

      fn call(&mut self, uri: Uri) -> Self::Future {
        match self {
          $(
              #[cfg(feature = $name_str)]
              ProxyConn::$name_camel(c) => ConnFuture::$name_camel(c.call(uri))
          ),+
        }
      }
    }

    pub enum Proxy {
      $(
        #[cfg(feature = $name_str)]
        $name_camel(ProxyMiddleware<$name::Conn>),
      )+
    }

    #[async_trait]
    impl Middleware for Proxy {
      async fn handle(
          &self,
          req: Request,
          extensions: &mut http::Extensions,
          next: Next<'_>,
      ) -> Result<Response, Error> {
          match self {
            $(
              #[cfg(feature = $name_str)]
              Proxy::$name_camel(mw) => mw.handle(req, extensions, next).await,
            )+
          }
      }
    }

    impl Proxy {
      pub fn from_url(url: &str) -> $crate::Result<Self> {
        if let Some(init) = url.split(":").next() {
          $(
            #[cfg(feature = $name_str)]
            if init == $name::SCHEME {
                return Ok(Proxy::$name_camel($name::from_url(url)?));
            }
          )+
        }
        Err(UnsupportedProtocol(url.to_string()))
      }
    }
  };
}