Refactor all of the tls
This commit is contained in:
@@ -15,11 +15,17 @@ jobs:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install rustfmt
|
||||
run: rustup component add rustfmt
|
||||
- name: Run fmt
|
||||
- name: Check fmt
|
||||
run: cargo fmt -- --check
|
||||
- name: Run check
|
||||
- name: Check all
|
||||
run: cargo check --all
|
||||
- name: Run check with no default features
|
||||
run: cargo check --all --no-default-features
|
||||
- name: Check with 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
|
||||
run: cargo test --all
|
||||
|
||||
@@ -14,5 +14,6 @@ proc-macro2 = "1.0"
|
||||
|
||||
[features]
|
||||
default = ["transport"]
|
||||
rustfmt = []
|
||||
# TODO: reenable this feature
|
||||
# rustfmt = []
|
||||
transport = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use tonic::{Request, Response, Server, Status};
|
||||
use tonic::{transport::Server, Request, Response, Status};
|
||||
|
||||
pub mod hello_world {
|
||||
include!(concat!(env!("OUT_DIR"), "/helloworld.rs"));
|
||||
|
||||
@@ -14,7 +14,7 @@ path = "src/bin/server.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = "=0.2.0-alpha.4"
|
||||
tonic = { path = "../tonic" }
|
||||
tonic = { path = "../tonic", features = ["openssl"] }
|
||||
prost = "0.5"
|
||||
prost-derive = "0.5"
|
||||
bytes = "0.4"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
use structopt::{clap::arg_enum, StructOpt};
|
||||
use tonic::transport::Endpoint;
|
||||
use tonic::transport::{Certificate, Endpoint};
|
||||
use tonic_interop::client;
|
||||
|
||||
#[derive(StructOpt)]
|
||||
@@ -31,8 +31,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.clone();
|
||||
|
||||
if matches.use_tls {
|
||||
let ca = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
||||
endpoint.tls_cert(ca, Some("foo.test.google.fr".into()));
|
||||
let pem = tokio::fs::read("tonic-interop/data/ca.pem").await?;
|
||||
let ca = Certificate::from_pem(pem);
|
||||
endpoint.openssl_tls(ca, Some("foo.test.google.fr".into()));
|
||||
}
|
||||
|
||||
let channel = endpoint.channel()?;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use structopt::StructOpt;
|
||||
use tonic::Server;
|
||||
use tonic::transport::{Identity, Server};
|
||||
use tonic_interop::{server, MergeTrailers};
|
||||
// TODO: move GrpcService out of client since it can be used for the
|
||||
// server too.
|
||||
@@ -24,9 +24,11 @@ async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
let mut builder = Server::builder();
|
||||
|
||||
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?;
|
||||
builder.tls(ca, key);
|
||||
|
||||
let identity = Identity::from_pem(cert, key);
|
||||
builder.openssl_tls(identity);
|
||||
}
|
||||
|
||||
builder.interceptor_fn(|svc, req| {
|
||||
|
||||
+4
-4
@@ -32,7 +32,7 @@ tower-load = { version = "=0.3.0-alpha.1", optional = true }
|
||||
|
||||
# openssl
|
||||
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
|
||||
tokio-rustls = { version = "0.12.0-alpha.2", optional = true }
|
||||
@@ -43,7 +43,6 @@ transport = [
|
||||
"hyper",
|
||||
"tower-1",
|
||||
"tokio",
|
||||
"native-tls",
|
||||
]
|
||||
tower-1 = [
|
||||
"tower",
|
||||
@@ -51,5 +50,6 @@ tower-1 = [
|
||||
"tower-balance",
|
||||
"tower-load",
|
||||
]
|
||||
native-tls = ["openssl", "tokio-openssl"]
|
||||
# rustls = ["tokio-rustls"]
|
||||
openssl = ["openssl1", "tokio-openssl", "tls"]
|
||||
rustls = ["tokio-rustls", "tls"]
|
||||
tls = []
|
||||
|
||||
@@ -51,8 +51,6 @@ pub use codec::Streaming;
|
||||
pub use request::Request;
|
||||
pub use response::Response;
|
||||
pub use status::{Code, Status};
|
||||
#[doc(inline)]
|
||||
pub use transport::{Channel, Server};
|
||||
|
||||
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 http::uri::{InvalidUriBytes, Uri};
|
||||
use std::{convert::TryFrom, time::Duration};
|
||||
@@ -9,7 +11,8 @@ pub struct Endpoint {
|
||||
pub(super) timeout: Option<Duration>,
|
||||
pub(super) concurrency_limit: Option<usize>,
|
||||
pub(super) rate_limit: Option<(u64, Duration)>,
|
||||
pub(super) cert: Option<Cert>,
|
||||
#[cfg(feature = "tls")]
|
||||
pub(super) tls: Option<TlsConnector>,
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
@@ -38,12 +41,19 @@ impl Endpoint {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tls_cert(&mut self, ca: Vec<u8>, domain: Option<String>) -> &mut Self {
|
||||
self.cert = Some(Cert {
|
||||
ca,
|
||||
domain: domain.unwrap_or_else(|| self.uri.clone().to_string()),
|
||||
key: None,
|
||||
});
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn openssl_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_openssl(ca, domain).unwrap();
|
||||
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
|
||||
}
|
||||
|
||||
@@ -61,7 +71,8 @@ impl From<Uri> for Endpoint {
|
||||
concurrency_limit: None,
|
||||
rate_limit: 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::error::Error;
|
||||
pub use self::server::Server;
|
||||
pub use self::tls::{Certificate, Identity};
|
||||
pub use hyper::Body;
|
||||
|
||||
pub(crate) use self::error::ErrorKind;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use super::{
|
||||
service::{layer_fn, BoxedIo},
|
||||
tls::{Cert, TlsAcceptor},
|
||||
};
|
||||
use super::service::{layer_fn, BoxedIo};
|
||||
#[cfg(feature = "tls")]
|
||||
use super::{service::TlsAcceptor, tls::Identity};
|
||||
use crate::body::BoxBody;
|
||||
use futures_core::Stream;
|
||||
use futures_util::{ready, try_future::MapErr, TryFutureExt, TryStreamExt};
|
||||
use http::{Request, Response};
|
||||
use hyper::server::{accept::Accept, conn};
|
||||
use hyper::Body;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
net::SocketAddr,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::layer::util::Stack;
|
||||
@@ -48,9 +47,10 @@ impl Server {
|
||||
///
|
||||
#[derive(Default)]
|
||||
pub struct Builder {
|
||||
tls: Option<(Vec<u8>, Vec<u8>)>,
|
||||
interceptor: Option<Interceptor>,
|
||||
// concurrency_limit: Option<usize>,
|
||||
#[cfg(feature = "tls")]
|
||||
tls: Option<TlsAcceptor>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
@@ -59,8 +59,17 @@ impl Builder {
|
||||
}
|
||||
|
||||
/// Add a tls cert.
|
||||
pub fn tls(&mut self, pem: Vec<u8>, key: Vec<u8>) -> &mut Self {
|
||||
self.tls = Some((pem, key));
|
||||
#[cfg(feature = "openssl")]
|
||||
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
|
||||
}
|
||||
|
||||
@@ -94,23 +103,28 @@ impl Builder {
|
||||
S::Future: Send + 'static,
|
||||
S::Error: Into<crate::Error> + Send,
|
||||
{
|
||||
let tls = if let Some(tls) = self.tls {
|
||||
let cert = Cert {
|
||||
ca: tls.0,
|
||||
key: Some(tls.1),
|
||||
domain: String::new(),
|
||||
};
|
||||
let interceptor = self.interceptor.clone();
|
||||
|
||||
Some(TlsAcceptor::new(cert).map_err(map_err)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let incoming = hyper::server::accept::from_stream(async_stream::try_stream! {
|
||||
let mut tcp = TcpIncoming::bind(addr)?;
|
||||
|
||||
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 {
|
||||
inner: svc,
|
||||
interceptor: self.interceptor.clone(),
|
||||
interceptor,
|
||||
};
|
||||
|
||||
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)]
|
||||
struct TcpIncoming {
|
||||
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 hyper::client::conn::Builder;
|
||||
use hyper::client::service::Connect as HyperConnect;
|
||||
@@ -27,8 +27,12 @@ pub struct Connection {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn new(mut endpoint: Endpoint) -> Result<Self, crate::Error> {
|
||||
let connector = Connector::new(endpoint.cert.take())?;
|
||||
pub fn new(endpoint: Endpoint) -> Result<Self, crate::Error> {
|
||||
#[cfg(feature = "tls")]
|
||||
let connector = connector(endpoint.tls.clone());
|
||||
|
||||
#[cfg(not(feature = "tls"))]
|
||||
let connector = connector();
|
||||
|
||||
let settings = Builder::new().http2_only(true).clone();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::io::BoxedIo;
|
||||
use crate::transport::tls::{Cert, TlsConnector};
|
||||
#[cfg(feature = "tls")]
|
||||
use super::tls::TlsConnector;
|
||||
use http::Uri;
|
||||
use hyper::client::connect::HttpConnector;
|
||||
use std::future::Future;
|
||||
@@ -8,25 +9,31 @@ use std::task::{Context, Poll};
|
||||
use tower_make::MakeConnection;
|
||||
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 {
|
||||
http: HttpConnector,
|
||||
#[cfg(feature = "tls")]
|
||||
tls: Option<TlsConnector>,
|
||||
}
|
||||
|
||||
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();
|
||||
http.enforce_http(false);
|
||||
|
||||
let tls = if let Some(cert) = cert {
|
||||
Some(TlsConnector::new(cert)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self { http, tls })
|
||||
Self { http, tls }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,23 +49,23 @@ impl Service<Uri> for Connector {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Box::pin(connect(io, tls))
|
||||
}
|
||||
}
|
||||
Box::pin(async move {
|
||||
let io = connect.await?;
|
||||
|
||||
async fn connect(
|
||||
connect: ConnectFuture,
|
||||
tls: Option<TlsConnector>,
|
||||
) -> Result<BoxedIo, crate::Error> {
|
||||
let io = connect.await?;
|
||||
#[cfg(feature = "tls")]
|
||||
{
|
||||
if let Some(tls) = tls {
|
||||
let conn = tls.connect(io).await?;
|
||||
return Ok(BoxedIo::new(conn));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tls) = tls {
|
||||
let conn = tls.connect(io).await?;
|
||||
Ok(BoxedIo::new(conn))
|
||||
} else {
|
||||
Ok(BoxedIo::new(io))
|
||||
Ok(BoxedIo::new(io))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ mod connector;
|
||||
mod discover;
|
||||
mod io;
|
||||
mod layer;
|
||||
#[cfg(feature = "tls")]
|
||||
mod tls;
|
||||
|
||||
pub(crate) use self::add_origin::AddOrigin;
|
||||
pub(crate) use self::boxed::BoxService;
|
||||
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::io::BoxedIo;
|
||||
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