diff --git a/.travis.yml b/.travis.yml index 48a1414ac..767e9fde6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,12 +32,12 @@ script: - | if [[ "$TRAVIS_RUST_VERSION" != "nightly" ]]; then cargo clean - cargo test --features="ssl,tls" -- --nocapture + cargo test --features="ssl,tls,rust-tls" -- --nocapture fi - | if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install -f cargo-tarpaulin - cargo tarpaulin --features="ssl,tls" --out Xml + cargo tarpaulin --features="ssl,tls,rust-tls" --out Xml bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" fi @@ -46,7 +46,7 @@ script: after_success: - | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_RUST_VERSION" == "beta" ]]; then - cargo doc --features "ssl,tls" --no-deps && + cargo doc --features "ssl,tls,rust-tls" --no-deps && echo "" > target/doc/index.html && git clone https://github.com/davisp/ghp-import.git && ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && diff --git a/src/ssl/mod.rs b/src/ssl/mod.rs index 81a364df5..906ee615c 100644 --- a/src/ssl/mod.rs +++ b/src/ssl/mod.rs @@ -13,7 +13,10 @@ mod nativetls; #[cfg(feature = "tls")] pub use self::nativetls::{NativeTlsAcceptor, TlsStream}; -pub(crate) const MAX_CONN: AtomicUsize = AtomicUsize::new(256); +#[cfg(feature = "rust-tls")] +mod rustls; +#[cfg(feature = "rust-tls")] +pub use self::rustls::RustlsAcceptor; /// Sets the maximum per-worker concurrent ssl connection establish process. /// @@ -25,11 +28,8 @@ pub fn max_concurrent_ssl_connect(num: usize) { MAX_CONN.store(num, Ordering::Relaxed); } +pub(crate) const MAX_CONN: AtomicUsize = AtomicUsize::new(256); + thread_local! { static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); } - -// #[cfg(feature = "rust-tls")] -// mod rustls; -// #[cfg(feature = "rust-tls")] -// pub use self::rustls::RustlsAcceptor; diff --git a/src/ssl/rustls.rs b/src/ssl/rustls.rs new file mode 100644 index 000000000..ef4c966be --- /dev/null +++ b/src/ssl/rustls.rs @@ -0,0 +1,103 @@ +use std::io; +use std::marker::PhantomData; +use std::sync::Arc; + +use futures::{future::ok, future::FutureResult, Async, Future, Poll}; +use rustls::{ServerConfig, ServerSession}; +use tokio_io::{AsyncRead, AsyncWrite}; +use tokio_rustls::{AcceptAsync, ServerConfigExt, TlsStream}; + +use super::MAX_CONN_COUNTER; +use counter::{Counter, CounterGuard}; +use service::{NewService, Service}; + +/// Support `SSL` connections via rustls package +/// +/// `rust-tls` feature enables `RustlsAcceptor` type +pub struct RustlsAcceptor { + config: Arc, + io: PhantomData, +} + +impl RustlsAcceptor { + /// Create `RustlsAcceptor` new service + pub fn new(config: ServerConfig) -> Self { + RustlsAcceptor { + config: Arc::new(config), + io: PhantomData, + } + } +} + +impl Clone for RustlsAcceptor { + fn clone(&self) -> Self { + Self { + config: self.config.clone(), + io: PhantomData, + } + } +} + +impl NewService for RustlsAcceptor { + type Request = T; + type Response = TlsStream; + type Error = io::Error; + type Service = RustlsAcceptorService; + type InitError = (); + type Future = FutureResult; + + fn new_service(&self) -> Self::Future { + MAX_CONN_COUNTER.with(|conns| { + ok(RustlsAcceptorService { + config: self.config.clone(), + conns: conns.clone(), + io: PhantomData, + }) + }) + } +} + +pub struct RustlsAcceptorService { + config: Arc, + io: PhantomData, + conns: Counter, +} + +impl Service for RustlsAcceptorService { + type Request = T; + type Response = TlsStream; + type Error = io::Error; + type Future = RustlsAcceptorServiceFut; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + if self.conns.available() { + Ok(Async::Ready(())) + } else { + Ok(Async::NotReady) + } + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + RustlsAcceptorServiceFut { + _guard: self.conns.get(), + fut: ServerConfigExt::accept_async(&self.config, req), + } + } +} + +pub struct RustlsAcceptorServiceFut +where + T: AsyncRead + AsyncWrite, +{ + fut: AcceptAsync, + _guard: CounterGuard, +} + +impl Future for RustlsAcceptorServiceFut { + type Item = TlsStream; + type Error = io::Error; + + fn poll(&mut self) -> Poll { + self.fut.poll() + } +}