actix_tls/connect/
openssl.rs

1//! OpenSSL based connector service.
2//!
3//! See [`TlsConnector`] for main connector service factory docs.
4
5use std::{
6    future::Future,
7    io,
8    pin::Pin,
9    task::{Context, Poll},
10};
11
12use actix_rt::net::ActixStream;
13use actix_service::{Service, ServiceFactory};
14use actix_utils::future::{ok, Ready};
15use futures_core::ready;
16use openssl::ssl::SslConnector;
17use tokio_openssl::SslStream as AsyncSslStream;
18use tracing::trace;
19
20use crate::connect::{Connection, Host};
21
22pub mod reexports {
23    //! Re-exports from `openssl` and `tokio-openssl` that are useful for connectors.
24
25    pub use openssl::ssl::{Error, HandshakeError, SslConnector, SslConnectorBuilder, SslMethod};
26    pub use tokio_openssl::SslStream as AsyncSslStream;
27}
28
29/// Connector service factory using `openssl`.
30pub struct TlsConnector {
31    connector: SslConnector,
32}
33
34impl TlsConnector {
35    /// Constructs new connector service factory from an `openssl` connector.
36    pub fn new(connector: SslConnector) -> Self {
37        TlsConnector { connector }
38    }
39
40    /// Constructs new connector service from an `openssl` connector.
41    pub fn service(connector: SslConnector) -> TlsConnectorService {
42        TlsConnectorService { connector }
43    }
44}
45
46impl Clone for TlsConnector {
47    fn clone(&self) -> Self {
48        Self {
49            connector: self.connector.clone(),
50        }
51    }
52}
53
54impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
55where
56    R: Host,
57    IO: ActixStream + 'static,
58{
59    type Response = Connection<R, AsyncSslStream<IO>>;
60    type Error = io::Error;
61    type Config = ();
62    type Service = TlsConnectorService;
63    type InitError = ();
64    type Future = Ready<Result<Self::Service, Self::InitError>>;
65
66    fn new_service(&self, _: ()) -> Self::Future {
67        ok(TlsConnectorService {
68            connector: self.connector.clone(),
69        })
70    }
71}
72
73/// Connector service using `openssl`.
74pub struct TlsConnectorService {
75    connector: SslConnector,
76}
77
78impl Clone for TlsConnectorService {
79    fn clone(&self) -> Self {
80        Self {
81            connector: self.connector.clone(),
82        }
83    }
84}
85
86impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
87where
88    R: Host,
89    IO: ActixStream,
90{
91    type Response = Connection<R, AsyncSslStream<IO>>;
92    type Error = io::Error;
93    type Future = ConnectFut<R, IO>;
94
95    actix_service::always_ready!();
96
97    fn call(&self, stream: Connection<R, IO>) -> Self::Future {
98        trace!("TLS handshake start for: {:?}", stream.hostname());
99
100        let (io, stream) = stream.replace_io(());
101        let host = stream.hostname();
102
103        let config = self
104            .connector
105            .configure()
106            .expect("SSL connect configuration was invalid.");
107
108        let ssl = config
109            .into_ssl(host)
110            .expect("SSL connect configuration was invalid.");
111
112        ConnectFut {
113            io: Some(AsyncSslStream::new(ssl, io).unwrap()),
114            stream: Some(stream),
115        }
116    }
117}
118
119/// Connect future for OpenSSL service.
120#[doc(hidden)]
121pub struct ConnectFut<R, IO> {
122    io: Option<AsyncSslStream<IO>>,
123    stream: Option<Connection<R, ()>>,
124}
125
126impl<R: Host, IO> Future for ConnectFut<R, IO>
127where
128    R: Host,
129    IO: ActixStream,
130{
131    type Output = Result<Connection<R, AsyncSslStream<IO>>, io::Error>;
132
133    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134        let this = self.get_mut();
135
136        match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
137            Ok(_) => {
138                let stream = this.stream.take().unwrap();
139                trace!("TLS handshake success: {:?}", stream.hostname());
140                Poll::Ready(Ok(stream.replace_io(this.io.take().unwrap()).1))
141            }
142            Err(err) => {
143                trace!("TLS handshake error: {:?}", err);
144                Poll::Ready(Err(io::Error::other(format!("{err}"))))
145            }
146        }
147    }
148}