From 9fbe6a1f6d0ee5d08d7939c332549ce5490c7951 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 2 Dec 2019 11:30:27 +0600 Subject: [PATCH] refactor server configuration and tls support --- Cargo.toml | 4 +- actix-rt/Cargo.toml | 9 +- actix-rt/src/lib.rs | 34 ++- actix-server-config/Cargo.toml | 29 -- actix-server-config/changes.md | 21 -- actix-server-config/src/lib.rs | 249 ------------------ actix-server/Cargo.toml | 28 +- actix-server/src/builder.rs | 19 +- actix-server/src/config.rs | 19 +- actix-server/src/lib.rs | 3 - actix-server/src/service.rs | 13 +- actix-server/src/signals.rs | 8 +- actix-server/src/socket.rs | 7 +- actix-server/src/ssl/openssl.rs | 138 ---------- actix-tls/CHANGES.md | 90 +++++++ actix-tls/Cargo.toml | 57 ++++ .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 actix-tls/src/lib.rs | 51 ++++ actix-tls/src/openssl.rs | 111 ++++++++ actix-utils/src/counter.rs | 2 + 21 files changed, 366 insertions(+), 526 deletions(-) delete mode 100644 actix-server-config/Cargo.toml delete mode 100644 actix-server-config/changes.md delete mode 100644 actix-server-config/src/lib.rs delete mode 100644 actix-server/src/ssl/openssl.rs create mode 100644 actix-tls/CHANGES.md create mode 100644 actix-tls/Cargo.toml rename {actix-server-config => actix-tls}/LICENSE-APACHE (100%) rename {actix-server-config => actix-tls}/LICENSE-MIT (100%) create mode 100644 actix-tls/src/lib.rs create mode 100644 actix-tls/src/openssl.rs diff --git a/Cargo.toml b/Cargo.toml index b7646adb..ced73d14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,9 @@ members = [ "actix-macros", "actix-service", "actix-server", - "actix-server-config", "actix-testing", "actix-threadpool", + "actix-tls", "actix-utils", "router", ] @@ -21,9 +21,9 @@ actix-ioframe = { path = "actix-ioframe" } actix-rt = { path = "actix-rt" } actix-macros = { path = "actix-macros" } actix-server = { path = "actix-server" } -actix-server-config = { path = "actix-server-config" } actix-service = { path = "actix-service" } actix-testing = { path = "actix-testing" } actix-threadpool = { path = "actix-threadpool" } +actix-tls = { path = "actix-tls" } actix-utils = { path = "actix-utils" } actix-router = { path = "router" } diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index 340ac4c4..5f7910bd 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "1.0.0-alpha.1" +version = "1.0.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix runtime" keywords = ["network", "framework", "async", "futures"] @@ -21,10 +21,9 @@ path = "src/lib.rs" actix-macros = "0.1.0-alpha.1" actix-threadpool = "0.2" futures = "0.3.1" +copyless = "0.1.4" -tokio = { version = "=0.2.0-alpha.6", features=["rt-current-thread","tcp","uds","udp","timer"] } +tokio = { version = "=0.2.0-alpha.6", features=["rt-current-thread","tcp","uds","udp","timer","signal"] } tokio-executor = "=0.2.0-alpha.6" tokio-net = "=0.2.0-alpha.6" -tokio-timer = "=0.3.0-alpha.6" - -copyless = "0.1.4" +tokio-timer = "=0.3.0-alpha.6" \ No newline at end of file diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index cf1c359c..7a75f95a 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -32,6 +32,28 @@ where Arbiter::spawn(f); } +/// Asynchronous signal handling +pub mod signal { + #[cfg(unix)] + pub mod unix { + pub use tokio_net::signal::unix::*; + } + pub use tokio_net::signal::{ctrl_c, CtrlC}; +} + +/// TCP/UDP/Unix bindings +pub mod net { + pub use tokio::net::UdpSocket; + pub use tokio::net::{TcpListener, TcpStream}; + + #[cfg(unix)] + mod unix { + pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; + } + + pub use self::unix::*; +} + /// Utilities for tracking time. pub mod time { use std::time::{Duration, Instant}; @@ -52,15 +74,3 @@ pub mod time { Interval::new(start, duration) } } - -pub mod net { - pub use tokio::net::UdpSocket; - pub use tokio::net::{TcpListener, TcpStream}; - - #[cfg(unix)] - mod unix { - pub use tokio::net::{UnixDatagram, UnixListener, UnixStream}; - } - - pub use self::unix::*; -} diff --git a/actix-server-config/Cargo.toml b/actix-server-config/Cargo.toml deleted file mode 100644 index f7b1afdb..00000000 --- a/actix-server-config/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "actix-server-config" -version = "0.3.0-alpha.1" -authors = ["Nikolay Kim "] -description = "Actix server config utils" -homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-net.git" -license = "MIT/Apache-2.0" -edition = "2018" -workspace = ".." - -[lib] -name = "actix_server_config" -path = "src/lib.rs" - -[package.metadata.docs.rs] -features = ["openssl"] #, "rustls"] - -[features] -default = [] -openssl = ["tokio-openssl"] -# rustls = ["tokio-rustls"] - -[dependencies] -actix-codec = "0.2.0-alpha.1" -tokio-net = { version = "=0.2.0-alpha.6", features = ["tcp", "uds"] } -tokio-openssl = { version = "0.4.0-alpha.6", optional = true } -# tokio-rustls = { version = "0.12.0-alpha.8", optional = true } -# tokio-rustls = { git = "https://github.com/quininer/tokio-rustls.git", branch = "tokio-0.2", optional = true } diff --git a/actix-server-config/changes.md b/actix-server-config/changes.md deleted file mode 100644 index 9af452a5..00000000 --- a/actix-server-config/changes.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changes - -## [0.2.0] - 2019-10-03 - -### Changed - -* Update `rustls` to 0.16 -* Minimum required Rust version upped to 1.37.0 - -## [0.1.2] - 2019-07-18 - -### Added - -* Add unix domnain sockets support - - -## [0.1.1] - 2019-04-16 - -### Added - -* `IoStream` trait and impls for TcpStream, SslStream and TlsStream diff --git a/actix-server-config/src/lib.rs b/actix-server-config/src/lib.rs deleted file mode 100644 index 5b9a7dbe..00000000 --- a/actix-server-config/src/lib.rs +++ /dev/null @@ -1,249 +0,0 @@ -//! Actix server config utils. - -use std::cell::Cell; -use std::net::SocketAddr; -use std::rc::Rc; -use std::{fmt, io, net, ops, time}; - -use actix_codec::{AsyncRead, AsyncWrite}; -use tokio_net::tcp::TcpStream; - -#[derive(Debug, Clone)] -pub struct ServerConfig { - addr: SocketAddr, - secure: Rc>, -} - -impl ServerConfig { - #[inline] - pub fn new(addr: SocketAddr) -> Self { - ServerConfig { - addr, - secure: Rc::new(Cell::new(false)), - } - } - - /// Returns the address of the local half of this TCP server socket - #[inline] - pub fn local_addr(&self) -> SocketAddr { - self.addr - } - - /// Returns true if connection is secure (tls enabled) - #[inline] - pub fn secure(&self) -> bool { - self.secure.as_ref().get() - } - - /// Set secure flag - #[inline] - pub fn set_secure(&self) { - self.secure.as_ref().set(true) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Protocol { - Unknown, - Http10, - Http11, - Http2, - Proto1, - Proto2, - Proto3, - Proto4, - Proto5, - Proto6, -} - -pub struct Io { - io: T, - proto: Protocol, - params: P, -} - -impl Unpin for Io {} - -impl Io { - pub fn new(io: T) -> Self { - Self { - io, - proto: Protocol::Unknown, - params: (), - } - } -} - -impl Io { - /// Reconstruct from a parts. - pub fn from_parts(io: T, params: P, proto: Protocol) -> Self { - Self { io, params, proto } - } - - /// Deconstruct into a parts. - pub fn into_parts(self) -> (T, P, Protocol) { - (self.io, self.params, self.proto) - } - - /// Returns a shared reference to the underlying stream. - pub fn get_ref(&self) -> &T { - &self.io - } - - /// Returns a mutable reference to the underlying stream. - pub fn get_mut(&mut self) -> &mut T { - &mut self.io - } - - /// Get selected protocol - pub fn protocol(&self) -> Protocol { - self.proto - } - - /// Return new Io object with new parameter. - pub fn set(self, params: U) -> Io { - Io { - params, - io: self.io, - proto: self.proto, - } - } - - /// Maps an Io<_, P> to Io<_, U> by applying a function to a contained value. - pub fn map(self, op: F) -> Io - where - F: FnOnce(P) -> U, - { - Io { - io: self.io, - proto: self.proto, - params: op(self.params), - } - } -} - -impl ops::Deref for Io { - type Target = T; - - fn deref(&self) -> &T { - &self.io - } -} - -impl ops::DerefMut for Io { - fn deref_mut(&mut self) -> &mut T { - &mut self.io - } -} - -impl fmt::Debug for Io { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Io {{{:?}}}", self.io) - } -} - -/// Low-level io stream operations -pub trait IoStream: AsyncRead + AsyncWrite + Unpin { - /// Returns the socket address of the remote peer of this TCP connection. - fn peer_addr(&self) -> Option { - None - } - - /// Sets the value of the TCP_NODELAY option on this socket. - fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()>; - - fn set_linger(&mut self, dur: Option) -> io::Result<()>; - - fn set_keepalive(&mut self, dur: Option) -> io::Result<()>; -} - -impl IoStream for TcpStream { - #[inline] - fn peer_addr(&self) -> Option { - TcpStream::peer_addr(self).ok() - } - - #[inline] - fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { - TcpStream::set_nodelay(self, nodelay) - } - - #[inline] - fn set_linger(&mut self, dur: Option) -> io::Result<()> { - TcpStream::set_linger(self, dur) - } - - #[inline] - fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - TcpStream::set_keepalive(self, dur) - } -} - -#[cfg(feature = "openssl")] -impl IoStream for tokio_openssl::SslStream { - #[inline] - fn peer_addr(&self) -> Option { - self.get_ref().peer_addr() - } - - #[inline] - fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { - self.get_mut().set_nodelay(nodelay) - } - - #[inline] - fn set_linger(&mut self, dur: Option) -> io::Result<()> { - self.get_mut().set_linger(dur) - } - - #[inline] - fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - self.get_mut().set_keepalive(dur) - } -} - -#[cfg(feature = "rustls")] -impl IoStream for tokio_rustls::server::TlsStream { - #[inline] - fn peer_addr(&self) -> Option { - self.get_ref().0.peer_addr() - } - - #[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) - } - - #[inline] - fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - self.get_mut().0.set_keepalive(dur) - } -} - -#[cfg(unix)] -impl IoStream for tokio_net::uds::UnixStream { - #[inline] - fn peer_addr(&self) -> Option { - None - } - - #[inline] - fn set_nodelay(&mut self, _: bool) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn set_linger(&mut self, _: Option) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn set_keepalive(&mut self, _: Option) -> io::Result<()> { - Ok(()) - } -} diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 8e4b2fcc..5ff60e13 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "0.8.0-alpha.1" +version = "0.8.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix server - General purpose tcp server" keywords = ["network", "framework", "async", "futures"] @@ -13,23 +13,16 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" workspace = ".." -[package.metadata.docs.rs] -features = ["nativetls", "openssl", "rustls", "uds"] - [lib] name = "actix_server" path = "src/lib.rs" [features] default = [] -nativetls = ["native-tls", "tokio-tls"] -openssl = ["open-ssl", "tokio-openssl", "actix-server-config/openssl"] -# rustls = ["rust-tls", "tokio-rustls", "webpki", "webpki-roots", "actix-server-config/rustls"] [dependencies] actix-service = "1.0.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.2" actix-codec = "0.2.0-alpha.1" log = "0.4" @@ -38,28 +31,13 @@ mio = "0.6.19" net2 = "0.2" futures = "0.3.1" slab = "0.4" -tokio-net = { version = "0.2.0-alpha.6", features = ["signal", "tcp", "uds"] } +tokio-net = { version = "0.2.0-alpha.6", features = ["signal", "tcp", "uds"] } futures-core-preview = "0.3.0-alpha.19" # unix domain sockets mio-uds = { version = "0.6.7" } -# nativetls -native-tls = { version = "0.2", optional = true } -tokio-tls = { version = "0.3.0-alpha.6", optional = true } - -# openssl -open-ssl = { version = "0.10", package = "openssl", optional = true } -tokio-openssl = { version = "0.4.0-alpha.6", optional = true } - -# rustls -rust-tls = { version = "0.16.0", package = "rustls", optional = true } -# tokio-rustls = { version = "0.12.0-alpha.2", optional = true } -# tokio-rustls = { git = "https://github.com/quininer/tokio-rustls.git", branch = "tokio-0.2", optional = true } -webpki = { version = "0.21", optional = true } -webpki-roots = { version = "0.17", optional = true } - [dev-dependencies] bytes = "0.4" actix-codec = "0.2.0-alpha.1" diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index 0004cdcc..02f94533 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -3,6 +3,7 @@ use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use std::{io, mem, net}; +use actix_rt::net::TcpStream; use actix_rt::{spawn, time::delay, Arbiter, System}; use futures::channel::mpsc::{unbounded, UnboundedReceiver}; use futures::channel::oneshot; @@ -12,7 +13,6 @@ use futures::{ready, Future, FutureExt, Stream, StreamExt}; use log::{error, info}; use net2::TcpBuilder; use num_cpus; -use tokio_net::tcp::TcpStream; use crate::accept::{AcceptLoop, AcceptNotify, Command}; use crate::config::{ConfiguredService, ServiceConfig}; @@ -21,7 +21,7 @@ use crate::service::{InternalServiceFactory, ServiceFactory, StreamNewService}; use crate::signals::{Signal, Signals}; use crate::socket::StdListener; use crate::worker::{self, Worker, WorkerAvailability, WorkerClient}; -use crate::{ssl, Token}; +use crate::Token; /// Server builder pub struct ServerBuilder { @@ -104,17 +104,6 @@ impl ServerBuilder { self } - /// Sets the maximum per-worker concurrent connection establish process. - /// - /// All listeners will stop accepting connections when this limit is reached. It - /// can be used to limit the global SSL CPU usage. - /// - /// By default max connections is set to a 256. - pub fn maxconnrate(self, num: usize) -> Self { - ssl::max_concurrent_ssl_connect(num); - self - } - /// Stop actix system. pub fn system_exit(mut self) -> Self { self.exit = true; @@ -191,7 +180,7 @@ impl ServerBuilder { /// Add new unix domain service to the server. pub fn bind_uds(self, name: N, addr: U, factory: F) -> io::Result where - F: ServiceFactory, + F: ServiceFactory, N: AsRef, U: AsRef, { @@ -221,7 +210,7 @@ impl ServerBuilder { factory: F, ) -> io::Result where - F: ServiceFactory, + F: ServiceFactory, { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; let token = self.token.next(); diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index 36e94a37..1ab6b9da 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use std::{fmt, io, net}; -use actix_server_config::{Io, ServerConfig}; +use actix_rt::net::TcpStream; use actix_service as actix; use futures::future::{Future, FutureExt, LocalBoxFuture}; use log::error; -use tokio_net::tcp::TcpStream; use super::builder::bind_addr; use super::service::{ @@ -113,8 +112,6 @@ impl InternalServiceFactory for ConfiguredService { self.rt.configure(&mut rt); rt.validate(); - let names = self.names.clone(); - // construct services async move { let services = rt.services; @@ -124,9 +121,7 @@ impl InternalServiceFactory for ConfiguredService { } let mut res = vec![]; for (token, ns) in services.into_iter() { - let config = ServerConfig::new(names[&token].1); - - let newserv = ns.new_service(&config); + let newserv = ns.new_service(&()); match newserv.await { Ok(serv) => { res.push((token, serv)); @@ -196,7 +191,7 @@ impl ServiceRuntime { pub fn service(&mut self, name: &str, service: F) where F: actix::IntoServiceFactory, - T: actix::ServiceFactory> + 'static, + T: actix::ServiceFactory + 'static, T::Future: 'static, T::Service: 'static, T::InitError: fmt::Debug, @@ -229,7 +224,7 @@ type BoxedNewService = Box< Response = (), Error = (), InitError = (), - Config = ServerConfig, + Config = (), Service = BoxedServerService, Future = LocalBoxFuture<'static, Result>, >, @@ -241,7 +236,7 @@ struct ServiceFactory { impl actix::ServiceFactory for ServiceFactory where - T: actix::ServiceFactory>, + T: actix::ServiceFactory, T::Future: 'static, T::Service: 'static, T::Error: 'static, @@ -251,11 +246,11 @@ where type Response = (); type Error = (); type InitError = (); - type Config = ServerConfig; + type Config = (); type Service = BoxedServerService; type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, cfg: &ServerConfig) -> Self::Future { + fn new_service(&self, cfg: &()) -> Self::Future { let fut = self.inner.new_service(cfg); async move { return match fut.await { diff --git a/actix-server/src/lib.rs b/actix-server/src/lib.rs index 97687c2f..4e9b5f71 100644 --- a/actix-server/src/lib.rs +++ b/actix-server/src/lib.rs @@ -8,11 +8,8 @@ mod server; mod service; mod signals; mod socket; -pub mod ssl; mod worker; -pub use actix_server_config::{Io, IoStream, Protocol, ServerConfig}; - pub use self::builder::ServerBuilder; pub use self::config::{ServiceConfig, ServiceRuntime}; pub use self::server::Server; diff --git a/actix-server/src/service.rs b/actix-server/src/service.rs index 2bf7a570..e883c375 100644 --- a/actix-server/src/service.rs +++ b/actix-server/src/service.rs @@ -4,7 +4,6 @@ use std::task::{Context, Poll}; use std::time::Duration; use actix_rt::spawn; -use actix_server_config::{Io, ServerConfig}; use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory}; use futures::future::{err, ok, LocalBoxFuture, Ready}; use futures::{FutureExt, TryFutureExt}; @@ -25,7 +24,7 @@ pub(crate) enum ServerMessage { } pub trait ServiceFactory: Send + Clone + 'static { - type Factory: actix::ServiceFactory>; + type Factory: actix::ServiceFactory; fn create(&self) -> Self::Factory; } @@ -59,7 +58,7 @@ impl StreamService { impl Service for StreamService where - T: Service>, + T: Service, T::Future: 'static, T::Error: 'static, I: FromStream, @@ -81,7 +80,7 @@ where }); if let Ok(stream) = stream { - let f = self.service.call(Io::new(stream)); + let f = self.service.call(stream); spawn( async move { let _ = f.await; @@ -149,11 +148,9 @@ where fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { let token = self.token; - let config = ServerConfig::new(self.addr); - self.inner .create() - .new_service(&config) + .new_service(&()) .map_err(|_| ()) .map_ok(move |inner| { let service: BoxedServerService = Box::new(StreamService::new(inner)); @@ -180,7 +177,7 @@ impl InternalServiceFactory for Box { impl ServiceFactory for F where F: Fn() -> T + Send + Clone + 'static, - T: actix::ServiceFactory>, + T: actix::ServiceFactory, I: FromStream, { type Factory = T; diff --git a/actix-server/src/signals.rs b/actix-server/src/signals.rs index 6194f2f1..55f23b8f 100644 --- a/actix-server/src/signals.rs +++ b/actix-server/src/signals.rs @@ -23,9 +23,9 @@ pub(crate) enum Signal { pub(crate) struct Signals { srv: Server, #[cfg(not(unix))] - stream: tokio_net::signal::CtrlC, + stream: actix_rt::signal::CtrlC, #[cfg(unix)] - streams: Vec<(Signal, tokio_net::signal::unix::Signal)>, + streams: Vec<(Signal, actix_rt::signal::unix::Signal)>, } impl Signals { @@ -33,13 +33,13 @@ impl Signals { actix_rt::spawn({ #[cfg(not(unix))] { - let stream = tokio_net::signal::ctrl_c()?; + let stream = actix_rt::signal::ctrl_c()?; Signals { srv, stream } } #[cfg(unix)] { - use tokio_net::signal::unix; + use actix_rt::signal::unix; let mut streams = Vec::new(); diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index 9030310a..743ba462 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -1,8 +1,9 @@ use std::{fmt, io, net}; use actix_codec::{AsyncRead, AsyncWrite}; +use actix_rt::net::TcpStream; + use tokio_net::driver::Handle; -use tokio_net::tcp::TcpStream; pub(crate) enum StdListener { Tcp(net::TcpListener), @@ -161,12 +162,12 @@ impl FromStream for TcpStream { } #[cfg(all(unix))] -impl FromStream for tokio_net::uds::UnixStream { +impl FromStream for actix_rt::net::UnixStream { fn from_stdstream(sock: StdStream) -> io::Result { match sock { StdStream::Tcp(_) => panic!("Should not happen, bug in server impl"), StdStream::Uds(stream) => { - tokio_net::uds::UnixStream::from_std(stream, &Handle::default()) + actix_rt::net::UnixStream::from_std(stream, &Handle::default()) } } } diff --git a/actix-server/src/ssl/openssl.rs b/actix-server/src/ssl/openssl.rs deleted file mode 100644 index ebd05397..00000000 --- a/actix-server/src/ssl/openssl.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::future::Future; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use open_ssl::ssl::SslAcceptor; -use tokio_openssl::{HandshakeError, SslStream}; - -use crate::counter::{Counter, CounterGuard}; -use crate::ssl::MAX_CONN_COUNTER; -use crate::{Io, Protocol, ServerConfig}; - -/// Support `SSL` connections via openssl package -/// -/// `ssl` feature enables `OpensslAcceptor` type -pub struct OpensslAcceptor { - acceptor: SslAcceptor, - io: PhantomData<(T, P)>, -} - -impl OpensslAcceptor { - /// Create default `OpensslAcceptor` - pub fn new(acceptor: SslAcceptor) -> Self { - OpensslAcceptor { - acceptor, - io: PhantomData, - } - } -} - -impl Clone for OpensslAcceptor { - fn clone(&self) -> Self { - Self { - acceptor: self.acceptor.clone(), - io: PhantomData, - } - } -} - -impl ServiceFactory for OpensslAcceptor { - type Request = Io; - type Response = Io, P>; - type Error = HandshakeError; - type Config = ServerConfig; - type Service = OpensslAcceptorService; - type InitError = (); - type Future = Ready>; - - fn new_service(&self, cfg: &ServerConfig) -> Self::Future { - cfg.set_secure(); - - MAX_CONN_COUNTER.with(|conns| { - ok(OpensslAcceptorService { - acceptor: self.acceptor.clone(), - conns: conns.clone(), - io: PhantomData, - }) - }) - } -} - -pub struct OpensslAcceptorService { - acceptor: SslAcceptor, - conns: Counter, - io: PhantomData<(T, P)>, -} - -impl Service for OpensslAcceptorService { - type Request = Io; - type Response = Io, P>; - type Error = HandshakeError; - type Future = OpensslAcceptorServiceFut; - - fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { - if self.conns.available(ctx) { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - - fn call(&mut self, req: Self::Request) -> Self::Future { - let (io, params, _) = req.into_parts(); - let acc = self.acceptor.clone(); - OpensslAcceptorServiceFut { - _guard: self.conns.get(), - fut: async move { - let acc = acc; - tokio_openssl::accept(&acc, io).await - } - .boxed_local(), - params: Some(params), - } - } -} - -pub struct OpensslAcceptorServiceFut -where - T: AsyncRead + AsyncWrite, -{ - fut: LocalBoxFuture<'static, Result, HandshakeError>>, - params: Option

