Add both rustls and openssl client tls implementation
This commit is contained in:
@@ -9,7 +9,7 @@ pub mod hello_world {
|
|||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let origin = http::Uri::from_static("http://[::1]:50051");
|
let origin = http::Uri::from_static("http://[::1]:50051");
|
||||||
|
|
||||||
let svc = Client::connect(origin)?;
|
let svc = Client::builder().build(origin)?;
|
||||||
|
|
||||||
let mut client = hello_world::GreeterClient::new(svc);
|
let mut client = hello_world::GreeterClient::new(svc);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ mod route_guide {
|
|||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let origin = http::Uri::from_static("http://[::1]:10000");
|
let origin = http::Uri::from_static("http://[::1]:10000");
|
||||||
|
|
||||||
let svc = Client::connect(origin)?;
|
let svc = Client::builder().build(origin)?;
|
||||||
let mut client = route_guide::RouteGuideClient::new(svc);
|
let mut client = route_guide::RouteGuideClient::new(svc);
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let test_cases = matches.test_case;
|
let test_cases = matches.test_case;
|
||||||
|
|
||||||
let addr = "localhost:8080";
|
let addr = "localhost:10000";
|
||||||
let origin = http::Uri::from_shared(format!("https://{}", addr).into()).unwrap();
|
let origin = http::Uri::from_shared(format!("http://{}", addr).into()).unwrap();
|
||||||
|
|
||||||
let mut client = client::create(origin.clone()).await?;
|
let mut client = client::create(origin.clone()).await?;
|
||||||
let mut unimplemented_client = client::create_unimplemented(origin).await?;
|
let mut unimplemented_client = client::create_unimplemented(origin).await?;
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ const SPECIAL_TEST_STATUS_MESSAGE: &'static str =
|
|||||||
"\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n";
|
"\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n";
|
||||||
|
|
||||||
pub async fn create(origin: http::Uri) -> Result<TestClient, Box<dyn std::error::Error>> {
|
pub async fn create(origin: http::Uri) -> Result<TestClient, Box<dyn std::error::Error>> {
|
||||||
let svc = Client::connect_with_tls(origin, "tonic-interop/data/ca.pem").await?;
|
let ca = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
||||||
|
|
||||||
|
let svc = Client::builder()
|
||||||
|
.tls(ca)
|
||||||
|
.tls_override_domain("foo.test.google.fr")
|
||||||
|
.build(origin)?;
|
||||||
|
|
||||||
Ok(TestServiceClient::new(svc))
|
Ok(TestServiceClient::new(svc))
|
||||||
}
|
}
|
||||||
@@ -30,7 +35,12 @@ pub async fn create(origin: http::Uri) -> Result<TestClient, Box<dyn std::error:
|
|||||||
pub async fn create_unimplemented(
|
pub async fn create_unimplemented(
|
||||||
origin: http::Uri,
|
origin: http::Uri,
|
||||||
) -> Result<UnimplementedClient, Box<dyn std::error::Error>> {
|
) -> Result<UnimplementedClient, Box<dyn std::error::Error>> {
|
||||||
let svc = Client::connect(origin)?;
|
let ca = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
||||||
|
|
||||||
|
let svc = Client::builder()
|
||||||
|
.tls(ca)
|
||||||
|
.tls_override_domain("foo.test.google.fr")
|
||||||
|
.build(origin)?;
|
||||||
|
|
||||||
Ok(UnimplementedServiceClient::new(svc))
|
Ok(UnimplementedServiceClient::new(svc))
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -26,12 +26,16 @@ pin-project = "0.4.0-alpha.2"
|
|||||||
hyper = { git = "https://github.com/hyperium/hyper", optional = true}
|
hyper = { git = "https://github.com/hyperium/hyper", optional = true}
|
||||||
tokio = { version = "=0.2.0-alpha.4", default-features = false, features = ["tcp"], optional = true }
|
tokio = { version = "=0.2.0-alpha.4", default-features = false, features = ["tcp"], optional = true }
|
||||||
tower-make = "=0.1.0-alpha.2"
|
tower-make = "=0.1.0-alpha.2"
|
||||||
tokio-rustls = { path = "../../tokio-rustls", optional = true }
|
|
||||||
tokio-openssl = "=0.4.0-alpha.4"
|
|
||||||
openssl = "*"
|
|
||||||
tower-reconnect = { path = "../../tower/tower-reconnect", optional = true }
|
tower-reconnect = { path = "../../tower/tower-reconnect", optional = true }
|
||||||
tower-buffer = { path = "../../tower/tower-buffer", optional = true }
|
tower-buffer = { path = "../../tower/tower-buffer", optional = true }
|
||||||
|
|
||||||
|
# openssl
|
||||||
|
tokio-openssl = { version = "=0.4.0-alpha.4", optional = true }
|
||||||
|
openssl = { version = "0.10", optional = true }
|
||||||
|
|
||||||
|
# rustls
|
||||||
|
tokio-rustls = { path = "../../tokio-rustls", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["transport"]
|
default = ["transport"]
|
||||||
transport = [
|
transport = [
|
||||||
@@ -39,5 +43,7 @@ transport = [
|
|||||||
"tower-reconnect",
|
"tower-reconnect",
|
||||||
"tower-buffer",
|
"tower-buffer",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"openssl-1",
|
||||||
]
|
]
|
||||||
|
openssl-1 = ["openssl", "tokio-openssl"]
|
||||||
|
rustls = ["tokio-rustls"]
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
use super::tls::TlsConnector;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::BoxBody,
|
body::BoxBody,
|
||||||
service::{AddOrigin, BoxService, GrpcService},
|
service::{AddOrigin, BoxService, GrpcService},
|
||||||
};
|
};
|
||||||
use futures_util::try_future::{MapErr, TryFutureExt};
|
use futures_util::try_future::{MapErr, TryFutureExt};
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use hyper::client::conn::Builder;
|
use hyper::client::conn;
|
||||||
use hyper::client::connect::HttpConnector;
|
use hyper::client::connect::HttpConnector;
|
||||||
use hyper::client::service::Connect;
|
use hyper::client::service::Connect;
|
||||||
use hyper::{Request, Response};
|
use hyper::{Request, Response};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::path::Path;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use tower_buffer::{future::ResponseFuture, Buffer};
|
use tower_buffer::{future::ResponseFuture, Buffer};
|
||||||
@@ -33,34 +31,8 @@ pub struct Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn connect(addr: Uri) -> Result<Self, super::Error> {
|
pub fn builder() -> Builder {
|
||||||
let settings = Builder::new().http2_only(true).clone();
|
Builder::new()
|
||||||
let maker = Connect::new(HttpConnector::new(), settings);
|
|
||||||
let svc = tower_reconnect::Reconnect::new(maker, addr.clone());
|
|
||||||
|
|
||||||
let svc = AddOrigin::new(svc, addr);
|
|
||||||
let svc = BoxService::new(svc);
|
|
||||||
|
|
||||||
let svc = Buffer::new(Box::new(svc) as Inner, 100);
|
|
||||||
|
|
||||||
Ok(Self { svc })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn connect_with_tls<P: AsRef<Path>>(addr: Uri, ca: P) -> Result<Self, super::Error> {
|
|
||||||
let settings = Builder::new().http2_only(true).clone();
|
|
||||||
|
|
||||||
// let tls_connector = TlsConnector::load(ca).await?;
|
|
||||||
let tls_connector = super::openssl::TlsConnector::load(ca).await?;
|
|
||||||
|
|
||||||
let maker = Connect::new(tls_connector, settings);
|
|
||||||
let svc = tower_reconnect::Reconnect::new(maker, addr.clone());
|
|
||||||
|
|
||||||
let svc = AddOrigin::new(svc, addr);
|
|
||||||
let svc = BoxService::new(svc);
|
|
||||||
|
|
||||||
let svc = Buffer::new(Box::new(svc) as Inner, 100);
|
|
||||||
|
|
||||||
Ok(Self { svc })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,3 +55,85 @@ impl GrpcService<BoxBody> for Client {
|
|||||||
.map_err(|e| super::Error::from((super::ErrorKind::Client, e)))
|
.map_err(|e| super::Error::from((super::ErrorKind::Client, e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Builder {
|
||||||
|
ca: Option<Vec<u8>>,
|
||||||
|
override_domain: Option<String>,
|
||||||
|
buffer_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ca: None,
|
||||||
|
override_domain: None,
|
||||||
|
buffer_size: 1024,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "openssl-1", feature = "rustls"))]
|
||||||
|
pub fn tls(&mut self, ca: Vec<u8>) -> &mut Self {
|
||||||
|
self.ca = Some(ca);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "openssl-1", feature = "rustls"))]
|
||||||
|
pub fn tls_override_domain<D: AsRef<str>>(&mut self, domain: D) -> &mut Self {
|
||||||
|
self.override_domain = Some(domain.as_ref().into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer(&mut self, size: usize) -> &mut Self {
|
||||||
|
self.buffer_size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build<T>(&self, uri: T) -> Result<Client, super::Error>
|
||||||
|
where
|
||||||
|
Uri: http::HttpTryFrom<T>,
|
||||||
|
{
|
||||||
|
let uri: Uri = match http::HttpTryFrom::try_from(uri) {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(e) => panic!("Invalid uri: {}", e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = conn::Builder::new().http2_only(true).clone();
|
||||||
|
|
||||||
|
let svc = if let Some(ca) = &self.ca {
|
||||||
|
let domain = self
|
||||||
|
.override_domain
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| uri.to_string());
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "openssl-1", feature = "rustls")))]
|
||||||
|
panic!("tls configured when no tls implementation feature was selected!");
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl-1")]
|
||||||
|
let connector = super::openssl::TlsConnector::new(ca.clone(), domain)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
#[cfg(not(feature = "openssl-1"))]
|
||||||
|
let connector = super::openssl::TlsConnector::new(ca.clone(), domain)?;
|
||||||
|
|
||||||
|
let maker = Connect::new(connector, settings);
|
||||||
|
let svc = tower_reconnect::Reconnect::new(maker, uri.clone());
|
||||||
|
|
||||||
|
let svc = AddOrigin::new(svc, uri);
|
||||||
|
let svc = BoxService::new(svc);
|
||||||
|
Buffer::new(Box::new(svc) as Inner, 100)
|
||||||
|
} else {
|
||||||
|
let connector = HttpConnector::new();
|
||||||
|
let maker = Connect::new(connector, settings);
|
||||||
|
let svc = tower_reconnect::Reconnect::new(maker, uri.clone());
|
||||||
|
|
||||||
|
let svc = AddOrigin::new(svc, uri);
|
||||||
|
let svc = BoxService::new(svc);
|
||||||
|
Buffer::new(Box::new(svc) as Inner, 100)
|
||||||
|
};
|
||||||
|
// let connector = super::rustls::TlsConnector::load(ca).await?;
|
||||||
|
// let connector = super::openssl::TlsConnector::load(ca).await?;
|
||||||
|
|
||||||
|
Ok(Client { svc })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
mod client;
|
mod client;
|
||||||
|
#[cfg(feature = "openssl-1")]
|
||||||
mod openssl;
|
mod openssl;
|
||||||
mod tls;
|
#[cfg(feature = "rustls")]
|
||||||
|
mod rustls;
|
||||||
|
|
||||||
pub use self::client::Client;
|
pub use self::client::Client;
|
||||||
|
|
||||||
@@ -14,7 +16,7 @@ pub struct Error {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum ErrorKind {
|
pub(crate) enum ErrorKind {
|
||||||
Client,
|
Client,
|
||||||
UnableToNegotiateH2, // Server,
|
// Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ErrorKind> for Error {
|
impl From<ErrorKind> for Error {
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
use hyper::client::connect::HttpConnector;
|
use hyper::client::connect::HttpConnector;
|
||||||
use openssl::ssl::{ConnectConfiguration, SslConnector, SslMethod};
|
use openssl::ssl::{SslConnector, SslMethod};
|
||||||
|
use openssl::x509::X509;
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
path::Path,
|
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use tokio::{fs, net::TcpStream};
|
use tokio::net::TcpStream;
|
||||||
use tokio_openssl::{connect, SslStream};
|
use tokio_openssl::{connect, SslStream};
|
||||||
use tower_make::MakeConnection;
|
use tower_make::MakeConnection;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
const ALPN_H2: &str = "h2";
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TlsConnector {
|
pub struct TlsConnector {
|
||||||
http: HttpConnector,
|
http: HttpConnector,
|
||||||
config: SslConnector,
|
config: SslConnector,
|
||||||
|
domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TlsConnector {
|
impl TlsConnector {
|
||||||
pub async fn load<P: AsRef<Path>>(ca: P) -> Result<Self, super::Error> {
|
pub fn new(ca: Vec<u8>, domain: String) -> Result<Self, super::Error> {
|
||||||
let mut config = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut config = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
|
||||||
config.set_alpn_protos(ALPN_H2.as_bytes()).unwrap();
|
config.set_alpn_protos(b"\x02h2").unwrap();
|
||||||
|
|
||||||
config.set_ca_file(ca).unwrap();
|
let ca = X509::from_pem(&ca[..]).unwrap();
|
||||||
|
|
||||||
|
config.cert_store_mut().add_cert(ca).unwrap();
|
||||||
|
|
||||||
let config = config.build();
|
let config = config.build();
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ impl TlsConnector {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
http,
|
http,
|
||||||
config,
|
config,
|
||||||
|
domain,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,10 +57,10 @@ impl Service<Uri> for TlsConnector {
|
|||||||
fn call(&mut self, uri: Uri) -> Self::Future {
|
fn call(&mut self, uri: Uri) -> Self::Future {
|
||||||
let config = self.config.configure().unwrap();
|
let config = self.config.configure().unwrap();
|
||||||
let tcp = self.http.make_connection(uri.clone());
|
let tcp = self.http.make_connection(uri.clone());
|
||||||
|
let domain = self.domain.clone();
|
||||||
|
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
let io = tcp.await.unwrap();
|
let io = tcp.await.unwrap();
|
||||||
let domain = "foo.test.google.fr";
|
|
||||||
let tls = connect(config, &domain, io).await.unwrap();
|
let tls = connect(config, &domain, io).await.unwrap();
|
||||||
Ok(tls)
|
Ok(tls)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use tokio_rustls::{
|
|||||||
use tower_make::MakeConnection;
|
use tower_make::MakeConnection;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
const ALPN_H2: &str = "h2";
|
const ALPN_H2: &str = "\x02h2";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TlsConnector {
|
pub struct TlsConnector {
|
||||||
@@ -26,14 +26,7 @@ pub struct TlsConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TlsConnector {
|
impl TlsConnector {
|
||||||
pub async fn load<P: AsRef<Path>>(ca: P) -> Result<Self, super::Error> {
|
pub fn new(ca: Vec<u8>, domain: String) -> Self {
|
||||||
let pem = fs::read(ca)
|
|
||||||
.await
|
|
||||||
.map_err(|e| super::Error::from((super::ErrorKind::Client, e.into())))?;
|
|
||||||
Ok(TlsConnector::new(pem))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(ca: Vec<u8>) -> Self {
|
|
||||||
let mut buf = std::io::Cursor::new(ca);
|
let mut buf = std::io::Cursor::new(ca);
|
||||||
|
|
||||||
let mut config = ClientConfig::new();
|
let mut config = ClientConfig::new();
|
||||||
Reference in New Issue
Block a user