Refactor all of the tls
This commit is contained in:
@@ -15,11 +15,17 @@ jobs:
|
|||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Install rustfmt
|
- name: Install rustfmt
|
||||||
run: rustup component add rustfmt
|
run: rustup component add rustfmt
|
||||||
- name: Run fmt
|
- name: Check fmt
|
||||||
run: cargo fmt -- --check
|
run: cargo fmt -- --check
|
||||||
- name: Run check
|
- name: Check all
|
||||||
run: cargo check --all
|
run: cargo check --all
|
||||||
- name: Run check with no default features
|
- name: Check with no default features
|
||||||
run: cargo check --all --no-default-features
|
run: cargo check -p tonic --no-default-features
|
||||||
|
- name: Check with transport no tls
|
||||||
|
run: cargo check -p tonic
|
||||||
|
- name: Check with transport w/ openssl
|
||||||
|
run: cargo check -p tonic --features openssl
|
||||||
|
- name: Check with transport w/ rustls
|
||||||
|
run: cargo check -p tonic --features rustls
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --all
|
run: cargo test --all
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ proc-macro2 = "1.0"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["transport"]
|
default = ["transport"]
|
||||||
rustfmt = []
|
# TODO: reenable this feature
|
||||||
|
# rustfmt = []
|
||||||
transport = []
|
transport = []
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use tonic::{Request, Response, Server, Status};
|
use tonic::{transport::Server, Request, Response, Status};
|
||||||
|
|
||||||
pub mod hello_world {
|
pub mod hello_world {
|
||||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ path = "src/bin/server.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = "=0.2.0-alpha.4"
|
tokio = "=0.2.0-alpha.4"
|
||||||
tonic = { path = "../tonic" }
|
tonic = { path = "../tonic", features = ["openssl"] }
|
||||||
prost = "0.5"
|
prost = "0.5"
|
||||||
prost-derive = "0.5"
|
prost-derive = "0.5"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use structopt::{clap::arg_enum, StructOpt};
|
use structopt::{clap::arg_enum, StructOpt};
|
||||||
use tonic::transport::Endpoint;
|
use tonic::transport::{Certificate, Endpoint};
|
||||||
use tonic_interop::client;
|
use tonic_interop::client;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
@@ -31,8 +31,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
if matches.use_tls {
|
if matches.use_tls {
|
||||||
let ca = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
let pem = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
||||||
endpoint.tls_cert(ca, Some("foo.test.google.fr".into()));
|
let ca = Certificate::from_pem(pem);
|
||||||
|
endpoint.openssl_tls(ca, Some("foo.test.google.fr".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let channel = endpoint.channel()?;
|
let channel = endpoint.channel()?;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use tonic::Server;
|
use tonic::transport::{Identity, Server};
|
||||||
use tonic_interop::{server, MergeTrailers};
|
use tonic_interop::{server, MergeTrailers};
|
||||||
// TODO: move GrpcService out of client since it can be used for the
|
// TODO: move GrpcService out of client since it can be used for the
|
||||||
// server too.
|
// server too.
|
||||||
@@ -24,9 +24,11 @@ async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
|||||||
let mut builder = Server::builder();
|
let mut builder = Server::builder();
|
||||||
|
|
||||||
if matches.use_tls {
|
if matches.use_tls {
|
||||||
let ca = tokio::fs::read("tonic-interop/data/server1.pem").await?;
|
let cert = tokio::fs::read("tonic-interop/data/server1.pem").await?;
|
||||||
let key = tokio::fs::read("tonic-interop/data/server1.key").await?;
|
let key = tokio::fs::read("tonic-interop/data/server1.key").await?;
|
||||||
builder.tls(ca, key);
|
|
||||||
|
let identity = Identity::from_pem(cert, key);
|
||||||
|
builder.openssl_tls(identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.interceptor_fn(|svc, req| {
|
builder.interceptor_fn(|svc, req| {
|
||||||
|
|||||||
+4
-4
@@ -32,7 +32,7 @@ tower-load = { version = "=0.3.0-alpha.1", optional = true }
|
|||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
tokio-openssl = { version = "=0.4.0-alpha.4", optional = true }
|
tokio-openssl = { version = "=0.4.0-alpha.4", optional = true }
|
||||||
openssl = { version = "0.10", optional = true }
|
openssl1 = { package = "openssl", version = "0.10", optional = true }
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
tokio-rustls = { version = "0.12.0-alpha.2", optional = true }
|
tokio-rustls = { version = "0.12.0-alpha.2", optional = true }
|
||||||
@@ -43,7 +43,6 @@ transport = [
|
|||||||
"hyper",
|
"hyper",
|
||||||
"tower-1",
|
"tower-1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"native-tls",
|
|
||||||
]
|
]
|
||||||
tower-1 = [
|
tower-1 = [
|
||||||
"tower",
|
"tower",
|
||||||
@@ -51,5 +50,6 @@ tower-1 = [
|
|||||||
"tower-balance",
|
"tower-balance",
|
||||||
"tower-load",
|
"tower-load",
|
||||||
]
|
]
|
||||||
native-tls = ["openssl", "tokio-openssl"]
|
openssl = ["openssl1", "tokio-openssl", "tls"]
|
||||||
# rustls = ["tokio-rustls"]
|
rustls = ["tokio-rustls", "tls"]
|
||||||
|
tls = []
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ pub use codec::Streaming;
|
|||||||
pub use request::Request;
|
pub use request::Request;
|
||||||
pub use response::Response;
|
pub use response::Response;
|
||||||
pub use status::{Code, Status};
|
pub use status::{Code, Status};
|
||||||
#[doc(inline)]
|
|
||||||
pub use transport::{Channel, Server};
|
|
||||||
|
|
||||||
pub(crate) type Error = Box<dyn std::error::Error + Send + Sync>;
|
pub(crate) type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use super::{channel::Channel, tls::Cert};
|
use super::channel::Channel;
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
use super::{service::TlsConnector, tls::Certificate};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use http::uri::{InvalidUriBytes, Uri};
|
use http::uri::{InvalidUriBytes, Uri};
|
||||||
use std::{convert::TryFrom, time::Duration};
|
use std::{convert::TryFrom, time::Duration};
|
||||||
@@ -9,7 +11,8 @@ pub struct Endpoint {
|
|||||||
pub(super) timeout: Option<Duration>,
|
pub(super) timeout: Option<Duration>,
|
||||||
pub(super) concurrency_limit: Option<usize>,
|
pub(super) concurrency_limit: Option<usize>,
|
||||||
pub(super) rate_limit: Option<(u64, Duration)>,
|
pub(super) rate_limit: Option<(u64, Duration)>,
|
||||||
pub(super) cert: Option<Cert>,
|
#[cfg(feature = "tls")]
|
||||||
|
pub(super) tls: Option<TlsConnector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
@@ -38,12 +41,19 @@ impl Endpoint {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tls_cert(&mut self, ca: Vec<u8>, domain: Option<String>) -> &mut Self {
|
#[cfg(feature = "openssl")]
|
||||||
self.cert = Some(Cert {
|
pub fn openssl_tls(&mut self, ca: Certificate, domain: Option<String>) -> &mut Self {
|
||||||
ca,
|
let domain = domain.unwrap_or_else(|| self.uri.clone().to_string());
|
||||||
domain: domain.unwrap_or_else(|| self.uri.clone().to_string()),
|
let tls = TlsConnector::new_with_openssl(ca, domain).unwrap();
|
||||||
key: None,
|
self.tls = Some(tls);
|
||||||
});
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub fn rustls_tls(&mut self, ca: Certificate, domain: Option<String>) -> &mut Self {
|
||||||
|
let domain = domain.unwrap_or_else(|| self.uri.clone().to_string());
|
||||||
|
let tls = TlsConnector::new_with_rustls(ca, domain).unwrap();
|
||||||
|
self.tls = Some(tls);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +71,8 @@ impl From<Uri> for Endpoint {
|
|||||||
concurrency_limit: None,
|
concurrency_limit: None,
|
||||||
rate_limit: None,
|
rate_limit: None,
|
||||||
timeout: None,
|
timeout: None,
|
||||||
cert: None,
|
#[cfg(feature = "tls")]
|
||||||
|
tls: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub use self::channel::Channel;
|
|||||||
pub use self::endpoint::Endpoint;
|
pub use self::endpoint::Endpoint;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::server::Server;
|
pub use self::server::Server;
|
||||||
|
pub use self::tls::{Certificate, Identity};
|
||||||
pub use hyper::Body;
|
pub use hyper::Body;
|
||||||
|
|
||||||
pub(crate) use self::error::ErrorKind;
|
pub(crate) use self::error::ErrorKind;
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
use super::{
|
use super::service::{layer_fn, BoxedIo};
|
||||||
service::{layer_fn, BoxedIo},
|
#[cfg(feature = "tls")]
|
||||||
tls::{Cert, TlsAcceptor},
|
use super::{service::TlsAcceptor, tls::Identity};
|
||||||
};
|
|
||||||
use crate::body::BoxBody;
|
use crate::body::BoxBody;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use futures_util::{ready, try_future::MapErr, TryFutureExt, TryStreamExt};
|
use futures_util::{ready, try_future::MapErr, TryFutureExt, TryStreamExt};
|
||||||
use http::{Request, Response};
|
use http::{Request, Response};
|
||||||
use hyper::server::{accept::Accept, conn};
|
use hyper::server::{accept::Accept, conn};
|
||||||
use hyper::Body;
|
use hyper::Body;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use tower::layer::util::Stack;
|
use tower::layer::util::Stack;
|
||||||
@@ -48,9 +47,10 @@ impl Server {
|
|||||||
///
|
///
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
tls: Option<(Vec<u8>, Vec<u8>)>,
|
|
||||||
interceptor: Option<Interceptor>,
|
interceptor: Option<Interceptor>,
|
||||||
// concurrency_limit: Option<usize>,
|
// concurrency_limit: Option<usize>,
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
tls: Option<TlsAcceptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
@@ -59,8 +59,17 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add a tls cert.
|
/// Add a tls cert.
|
||||||
pub fn tls(&mut self, pem: Vec<u8>, key: Vec<u8>) -> &mut Self {
|
#[cfg(feature = "openssl")]
|
||||||
self.tls = Some((pem, key));
|
pub fn openssl_tls(&mut self, identity: Identity) -> &mut Self {
|
||||||
|
let acceptor = TlsAcceptor::new_with_openssl(identity).unwrap();
|
||||||
|
self.tls = Some(acceptor);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub fn rustls_tls(&mut self, identity: Identity) -> &mut Self {
|
||||||
|
let acceptor = TlsAcceptor::new_with_rustls(identity).unwrap();
|
||||||
|
self.tls = Some(acceptor);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,23 +103,28 @@ impl Builder {
|
|||||||
S::Future: Send + 'static,
|
S::Future: Send + 'static,
|
||||||
S::Error: Into<crate::Error> + Send,
|
S::Error: Into<crate::Error> + Send,
|
||||||
{
|
{
|
||||||
let tls = if let Some(tls) = self.tls {
|
let interceptor = self.interceptor.clone();
|
||||||
let cert = Cert {
|
|
||||||
ca: tls.0,
|
|
||||||
key: Some(tls.1),
|
|
||||||
domain: String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(TlsAcceptor::new(cert).map_err(map_err)?)
|
let incoming = hyper::server::accept::from_stream(async_stream::try_stream! {
|
||||||
} else {
|
let mut tcp = TcpIncoming::bind(addr)?;
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let incoming = hyper::server::accept::from_stream(incoming(addr, tls));
|
while let Some(stream) = tcp.try_next().await? {
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
{
|
||||||
|
if let Some(tls) = &self.tls {
|
||||||
|
let io = tls.connect(stream.into_inner()).await?;
|
||||||
|
yield BoxedIo::new(io);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield BoxedIo::new(stream);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let svc = MakeSvc {
|
let svc = MakeSvc {
|
||||||
inner: svc,
|
inner: svc,
|
||||||
interceptor: self.interceptor.clone(),
|
interceptor,
|
||||||
};
|
};
|
||||||
|
|
||||||
hyper::Server::builder(incoming)
|
hyper::Server::builder(incoming)
|
||||||
@@ -133,24 +147,6 @@ impl fmt::Debug for Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn incoming(
|
|
||||||
addr: SocketAddr,
|
|
||||||
tls: Option<TlsAcceptor>,
|
|
||||||
) -> impl futures_core::Stream<Item = Result<BoxedIo, crate::Error>> {
|
|
||||||
async_stream::try_stream! {
|
|
||||||
let mut tcp = TcpIncoming::bind(addr)?;
|
|
||||||
|
|
||||||
while let Some(stream) = tcp.try_next().await? {
|
|
||||||
if let Some(tls) = &tls {
|
|
||||||
let io = tls.connect(stream.into_inner()).await?;
|
|
||||||
yield BoxedIo::new(io);
|
|
||||||
} else {
|
|
||||||
yield BoxedIo::new(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TcpIncoming {
|
struct TcpIncoming {
|
||||||
inner: conn::AddrIncoming,
|
inner: conn::AddrIncoming,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{layer::ServiceBuilderExt, AddOrigin, Connector};
|
use super::{connector, layer::ServiceBuilderExt, AddOrigin};
|
||||||
use crate::{body::BoxBody, transport::Endpoint};
|
use crate::{body::BoxBody, transport::Endpoint};
|
||||||
use hyper::client::conn::Builder;
|
use hyper::client::conn::Builder;
|
||||||
use hyper::client::service::Connect as HyperConnect;
|
use hyper::client::service::Connect as HyperConnect;
|
||||||
@@ -27,8 +27,12 @@ pub struct Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
pub fn new(mut endpoint: Endpoint) -> Result<Self, crate::Error> {
|
pub fn new(endpoint: Endpoint) -> Result<Self, crate::Error> {
|
||||||
let connector = Connector::new(endpoint.cert.take())?;
|
#[cfg(feature = "tls")]
|
||||||
|
let connector = connector(endpoint.tls.clone());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "tls"))]
|
||||||
|
let connector = connector();
|
||||||
|
|
||||||
let settings = Builder::new().http2_only(true).clone();
|
let settings = Builder::new().http2_only(true).clone();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use super::io::BoxedIo;
|
use super::io::BoxedIo;
|
||||||
use crate::transport::tls::{Cert, TlsConnector};
|
#[cfg(feature = "tls")]
|
||||||
|
use super::tls::TlsConnector;
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use hyper::client::connect::HttpConnector;
|
use hyper::client::connect::HttpConnector;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
@@ -8,25 +9,31 @@ use std::task::{Context, Poll};
|
|||||||
use tower_make::MakeConnection;
|
use tower_make::MakeConnection;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
type ConnectFuture = <HttpConnector as MakeConnection<Uri>>::Future;
|
#[cfg(not(feature = "tls"))]
|
||||||
|
pub(crate) fn connector() -> HttpConnector {
|
||||||
|
let mut http = HttpConnector::new();
|
||||||
|
http.enforce_http(false);
|
||||||
|
http
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
pub(crate) fn connector(tls: Option<TlsConnector>) -> Connector {
|
||||||
|
Connector::new(tls)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct Connector {
|
pub(crate) struct Connector {
|
||||||
http: HttpConnector,
|
http: HttpConnector,
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
tls: Option<TlsConnector>,
|
tls: Option<TlsConnector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connector {
|
impl Connector {
|
||||||
pub(crate) fn new(cert: Option<Cert>) -> Result<Self, crate::Error> {
|
#[cfg(feature = "tls")]
|
||||||
|
pub(crate) fn new(tls: Option<TlsConnector>) -> Self {
|
||||||
let mut http = HttpConnector::new();
|
let mut http = HttpConnector::new();
|
||||||
http.enforce_http(false);
|
http.enforce_http(false);
|
||||||
|
|
||||||
let tls = if let Some(cert) = cert {
|
Self { http, tls }
|
||||||
Some(TlsConnector::new(cert)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self { http, tls })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,23 +49,23 @@ impl Service<Uri> for Connector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, uri: Uri) -> Self::Future {
|
fn call(&mut self, uri: Uri) -> Self::Future {
|
||||||
let io = MakeConnection::make_connection(&mut self.http, uri);
|
let connect = MakeConnection::make_connection(&mut self.http, uri);
|
||||||
|
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
let tls = self.tls.clone();
|
let tls = self.tls.clone();
|
||||||
|
|
||||||
Box::pin(connect(io, tls))
|
Box::pin(async move {
|
||||||
}
|
let io = connect.await?;
|
||||||
}
|
|
||||||
|
|
||||||
async fn connect(
|
#[cfg(feature = "tls")]
|
||||||
connect: ConnectFuture,
|
{
|
||||||
tls: Option<TlsConnector>,
|
if let Some(tls) = tls {
|
||||||
) -> Result<BoxedIo, crate::Error> {
|
let conn = tls.connect(io).await?;
|
||||||
let io = connect.await?;
|
return Ok(BoxedIo::new(conn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(tls) = tls {
|
Ok(BoxedIo::new(io))
|
||||||
let conn = tls.connect(io).await?;
|
})
|
||||||
Ok(BoxedIo::new(conn))
|
|
||||||
} else {
|
|
||||||
Ok(BoxedIo::new(io))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ mod connector;
|
|||||||
mod discover;
|
mod discover;
|
||||||
mod io;
|
mod io;
|
||||||
mod layer;
|
mod layer;
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
mod tls;
|
||||||
|
|
||||||
pub(crate) use self::add_origin::AddOrigin;
|
pub(crate) use self::add_origin::AddOrigin;
|
||||||
pub(crate) use self::boxed::BoxService;
|
pub(crate) use self::boxed::BoxService;
|
||||||
pub(crate) use self::connection::Connection;
|
pub(crate) use self::connection::Connection;
|
||||||
pub(crate) use self::connector::Connector;
|
pub(crate) use self::connector::connector;
|
||||||
pub(crate) use self::discover::ServiceList;
|
pub(crate) use self::discover::ServiceList;
|
||||||
pub(crate) use self::io::BoxedIo;
|
pub(crate) use self::io::BoxedIo;
|
||||||
pub(crate) use self::layer::layer_fn;
|
pub(crate) use self::layer::layer_fn;
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
pub(crate) use self::tls::{TlsAcceptor, TlsConnector};
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
use super::io::BoxedIo;
|
||||||
|
use crate::transport::{Certificate, Identity};
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
use openssl1::{
|
||||||
|
pkey::PKey,
|
||||||
|
ssl::{SslAcceptor, SslConnector, SslMethod},
|
||||||
|
x509::X509,
|
||||||
|
};
|
||||||
|
use std::{fmt, sync::Arc};
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
use tokio_rustls::{
|
||||||
|
rustls::{internal::pemfile, ClientConfig, NoClientAuth, ServerConfig},
|
||||||
|
webpki::DNSNameRef,
|
||||||
|
TlsAcceptor as RustlsAcceptor, TlsConnector as RustlsConnector,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// h2 alpn in wire format for openssl.
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
const ALPN_H2_WIRE: &[u8] = b"\x02h2";
|
||||||
|
/// h2 alpn in plain format for rustls.
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
const ALPN_H2: &str = "h2";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Cert {
|
||||||
|
pub(crate) ca: Vec<u8>,
|
||||||
|
pub(crate) key: Option<Vec<u8>>,
|
||||||
|
pub(crate) domain: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct TlsConnector {
|
||||||
|
inner: Connector,
|
||||||
|
domain: Arc<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum Connector {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Openssl(SslConnector),
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Rustls(Arc<ClientConfig>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsConnector {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
pub(crate) fn new_with_openssl(
|
||||||
|
cert: Certificate,
|
||||||
|
domain: String,
|
||||||
|
) -> Result<Self, crate::Error> {
|
||||||
|
let mut config = SslConnector::builder(SslMethod::tls())?;
|
||||||
|
|
||||||
|
config.set_alpn_protos(ALPN_H2_WIRE)?;
|
||||||
|
|
||||||
|
let ca = X509::from_pem(&cert.pem[..])?;
|
||||||
|
|
||||||
|
config.cert_store_mut().add_cert(ca)?;
|
||||||
|
|
||||||
|
let config = config.build();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
inner: Connector::Openssl(config),
|
||||||
|
domain: Arc::new(domain),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub(crate) fn new_with_rustls(cert: Certificate, domain: String) -> Result<Self, crate::Error> {
|
||||||
|
let mut buf = std::io::Cursor::new(&cert.pem[..]);
|
||||||
|
|
||||||
|
let mut config = ClientConfig::new();
|
||||||
|
|
||||||
|
config.root_store.add_pem_file(&mut buf).unwrap();
|
||||||
|
config.set_protocols(&[Vec::from(&ALPN_H2[..])]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
inner: Connector::Rustls(Arc::new(config)),
|
||||||
|
domain: Arc::new(domain),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Write an either tlsstream to avoid this box
|
||||||
|
pub(crate) async fn connect(&self, io: TcpStream) -> Result<BoxedIo, crate::Error> {
|
||||||
|
let tls_io = match &self.inner {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Connector::Openssl(connector) => {
|
||||||
|
let config = connector.configure()?;
|
||||||
|
let tls = tokio_openssl::connect(config, &self.domain, io).await?;
|
||||||
|
|
||||||
|
// TODO: check that we actually got an h2 stream
|
||||||
|
BoxedIo::new(tls)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Connector::Rustls(config) => {
|
||||||
|
let dns = DNSNameRef::try_from_ascii_str(self.domain.as_str())
|
||||||
|
.unwrap()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let io = RustlsConnector::from(config.clone())
|
||||||
|
.connect(dns.as_ref(), io)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// TODO: check that we actually got an h2 stream
|
||||||
|
|
||||||
|
BoxedIo::new(io)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => unreachable!("Reached a tls config point with neither feature enabled!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(tls_io)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for TlsConnector {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("TlsConnector")
|
||||||
|
.field(
|
||||||
|
"inner",
|
||||||
|
match &self.inner {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Connector::Openssl(_) => &"Openssl",
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Connector::Rustls(_) => &"Rustls",
|
||||||
|
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => &"None",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct TlsAcceptor {
|
||||||
|
inner: Acceptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum Acceptor {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Openssl(SslAcceptor),
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Rustls(Arc<ServerConfig>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsAcceptor {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
pub(crate) fn new_with_openssl(identity: Identity) -> Result<Self, crate::Error> {
|
||||||
|
let key = PKey::private_key_from_pem(&identity.key[..])?;
|
||||||
|
let cert = X509::from_pem(&identity.cert.pem[..])?;
|
||||||
|
|
||||||
|
let mut config = SslAcceptor::mozilla_modern(SslMethod::tls())?;
|
||||||
|
|
||||||
|
config.set_alpn_protos(ALPN_H2_WIRE)?;
|
||||||
|
config.set_private_key(&key)?;
|
||||||
|
config.set_certificate(&cert)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
inner: Acceptor::Openssl(config.build()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub(crate) fn new_with_rustls(identity: Identity) -> Result<Self, crate::Error> {
|
||||||
|
let cert = {
|
||||||
|
let mut cert = std::io::Cursor::new(&identity.cert.pem[..]);
|
||||||
|
pemfile::certs(&mut cert).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = {
|
||||||
|
let mut key = std::io::Cursor::new(&identity.key[..]);
|
||||||
|
pemfile::pkcs8_private_keys(&mut key).unwrap().remove(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut config = ServerConfig::new(NoClientAuth::new());
|
||||||
|
|
||||||
|
config.set_single_cert(cert, key)?;
|
||||||
|
config.set_protocols(&[Vec::from(&ALPN_H2[..])]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
inner: Acceptor::Rustls(Arc::new(config)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn connect(&self, io: TcpStream) -> Result<BoxedIo, crate::Error> {
|
||||||
|
let io = match &self.inner {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Acceptor::Openssl(acceptor) => {
|
||||||
|
let tls = tokio_openssl::accept(&acceptor, io).await?;
|
||||||
|
BoxedIo::new(tls)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Acceptor::Rustls(config) => {
|
||||||
|
let acceptor = RustlsAcceptor::from(config.clone());
|
||||||
|
let tls = acceptor.accept(io).await?;
|
||||||
|
|
||||||
|
BoxedIo::new(tls)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => unreachable!("Reached a tls config point with neither feature enabled!"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(io)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for TlsAcceptor {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("TlsAcceptor")
|
||||||
|
.field(
|
||||||
|
"inner",
|
||||||
|
match &self.inner {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Acceptor::Openssl(_) => &"Openssl",
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Acceptor::Rustls(_) => &"Rustls",
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => &"None",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Certificate {
|
||||||
|
pub(crate) pem: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Identity {
|
||||||
|
pub(crate) cert: Certificate,
|
||||||
|
pub(crate) key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Certificate {
|
||||||
|
pub fn from_pem(pem: Vec<u8>) -> Self {
|
||||||
|
Self { pem }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identity {
|
||||||
|
pub fn from_pem(cert: Vec<u8>, key: Vec<u8>) -> Self {
|
||||||
|
let cert = Certificate::from_pem(cert);
|
||||||
|
Self { cert, key }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// TODO: bring back rustls
|
|
||||||
// #[cfg(feature = "native-tls")]
|
|
||||||
// #[cfg(not(feature = "rustls"))]
|
|
||||||
// #[path = "rustls.rs"]
|
|
||||||
// mod imp;
|
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
|
||||||
#[cfg(not(feature = "rustls"))]
|
|
||||||
#[path = "openssl.rs"]
|
|
||||||
mod imp;
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Cert {
|
|
||||||
pub(crate) ca: Vec<u8>,
|
|
||||||
pub(crate) key: Option<Vec<u8>>,
|
|
||||||
pub(crate) domain: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct TlsConnector {
|
|
||||||
inner: imp::TlsConnector,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsConnector {
|
|
||||||
pub(crate) fn new(cert: Cert) -> Result<Self, crate::Error> {
|
|
||||||
let inner = imp::TlsConnector::new(cert)?;
|
|
||||||
Ok(Self { inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn connect(&self, io: TcpStream) -> Result<imp::TlsStream, crate::Error> {
|
|
||||||
self.inner.connect(io).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TlsConnector {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("TlsConnector").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct TlsAcceptor {
|
|
||||||
inner: imp::TlsAcceptor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsAcceptor {
|
|
||||||
pub(crate) fn new(cert: Cert) -> Result<Self, crate::Error> {
|
|
||||||
let inner = imp::TlsAcceptor::new(cert)?;
|
|
||||||
Ok(Self { inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn connect(&self, io: TcpStream) -> Result<imp::TlsStream, crate::Error> {
|
|
||||||
self.inner.connect(io).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TlsAcceptor {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("TlsAcceptor").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
use super::Cert;
|
|
||||||
use openssl::ssl::{SslAcceptor, SslConnector, SslMethod};
|
|
||||||
use openssl::{pkey::PKey, x509::X509};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
use tokio_openssl::SslStream;
|
|
||||||
|
|
||||||
const ALPN_H2: &[u8] = b"\x02h2";
|
|
||||||
|
|
||||||
pub(crate) type TlsStream = SslStream<TcpStream>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct TlsConnector {
|
|
||||||
config: SslConnector,
|
|
||||||
domain: Arc<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsConnector {
|
|
||||||
pub(crate) fn new(cert: Cert) -> Result<Self, crate::Error> {
|
|
||||||
let Cert { ca, domain, .. } = cert;
|
|
||||||
let mut config = SslConnector::builder(SslMethod::tls()).unwrap();
|
|
||||||
|
|
||||||
config.set_alpn_protos(ALPN_H2)?;
|
|
||||||
|
|
||||||
let ca = X509::from_pem(&ca[..])?;
|
|
||||||
|
|
||||||
config.cert_store_mut().add_cert(ca)?;
|
|
||||||
|
|
||||||
let config = config.build();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
config,
|
|
||||||
domain: Arc::new(domain),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn connect(&self, io: TcpStream) -> Result<TlsStream, crate::Error> {
|
|
||||||
let config = self.config.configure()?;
|
|
||||||
let tls = tokio_openssl::connect(config, &self.domain, io).await?;
|
|
||||||
Ok(tls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct TlsAcceptor {
|
|
||||||
config: SslAcceptor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsAcceptor {
|
|
||||||
pub(crate) fn new(cert: Cert) -> Result<Self, crate::Error> {
|
|
||||||
let Cert { ca, key, .. } = cert;
|
|
||||||
|
|
||||||
let key = PKey::private_key_from_pem(&key.unwrap()[..])?;
|
|
||||||
let ca = X509::from_pem(&ca[..])?;
|
|
||||||
|
|
||||||
let mut config = SslAcceptor::mozilla_modern(SslMethod::tls())?;
|
|
||||||
|
|
||||||
config.set_alpn_protos(ALPN_H2)?;
|
|
||||||
config.set_private_key(&key)?;
|
|
||||||
config.set_certificate(&ca)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
config: config.build(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn connect(&self, io: TcpStream) -> Result<TlsStream, crate::Error> {
|
|
||||||
let config = self.config.clone();
|
|
||||||
let tls = tokio_openssl::accept(&config, io).await?;
|
|
||||||
Ok(tls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
use http::Uri;
|
|
||||||
use hyper::client::connect::HttpConnector;
|
|
||||||
use std::{
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
use tokio::net::TcpStream;
|
|
||||||
use tokio_rustls::{
|
|
||||||
client::TlsStream,
|
|
||||||
rustls::{ClientConfig, Session},
|
|
||||||
webpki::DNSNameRef,
|
|
||||||
TlsConnector as RustlsConnector,
|
|
||||||
};
|
|
||||||
use tower_make::MakeConnection;
|
|
||||||
use tower_service::Service;
|
|
||||||
|
|
||||||
const ALPN_H2: &str = "h2";
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TlsConnector {
|
|
||||||
http: HttpConnector,
|
|
||||||
config: Arc<ClientConfig>,
|
|
||||||
domain: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TlsConnector {
|
|
||||||
#[cfg_attr(feature = "openssl-1", allow(dead_code))]
|
|
||||||
pub fn new(ca: Vec<u8>, domain: String) -> Self {
|
|
||||||
let mut buf = std::io::Cursor::new(ca);
|
|
||||||
|
|
||||||
let mut config = ClientConfig::new();
|
|
||||||
|
|
||||||
config.root_store.add_pem_file(&mut buf).unwrap();
|
|
||||||
config.set_protocols(&[Vec::from(&ALPN_H2[..])]);
|
|
||||||
|
|
||||||
let mut http = HttpConnector::new();
|
|
||||||
http.enforce_http(false);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
http,
|
|
||||||
config: Arc::new(config),
|
|
||||||
domain,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Service<Uri> for TlsConnector {
|
|
||||||
type Response = TlsStream<TcpStream>;
|
|
||||||
type Error = super::Error;
|
|
||||||
|
|
||||||
type Future =
|
|
||||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
MakeConnection::poll_ready(&mut self.http, cx)
|
|
||||||
.map_err(|e| super::Error::from((super::ErrorKind::Client, e.into())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, uri: Uri) -> Self::Future {
|
|
||||||
let dns = DNSNameRef::try_from_ascii_str(self.domain.as_str())
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
let config = self.config.clone();
|
|
||||||
let connect = self.http.make_connection(uri.clone());
|
|
||||||
|
|
||||||
let fut = async move {
|
|
||||||
let io = match connect.await {
|
|
||||||
Ok(io) => io,
|
|
||||||
Err(e) => return Err(super::Error::from((super::ErrorKind::Client, e.into()))),
|
|
||||||
};
|
|
||||||
|
|
||||||
RustlsConnector::from(config)
|
|
||||||
.connect(dns.as_ref(), io)
|
|
||||||
.await
|
|
||||||
.map_err(|e| super::Error::from((super::ErrorKind::Client, e.into())))
|
|
||||||
.and_then(|conn| {
|
|
||||||
let (_, session) = conn.get_ref();
|
|
||||||
let negotiated_protocol = session.get_alpn_protocol();
|
|
||||||
|
|
||||||
if Some(ALPN_H2.as_bytes()) == negotiated_protocol.as_ref().map(|x| &**x) {
|
|
||||||
Ok(conn)
|
|
||||||
} else {
|
|
||||||
Err(super::Error::from(super::ErrorKind::Client).into())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
Box::pin(fut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user