-
Hey, I use tonic to build a grpc client. I need to connect to upstream grpc service with proxy. I searched for help in docs, examples and google but I got nothing about setting a proxy. It seems that tonic currently doesn't support connecting to grpc server with proxy . I'm a new tonic user and I found it hard for me to solve such problem alone and I come here for help. Does tonic support proxy for connecting to grpc service? If not, any alternative ways? BTW: |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
I would take a look at the hyper example in the tonic folder and see how you can use it since as you said the transport just use it under the hood. The transport does not support proxies as its designed to be simple. |
Beta Was this translation helpful? Give feedback.
-
The following example works! use hyper::client::{Client, HttpConnector};
use hyper::Uri;
use hyper_socks2::SocksConnector;
use hyper_openssl::HttpsConnector;
use openssl::ssl::{SslMethod, SslConnector};
use tonic::metadata::{AsciiMetadataValue, MetadataValue};
// ......
// here is your socks5 proxy addr
// must be like "socks5://host:port", scheme is required by HttpConnector
let socks5_addr = "socks5://10.10.2.1:10003";
let mut connector = HttpConnector::new();
// "enforce_http(false)" is necessary, without it compiler will not complain but it actually will panic :)
connector.enforce_http(false);
// now following the official instruction of crate hyper_socks2
let proxy = SocksConnector {
proxy_addr: socks5_addr.parse::<Uri>().unwrap(),
auth: None,
connector,
};
// we must delete this, because hyper_socks2 will not help us negotiate HTTP2. You MUST implement it by yourself...
// let proxy = proxy.with_tls().unwrap();
// negotiate h2
let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap();
ssl.set_alpn_protos(b"\x02h2").unwrap();
let connector = HttpsConnector::with_connector(proxy, ssl).unwrap();
// ok now we get HttpsConnector! And then we will build the hyper client. The following is easy.
let client = Client::builder().http2_only(true).build(connector);
// here is the upstream you are going to connect to. Modify it by yourself.
let uri = Uri::from_static("https://example.com");
let mut add_origin = tower::service_fn(|mut req: hyper::Request<tonic::body::BoxBody>| {
let uri = Uri::builder()
.scheme(uri.scheme().unwrap().clone())
.authority(uri.authority().unwrap().clone())
.path_and_query(req.uri().path_and_query().unwrap().clone())
.build()
.unwrap();
*req.uri_mut() = uri;
client.request(req)
});
// ...
// `grpc_playurl` is generated by tonic
let mut final_client = grpc_playurl::play_url_client::PlayUrlClient::with_interceptor(
add_origin,
move |mut req: tonic::Request<()>| {
// non binary example 1
req.metadata_mut()
.insert("example-key-1", AsciiMetadataValue::from_static("example-value-static"));
// non binary example 2
req.metadata_mut().insert(
"example-key-2",
AsciiMetadataValue::try_from(example-value-ref).unwrap(),
);
// binary example
// "gen_metadata_bin" is my own func, it returns type MetadataValue<Binary>
// macro_rules reference
/*
macro_rules! grpc_gen_binary_metadata_value {
($raw_data:expr) => {{
let mut buffer = vec![];
$raw_data.encode(&mut buffer).unwrap();
tonic::metadata::BinaryMetadataValue::from_bytes(&buffer)
}};
}
*/
req.metadata_mut()
.insert_bin("example-key-bin", gen_metadata_bin(&client_info));
Ok(req)
},
);
// `play_view` is generated by tonic
// `request_content` is your gRPC related struct
let response = final_client.play_view(request_content).await.unwrap();
println!("{:?}", response); It's really complex and troublesome, I think. However, this is the only way I have figured out, for I'm new in rust, as well as tonic. I hope someone can make some suggestions for optimizing it... let channel = Channel::from_static("http://[::1]:50051").with_proxy_socks5("socks5://127.0.0.1:8888").connect().await.unwrap(); |
Beta Was this translation helpful? Give feedback.
The following example works!
I have marked all the places that can be modified.