mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 18:34:36 +01:00
add rustls
This commit is contained in:
parent
b4ed564e5d
commit
196da6d570
11
Cargo.toml
11
Cargo.toml
@ -17,7 +17,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["tls", "alpn", "session", "brotli", "flate2-c"]
|
features = ["tls", "alpn", "rust-tls", "session", "brotli", "flate2-c"]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "actix/actix-web", branch = "master" }
|
travis-ci = { repository = "actix/actix-web", branch = "master" }
|
||||||
@ -37,6 +37,9 @@ tls = ["native-tls", "tokio-tls"]
|
|||||||
# openssl
|
# openssl
|
||||||
alpn = ["openssl", "tokio-openssl"]
|
alpn = ["openssl", "tokio-openssl"]
|
||||||
|
|
||||||
|
# rustls
|
||||||
|
rust-tls = ["rustls", "tokio-rustls", "webpki", "webpki-roots"]
|
||||||
|
|
||||||
# sessions feature, session require "ring" crate and c compiler
|
# sessions feature, session require "ring" crate and c compiler
|
||||||
session = ["cookie/secure"]
|
session = ["cookie/secure"]
|
||||||
|
|
||||||
@ -104,6 +107,12 @@ tokio-tls = { version="0.1", optional = true }
|
|||||||
openssl = { version="0.10", optional = true }
|
openssl = { version="0.10", optional = true }
|
||||||
tokio-openssl = { version="0.2", 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
|
# forked url_encoded
|
||||||
itoa = "0.4"
|
itoa = "0.4"
|
||||||
dtoa = "0.4"
|
dtoa = "0.4"
|
||||||
|
@ -22,12 +22,25 @@ use openssl::ssl::{Error as OpensslError, SslConnector, SslMethod};
|
|||||||
use tokio_openssl::SslConnectorExt;
|
use tokio_openssl::SslConnectorExt;
|
||||||
|
|
||||||
#[cfg(all(feature = "tls", not(feature = "alpn")))]
|
#[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")))]
|
#[cfg(all(feature = "tls", not(feature = "alpn")))]
|
||||||
use tokio_tls::TlsConnectorExt;
|
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 server::IoStream;
|
||||||
use {HAS_OPENSSL, HAS_TLS};
|
use {HAS_OPENSSL, HAS_TLS, HAS_RUSTLS};
|
||||||
|
|
||||||
/// Client connector usage stats
|
/// Client connector usage stats
|
||||||
#[derive(Default, Message)]
|
#[derive(Default, Message)]
|
||||||
@ -139,6 +152,11 @@ pub enum ClientConnectorError {
|
|||||||
#[fail(display = "{}", _0)]
|
#[fail(display = "{}", _0)]
|
||||||
SslError(#[cause] TlsError),
|
SslError(#[cause] TlsError),
|
||||||
|
|
||||||
|
/// SSL error
|
||||||
|
#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))]
|
||||||
|
#[fail(display = "{}", _0)]
|
||||||
|
SslError(#[cause] TLSError),
|
||||||
|
|
||||||
/// Resolver error
|
/// Resolver error
|
||||||
#[fail(display = "{}", _0)]
|
#[fail(display = "{}", _0)]
|
||||||
Resolver(#[cause] ResolverError),
|
Resolver(#[cause] ResolverError),
|
||||||
@ -193,6 +211,8 @@ pub struct ClientConnector {
|
|||||||
connector: SslConnector,
|
connector: SslConnector,
|
||||||
#[cfg(all(feature = "tls", not(feature = "alpn")))]
|
#[cfg(all(feature = "tls", not(feature = "alpn")))]
|
||||||
connector: TlsConnector,
|
connector: TlsConnector,
|
||||||
|
#[cfg(all(feature = "rust-tls", not(any(feature = "alpn", feature = "tls"))))]
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
|
||||||
stats: ClientConnectorStats,
|
stats: ClientConnectorStats,
|
||||||
subscriber: Option<Recipient<ClientConnectorStats>>,
|
subscriber: Option<Recipient<ClientConnectorStats>>,
|
||||||
@ -262,8 +282,16 @@ impl Default for ClientConnector {
|
|||||||
paused: Paused::No,
|
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();
|
let (tx, rx) = mpsc::unbounded();
|
||||||
ClientConnector {
|
ClientConnector {
|
||||||
@ -325,7 +353,7 @@ impl ClientConnector {
|
|||||||
/// # actix::System::current().stop();
|
/// # actix::System::current().stop();
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// })
|
/// })
|
||||||
/// );
|
/// });
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_connector(connector: SslConnector) -> ClientConnector {
|
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<ClientConfig>) -> 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.
|
/// Set total number of simultaneous connections.
|
||||||
///
|
///
|
||||||
/// If limit is 0, the connector has no limit.
|
/// 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 {
|
match res {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let _ = waiter.tx.send(Err(err.into()));
|
let _ = waiter.tx.send(Err(err.into()));
|
||||||
@ -784,7 +925,7 @@ impl Handler<Connect> for ClientConnector {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// check ssl availability
|
// 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));
|
return ActorResponse::reply(Err(ClientConnectorError::SslIsNotSupported));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
src/lib.rs
14
src/lib.rs
@ -151,6 +151,15 @@ extern crate openssl;
|
|||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
extern crate tokio_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 application;
|
||||||
mod body;
|
mod body;
|
||||||
mod context;
|
mod context;
|
||||||
@ -224,6 +233,11 @@ pub(crate) const HAS_TLS: bool = true;
|
|||||||
#[cfg(not(feature = "tls"))]
|
#[cfg(not(feature = "tls"))]
|
||||||
pub(crate) const HAS_TLS: bool = false;
|
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 {
|
pub mod dev {
|
||||||
//! The `actix-web` prelude for library developers
|
//! The `actix-web` prelude for library developers
|
||||||
//!
|
//!
|
||||||
|
@ -310,3 +310,46 @@ impl IoStream for TlsStream<TcpStream> {
|
|||||||
self.get_mut().get_mut().set_linger(dur)
|
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<TcpStream, ClientSession> {
|
||||||
|
#[inline]
|
||||||
|
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
|
||||||
|
let _ = <Self as AsyncWrite>::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<time::Duration>) -> io::Result<()> {
|
||||||
|
self.get_mut().0.set_linger(dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
impl IoStream for TlsStream<TcpStream, ServerSession> {
|
||||||
|
#[inline]
|
||||||
|
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
|
||||||
|
let _ = <Self as AsyncWrite>::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<time::Duration>) -> io::Result<()> {
|
||||||
|
self.get_mut().0.set_linger(dur)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,6 +22,9 @@ use native_tls::TlsAcceptor;
|
|||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
use openssl::ssl::{AlpnError, SslAcceptorBuilder};
|
use openssl::ssl::{AlpnError, SslAcceptorBuilder};
|
||||||
|
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
use rustls::ServerConfig;
|
||||||
|
|
||||||
use super::channel::{HttpChannel, WrapperStream};
|
use super::channel::{HttpChannel, WrapperStream};
|
||||||
use super::settings::{ServerSettings, WorkerSettings};
|
use super::settings::{ServerSettings, WorkerSettings};
|
||||||
use super::worker::{Conn, SocketInfo, StopWorker, StreamHandlerType, Worker};
|
use super::worker::{Conn, SocketInfo, StopWorker, StreamHandlerType, Worker};
|
||||||
@ -42,6 +45,14 @@ fn configure_alpn(builder: &mut SslAcceptorBuilder) -> io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "rust-tls", not(feature = "alpn")))]
|
||||||
|
fn configure_alpn(builder: &mut Arc<ServerConfig>) -> io::Result<()> {
|
||||||
|
Arc::<ServerConfig>::get_mut(builder)
|
||||||
|
.unwrap()
|
||||||
|
.set_protocols(&vec!["h2".to_string(), "http/1.1".to_string()]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// An HTTP Server
|
/// An HTTP Server
|
||||||
pub struct HttpServer<H>
|
pub struct HttpServer<H>
|
||||||
where
|
where
|
||||||
@ -265,6 +276,26 @@ where
|
|||||||
Ok(self)
|
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<ServerConfig>,
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
// 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<S: net::ToSocketAddrs>(&mut self, addr: S) -> io::Result<Vec<Socket>> {
|
fn bind2<S: net::ToSocketAddrs>(&mut self, addr: S) -> io::Result<Vec<Socket>> {
|
||||||
let mut err = None;
|
let mut err = None;
|
||||||
let mut succ = false;
|
let mut succ = false;
|
||||||
@ -343,6 +374,26 @@ where
|
|||||||
Ok(self)
|
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<S: net::ToSocketAddrs>(
|
||||||
|
mut self, addr: S, mut builder: Arc<ServerConfig>,
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
// 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(
|
fn start_workers(
|
||||||
&mut self, settings: &ServerSettings, sockets: &Slab<SocketInfo>,
|
&mut self, settings: &ServerSettings, sockets: &Slab<SocketInfo>,
|
||||||
) -> Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)> {
|
) -> Vec<(usize, mpsc::UnboundedSender<Conn<net::TcpStream>>)> {
|
||||||
|
@ -8,7 +8,7 @@ use tokio::executor::current_thread;
|
|||||||
use tokio_reactor::Handle;
|
use tokio_reactor::Handle;
|
||||||
use tokio_tcp::TcpStream;
|
use tokio_tcp::TcpStream;
|
||||||
|
|
||||||
#[cfg(any(feature = "tls", feature = "alpn"))]
|
#[cfg(any(feature = "tls", feature = "alpn", feature = "rust-tls"))]
|
||||||
use futures::future;
|
use futures::future;
|
||||||
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
@ -21,6 +21,13 @@ use openssl::ssl::SslAcceptor;
|
|||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
use tokio_openssl::SslAcceptorExt;
|
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::msgs::StopArbiter;
|
||||||
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response};
|
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response};
|
||||||
|
|
||||||
@ -170,6 +177,8 @@ pub(crate) enum StreamHandlerType {
|
|||||||
Tls(TlsAcceptor),
|
Tls(TlsAcceptor),
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
Alpn(SslAcceptor),
|
Alpn(SslAcceptor),
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
Rustls(Arc<ServerConfig>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandlerType {
|
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",
|
StreamHandlerType::Tls(_) => "https",
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
StreamHandlerType::Alpn(_) => "https",
|
StreamHandlerType::Alpn(_) => "https",
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
StreamHandlerType::Rustls(_) => "https",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
src/test.rs
45
src/test.rs
@ -15,6 +15,10 @@ use tokio::runtime::current_thread::Runtime;
|
|||||||
|
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
use openssl::ssl::SslAcceptorBuilder;
|
use openssl::ssl::SslAcceptorBuilder;
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
use rustls::ServerConfig;
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use application::{App, HttpApplication};
|
use application::{App, HttpApplication};
|
||||||
use body::Binary;
|
use body::Binary;
|
||||||
@ -140,7 +144,19 @@ impl TestServer {
|
|||||||
builder.set_verify(SslVerifyMode::NONE);
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
ClientConnector::with_connector(builder.build()).start()
|
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()
|
ClientConnector::default().start()
|
||||||
}
|
}
|
||||||
@ -165,16 +181,16 @@ impl TestServer {
|
|||||||
pub fn url(&self, uri: &str) -> String {
|
pub fn url(&self, uri: &str) -> String {
|
||||||
if uri.starts_with('/') {
|
if uri.starts_with('/') {
|
||||||
format!(
|
format!(
|
||||||
"{}://{}{}",
|
"{}://localhost:{}{}",
|
||||||
if self.ssl { "https" } else { "http" },
|
if self.ssl { "https" } else { "http" },
|
||||||
self.addr,
|
self.addr.port(),
|
||||||
uri
|
uri
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{}://{}/{}",
|
"{}://localhost:{}/{}",
|
||||||
if self.ssl { "https" } else { "http" },
|
if self.ssl { "https" } else { "http" },
|
||||||
self.addr,
|
self.addr.port(),
|
||||||
uri
|
uri
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -241,6 +257,8 @@ pub struct TestServerBuilder<S> {
|
|||||||
state: Box<Fn() -> S + Sync + Send + 'static>,
|
state: Box<Fn() -> S + Sync + Send + 'static>,
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
ssl: Option<SslAcceptorBuilder>,
|
ssl: Option<SslAcceptorBuilder>,
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
ssl: Option<Arc<ServerConfig>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static> TestServerBuilder<S> {
|
impl<S: 'static> TestServerBuilder<S> {
|
||||||
@ -251,7 +269,7 @@ impl<S: 'static> TestServerBuilder<S> {
|
|||||||
{
|
{
|
||||||
TestServerBuilder {
|
TestServerBuilder {
|
||||||
state: Box::new(state),
|
state: Box::new(state),
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(any(feature = "alpn", feature = "rust-tls"))]
|
||||||
ssl: None,
|
ssl: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,6 +281,13 @@ impl<S: 'static> TestServerBuilder<S> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
/// Create ssl server
|
||||||
|
pub fn ssl(mut self, ssl: Arc<ServerConfig>) -> Self {
|
||||||
|
self.ssl = Some(ssl);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
/// Configure test application and run test server
|
/// Configure test application and run test server
|
||||||
pub fn start<F>(mut self, config: F) -> TestServer
|
pub fn start<F>(mut self, config: F) -> TestServer
|
||||||
@ -271,9 +296,9 @@ impl<S: 'static> TestServerBuilder<S> {
|
|||||||
{
|
{
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(any(feature = "alpn", feature = "rust-tls"))]
|
||||||
let ssl = self.ssl.is_some();
|
let ssl = self.ssl.is_some();
|
||||||
#[cfg(not(feature = "alpn"))]
|
#[cfg(not(any(feature = "alpn", feature = "rust-tls")))]
|
||||||
let ssl = false;
|
let ssl = false;
|
||||||
|
|
||||||
// run server in separate thread
|
// run server in separate thread
|
||||||
@ -293,7 +318,7 @@ impl<S: 'static> TestServerBuilder<S> {
|
|||||||
tx.send((System::current(), local_addr, TestServer::get_conn()))
|
tx.send((System::current(), local_addr, TestServer::get_conn()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(any(feature = "alpn", feature = "rust-tls"))]
|
||||||
{
|
{
|
||||||
let ssl = self.ssl.take();
|
let ssl = self.ssl.take();
|
||||||
if let Some(ssl) = ssl {
|
if let Some(ssl) = ssl {
|
||||||
@ -302,7 +327,7 @@ impl<S: 'static> TestServerBuilder<S> {
|
|||||||
srv.listen(tcp).start();
|
srv.listen(tcp).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "alpn"))]
|
#[cfg(not(any(feature = "alpn", feature = "rust-tls")))]
|
||||||
{
|
{
|
||||||
srv.listen(tcp).start();
|
srv.listen(tcp).start();
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
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
|
||||||
1QU=
|
E6wWgkuCslMgQlEJ+TM5RhYrI5/rVZQhvmgcob/9gPZv
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
@ -12,6 +12,8 @@ use rand::Rng;
|
|||||||
|
|
||||||
#[cfg(feature = "alpn")]
|
#[cfg(feature = "alpn")]
|
||||||
extern crate openssl;
|
extern crate openssl;
|
||||||
|
#[cfg(feature = "rust-tls")]
|
||||||
|
extern crate rustls;
|
||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
@ -272,3 +274,43 @@ fn test_ws_server_ssl() {
|
|||||||
assert_eq!(item, data);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user