Add both rustls and openssl client tls implementation

This commit is contained in:
Lucio Franco
2019-09-01 01:37:19 -04:00
parent d43b2a3c63
commit cc2fa986a6
9 changed files with 128 additions and 62 deletions
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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();
+2 -2
View File
@@ -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?;
+12 -2
View File
@@ -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
View File
@@ -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"]
+85 -31
View File
@@ -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 })
}
}
+4 -2
View File
@@ -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 {
+11 -10
View File
@@ -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();