, - _guard: CounterGuard, -} - -impl Unpin for OpensslAcceptorServiceFut {} - -impl Future for OpensslAcceptorServiceFut { - type Output = Result, P>, HandshakeError>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - let io = futures::ready!(Pin::new(&mut this.fut).poll(cx))?; - let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() { - const H2: &[u8] = b"\x02h2"; - const HTTP10: &[u8] = b"\x08http/1.0"; - const HTTP11: &[u8] = b"\x08http/1.1"; - - if protos.windows(3).any(|window| window == H2) { - Protocol::Http2 - } else if protos.windows(9).any(|window| window == HTTP11) { - Protocol::Http11 - } else if protos.windows(9).any(|window| window == HTTP10) { - Protocol::Http10 - } else { - Protocol::Unknown - } - } else { - Protocol::Unknown - }; - - Poll::Ready(Ok(Io::from_parts(io, this.params.take().unwrap(), proto))) - } -} diff --git a/actix-tls/CHANGES.md b/actix-tls/CHANGES.md new file mode 100644 index 00000000..baea3d30 --- /dev/null +++ b/actix-tls/CHANGES.md @@ -0,0 +1,90 @@ +# Changes + +## [0.3.0] - 2019-10-03 + +### Changed + +* Update `rustls` to 0.16 +* Minimum required Rust version upped to 1.37.0 + +## [0.2.5] - 2019-09-05 + +* Add `TcpConnectService` + +## [0.2.4] - 2019-09-02 + +* Use arbiter's storage for default async resolver + +## [0.2.3] - 2019-08-05 + +* Add `ConnectService` and `OpensslConnectService` + +## [0.2.2] - 2019-07-24 + +* Add `rustls` support + +## [0.2.1] - 2019-07-17 + +### Added + +* Expose Connect addrs #30 + +### Changed + +* Update `derive_more` to 0.15 + + +## [0.2.0] - 2019-05-12 + +### Changed + +* Upgrade to actix-service 0.4 + + +## [0.1.5] - 2019-04-19 + +### Added + +* `Connect::set_addr()` + +### Changed + +* Use trust-dns-resolver 0.11.0 + + +## [0.1.4] - 2019-04-12 + +### Changed + +* Do not start default resolver immediately for default connector. + + +## [0.1.3] - 2019-04-11 + +### Changed + +* Start trust-dns default resolver on first use + +## [0.1.2] - 2019-04-04 + +### Added + +* Log error if dns system config could not be loaded. + +### Changed + +* Rename connect Connector to TcpConnector #10 + + +## [0.1.1] - 2019-03-15 + +### Fixed + +* Fix error handling for single address + + +## [0.1.0] - 2019-03-14 + +* Refactor resolver and connector services + +* Rename crate diff --git a/actix-tls/Cargo.toml b/actix-tls/Cargo.toml new file mode 100644 index 00000000..ebcbd867 --- /dev/null +++ b/actix-tls/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "actix-tls" +version = "1.0.0-alpha.1" +authors = ["Nikolay Kim "] +description = "Actix tls services" +keywords = ["network", "framework", "async", "futures"] +homepage = "https://actix.rs" +repository = "https://github.com/actix/actix-net.git" +documentation = "https://docs.rs/actix-tls/" +categories = ["network-programming", "asynchronous"] +license = "MIT/Apache-2.0" +edition = "2018" +workspace = ".." + +[package.metadata.docs.rs] +features = ["server", "openssl", "rustls"] + +[lib] +name = "actix_tls" +path = "src/lib.rs" + +[features] +default = [] + +# openssl +openssl = ["open-ssl", "tokio-openssl"] + +# rustls +rustls = ["rust-tls", "webpki"] + +[dependencies] +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" +actix-utils = "0.5.0-alpha.1" +actix-rt = "1.0.0-alpha.1" +derive_more = "0.99" +either = "1.5.2" +futures = "0.3.1" +log = "0.4" + +# server feature +actix-server = { version = "0.8.0-alpha.1", optional=true } + +# openssl +open-ssl = { version="0.10", package = "openssl", optional = true } +tokio-openssl = { version = "=0.4.0-alpha.6", optional = true } + +# rustls +rust-tls = { version = "0.16.0", package = "rustls", optional = true } +webpki = { version = "0.21", optional = true } +webpki-roots = { version = "0.17", optional = true } +# tokio-rustls = { version = "0.12.0-alpha.2", optional = true } +# tokio-rustls = { git = "https://github.com/quininer/tokio-rustls.git", branch = "tokio-0.2", optional = true } + +[dev-dependencies] +bytes = "0.4" +actix-testing = { version="0.3.0-alpha.1" } diff --git a/actix-server-config/LICENSE-APACHE b/actix-tls/LICENSE-APACHE similarity index 100% rename from actix-server-config/LICENSE-APACHE rename to actix-tls/LICENSE-APACHE diff --git a/actix-server-config/LICENSE-MIT b/actix-tls/LICENSE-MIT similarity index 100% rename from actix-server-config/LICENSE-MIT rename to actix-tls/LICENSE-MIT diff --git a/actix-tls/src/lib.rs b/actix-tls/src/lib.rs new file mode 100644 index 00000000..53b850d4 --- /dev/null +++ b/actix-tls/src/lib.rs @@ -0,0 +1,51 @@ +//! SSL Services +use std::sync::atomic::{AtomicUsize, Ordering}; + +use actix_utils::counter::Counter; + +#[cfg(feature = "openssl")] +pub mod openssl; + +//#[cfg(feature = "rustls")] +//mod rustls; +//#[cfg(feature = "rustls")] +//pub use self::rustls::RustlsAcceptor; + +/// Sets the maximum per-worker concurrent ssl connection establish process. +/// +/// All listeners will stop accepting connections when this limit is +/// reached. It can be used to limit the global SSL CPU usage. +/// +/// By default max connections is set to a 256. +pub fn max_concurrent_ssl_connect(num: usize) { + MAX_CONN.store(num, Ordering::Relaxed); +} + +pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); + +thread_local! { + static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); +} + +/// Ssl error combinded with service error. +#[derive(Debug)] +pub enum SslError { + Ssl(E1), + Service(E2), +} + +pub trait ServerBuilderExt: Sized { + /// Sets the maximum per-worker concurrent connection establish process. + /// + /// All listeners will stop accepting connections when this limit is reached. It + /// can be used to limit the global SSL CPU usage. + /// + /// By default max connections is set to a 256. + fn maxconnrate(self, num: usize) -> Self { + max_concurrent_ssl_connect(num); + self + } +} + +#[cfg(feature = "server")] +impl ServerBuilderExt for actix_server::ServerBuilder {} diff --git a/actix-tls/src/openssl.rs b/actix-tls/src/openssl.rs new file mode 100644 index 00000000..8e6641fd --- /dev/null +++ b/actix-tls/src/openssl.rs @@ -0,0 +1,111 @@ +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +pub use tokio_openssl::{HandshakeError, SslStream}; + +use actix_codec::{AsyncRead, AsyncWrite}; +use actix_service::{Service, ServiceFactory}; +use actix_utils::counter::{Counter, CounterGuard}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use open_ssl::ssl::SslAcceptor; + +use crate::MAX_CONN_COUNTER; + +/// Support `TLS` server connections via openssl package +/// +/// `openssl` feature enables `Acceptor` type +pub struct Acceptor { + acceptor: SslAcceptor, + io: PhantomData, +} + +impl Acceptor { + /// Create default `OpensslAcceptor` + pub fn new(acceptor: SslAcceptor) -> Self { + Acceptor { + acceptor, + io: PhantomData, + } + } +} + +impl Clone for Acceptor { + fn clone(&self) -> Self { + Self { + acceptor: self.acceptor.clone(), + io: PhantomData, + } + } +} + +impl ServiceFactory for Acceptor { + type Request = T; + type Response = SslStream; + type Error = HandshakeError; + type Config = (); + type Service = AcceptorService; + type InitError = (); + type Future = Ready>; + + fn new_service(&self, _: &()) -> Self::Future { + MAX_CONN_COUNTER.with(|conns| { + ok(AcceptorService { + acceptor: self.acceptor.clone(), + conns: conns.clone(), + io: PhantomData, + }) + }) + } +} + +pub struct AcceptorService { + acceptor: SslAcceptor, + conns: Counter, + io: PhantomData, +} + +impl Service for AcceptorService { + type Request = T; + type Response = SslStream; + type Error = HandshakeError; + type Future = AcceptorServiceResponse; + + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + if self.conns.available(ctx) { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + let acc = self.acceptor.clone(); + AcceptorServiceResponse { + _guard: self.conns.get(), + fut: async move { + let acc = acc; + tokio_openssl::accept(&acc, req).await + } + .boxed_local(), + } + } +} + +pub struct AcceptorServiceResponse +where + T: AsyncRead + AsyncWrite, +{ + fut: LocalBoxFuture<'static, Result, HandshakeError>>, + _guard: CounterGuard, +} + +impl Future for AcceptorServiceResponse { + type Output = Result, HandshakeError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let io = futures::ready!(Pin::new(&mut self.fut).poll(cx))?; + Poll::Ready(Ok(io)) + } +} diff --git a/actix-utils/src/counter.rs b/actix-utils/src/counter.rs index 07d62454..cffa9541 100644 --- a/actix-utils/src/counter.rs +++ b/actix-utils/src/counter.rs @@ -52,6 +52,8 @@ impl CounterGuard { } } +impl Unpin for CounterGuard {} + impl Drop for CounterGuard { fn drop(&mut self) { self.0.dec();