Как настроить клиентскую переменную гипер 0.11.x, которая полиморфна по соединителю? - PullRequest
0 голосов
/ 11 июня 2018

Я не могу понять, как заставить Rust принять клиента и прокси-клиента в одной переменной.Хотя я все еще новичок в Rust, у меня есть базовое понимание программирования.До сих пор я пробовал структуры (но не impl's), приведение типов, неинициализированные переменные, но ничего не работает.

extern crate futures;
extern crate hyper;
extern crate hyper_proxy;
extern crate stopwatch;
extern crate tokio_core;

use futures::{Future, Stream};
use hyper::client::HttpConnector;
use hyper::Client;
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
use tokio_core::reactor::Core;

fn main() {
    let use_proxy = true;
    let proxy_uri: Option<String> = Some("http://localhost:8118".to_owned());

    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let mut proxy = None;
    // looking for polymorphic variable that works with both proxyed and unproxyed hyper clients
    let mut client: hyper::Client<hyper::client::HttpConnector, hyper::Body>;

    if use_proxy && proxy_uri.is_some() {
        println!("Using proxy: {}", proxy_uri.unwrap().as_str());
        proxy = Some({
            let proxy_uri = proxy_uri.unwrap().parse().unwrap();
            let mut proxy = Proxy::new(Intercept::All, proxy_uri);
            let connector = HttpConnector::new(4, &handle);
            let proxy_connector = ProxyConnector::from_proxy(connector, proxy).unwrap();
            proxy_connector
        });
        client = Client::configure()
            .connector(proxy.clone().unwrap())
            .build(&handle);
    } else {
        client = Client::configure()
            .connector(HttpConnector::new(4, &handle))
            .build(&handle);
    }

    // use hyper client below
}
[dependencies]
futures = "0.1.21"
hyper = "0.11.27"
tokio-core = "0.1.17"
hyper-proxy = "0.4.1"
stopwatch = "0.0.7"

Я сделал GitHub-репо всех файлов .

Я получаю эту ошибку при попытке компиляции:

 error[E0308]: mismatched types
  --> src/main.rs:32:18
   |
32 |           client = Client::configure()
   |  __________________^
33 | |             .connector(proxy.clone().unwrap())
34 | |             .build(&handle);
   | |___________________________^ expected struct `hyper::client::HttpConnector`, found struct `hyper_proxy::ProxyConnector`
   |
   = note: expected type `hyper::Client<hyper::client::HttpConnector, _>`
              found type `hyper::Client<hyper_proxy::ProxyConnector<hyper::client::HttpConnector>, _>`

Если есть лучший подход к этому, я также хотел бы знать об этом.

Ответы [ 2 ]

0 голосов
/ 11 июня 2018

Основываясь на ответе @ Shepmaster, приведенный ниже код был протестирован с использованием Privoxy.

Мы используем двойные предложения if, чтобы сначала извлечь тип, скрытый ProxyOrNotConnector, а затем изменить запрос http для использования прокси.

if let ProxyOrNotConnector::Proxy(x) = connector.clone() {
    if let Some(headers) = x.http_headers(&uri) {
        req.headers_mut().extend(headers.iter());
        req.set_proxy(true);
    }
}

Полный код:

extern crate futures;
extern crate hyper;
extern crate hyper_proxy;
extern crate tokio_core;
extern crate tokio_io;

use futures::{Future, Stream};
use hyper::{Chunk, Method, Request, Uri, client::{Config, HttpConnector, Service}};
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
use std::io;
use tokio_core::reactor::Core;
use tokio_io::{AsyncRead, AsyncWrite};

trait AsyncRw: AsyncWrite + AsyncRead {}
impl<T> AsyncRw for T
where
    T: AsyncWrite + AsyncRead,
{
}

#[derive(Clone)]
enum ProxyOrNotConnector {
    Proxy(ProxyConnector<HttpConnector>),
    Not(HttpConnector),
}

impl Service for ProxyOrNotConnector {
    type Request = Uri;
    type Response = Box<AsyncRw>;
    type Error = io::Error;

    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        match self {
            ProxyOrNotConnector::Proxy(p) => {
                let x = p.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
            ProxyOrNotConnector::Not(n) => {
                let x = n.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
        }
    }
}

fn main() {
    let proxy_uri = Some("http://localhost:8118");
    let use_proxy = true;
    let uri: Uri = "http://httpbin.org/ip".parse().unwrap();

    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let http_connector = HttpConnector::new(4, &handle);

    let connector = match (proxy_uri, use_proxy) {
        (Some(proxy_uri), true) => {
            println!("Using proxy: {}", proxy_uri);
            let proxy_uri = proxy_uri.parse().unwrap();
            let proxy = Some(Proxy::new(Intercept::All, proxy_uri));
            let proxy_connector =
                ProxyConnector::from_proxy(http_connector, proxy.unwrap()).unwrap();
            ProxyOrNotConnector::Proxy(proxy_connector)
        }
        _ => ProxyOrNotConnector::Not(http_connector),
    };

    let client = Config::default().connector(connector.clone()).build(&handle);
    let mut req: hyper::Request;
    match use_proxy {
        true => {
            req = Request::new(Method::Get, uri.clone());
            if let ProxyOrNotConnector::Proxy(x) = connector.clone() {
                if let Some(headers) = x.http_headers(&uri) {
                    req.headers_mut().extend(headers.iter());
                    req.set_proxy(true);
                }
            }
        }
        false => req = Request::new(Method::Get, uri.clone()),
    }

    let future_http = client
        .request(req)
        .and_then(|res| res.body().concat2())
        .map(move |body: Chunk| ::std::str::from_utf8(&body).unwrap().to_string());

    let x = core.run(future_http).unwrap();
    println!("{:?}", x);
}
0 голосов
/ 11 июня 2018

Это решение не очень красивое, но оно работает.

Мы начинаем с создания перечисления для обработки двух случаев:

enum ProxyOrNotConnector {
    Proxy(ProxyConnector<HttpConnector>),
    Not(HttpConnector),
}

Это перечисление может быть одним типом, представляющим обаслучаев.Построить его просто с помощью оператора match:

let http_connector = HttpConnector::new(4, &handle);

let connector = match (proxy_uri, use_proxy) {
    (Some(proxy_uri), true) => {
        println!("Using proxy: {}", proxy_uri);
        let proxy_uri = proxy_uri.parse().unwrap();
        let mut proxy = Proxy::new(Intercept::All, proxy_uri);
        let proxy_connector = ProxyConnector::from_proxy(http_connector, proxy).unwrap();
        ProxyOrNotConnector::Proxy(proxy_connector)
    }
    _ => ProxyOrNotConnector::Not(http_connector),
};

Затем мы можем создать Client, используя этот соединитель:

let client = Config::default().connector(connector).build(&handle);

Это не будет работать, пока мы нереализовано Connect для нашего перечисления.Существует общая реализация Connect для любого типа, которая правильно реализует Service, поэтому мы идем по этому пути:

impl Service for ProxyOrNotConnector {
    type Request = Uri;
    type Response = Box<AsyncRw>;
    type Error = io::Error;

    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        match self {
            ProxyOrNotConnector::Proxy(p) => {
                let x = p.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
            ProxyOrNotConnector::Not(n) => {
                let x = n.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
        }
    }
}

Мы используем несколько объектов trait для выполнения полиморфизма во время выполнения: один на будущеевозвращается через соединение и другое для каждого значения, полученного этим будущим.

Полный код:

extern crate futures;
extern crate hyper;
extern crate hyper_proxy;
extern crate tokio_core;
extern crate tokio_io;

use futures::Future;
use hyper::{
    client::{Config, HttpConnector, Service},
    Uri,
};
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
use std::io;
use tokio_core::reactor::Core;
use tokio_io::{AsyncRead, AsyncWrite};

trait AsyncRw: AsyncWrite + AsyncRead {}
impl<T> AsyncRw for T where T: AsyncWrite + AsyncRead {}

enum ProxyOrNotConnector {
    Proxy(ProxyConnector<HttpConnector>),
    Not(HttpConnector),
}

impl Service for ProxyOrNotConnector {
    type Request = Uri;
    type Response = Box<AsyncRw>;
    type Error = io::Error;

    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        match self {
            ProxyOrNotConnector::Proxy(p) => {
                let x = p.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
            ProxyOrNotConnector::Not(n) => {
                let x = n.call(req);
                let y = x.map(|y| Box::new(y) as Box<AsyncRw>);
                Box::new(y)
            }
        }
    }
}

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let proxy_uri = Some("http://127.0.0.1");
    let use_proxy = true;

    let http_connector = HttpConnector::new(4, &handle);

    let connector = match (proxy_uri, use_proxy) {
        (Some(proxy_uri), true) => {
            println!("Using proxy: {}", proxy_uri);
            let proxy_uri = proxy_uri.parse().unwrap();
            let mut proxy = Proxy::new(Intercept::All, proxy_uri);
            let proxy_connector = ProxyConnector::from_proxy(http_connector, proxy).unwrap();
            ProxyOrNotConnector::Proxy(proxy_connector)
        }
        _ => ProxyOrNotConnector::Not(http_connector),
    };

    let client = Config::default().connector(connector).build(&handle);
    let g = client.get("http://127.0.0.1/".parse().unwrap());

    let x = core.run(g).unwrap();
    println!("{:?}", x);
}

На самом деле у меня нет прокси, на котором можно тестировать, но он компилируется исообщить о разумной ошибке о невозможности подключения.

...