Repository: swimos/swim-rust (language: rust)

Issue:
Add granular support for TLS on the server
Currently `ServerBuilder` can enable TLS support using the `add_tls_support(mut self, config: TlsConfig)` method, however the `TlsConfig` requires both a `ClientConfig` and a `ServerConfig`.

It should be possible to create servers that can open server downlinks with TLS, without requiring them to have TLS support themselves.


Discussion / hints:
It should probably be the case that _outgoing_ TLS is just there by default.

Fix patch:
diff --git a/runtime/swimos_remote/src/net/plain.rs b/runtime/swimos_remote/src/net/plain.rs
index 4de8160d7..1abff96ec 100644
--- a/runtime/swimos_remote/src/net/plain.rs
+++ b/runtime/swimos_remote/src/net/plain.rs
@@ -71,13 +71,13 @@ impl ClientConnections for TokioPlainTextNetworking {
         .boxed()
     }
 
-    fn lookup(&self, host: String, port: u16) -> BoxFuture<'static, IoResult<Vec<SocketAddr>>> {
-        self.resolver.resolve(host, port)
-    }
-
     fn dns_resolver(&self) -> BoxDnsResolver {
         Box::new(self.resolver.clone())
     }
+
+    fn lookup(&self, host: String, port: u16) -> BoxFuture<'static, IoResult<Vec<SocketAddr>>> {
+        self.resolver.resolve(host, port)
+    }
 }
 
 impl ServerConnections for TokioPlainTextNetworking {
diff --git a/runtime/swimos_tls/src/net/mod.rs b/runtime/swimos_tls/src/net/mod.rs
index f59177ece..d583e03c0 100644
--- a/runtime/swimos_tls/src/net/mod.rs
+++ b/runtime/swimos_tls/src/net/mod.rs
@@ -17,25 +17,26 @@ mod server;
 #[cfg(test)]
 mod tests;
 
-use std::{net::SocketAddr, sync::Arc};
+use std::net::SocketAddr;
 
 pub use client::RustlsClientNetworking;
+use futures::future::Either;
 use futures::TryFutureExt;
 use futures::{future::BoxFuture, FutureExt};
 pub use server::{RustlsListener, RustlsServerNetworking};
 use swimos_api::net::Scheme;
+use swimos_remote::net::plain::TokioPlainTextNetworking;
 use swimos_remote::net::{
-    dns::{BoxDnsResolver, Resolver},
-    ClientConnections, ConnResult, IoResult, ServerConnections,
+    dns::BoxDnsResolver, ClientConnections, ConnResult, IoResult, ServerConnections,
 };
 
 use crate::{
-    config::{CertFormat, CertificateFile, TlsConfig},
+    config::{CertFormat, CertificateFile},
     errors::TlsError,
     maybe::MaybeTlsStream,
 };
 
-use self::server::MaybeRustlsListener;
+use self::server::MaybeRustTlsListener;
 
 fn load_cert_file(file: CertificateFile) -> Result<Vec<rustls::Certificate>, TlsError> {
     let CertificateFile { format, body } = file;
@@ -49,29 +50,32 @@ fn load_cert_file(file: CertificateFile) -> Result<Vec<rustls::Certificate>, Tls
     Ok(certs.into_iter().map(rustls::Certificate).collect())
 }
 
-/// Combined implementation of [`ClientConnections`] and [`ServerConnections`] that wraps both
-/// [`RustlsClientNetworking`] and [`RustlsServerNetworking`]. The server part is adapted to
+/// Combined implementation of [`ClientConnections`] and [`ServerConnections`] that wraps
+/// [`RustlsClientNetworking`], [`RustlsServerNetworking`] and [`TokioPlainTextNetworking`]. The server part is adapted to
 /// produce [`MaybeTlsStream`] connections so that there is a unified client/server socket type,
 /// inducing an implementation of [`super::ExternalConnections`].
 #[derive(Clone)]
 pub struct RustlsNetworking {
     client: RustlsClientNetworking,
-    server: RustlsServerNetworking,
+    server: Either<TokioPlainTextNetworking, RustlsServerNetworking>,
 }
 
 impl RustlsNetworking {
-    pub fn new(client: RustlsClientNetworking, server: RustlsServerNetworking) -> Self {
-        RustlsNetworking { client, server }
+    pub fn new_plain_text(
+        client: RustlsClientNetworking,
+        server: TokioPlainTextNetworking,
+    ) -> Self {
+        RustlsNetworking {
+            client,
+            server: Either::Left(server),
+        }
     }
 
-    pub fn try_from_config(resolver: Arc<Resolver>, config: TlsConfig) -> Result<Self, TlsError> {
-        let TlsConfig {
-            client: client_conf,
-            server: server_conf,
-        } = config;
-        let client = RustlsClientNetworking::try_from_config(resolver, client_conf)?;
-        let server = RustlsServerNetworking::try_from(server_conf)?;
-        Ok(RustlsNetworking { client, server })
+    pub fn new_tls(client: RustlsClientNetworking, server: RustlsServerNetworking) -> Self {
+        RustlsNetworking {
+            client,
+            server: Either::Right(server),
+        }
     }
 }
 
@@ -99,15 +103,21 @@ impl ClientConnections for RustlsNetworking {
 impl ServerConnections for RustlsNetworking {
     type ServerSocket = MaybeTlsStream;
 
-    type ListenerType = MaybeRustlsListener;
+    type ListenerType = MaybeRustTlsListener;
 
     fn bind(
         &self,
         addr: SocketAddr,
     ) -> BoxFuture<'static, ConnResult<(SocketAddr, Self::ListenerType)>> {
-        self.server
-            .make_listener(addr)
-            .map_ok(|(addr, listener)| (addr, MaybeRustlsListener::from(listener)))
-            .boxed()
+        match &self.server {
+            Either::Left(plain_text_server) => plain_text_server
+                .bind(addr)
+                .map_ok(|(addr, listener)| (addr, MaybeRustTlsListener::from(listener)))
+                .boxed(),
+            Either::Right(tls_server) => tls_server
+                .make_listener(addr)
+                .map_ok(|(addr, listener)| (addr, MaybeRustTlsListener::from(listener)))
+                .boxed(),
+        }
     }
 }
diff --git a/runtime/swimos_tls/src/net/server.rs b/runtime/swimos_tls/src/net/server.rs
index a0de54d9d..26a3af998 100644
--- a/runtime/swimos_tls/src/net/server.rs
+++ b/runtime/swimos_tls/src/net/server.rs
@@ -226,27 +226,45 @@ fn tls_accept_stream(
     })
 }
 
-/// This wraps connections for a [`RustlsListener`] as [`crate::maybe::MaybeTlsStream`] to unify server and client
+/// This wraps connections for [`TcpListener`] and [`RustlsListener`] as [`crate::maybe::MaybeTlsStream`] to unify server and client
 /// connection types.
-pub struct MaybeRustlsListener {
-    inner: RustlsListener,
+pub struct MaybeRustTlsListener {
+    inner: Either<TcpListener, RustlsListener>,
 }
 
-impl From<RustlsListener> for MaybeRustlsListener {
+impl From<TcpListener> for MaybeRustTlsListener {
+    fn from(inner: TcpListener) -> Self {
+        MaybeRustTlsListener {
+            inner: Either::Left(inner),
+        }
+    }
+}
+
+impl From<RustlsListener> for MaybeRustTlsListener {
     fn from(inner: RustlsListener) -> Self {
-        MaybeRustlsListener { inner }
+        MaybeRustTlsListener {
+            inner: Either::Right(inner),
+        }
     }
 }
 
-impl Listener<MaybeTlsStream> for MaybeRustlsListener {
+impl Listener<MaybeTlsStream> for MaybeRustTlsListener {
     type AcceptStream = BoxListenerStream<MaybeTlsStream>;
 
     fn into_stream(self) -> Self::AcceptStream {
-        let MaybeRustlsListener {
-            inner: RustlsListener { listener, acceptor },
-        } = self;
-        tls_accept_stream(listener, acceptor)
-            .map_ok(|(sock, scheme, addr)| (MaybeTlsStream::Tls(sock), scheme, addr))
-            .boxed()
+        let MaybeRustTlsListener { inner } = self;
+
+        match inner {
+            Either::Left(listener) => listener
+                .into_stream()
+                .map_ok(|(sock, scheme, addr)| (MaybeTlsStream::Plain(sock), scheme, addr))
+                .boxed(),
+
+            Either::Right(RustlsListener { listener, acceptor }) => {
+                tls_accept_stream(listener, acceptor)
+                    .map_ok(|(sock, scheme, addr)| (MaybeTlsStream::Tls(sock), scheme, addr))
+                    .boxed()
+            }
+        }
     }
 }
diff --git a/server/swimos_server_app/src/server/builder/mod.rs b/server/swimos_server_app/src/server/builder/mod.rs
index d6f3580ec..222458a5b 100644
--- a/server/swimos_server_app/src/server/builder/mod.rs
+++ b/server/swimos_server_app/src/server/builder/mod.rs
@@ -22,8 +22,11 @@ use ratchet::{
     NoExtProvider, WebSocketStream,
 };
 use swimos_api::{agent::Agent, error::StoreError, store::StoreDisabled};
-use swimos_remote::net::{dns::Resolver, plain::TokioPlainTextNetworking, ExternalConnections};
-use swimos_tls::{RustlsNetworking, TlsConfig};
+use swimos_remote::net::plain::TokioPlainTextNetworking;
+use swimos_remote::net::{dns::Resolver, ExternalConnections};
+use swimos_tls::{
+    ClientConfig, RustlsClientNetworking, RustlsNetworking, RustlsServerNetworking, TlsConfig,
+};
 use swimos_utilities::routing::route_pattern::RoutePattern;
 
 use crate::{
@@ -175,10 +178,15 @@ impl ServerBuilder {
             introspection,
         };
         if let Some(tls_conf) = tls_config {
-            let networking = RustlsNetworking::try_from_config(resolver, tls_conf)?;
+            let client = RustlsClientNetworking::try_from_config(resolver, tls_conf.client)?;
+            let server = RustlsServerNetworking::try_from(tls_conf.server)?;
+            let networking = RustlsNetworking::new_tls(client, server);
             Ok(with_store(bind_to, routes, networking, config)?)
         } else {
-            let networking = TokioPlainTextNetworking::new(resolver);
+            let client =
+                RustlsClientNetworking::try_from_config(resolver.clone(), ClientConfig::default())?;
+            let server = TokioPlainTextNetworking::new(resolver);
+            let networking = RustlsNetworking::new_plain_text(client, server);
             Ok(with_store(bind_to, routes, networking, config)?)
         }
     }


Test patch: