如何在连接器上配置多态的 hyper 0.11.x 客户端变量?
How do I configure a hyper 0.11.x client variable that is polymorphic over the connector?
我似乎无法弄清楚如何让 Rust 在同一变量中接受客户端和代理客户端。虽然我对 Rust 还是个新手,但我对编程有基本的了解。到目前为止,我已经尝试过结构(但没有实现)、类型转换、未初始化的变量,但没有任何效果。
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"
我做了a GitHub repo of all the files.
我在尝试编译时遇到此错误:
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>, _>`
如果有更好的处理方法,我也想了解一下
这个解决方案并不漂亮,但确实有效。
我们首先创建一个枚举来处理这两种情况:
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
之前,这不会起作用。对于以正确方式实现 Service
的任何类型,都有 Connect
的全面实现,所以我们走那条路:
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)
}
}
}
}
我们使用多个特征对象来执行运行时多态性:一个用于连接返回的未来,另一个用于该未来产生的每个值。
完整代码:
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);
}
我实际上并没有可供测试的代理,但它确实编译并报告了一个关于无法连接的合理错误。
基于@Shepmaster 的回答,下面的代码已经使用 Privoxy 进行了测试。
我们使用double if let 子句首先提取被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);
}
我似乎无法弄清楚如何让 Rust 在同一变量中接受客户端和代理客户端。虽然我对 Rust 还是个新手,但我对编程有基本的了解。到目前为止,我已经尝试过结构(但没有实现)、类型转换、未初始化的变量,但没有任何效果。
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"
我做了a GitHub repo of all the files.
我在尝试编译时遇到此错误:
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>, _>`
如果有更好的处理方法,我也想了解一下
这个解决方案并不漂亮,但确实有效。
我们首先创建一个枚举来处理这两种情况:
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
之前,这不会起作用。对于以正确方式实现 Service
的任何类型,都有 Connect
的全面实现,所以我们走那条路:
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)
}
}
}
}
我们使用多个特征对象来执行运行时多态性:一个用于连接返回的未来,另一个用于该未来产生的每个值。
完整代码:
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);
}
我实际上并没有可供测试的代理,但它确实编译并报告了一个关于无法连接的合理错误。
基于@Shepmaster 的回答,下面的代码已经使用 Privoxy 进行了测试。
我们使用double if let 子句首先提取被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);
}