From 196da6d570b1f93b6892f4d7ba11fdd9d8ef630f Mon Sep 17 00:00:00 2001 From: Marat Safin Date: Sun, 29 Jul 2018 09:43:04 +0300 Subject: [PATCH] add rustls --- Cargo.toml | 11 ++- src/client/connector.rs | 153 ++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 14 ++++ src/server/mod.rs | 43 +++++++++++ src/server/srv.rs | 51 ++++++++++++++ src/server/worker.rs | 43 ++++++++++- src/test.rs | 45 +++++++++--- tests/cert.pem | 58 +++++++-------- tests/test_ws.rs | 42 +++++++++++ 9 files changed, 413 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 29c2dadb..54bd0e38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] build = "build.rs" [package.metadata.docs.rs] -features = ["tls", "alpn", "session", "brotli", "flate2-c"] +features = ["tls", "alpn", "rust-tls", "session", "brotli", "flate2-c"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -37,6 +37,9 @@ tls = ["native-tls", "tokio-tls"] # openssl alpn = ["openssl", "tokio-openssl"] +# rustls +rust-tls = ["rustls", "tokio-rustls", "webpki", "webpki-roots"] + # sessions feature, session require "ring" crate and c compiler session = ["cookie/secure"] @@ -104,6 +107,12 @@ tokio-tls = { version="0.1", optional = true } openssl = { version="0.10", optional = true } tokio-openssl = { version="0.2", optional = true } +#rustls +rustls = { version = "0.13", optional = true } +tokio-rustls = { version = "0.7", optional = true } +webpki = { version = "0.18", optional = true } +webpki-roots = { version = "0.15", optional = true } + # forked url_encoded itoa = "0.4" dtoa = "0.4" diff --git a/src/client/connector.rs b/src/client/connector.rs index c2ff328e..a0054671 100644 --- a/src/client/connector.rs +++ b/src/client/connector.rs @@ -22,12 +22,25 @@ use openssl::ssl::{Error as OpensslError, SslConnector, SslMethod}; use tokio_openssl::SslConnectorExt; #[cfg(all(feature = "tls", not(feature = "alpn")))] -use native_tls::{Error as TlsError, TlsConnector}; +use native_tls::{Error as TlsError, TlsConnector, TlsStream}; #[cfg(all(feature = "tls", not(feature = "alpn")))] use tokio_tls::TlsConnectorExt; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use rustls::ClientConfig; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use std::io::Error as TLSError; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use std::sync::Arc; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use tokio_rustls::ClientConfigExt; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use webpki::DNSNameRef; +#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] +use webpki_roots; + use server::IoStream; -use {HAS_OPENSSL, HAS_TLS}; +use {HAS_OPENSSL, HAS_TLS, HAS_RUSTLS}; /// Client connector usage stats #[derive(Default, Message)] @@ -139,6 +152,11 @@ pub enum ClientConnectorError { #[fail(display = "{}", _0)] SslError(#[cause] TlsError), + /// SSL error + #[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] + #[fail(display = "{}", _0)] + SslError(#[cause] TLSError), + /// Resolver error #[fail(display = "{}", _0)] Resolver(#[cause] ResolverError), @@ -193,6 +211,8 @@ pub struct ClientConnector { connector: SslConnector, #[cfg(all(feature = "tls", not(feature = "alpn")))] connector: TlsConnector, + #[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] + connector: Arc, stats: ClientConnectorStats, subscriber: Option>, @@ -262,8 +282,16 @@ impl Default for ClientConnector { paused: Paused::No, } } + #[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] + { + let mut config = ClientConfig::new(); + config + .root_store + .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + ClientConnector::with_connector(Arc::new(config)) + } - #[cfg(not(any(feature = "alpn", feature = "tls")))] + #[cfg(not(any(feature = "alpn", feature = "tls", feature = "rust-tls")))] { let (tx, rx) = mpsc::unbounded(); ClientConnector { @@ -325,7 +353,7 @@ impl ClientConnector { /// # actix::System::current().stop(); /// Ok(()) /// }) - /// ); + /// }); /// } /// ``` pub fn with_connector(connector: SslConnector) -> ClientConnector { @@ -352,6 +380,75 @@ impl ClientConnector { } } + #[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] + /// Create `ClientConnector` actor with custom `SslConnector` instance. + /// + /// By default `ClientConnector` uses very a simple SSL configuration. + /// With `with_connector` method it is possible to use a custom + /// `SslConnector` object. + /// + /// ```rust + /// # #![cfg(feature = "rust-tls")] + /// # extern crate actix_web; + /// # extern crate futures; + /// # extern crate tokio; + /// # use futures::{future, Future}; + /// # use std::io::Write; + /// # use std::process; + /// # use actix_web::actix::Actor; + /// extern crate rustls; + /// extern crate webpki_roots; + /// use actix_web::{actix, client::ClientConnector, client::Connect}; + /// + /// use rustls::ClientConfig; + /// use std::sync::Arc; + /// + /// fn main() { + /// actix::run(|| { + /// // Start `ClientConnector` with custom `ClientConfig` + /// let mut config = ClientConfig::new(); + /// config + /// .root_store + /// .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + /// let conn = ClientConnector::with_connector(Arc::new(config)).start(); + /// + /// conn.send( + /// Connect::new("https://www.rust-lang.org").unwrap()) // <- connect to host + /// .map_err(|_| ()) + /// .and_then(|res| { + /// if let Ok(mut stream) = res { + /// stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + /// } + /// # actix::System::current().stop(); + /// Ok(()) + /// }) + /// }); + /// } + /// ``` + pub fn with_connector(connector: Arc) -> ClientConnector { + let (tx, rx) = mpsc::unbounded(); + + ClientConnector { + connector, + stats: ClientConnectorStats::default(), + subscriber: None, + acq_tx: tx, + acq_rx: Some(rx), + resolver: None, + conn_lifetime: Duration::from_secs(75), + conn_keep_alive: Duration::from_secs(15), + limit: 100, + limit_per_host: 0, + acquired: 0, + acquired_per_host: HashMap::new(), + available: HashMap::new(), + to_close: Vec::new(), + waiters: Some(HashMap::new()), + wait_timeout: None, + paused: Paused::No, + } + } + /// Set total number of simultaneous connections. /// /// If limit is 0, the connector has no limit. @@ -709,7 +806,51 @@ impl ClientConnector { } } - #[cfg(not(any(feature = "alpn", feature = "tls")))] + #[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))] + match res { + Err(err) => { + let _ = waiter.tx.send(Err(err.into())); + fut::Either::B(fut::err(())) + } + Ok(stream) => { + act.stats.opened += 1; + if conn.0.ssl { + let host = DNSNameRef::try_from_ascii_str(&key.host).unwrap(); + fut::Either::A( + act.connector + .connect_async(host, stream) + .into_actor(act) + .then(move |res, _, _| { + match res { + Err(e) => { + let _ = waiter.tx.send(Err( + ClientConnectorError::SslError(e), + )); + } + Ok(stream) => { + let _ = + waiter.tx.send(Ok(Connection::new( + conn.0.clone(), + Some(conn), + Box::new(stream), + ))); + } + } + fut::ok(()) + }), + ) + } else { + let _ = waiter.tx.send(Ok(Connection::new( + conn.0.clone(), + Some(conn), + Box::new(stream), + ))); + fut::Either::B(fut::ok(())) + } + } + } + + #[cfg(not(any(feature = "alpn", feature = "tls", feature = "rust-tls")))] match res { Err(err) => { let _ = waiter.tx.send(Err(err.into())); @@ -784,7 +925,7 @@ impl Handler for ClientConnector { }; // check ssl availability - if proto.is_secure() && !HAS_OPENSSL && !HAS_TLS { + if proto.is_secure() && !HAS_OPENSSL && !HAS_TLS && !HAS_RUSTLS { return ActorResponse::reply(Err(ClientConnectorError::SslIsNotSupported)); } diff --git a/src/lib.rs b/src/lib.rs index 528eb7b7..626bb95f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,15 @@ extern crate openssl; #[cfg(feature = "openssl")] extern crate tokio_openssl; +#[cfg(feature = "rust-tls")] +extern crate rustls; +#[cfg(feature = "rust-tls")] +extern crate tokio_rustls; +#[cfg(feature = "rust-tls")] +extern crate webpki; +#[cfg(feature = "rust-tls")] +extern crate webpki_roots; + mod application; mod body; mod context; @@ -224,6 +233,11 @@ pub(crate) const HAS_TLS: bool = true; #[cfg(not(feature = "tls"))] pub(crate) const HAS_TLS: bool = false; +#[cfg(feature = "rust-tls")] +pub(crate) const HAS_RUSTLS: bool = true; +#[cfg(not(feature = "rust-tls"))] +pub(crate) const HAS_RUSTLS: bool = false; + pub mod dev { //! The `actix-web` prelude for library developers //! diff --git a/src/server/mod.rs b/src/server/mod.rs index a302f5e7..dc8ecd81 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -310,3 +310,46 @@ impl IoStream for TlsStream { self.get_mut().get_mut().set_linger(dur) } } + +#[cfg(feature = "rust-tls")] +use rustls::{ClientSession, ServerSession}; +#[cfg(feature = "rust-tls")] +use tokio_rustls::TlsStream; + +#[cfg(feature = "rust-tls")] +impl IoStream for TlsStream { + #[inline] + fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> { + let _ = ::shutdown(self); + Ok(()) + } + + #[inline] + fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { + self.get_mut().0.set_nodelay(nodelay) + } + + #[inline] + fn set_linger(&mut self, dur: Option) -> io::Result<()> { + self.get_mut().0.set_linger(dur) + } +} + +#[cfg(feature = "rust-tls")] +impl IoStream for TlsStream { + #[inline] + fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> { + let _ = ::shutdown(self); + Ok(()) + } + + #[inline] + fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { + self.get_mut().0.set_nodelay(nodelay) + } + + #[inline] + fn set_linger(&mut self, dur: Option) -> io::Result<()> { + self.get_mut().0.set_linger(dur) + } +} diff --git a/src/server/srv.rs b/src/server/srv.rs index 02580d01..d6f5cf4d 100644 --- a/src/server/srv.rs +++ b/src/server/srv.rs @@ -22,6 +22,9 @@ use native_tls::TlsAcceptor; #[cfg(feature = "alpn")] use openssl::ssl::{AlpnError, SslAcceptorBuilder}; +#[cfg(feature = "rust-tls")] +use rustls::ServerConfig; + use super::channel::{HttpChannel, WrapperStream}; use super::settings::{ServerSettings, WorkerSettings}; use super::worker::{Conn, SocketInfo, StopWorker, StreamHandlerType, Worker}; @@ -42,6 +45,14 @@ fn configure_alpn(builder: &mut SslAcceptorBuilder) -> io::Result<()> { Ok(()) } +#[cfg(all(feature = "rust-tls", not(feature = "alpn")))] +fn configure_alpn(builder: &mut Arc) -> io::Result<()> { + Arc::::get_mut(builder) + .unwrap() + .set_protocols(&vec!["h2".to_string(), "http/1.1".to_string()]); + Ok(()) +} + /// An HTTP Server pub struct HttpServer where @@ -265,6 +276,26 @@ where Ok(self) } + #[cfg(all(feature = "rust-tls", not(feature = "alpn")))] + /// Use listener for accepting incoming tls connection requests + /// + /// This method sets alpn protocols to "h2" and "http/1.1" + pub fn listen_ssl( + mut self, lst: net::TcpListener, mut builder: Arc, + ) -> io::Result { + // alpn support + if !self.no_http2 { + configure_alpn(&mut builder)?; + } + let addr = lst.local_addr().unwrap(); + self.sockets.push(Socket { + addr, + lst, + tp: StreamHandlerType::Rustls(builder.clone()), + }); + Ok(self) + } + fn bind2(&mut self, addr: S) -> io::Result> { let mut err = None; let mut succ = false; @@ -343,6 +374,26 @@ where Ok(self) } + #[cfg(all(feature = "rust-tls", not(feature = "alpn")))] + /// Start listening for incoming tls connections. + /// + /// This method sets alpn protocols to "h2" and "http/1.1" + pub fn bind_ssl( + mut self, addr: S, mut builder: Arc, + ) -> io::Result { + // alpn support + if !self.no_http2 { + configure_alpn(&mut builder)?; + } + + let sockets = self.bind2(addr)?; + self.sockets.extend(sockets.into_iter().map(|mut s| { + s.tp = StreamHandlerType::Rustls(builder.clone()); + s + })); + Ok(self) + } + fn start_workers( &mut self, settings: &ServerSettings, sockets: &Slab, ) -> Vec<(usize, mpsc::UnboundedSender>)> { diff --git a/src/server/worker.rs b/src/server/worker.rs index 8fd3fe60..5e753ce5 100644 --- a/src/server/worker.rs +++ b/src/server/worker.rs @@ -8,7 +8,7 @@ use tokio::executor::current_thread; use tokio_reactor::Handle; use tokio_tcp::TcpStream; -#[cfg(any(feature = "tls", feature = "alpn"))] +#[cfg(any(feature = "tls", feature = "alpn", feature = "rust-tls"))] use futures::future; #[cfg(feature = "tls")] @@ -21,6 +21,13 @@ use openssl::ssl::SslAcceptor; #[cfg(feature = "alpn")] use tokio_openssl::SslAcceptorExt; +#[cfg(feature = "rust-tls")] +use rustls::{ServerConfig, Session}; +#[cfg(feature = "rust-tls")] +use std::sync::Arc; +#[cfg(feature = "rust-tls")] +use tokio_rustls::ServerConfigExt; + use actix::msgs::StopArbiter; use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response}; @@ -170,6 +177,8 @@ pub(crate) enum StreamHandlerType { Tls(TlsAcceptor), #[cfg(feature = "alpn")] Alpn(SslAcceptor), + #[cfg(feature = "rust-tls")] + Rustls(Arc), } impl StreamHandlerType { @@ -237,6 +246,36 @@ impl StreamHandlerType { }, )); } + #[cfg(feature = "rust-tls")] + StreamHandlerType::Rustls(ref acceptor) => { + let Conn { io, peer, .. } = msg; + let _ = io.set_nodelay(true); + let io = TcpStream::from_std(io, &Handle::default()) + .expect("failed to associate TCP stream"); + + current_thread::spawn(ServerConfigExt::accept_async(acceptor, io).then( + move |res| { + match res { + Ok(io) => { + let http2 = if let Some(p) = + io.get_ref().1.get_alpn_protocol() + { + p.len() == 2 && &p == &"h2" + } else { + false + }; + current_thread::spawn(HttpChannel::new( + h, io, peer, http2, + )); + } + Err(err) => { + trace!("Error during handling tls connection: {}", err) + } + }; + future::result(Ok(())) + }, + )); + } } } @@ -247,6 +286,8 @@ impl StreamHandlerType { StreamHandlerType::Tls(_) => "https", #[cfg(feature = "alpn")] StreamHandlerType::Alpn(_) => "https", + #[cfg(feature = "rust-tls")] + StreamHandlerType::Rustls(_) => "https", } } } diff --git a/src/test.rs b/src/test.rs index c2e5c756..f466db2d 100644 --- a/src/test.rs +++ b/src/test.rs @@ -15,6 +15,10 @@ use tokio::runtime::current_thread::Runtime; #[cfg(feature = "alpn")] use openssl::ssl::SslAcceptorBuilder; +#[cfg(feature = "rust-tls")] +use rustls::ServerConfig; +#[cfg(feature = "rust-tls")] +use std::sync::Arc; use application::{App, HttpApplication}; use body::Binary; @@ -140,7 +144,19 @@ impl TestServer { builder.set_verify(SslVerifyMode::NONE); ClientConnector::with_connector(builder.build()).start() } - #[cfg(not(feature = "alpn"))] + #[cfg(feature = "rust-tls")] + { + use rustls::ClientConfig; + use std::io::BufReader; + use std::fs::File; + let mut config = ClientConfig::new(); + let pem_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); + config + .root_store + .add_pem_file(pem_file).unwrap(); + ClientConnector::with_connector(Arc::new(config)).start() + } + #[cfg(not(any(feature = "alpn", feature = "rust-tls")))] { ClientConnector::default().start() } @@ -165,16 +181,16 @@ impl TestServer { pub fn url(&self, uri: &str) -> String { if uri.starts_with('/') { format!( - "{}://{}{}", + "{}://localhost:{}{}", if self.ssl { "https" } else { "http" }, - self.addr, + self.addr.port(), uri ) } else { format!( - "{}://{}/{}", + "{}://localhost:{}/{}", if self.ssl { "https" } else { "http" }, - self.addr, + self.addr.port(), uri ) } @@ -241,6 +257,8 @@ pub struct TestServerBuilder { state: Box S + Sync + Send + 'static>, #[cfg(feature = "alpn")] ssl: Option, + #[cfg(feature = "rust-tls")] + ssl: Option>, } impl TestServerBuilder { @@ -251,7 +269,7 @@ impl TestServerBuilder { { TestServerBuilder { state: Box::new(state), - #[cfg(feature = "alpn")] + #[cfg(any(feature = "alpn", feature = "rust-tls"))] ssl: None, } } @@ -263,6 +281,13 @@ impl TestServerBuilder { self } + #[cfg(feature = "rust-tls")] + /// Create ssl server + pub fn ssl(mut self, ssl: Arc) -> Self { + self.ssl = Some(ssl); + self + } + #[allow(unused_mut)] /// Configure test application and run test server pub fn start(mut self, config: F) -> TestServer @@ -271,9 +296,9 @@ impl TestServerBuilder { { let (tx, rx) = mpsc::channel(); - #[cfg(feature = "alpn")] + #[cfg(any(feature = "alpn", feature = "rust-tls"))] let ssl = self.ssl.is_some(); - #[cfg(not(feature = "alpn"))] + #[cfg(not(any(feature = "alpn", feature = "rust-tls")))] let ssl = false; // run server in separate thread @@ -293,7 +318,7 @@ impl TestServerBuilder { tx.send((System::current(), local_addr, TestServer::get_conn())) .unwrap(); - #[cfg(feature = "alpn")] + #[cfg(any(feature = "alpn", feature = "rust-tls"))] { let ssl = self.ssl.take(); if let Some(ssl) = ssl { @@ -302,7 +327,7 @@ impl TestServerBuilder { srv.listen(tcp).start(); } } - #[cfg(not(feature = "alpn"))] + #[cfg(not(any(feature = "alpn", feature = "rust-tls")))] { srv.listen(tcp).start(); } diff --git a/tests/cert.pem b/tests/cert.pem index 159aacea..db04fbfa 100644 --- a/tests/cert.pem +++ b/tests/cert.pem @@ -1,31 +1,31 @@ -----BEGIN CERTIFICATE----- -MIIFPjCCAyYCCQDvLYiYD+jqeTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMRAwDgYDVQQKDAdDb21wYW55MQww -CgYDVQQLDANPcmcxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xODAxMjUx -NzQ2MDFaFw0xOTAxMjUxNzQ2MDFaMGExCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD -QTELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBhbnkxDDAKBgNVBAsMA09yZzEY -MBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEA2WzIA2IpVR9Tb9EFhITlxuhE5rY2a3S6qzYNzQVgSFggxXEPn8k1 -sQEcer5BfAP986Sck3H0FvB4Bt/I8PwOtUCmhwcc8KtB5TcGPR4fjXnrpC+MIK5U -NLkwuyBDKziYzTdBj8kUFX1WxmvEHEgqToPOZfBgsS71cJAR/zOWraDLSRM54jXy -voLZN4Ti9rQagQrvTQ44Vz5ycDQy7UxtbUGh1CVv69vNVr7/SOOh/Nw5FNOZWLWr -odGyoec5wh9iqRZgRqiTUc6Lt7V2RWc2X2gjwST2UfI+U46Ip3oaQ7ZD4eAkoqND -xdniBZAykVG3c/99ux4BAESTF8fsNch6UticBxYMuTu+ouvP0psfI9wwwNliJDmA -CRUTB9AgRynbL1AzhqQoDfsb98IZfjfNOpwnwuLwpMAPhbgd5KNdZaIJ4Hb6/stI -yFElOExxd3TAxF2Gshd/lq1JcNHAZ1DSXV5MvOWT/NWgXwbIzUgQ8eIi+HuDYX2U -UuaB6R8tbd52H7rbUv6HrfinuSlKWqjSYLkiKHkwUpoMw8y9UycRSzs1E9nPwPTO -vRXb0mNCQeBCV9FvStNVXdCUTT8LGPv87xSD2pmt7LijlE6mHLG8McfcWkzA69un -CEHIFAFDimTuN7EBljc119xWFTcHMyoZAfFF+oTqwSbBGImruCxnaJECAwEAATAN -BgkqhkiG9w0BAQsFAAOCAgEApavsgsn7SpPHfhDSN5iZs1ILZQRewJg0Bty0xPfk -3tynSW6bNH3nSaKbpsdmxxomthNSQgD2heOq1By9YzeOoNR+7Pk3s4FkASnf3ToI -JNTUasBFFfaCG96s4Yvs8KiWS/k84yaWuU8c3Wb1jXs5Rv1qE1Uvuwat1DSGXSoD -JNluuIkCsC4kWkyq5pWCGQrabWPRTWsHwC3PTcwSRBaFgYLJaR72SloHB1ot02zL -d2age9dmFRFLLCBzP+D7RojBvL37qS/HR+rQ4SoQwiVc/JzaeqSe7ZbvEH9sZYEu -ALowJzgbwro7oZflwTWunSeSGDSltkqKjvWvZI61pwfHKDahUTmZ5h2y67FuGEaC -CIOUI8dSVSPKITxaq3JL4ze2e9/0Lt7hj19YK2uUmtMAW5Tirz4Yx5lyGH9U8Wur -y/X8VPxTc4A9TMlJgkyz0hqvhbPOT/zSWB10zXh0glKAsSBryAOEDxV1UygmSir7 -YV8Qaq+oyKUTMc1MFq5vZ07M51EPaietn85t8V2Y+k/8XYltRp32NxsypxAJuyxh -g/ko6RVTrWa1sMvz/F9LFqAdKiK5eM96lh9IU4xiLg4ob8aS/GRAA8oIFkZFhLrt -tOwjIUPmEPyHWFi8dLpNuQKYalLYhuwZftG/9xV+wqhKGZO9iPrpHSYBRTap8w2y -1QU= +MIIFXTCCA0WgAwIBAgIJAJ3tqfd0MLLNMA0GCSqGSIb3DQEBCwUAMGExCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDRjELMAkGA1UEBwwCU0YxEDAOBgNVBAoMB0NvbXBh +bnkxDDAKBgNVBAsMA09yZzEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tMB4XDTE4 +MDcyOTE4MDgzNFoXDTE5MDcyOTE4MDgzNFowYTELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNGMQswCQYDVQQHDAJTRjEQMA4GA1UECgwHQ29tcGFueTEMMAoGA1UECwwD +T3JnMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDZbMgDYilVH1Nv0QWEhOXG6ETmtjZrdLqrNg3NBWBIWCDF +cQ+fyTWxARx6vkF8A/3zpJyTcfQW8HgG38jw/A61QKaHBxzwq0HlNwY9Hh+Neeuk +L4wgrlQ0uTC7IEMrOJjNN0GPyRQVfVbGa8QcSCpOg85l8GCxLvVwkBH/M5atoMtJ +EzniNfK+gtk3hOL2tBqBCu9NDjhXPnJwNDLtTG1tQaHUJW/r281Wvv9I46H83DkU +05lYtauh0bKh5znCH2KpFmBGqJNRzou3tXZFZzZfaCPBJPZR8j5TjoinehpDtkPh +4CSio0PF2eIFkDKRUbdz/327HgEARJMXx+w1yHpS2JwHFgy5O76i68/Smx8j3DDA +2WIkOYAJFRMH0CBHKdsvUDOGpCgN+xv3whl+N806nCfC4vCkwA+FuB3ko11logng +dvr+y0jIUSU4THF3dMDEXYayF3+WrUlw0cBnUNJdXky85ZP81aBfBsjNSBDx4iL4 +e4NhfZRS5oHpHy1t3nYfuttS/oet+Ke5KUpaqNJguSIoeTBSmgzDzL1TJxFLOzUT +2c/A9M69FdvSY0JB4EJX0W9K01Vd0JRNPwsY+/zvFIPama3suKOUTqYcsbwxx9xa +TMDr26cIQcgUAUOKZO43sQGWNzXX3FYVNwczKhkB8UX6hOrBJsEYiau4LGdokQID +AQABoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggIB +AIX+Qb4QRBxHl5X2UjRyLfWVkimtGlwI8P+eJZL3DrHBH/TpqAaCvTf0EbRC32nm +ASDMwIghaMvyrW40QN6V/CWRRi25cXUfsIZr1iHAHK0eZJV8SWooYtt4iNrcUs3g +4OTvDxhNmDyNwV9AXhJsBKf80dCW6/84jItqVAj20/OO4Rkd2tEeI8NomiYBc6a1 +hgwvv02myYF5hG/xZ9YSqeroBCZHwGYoJJnSpMPqJsxbCVnx2/U9FzGwcRmNHFCe +0g7EJZd3//8Plza6nkTBjJ/V7JnLqMU+ltx4mAgZO8rfzIr84qZdt0YN33VJQhYq +seuMySxrsuaAoxAmm8IoK9cW4IPzx1JveBQiroNlq5YJGf2UW7BTc3gz6c2tINZi +7ailBVdhlMnDXAf3/9xiiVlRAHOxgZh/7sRrKU7kDEHM4fGoc0YyZBTQKndPYMwO +3Bd82rlQ4sd46XYutTrB+mBYClVrJs+OzbNedTsR61DVNKKsRG4mNPyKSAIgOfM5 +XmSvCMPN5JK9U0DsNIV2/SnVsmcklQczT35FLTxl9ntx8ys7ZYK+SppD7XuLfWMq +GT9YMWhlpw0aRDg/aayeeOcnsNBhzAFMcOpQj1t6Fgv4+zbS9BM2bT0hbX86xjkr +E6wWgkuCslMgQlEJ+TM5RhYrI5/rVZQhvmgcob/9gPZv -----END CERTIFICATE----- diff --git a/tests/test_ws.rs b/tests/test_ws.rs index 66a9153d..1ed80bf7 100644 --- a/tests/test_ws.rs +++ b/tests/test_ws.rs @@ -12,6 +12,8 @@ use rand::Rng; #[cfg(feature = "alpn")] extern crate openssl; +#[cfg(feature = "rust-tls")] +extern crate rustls; use actix::prelude::*; use actix_web::*; @@ -272,3 +274,43 @@ fn test_ws_server_ssl() { assert_eq!(item, data); } } + +#[test] +#[cfg(feature = "rust-tls")] +fn test_ws_server_ssl() { + extern crate rustls; + use rustls::{ServerConfig, NoClientAuth}; + use rustls::internal::pemfile::{certs, rsa_private_keys}; + use std::io::BufReader; + use std::sync::Arc; + use std::fs::File; + + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); + let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); + let cert_chain = certs(cert_file).unwrap(); + let mut keys = rsa_private_keys(key_file).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let mut srv = test::TestServer::build().ssl(Arc::new(config)).start(|app| { + app.handler(|req| { + ws::start( + req, + Ws2 { + count: 0, + bin: false, + }, + ) + }) + }); + + let (mut reader, _writer) = srv.ws().unwrap(); + + let data = Some(ws::Message::Text("0".repeat(65_536))); + for _ in 0..10_000 { + let (item, r) = srv.execute(reader.into_future()).unwrap(); + reader = r; + assert_eq!(item, data); + } +}