1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-10-03 16:51:58 +00:00

refactor ssl support

This commit is contained in:
Nikolay Kim 2018-08-03 16:09:46 -07:00
parent 036cf5e867
commit f3f1e04853
12 changed files with 879 additions and 613 deletions

View file

@ -4,9 +4,12 @@
### Added ### Added
* Added `HttpServer::max_connections()` and `HttpServer::max_sslrate()`, * Added `HttpServer::maxconn()` and `HttpServer::maxconnrate()`,
accept backpressure #250 accept backpressure #250
* Allow to customize connection handshake process via `HttpServer::listen_with()`
and `HttpServer::bind_with()` methods
### Fixed ### Fixed
* Use zlib instead of raw deflate for decoding and encoding payloads with * Use zlib instead of raw deflate for decoding and encoding payloads with

View file

@ -9,8 +9,8 @@ use tokio_timer::Delay;
use actix::{msgs::Execute, Arbiter, System}; use actix::{msgs::Execute, Arbiter, System};
use super::srv::{ServerCommand, Socket}; use super::srv::ServerCommand;
use super::worker::{Conn, WorkerClient}; use super::worker::{Conn, Socket, Token, WorkerClient};
pub(crate) enum Command { pub(crate) enum Command {
Pause, Pause,
@ -21,7 +21,7 @@ pub(crate) enum Command {
struct ServerSocketInfo { struct ServerSocketInfo {
addr: net::SocketAddr, addr: net::SocketAddr,
token: usize, token: Token,
sock: mio::net::TcpListener, sock: mio::net::TcpListener,
timeout: Option<Instant>, timeout: Option<Instant>,
} }
@ -31,20 +31,24 @@ pub(crate) struct AcceptNotify {
ready: mio::SetReadiness, ready: mio::SetReadiness,
maxconn: usize, maxconn: usize,
maxconn_low: usize, maxconn_low: usize,
maxsslrate: usize, maxconnrate: usize,
maxsslrate_low: usize, maxconnrate_low: usize,
} }
impl AcceptNotify { impl AcceptNotify {
pub fn new(ready: mio::SetReadiness, maxconn: usize, maxsslrate: usize) -> Self { pub fn new(ready: mio::SetReadiness, maxconn: usize, maxconnrate: usize) -> Self {
let maxconn_low = if maxconn > 10 { maxconn - 10 } else { 0 }; let maxconn_low = if maxconn > 10 { maxconn - 10 } else { 0 };
let maxsslrate_low = if maxsslrate > 10 { maxsslrate - 10 } else { 0 }; let maxconnrate_low = if maxconnrate > 10 {
maxconnrate - 10
} else {
0
};
AcceptNotify { AcceptNotify {
ready, ready,
maxconn, maxconn,
maxconn_low, maxconn_low,
maxsslrate, maxconnrate,
maxsslrate_low, maxconnrate_low,
} }
} }
@ -53,8 +57,8 @@ impl AcceptNotify {
let _ = self.ready.set_readiness(mio::Ready::readable()); let _ = self.ready.set_readiness(mio::Ready::readable());
} }
} }
pub fn notify_maxsslrate(&self, sslrate: usize) { pub fn notify_maxconnrate(&self, connrate: usize) {
if sslrate > self.maxsslrate_low && sslrate <= self.maxsslrate { if connrate > self.maxconnrate_low && connrate <= self.maxconnrate {
let _ = self.ready.set_readiness(mio::Ready::readable()); let _ = self.ready.set_readiness(mio::Ready::readable());
} }
} }
@ -78,7 +82,7 @@ pub(crate) struct AcceptLoop {
mpsc::UnboundedReceiver<ServerCommand>, mpsc::UnboundedReceiver<ServerCommand>,
)>, )>,
maxconn: usize, maxconn: usize,
maxsslrate: usize, maxconnrate: usize,
} }
impl AcceptLoop { impl AcceptLoop {
@ -94,7 +98,7 @@ impl AcceptLoop {
notify_ready, notify_ready,
notify_reg: Some(notify_reg), notify_reg: Some(notify_reg),
maxconn: 102_400, maxconn: 102_400,
maxsslrate: 256, maxconnrate: 256,
rx: Some(rx), rx: Some(rx),
srv: Some(mpsc::unbounded()), srv: Some(mpsc::unbounded()),
} }
@ -106,19 +110,19 @@ impl AcceptLoop {
} }
pub fn get_notify(&self) -> AcceptNotify { pub fn get_notify(&self) -> AcceptNotify {
AcceptNotify::new(self.notify_ready.clone(), self.maxconn, self.maxsslrate) AcceptNotify::new(self.notify_ready.clone(), self.maxconn, self.maxconnrate)
} }
pub fn max_connections(&mut self, num: usize) { pub fn maxconn(&mut self, num: usize) {
self.maxconn = num; self.maxconn = num;
} }
pub fn max_sslrate(&mut self, num: usize) { pub fn maxconnrate(&mut self, num: usize) {
self.maxsslrate = num; self.maxconnrate = num;
} }
pub(crate) fn start( pub(crate) fn start(
&mut self, socks: Vec<(usize, Socket)>, workers: Vec<WorkerClient>, &mut self, socks: Vec<Socket>, workers: Vec<WorkerClient>,
) -> mpsc::UnboundedReceiver<ServerCommand> { ) -> mpsc::UnboundedReceiver<ServerCommand> {
let (tx, rx) = self.srv.take().expect("Can not re-use AcceptInfo"); let (tx, rx) = self.srv.take().expect("Can not re-use AcceptInfo");
@ -127,7 +131,7 @@ impl AcceptLoop {
self.cmd_reg.take().expect("Can not re-use AcceptInfo"), self.cmd_reg.take().expect("Can not re-use AcceptInfo"),
self.notify_reg.take().expect("Can not re-use AcceptInfo"), self.notify_reg.take().expect("Can not re-use AcceptInfo"),
self.maxconn, self.maxconn,
self.maxsslrate, self.maxconnrate,
socks, socks,
tx, tx,
workers, workers,
@ -145,7 +149,7 @@ struct Accept {
timer: (mio::Registration, mio::SetReadiness), timer: (mio::Registration, mio::SetReadiness),
next: usize, next: usize,
maxconn: usize, maxconn: usize,
maxsslrate: usize, maxconnrate: usize,
backpressure: bool, backpressure: bool,
} }
@ -171,8 +175,8 @@ impl Accept {
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] #![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub(crate) fn start( pub(crate) fn start(
rx: sync_mpsc::Receiver<Command>, cmd_reg: mio::Registration, rx: sync_mpsc::Receiver<Command>, cmd_reg: mio::Registration,
notify_reg: mio::Registration, maxconn: usize, maxsslrate: usize, notify_reg: mio::Registration, maxconn: usize, maxconnrate: usize,
socks: Vec<(usize, Socket)>, srv: mpsc::UnboundedSender<ServerCommand>, socks: Vec<Socket>, srv: mpsc::UnboundedSender<ServerCommand>,
workers: Vec<WorkerClient>, workers: Vec<WorkerClient>,
) { ) {
let sys = System::current(); let sys = System::current();
@ -184,7 +188,7 @@ impl Accept {
System::set_current(sys); System::set_current(sys);
let mut accept = Accept::new(rx, socks, workers, srv); let mut accept = Accept::new(rx, socks, workers, srv);
accept.maxconn = maxconn; accept.maxconn = maxconn;
accept.maxsslrate = maxsslrate; accept.maxconnrate = maxconnrate;
// Start listening for incoming commands // Start listening for incoming commands
if let Err(err) = accept.poll.register( if let Err(err) = accept.poll.register(
@ -211,7 +215,7 @@ impl Accept {
} }
fn new( fn new(
rx: sync_mpsc::Receiver<Command>, socks: Vec<(usize, Socket)>, rx: sync_mpsc::Receiver<Command>, socks: Vec<Socket>,
workers: Vec<WorkerClient>, srv: mpsc::UnboundedSender<ServerCommand>, workers: Vec<WorkerClient>, srv: mpsc::UnboundedSender<ServerCommand>,
) -> Accept { ) -> Accept {
// Create a poll instance // Create a poll instance
@ -222,7 +226,7 @@ impl Accept {
// Start accept // Start accept
let mut sockets = Slab::new(); let mut sockets = Slab::new();
for (stoken, sock) in socks { for sock in socks {
let server = mio::net::TcpListener::from_std(sock.lst) let server = mio::net::TcpListener::from_std(sock.lst)
.expect("Can not create mio::net::TcpListener"); .expect("Can not create mio::net::TcpListener");
@ -240,7 +244,7 @@ impl Accept {
} }
entry.insert(ServerSocketInfo { entry.insert(ServerSocketInfo {
token: stoken, token: sock.token,
addr: sock.addr, addr: sock.addr,
sock: server, sock: server,
timeout: None, timeout: None,
@ -264,7 +268,7 @@ impl Accept {
next: 0, next: 0,
timer: (tm, tmr), timer: (tm, tmr),
maxconn: 102_400, maxconn: 102_400,
maxsslrate: 256, maxconnrate: 256,
backpressure: false, backpressure: false,
} }
} }
@ -427,7 +431,7 @@ impl Accept {
let mut idx = 0; let mut idx = 0;
while idx < self.workers.len() { while idx < self.workers.len() {
idx += 1; idx += 1;
if self.workers[self.next].available(self.maxconn, self.maxsslrate) { if self.workers[self.next].available(self.maxconn, self.maxconnrate) {
match self.workers[self.next].send(msg) { match self.workers[self.next].send(msg) {
Ok(_) => { Ok(_) => {
self.next = (self.next + 1) % self.workers.len(); self.next = (self.next + 1) % self.workers.len();

View file

@ -375,7 +375,7 @@ where
self.keepalive_timer.take(); self.keepalive_timer.take();
// search handler for request // search handler for request
for h in self.settings.handlers().iter_mut() { for h in self.settings.handlers().iter() {
msg = match h.handle(msg) { msg = match h.handle(msg) {
Ok(mut pipe) => { Ok(mut pipe) => {
if self.tasks.is_empty() { if self.tasks.is_empty() {

View file

@ -347,7 +347,7 @@ impl<H: HttpHandler + 'static> Entry<H> {
// start request processing // start request processing
let mut task = None; let mut task = None;
for h in settings.handlers().iter_mut() { for h in settings.handlers().iter() {
msg = match h.handle(msg) { msg = match h.handle(msg) {
Ok(t) => { Ok(t) => {
task = Some(t); task = Some(t);

View file

@ -1,10 +1,11 @@
//! Http server //! Http server
use std::net::Shutdown; use std::net::Shutdown;
use std::{io, time}; use std::{io, net, time};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use futures::{Async, Poll}; use futures::{Async, Future, Poll};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_reactor::Handle;
use tokio_tcp::TcpStream; use tokio_tcp::TcpStream;
pub(crate) mod accept; pub(crate) mod accept;
@ -21,11 +22,13 @@ pub(crate) mod message;
pub(crate) mod output; pub(crate) mod output;
pub(crate) mod settings; pub(crate) mod settings;
mod srv; mod srv;
mod ssl;
mod worker; mod worker;
pub use self::message::Request; pub use self::message::Request;
pub use self::settings::ServerSettings; pub use self::settings::ServerSettings;
pub use self::srv::HttpServer; pub use self::srv::HttpServer;
pub use self::ssl::*;
#[doc(hidden)] #[doc(hidden)]
pub use self::helpers::write_content_length; pub use self::helpers::write_content_length;
@ -72,6 +75,13 @@ where
HttpServer::new(factory) HttpServer::new(factory)
} }
bitflags! {
pub struct ServerFlags: u8 {
const HTTP1 = 0b0000_0001;
const HTTP2 = 0b0000_0010;
}
}
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
/// Server keep-alive setting /// Server keep-alive setting
pub enum KeepAlive { pub enum KeepAlive {
@ -179,6 +189,34 @@ impl<T: HttpHandler> IntoHttpHandler for T {
} }
} }
pub(crate) trait IntoAsyncIo {
type Io: AsyncRead + AsyncWrite;
fn into_async_io(self) -> Result<Self::Io, io::Error>;
}
impl IntoAsyncIo for net::TcpStream {
type Io = TcpStream;
fn into_async_io(self) -> Result<Self::Io, io::Error> {
TcpStream::from_std(self, &Handle::default())
}
}
/// Trait implemented by types that could accept incomming socket connections.
pub trait AcceptorService<Io: AsyncRead + AsyncWrite>: Clone {
/// Established connection type
type Accepted: IoStream;
/// Future describes async accept process.
type Future: Future<Item = Self::Accepted, Error = io::Error> + 'static;
/// Establish new connection
fn accept(&self, io: Io) -> Self::Future;
/// Scheme
fn scheme(&self) -> &'static str;
}
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug)] #[derive(Debug)]
pub enum WriterState { pub enum WriterState {
@ -267,90 +305,3 @@ impl IoStream for TcpStream {
TcpStream::set_linger(self, dur) TcpStream::set_linger(self, dur)
} }
} }
#[cfg(feature = "alpn")]
use tokio_openssl::SslStream;
#[cfg(feature = "alpn")]
impl IoStream for SslStream<TcpStream> {
#[inline]
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
let _ = self.get_mut().shutdown();
Ok(())
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.get_mut().get_mut().set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<time::Duration>) -> io::Result<()> {
self.get_mut().get_mut().set_linger(dur)
}
}
#[cfg(feature = "tls")]
use tokio_tls::TlsStream;
#[cfg(feature = "tls")]
impl IoStream for TlsStream<TcpStream> {
#[inline]
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
let _ = self.get_mut().shutdown();
Ok(())
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.get_mut().get_mut().set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<time::Duration>) -> io::Result<()> {
self.get_mut().get_mut().set_linger(dur)
}
}
#[cfg(feature = "rust-tls")]
use rustls::{ClientSession, ServerSession};
#[cfg(feature = "rust-tls")]
use tokio_rustls::TlsStream as RustlsStream;
#[cfg(feature = "rust-tls")]
impl IoStream for RustlsStream<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 RustlsStream<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)
}
}

View file

@ -132,7 +132,7 @@ impl ServerSettings {
const DATE_VALUE_LENGTH: usize = 29; const DATE_VALUE_LENGTH: usize = 29;
pub(crate) struct WorkerSettings<H> { pub(crate) struct WorkerSettings<H> {
h: RefCell<Vec<H>>, h: Vec<H>,
keep_alive: u64, keep_alive: u64,
ka_enabled: bool, ka_enabled: bool,
bytes: Rc<SharedBytesPool>, bytes: Rc<SharedBytesPool>,
@ -140,14 +140,14 @@ pub(crate) struct WorkerSettings<H> {
channels: Arc<AtomicUsize>, channels: Arc<AtomicUsize>,
node: RefCell<Node<()>>, node: RefCell<Node<()>>,
date: UnsafeCell<Date>, date: UnsafeCell<Date>,
sslrate: Arc<AtomicUsize>, connrate: Arc<AtomicUsize>,
notify: AcceptNotify, notify: AcceptNotify,
} }
impl<H> WorkerSettings<H> { impl<H> WorkerSettings<H> {
pub(crate) fn new( pub(crate) fn new(
h: Vec<H>, keep_alive: KeepAlive, settings: ServerSettings, h: Vec<H>, keep_alive: KeepAlive, settings: ServerSettings,
notify: AcceptNotify, channels: Arc<AtomicUsize>, sslrate: Arc<AtomicUsize>, notify: AcceptNotify, channels: Arc<AtomicUsize>, connrate: Arc<AtomicUsize>,
) -> WorkerSettings<H> { ) -> WorkerSettings<H> {
let (keep_alive, ka_enabled) = match keep_alive { let (keep_alive, ka_enabled) = match keep_alive {
KeepAlive::Timeout(val) => (val as u64, true), KeepAlive::Timeout(val) => (val as u64, true),
@ -156,7 +156,7 @@ impl<H> WorkerSettings<H> {
}; };
WorkerSettings { WorkerSettings {
h: RefCell::new(h), h,
bytes: Rc::new(SharedBytesPool::new()), bytes: Rc::new(SharedBytesPool::new()),
messages: RequestPool::pool(settings), messages: RequestPool::pool(settings),
node: RefCell::new(Node::head()), node: RefCell::new(Node::head()),
@ -164,7 +164,7 @@ impl<H> WorkerSettings<H> {
keep_alive, keep_alive,
ka_enabled, ka_enabled,
channels, channels,
sslrate, connrate,
notify, notify,
} }
} }
@ -177,8 +177,8 @@ impl<H> WorkerSettings<H> {
self.node.borrow_mut() self.node.borrow_mut()
} }
pub fn handlers(&self) -> RefMut<Vec<H>> { pub fn handlers(&self) -> &Vec<H> {
self.h.borrow_mut() &self.h
} }
pub fn keep_alive(&self) -> u64 { pub fn keep_alive(&self) -> u64 {
@ -230,13 +230,13 @@ impl<H> WorkerSettings<H> {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn ssl_conn_add(&self) { pub(crate) fn conn_rate_add(&self) {
self.sslrate.fetch_add(1, Ordering::Relaxed); self.connrate.fetch_add(1, Ordering::Relaxed);
} }
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn ssl_conn_del(&self) { pub(crate) fn conn_rate_del(&self) {
let val = self.sslrate.fetch_sub(1, Ordering::Relaxed); let val = self.connrate.fetch_sub(1, Ordering::Relaxed);
self.notify.notify_maxsslrate(val); self.notify.notify_maxconnrate(val);
} }
} }

View file

@ -10,15 +10,15 @@ use actix::{
use futures::sync::mpsc; use futures::sync::mpsc;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use net2::TcpBuilder;
use num_cpus; use num_cpus;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tcp::TcpStream;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use native_tls::TlsAcceptor; use native_tls::TlsAcceptor;
#[cfg(feature = "alpn")] #[cfg(feature = "alpn")]
use openssl::ssl::{AlpnError, SslAcceptorBuilder}; use openssl::ssl::SslAcceptorBuilder;
#[cfg(feature = "rust-tls")] #[cfg(feature = "rust-tls")]
use rustls::ServerConfig; use rustls::ServerConfig;
@ -26,43 +26,25 @@ use rustls::ServerConfig;
use super::accept::{AcceptLoop, AcceptNotify, Command}; use super::accept::{AcceptLoop, AcceptNotify, Command};
use super::channel::{HttpChannel, WrapperStream}; use super::channel::{HttpChannel, WrapperStream};
use super::settings::{ServerSettings, WorkerSettings}; use super::settings::{ServerSettings, WorkerSettings};
use super::worker::{ use super::worker::{Conn, StopWorker, Token, Worker, WorkerClient, WorkerFactory};
Conn, StopWorker, StreamHandlerType, Worker, WorkerClient, WorkersPool, use super::{AcceptorService, IntoHttpHandler, IoStream, KeepAlive};
};
use super::{IntoHttpHandler, IoStream, KeepAlive};
use super::{PauseServer, ResumeServer, StopServer}; use super::{PauseServer, ResumeServer, StopServer};
#[cfg(feature = "alpn")]
fn configure_alpn(builder: &mut SslAcceptorBuilder) -> io::Result<()> {
builder.set_alpn_protos(b"\x02h2\x08http/1.1")?;
builder.set_alpn_select_callback(|_, protos| {
const H2: &[u8] = b"\x02h2";
if protos.windows(3).any(|window| window == H2) {
Ok(b"h2")
} else {
Err(AlpnError::NOACK)
}
});
Ok(())
}
/// An HTTP Server /// An HTTP Server
pub struct HttpServer<H> pub struct HttpServer<H>
where where
H: IntoHttpHandler + 'static, H: IntoHttpHandler + 'static,
{ {
h: Option<Rc<WorkerSettings<H::Handler>>>,
threads: usize, threads: usize,
backlog: i32, factory: WorkerFactory<H>,
sockets: Vec<Socket>, workers: Vec<(usize, Addr<Worker>)>,
pool: WorkersPool<H>,
workers: Vec<(usize, Addr<Worker<H::Handler>>)>,
accept: AcceptLoop, accept: AcceptLoop,
exit: bool, exit: bool,
shutdown_timeout: u16, shutdown_timeout: u16,
signals: Option<Addr<signal::ProcessSignals>>, signals: Option<Addr<signal::ProcessSignals>>,
no_http2: bool, no_http2: bool,
no_signals: bool, no_signals: bool,
settings: Option<Rc<WorkerSettings<H::Handler>>>,
} }
pub(crate) enum ServerCommand { pub(crate) enum ServerCommand {
@ -76,12 +58,6 @@ where
type Context = Context<Self>; type Context = Context<Self>;
} }
pub(crate) struct Socket {
pub lst: net::TcpListener,
pub addr: net::SocketAddr,
pub tp: StreamHandlerType,
}
impl<H> HttpServer<H> impl<H> HttpServer<H>
where where
H: IntoHttpHandler + 'static, H: IntoHttpHandler + 'static,
@ -95,18 +71,16 @@ where
let f = move || (factory)().into_iter().collect(); let f = move || (factory)().into_iter().collect();
HttpServer { HttpServer {
h: None,
threads: num_cpus::get(), threads: num_cpus::get(),
backlog: 2048, factory: WorkerFactory::new(f),
pool: WorkersPool::new(f),
workers: Vec::new(), workers: Vec::new(),
sockets: Vec::new(),
accept: AcceptLoop::new(), accept: AcceptLoop::new(),
exit: false, exit: false,
shutdown_timeout: 30, shutdown_timeout: 30,
signals: None, signals: None,
no_http2: false, no_http2: false,
no_signals: false, no_signals: false,
settings: None,
} }
} }
@ -130,7 +104,7 @@ where
/// ///
/// This method should be called before `bind()` method call. /// This method should be called before `bind()` method call.
pub fn backlog(mut self, num: i32) -> Self { pub fn backlog(mut self, num: i32) -> Self {
self.backlog = num; self.factory.backlog = num;
self self
} }
@ -140,20 +114,19 @@ where
/// for each worker. /// for each worker.
/// ///
/// By default max connections is set to a 100k. /// By default max connections is set to a 100k.
pub fn max_connections(mut self, num: usize) -> Self { pub fn maxconn(mut self, num: usize) -> Self {
self.accept.max_connections(num); self.accept.maxconn(num);
self self
} }
/// Sets the maximum concurrent per-worker number of SSL handshakes. /// Sets the maximum per-worker concurrent connection establish process.
/// ///
/// All listeners will stop accepting connections when this limit is reached. It /// All listeners will stop accepting connections when this limit is reached. It
/// can be used to limit the global SSL CPU usage regardless of each worker /// can be used to limit the global SSL CPU usage.
/// capacity.
/// ///
/// By default max connections is set to a 256. /// By default max connections is set to a 256.
pub fn max_sslrate(mut self, num: usize) -> Self { pub fn maxconnrate(mut self, num: usize) -> Self {
self.accept.max_sslrate(num); self.accept.maxconnrate(num);
self self
} }
@ -161,7 +134,7 @@ where
/// ///
/// By default keep alive is set to a `Os`. /// By default keep alive is set to a `Os`.
pub fn keep_alive<T: Into<KeepAlive>>(mut self, val: T) -> Self { pub fn keep_alive<T: Into<KeepAlive>>(mut self, val: T) -> Self {
self.pool.keep_alive = val.into(); self.factory.keep_alive = val.into();
self self
} }
@ -171,7 +144,7 @@ where
/// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo. /// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo.
/// html#method.host) documentation for more information. /// html#method.host) documentation for more information.
pub fn server_hostname(mut self, val: String) -> Self { pub fn server_hostname(mut self, val: String) -> Self {
self.pool.host = Some(val); self.factory.host = Some(val);
self self
} }
@ -215,7 +188,7 @@ where
/// Get addresses of bound sockets. /// Get addresses of bound sockets.
pub fn addrs(&self) -> Vec<net::SocketAddr> { pub fn addrs(&self) -> Vec<net::SocketAddr> {
self.sockets.iter().map(|s| s.addr).collect() self.factory.addrs()
} }
/// Get addresses of bound sockets and the scheme for it. /// Get addresses of bound sockets and the scheme for it.
@ -225,10 +198,7 @@ where
/// and the user should be presented with an enumeration of which /// and the user should be presented with an enumeration of which
/// socket requires which protocol. /// socket requires which protocol.
pub fn addrs_with_scheme(&self) -> Vec<(net::SocketAddr, &str)> { pub fn addrs_with_scheme(&self) -> Vec<(net::SocketAddr, &str)> {
self.sockets self.factory.addrs_with_scheme()
.iter()
.map(|s| (s.addr, s.tp.scheme()))
.collect()
} }
/// Use listener for accepting incoming connection requests /// Use listener for accepting incoming connection requests
@ -236,175 +206,177 @@ where
/// HttpServer does not change any configuration for TcpListener, /// HttpServer does not change any configuration for TcpListener,
/// it needs to be configured before passing it to listen() method. /// it needs to be configured before passing it to listen() method.
pub fn listen(mut self, lst: net::TcpListener) -> Self { pub fn listen(mut self, lst: net::TcpListener) -> Self {
let addr = lst.local_addr().unwrap(); self.factory.listen(lst);
self.sockets.push(Socket {
addr,
lst,
tp: StreamHandlerType::Normal,
});
self self
} }
/// Use listener for accepting incoming connection requests
pub fn listen_with<A>(
mut self, lst: net::TcpListener, acceptor: A,
) -> io::Result<Self>
where
A: AcceptorService<TcpStream> + Send + 'static,
{
self.factory.listen_with(lst, acceptor);
Ok(self)
}
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::listen_with()` and `actix_web::server::NativeTlsAcceptor` instead"
)]
/// Use listener for accepting incoming tls connection requests /// Use listener for accepting incoming tls connection requests
/// ///
/// HttpServer does not change any configuration for TcpListener, /// HttpServer does not change any configuration for TcpListener,
/// it needs to be configured before passing it to listen() method. /// it needs to be configured before passing it to listen() method.
pub fn listen_tls(mut self, lst: net::TcpListener, acceptor: TlsAcceptor) -> Self { pub fn listen_tls(
let addr = lst.local_addr().unwrap(); self, lst: net::TcpListener, acceptor: TlsAcceptor,
self.sockets.push(Socket { ) -> io::Result<Self> {
addr, use super::NativeTlsAcceptor;
lst,
tp: StreamHandlerType::Tls(acceptor.clone()), self.listen_with(lst, NativeTlsAcceptor::new(acceptor))
});
self
} }
#[cfg(feature = "alpn")] #[cfg(feature = "alpn")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::listen_with()` and `actix_web::server::OpensslAcceptor` instead"
)]
/// Use listener for accepting incoming tls connection requests /// Use listener for accepting incoming tls connection requests
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn listen_ssl( pub fn listen_ssl(
mut self, lst: net::TcpListener, mut builder: SslAcceptorBuilder, self, lst: net::TcpListener, builder: SslAcceptorBuilder,
) -> io::Result<Self> { ) -> io::Result<Self> {
use super::{OpensslAcceptor, ServerFlags};
// alpn support // alpn support
if !self.no_http2 { let flags = if !self.no_http2 {
configure_alpn(&mut builder)?; ServerFlags::HTTP1
} } else {
let acceptor = builder.build(); ServerFlags::HTTP1 | ServerFlags::HTTP2
let addr = lst.local_addr().unwrap(); };
self.sockets.push(Socket {
addr, self.listen_with(lst, OpensslAcceptor::with_flags(builder, flags)?)
lst,
tp: StreamHandlerType::Alpn(acceptor.clone()),
});
Ok(self)
} }
#[cfg(feature = "rust-tls")] #[cfg(feature = "rust-tls")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::listen_with()` and `actix_web::server::RustlsAcceptor` instead"
)]
/// Use listener for accepting incoming tls connection requests /// Use listener for accepting incoming tls connection requests
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn listen_rustls( pub fn listen_rustls(
mut self, lst: net::TcpListener, mut builder: ServerConfig, self, lst: net::TcpListener, builder: ServerConfig,
) -> io::Result<Self> { ) -> io::Result<Self> {
use super::{RustlsAcceptor, ServerFlags};
// alpn support // alpn support
if !self.no_http2 { let flags = if !self.no_http2 {
builder.set_protocols(&vec!["h2".to_string(), "http/1.1".to_string()]); ServerFlags::HTTP1
}
let addr = lst.local_addr().unwrap();
self.sockets.push(Socket {
addr,
lst,
tp: StreamHandlerType::Rustls(Arc::new(builder)),
});
Ok(self)
}
fn bind2<S: net::ToSocketAddrs>(&mut self, addr: S) -> io::Result<Vec<Socket>> {
let mut err = None;
let mut succ = false;
let mut sockets = Vec::new();
for addr in addr.to_socket_addrs()? {
match create_tcp_listener(addr, self.backlog) {
Ok(lst) => {
succ = true;
let addr = lst.local_addr().unwrap();
sockets.push(Socket {
lst,
addr,
tp: StreamHandlerType::Normal,
});
}
Err(e) => err = Some(e),
}
}
if !succ {
if let Some(e) = err.take() {
Err(e)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Can not bind to address.",
))
}
} else { } else {
Ok(sockets) ServerFlags::HTTP1 | ServerFlags::HTTP2
} };
self.listen_with(lst, RustlsAcceptor::with_flags(builder, flags))
} }
/// The socket address to bind /// The socket address to bind
/// ///
/// To bind multiple addresses this method can be called multiple times. /// To bind multiple addresses this method can be called multiple times.
pub fn bind<S: net::ToSocketAddrs>(mut self, addr: S) -> io::Result<Self> { pub fn bind<S: net::ToSocketAddrs>(mut self, addr: S) -> io::Result<Self> {
let sockets = self.bind2(addr)?; self.factory.bind(addr)?;
self.sockets.extend(sockets); Ok(self)
}
/// Start listening for incoming connections with supplied acceptor.
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn bind_with<S, A>(mut self, addr: S, acceptor: A) -> io::Result<Self>
where
S: net::ToSocketAddrs,
A: AcceptorService<TcpStream> + Send + 'static,
{
self.factory.bind_with(addr, &acceptor)?;
Ok(self) Ok(self)
} }
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::bind_with()` and `actix_web::server::NativeTlsAcceptor` instead"
)]
/// The ssl socket address to bind /// The ssl socket address to bind
/// ///
/// To bind multiple addresses this method can be called multiple times. /// To bind multiple addresses this method can be called multiple times.
pub fn bind_tls<S: net::ToSocketAddrs>( pub fn bind_tls<S: net::ToSocketAddrs>(
mut self, addr: S, acceptor: TlsAcceptor, self, addr: S, acceptor: TlsAcceptor,
) -> io::Result<Self> { ) -> io::Result<Self> {
let sockets = self.bind2(addr)?; use super::NativeTlsAcceptor;
self.sockets.extend(sockets.into_iter().map(|mut s| {
s.tp = StreamHandlerType::Tls(acceptor.clone()); self.bind_with(addr, NativeTlsAcceptor::new(acceptor))
s
}));
Ok(self)
} }
#[cfg(feature = "alpn")] #[cfg(feature = "alpn")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::bind_with()` and `actix_web::server::OpensslAcceptor` instead"
)]
/// Start listening for incoming tls connections. /// Start listening for incoming tls connections.
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn bind_ssl<S: net::ToSocketAddrs>( pub fn bind_ssl<S>(self, addr: S, builder: SslAcceptorBuilder) -> io::Result<Self>
mut self, addr: S, mut builder: SslAcceptorBuilder, where
) -> io::Result<Self> { S: net::ToSocketAddrs,
// alpn support {
if !self.no_http2 { use super::{OpensslAcceptor, ServerFlags};
configure_alpn(&mut builder)?;
}
let acceptor = builder.build(); // alpn support
let sockets = self.bind2(addr)?; let flags = if !self.no_http2 {
self.sockets.extend(sockets.into_iter().map(|mut s| { ServerFlags::HTTP1
s.tp = StreamHandlerType::Alpn(acceptor.clone()); } else {
s ServerFlags::HTTP1 | ServerFlags::HTTP2
})); };
Ok(self)
self.bind_with(addr, OpensslAcceptor::with_flags(builder, flags)?)
} }
#[cfg(feature = "rust-tls")] #[cfg(feature = "rust-tls")]
#[doc(hidden)]
#[deprecated(
since = "0.7.4",
note = "please use `actix_web::HttpServer::bind_with()` and `actix_web::server::RustlsAcceptor` instead"
)]
/// Start listening for incoming tls connections. /// Start listening for incoming tls connections.
/// ///
/// This method sets alpn protocols to "h2" and "http/1.1" /// This method sets alpn protocols to "h2" and "http/1.1"
pub fn bind_rustls<S: net::ToSocketAddrs>( pub fn bind_rustls<S: net::ToSocketAddrs>(
mut self, addr: S, mut builder: ServerConfig, self, addr: S, builder: ServerConfig,
) -> io::Result<Self> { ) -> io::Result<Self> {
// alpn support use super::{RustlsAcceptor, ServerFlags};
if !self.no_http2 {
builder.set_protocols(&vec!["h2".to_string(), "http/1.1".to_string()]);
}
let builder = Arc::new(builder); // alpn support
let sockets = self.bind2(addr)?; let flags = if !self.no_http2 {
self.sockets.extend(sockets.into_iter().map(move |mut s| { ServerFlags::HTTP1
s.tp = StreamHandlerType::Rustls(builder.clone()); } else {
s ServerFlags::HTTP1 | ServerFlags::HTTP2
})); };
Ok(self)
self.bind_with(addr, RustlsAcceptor::with_flags(builder, flags))
} }
fn start_workers(&mut self, notify: &AcceptNotify) -> Vec<WorkerClient> { fn start_workers(&mut self, notify: &AcceptNotify) -> Vec<WorkerClient> {
// start workers // start workers
let mut workers = Vec::new(); let mut workers = Vec::new();
for idx in 0..self.threads { for idx in 0..self.threads {
let (worker, addr) = self.pool.start(idx, notify.clone()); let (worker, addr) = self.factory.start(idx, notify.clone());
workers.push(worker); workers.push(worker);
self.workers.push((idx, addr)); self.workers.push((idx, addr));
} }
@ -453,23 +425,18 @@ impl<H: IntoHttpHandler> HttpServer<H> {
/// } /// }
/// ``` /// ```
pub fn start(mut self) -> Addr<Self> { pub fn start(mut self) -> Addr<Self> {
if self.sockets.is_empty() { let sockets = self.factory.take_sockets();
if sockets.is_empty() {
panic!("HttpServer::bind() has to be called before start()"); panic!("HttpServer::bind() has to be called before start()");
} else { } else {
let mut addrs: Vec<(usize, Socket)> = Vec::new();
for socket in self.sockets.drain(..) {
let token = self.pool.insert(socket.addr, socket.tp.clone());
addrs.push((token, socket));
}
let notify = self.accept.get_notify(); let notify = self.accept.get_notify();
let workers = self.start_workers(&notify); let workers = self.start_workers(&notify);
// start accept thread // start accept thread
for (_, sock) in &addrs { for sock in &sockets {
info!("Starting server on http://{}", sock.addr); info!("Starting server on http://{}", sock.addr);
} }
let rx = self.accept.start(addrs, workers.clone()); let rx = self.accept.start(sockets, workers.clone());
// start http server actor // start http server actor
let signals = self.subscribe_to_signals(); let signals = self.subscribe_to_signals();
@ -511,64 +478,6 @@ impl<H: IntoHttpHandler> HttpServer<H> {
} }
} }
#[doc(hidden)]
#[cfg(feature = "tls")]
#[deprecated(
since = "0.6.0",
note = "please use `actix_web::HttpServer::bind_tls` instead"
)]
impl<H: IntoHttpHandler> HttpServer<H> {
/// Start listening for incoming tls connections.
pub fn start_tls(mut self, acceptor: TlsAcceptor) -> io::Result<Addr<Self>> {
for sock in &mut self.sockets {
match sock.tp {
StreamHandlerType::Normal => (),
_ => continue,
}
sock.tp = StreamHandlerType::Tls(acceptor.clone());
}
Ok(self.start())
}
}
#[doc(hidden)]
#[cfg(feature = "alpn")]
#[deprecated(
since = "0.6.0",
note = "please use `actix_web::HttpServer::bind_ssl` instead"
)]
impl<H: IntoHttpHandler> HttpServer<H> {
/// Start listening for incoming tls connections.
///
/// This method sets alpn protocols to "h2" and "http/1.1"
pub fn start_ssl(
mut self, mut builder: SslAcceptorBuilder,
) -> io::Result<Addr<Self>> {
// alpn support
if !self.no_http2 {
builder.set_alpn_protos(b"\x02h2\x08http/1.1")?;
builder.set_alpn_select_callback(|_, protos| {
const H2: &[u8] = b"\x02h2";
if protos.windows(3).any(|window| window == H2) {
Ok(b"h2")
} else {
Err(AlpnError::NOACK)
}
});
}
let acceptor = builder.build();
for sock in &mut self.sockets {
match sock.tp {
StreamHandlerType::Normal => (),
_ => continue,
}
sock.tp = StreamHandlerType::Alpn(acceptor.clone());
}
Ok(self.start())
}
}
impl<H: IntoHttpHandler> HttpServer<H> { impl<H: IntoHttpHandler> HttpServer<H> {
/// Start listening for incoming connections from a stream. /// Start listening for incoming connections from a stream.
/// ///
@ -580,14 +489,14 @@ impl<H: IntoHttpHandler> HttpServer<H> {
{ {
// set server settings // set server settings
let addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap(); let addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
let settings = ServerSettings::new(Some(addr), &self.pool.host, secure); let settings = ServerSettings::new(Some(addr), &self.factory.host, secure);
let apps: Vec<_> = (*self.pool.factory)() let apps: Vec<_> = (*self.factory.factory)()
.into_iter() .into_iter()
.map(|h| h.into_handler()) .map(|h| h.into_handler())
.collect(); .collect();
self.h = Some(Rc::new(WorkerSettings::new( self.settings = Some(Rc::new(WorkerSettings::new(
apps, apps,
self.pool.keep_alive, self.factory.keep_alive,
settings, settings,
AcceptNotify::default(), AcceptNotify::default(),
Arc::new(AtomicUsize::new(0)), Arc::new(AtomicUsize::new(0)),
@ -599,7 +508,7 @@ impl<H: IntoHttpHandler> HttpServer<H> {
let addr = HttpServer::create(move |ctx| { let addr = HttpServer::create(move |ctx| {
ctx.add_message_stream(stream.map_err(|_| ()).map(move |t| Conn { ctx.add_message_stream(stream.map_err(|_| ()).map(move |t| Conn {
io: WrapperStream::new(t), io: WrapperStream::new(t),
token: 0, token: Token::new(0),
peer: None, peer: None,
http2: false, http2: false,
})); }));
@ -672,7 +581,7 @@ impl<H: IntoHttpHandler> StreamHandler<ServerCommand, ()> for HttpServer<H> {
} }
let (worker, addr) = let (worker, addr) =
self.pool.start(new_idx, self.accept.get_notify()); self.factory.start(new_idx, self.accept.get_notify());
self.workers.push((new_idx, addr)); self.workers.push((new_idx, addr));
self.accept.send(Command::Worker(worker)); self.accept.send(Command::Worker(worker));
} }
@ -690,7 +599,7 @@ where
fn handle(&mut self, msg: Conn<T>, _: &mut Context<Self>) -> Self::Result { fn handle(&mut self, msg: Conn<T>, _: &mut Context<Self>) -> Self::Result {
Arbiter::spawn(HttpChannel::new( Arbiter::spawn(HttpChannel::new(
Rc::clone(self.h.as_ref().unwrap()), Rc::clone(self.settings.as_ref().unwrap()),
msg.io, msg.io,
msg.peer, msg.peer,
msg.http2, msg.http2,
@ -766,15 +675,3 @@ impl<H: IntoHttpHandler> Handler<StopServer> for HttpServer<H> {
} }
} }
} }
fn create_tcp_listener(
addr: net::SocketAddr, backlog: i32,
) -> io::Result<net::TcpListener> {
let builder = match addr {
net::SocketAddr::V4(_) => TcpBuilder::new_v4()?,
net::SocketAddr::V6(_) => TcpBuilder::new_v6()?,
};
builder.reuse_address(true)?;
builder.bind(addr)?;
Ok(builder.listen(backlog)?)
}

14
src/server/ssl/mod.rs Normal file
View file

@ -0,0 +1,14 @@
#[cfg(feature = "alpn")]
mod openssl;
#[cfg(feature = "alpn")]
pub use self::openssl::OpensslAcceptor;
#[cfg(feature = "tls")]
mod nativetls;
#[cfg(feature = "tls")]
pub use self::nativetls::NativeTlsAcceptor;
#[cfg(feature = "rust-tls")]
mod rustls;
#[cfg(feature = "rust-tls")]
pub use self::rustls::RustlsAcceptor;

View file

@ -0,0 +1,67 @@
use std::net::Shutdown;
use std::{io, time};
use futures::{Future, Poll};
use native_tls::TlsAcceptor;
use tokio_tls::{AcceptAsync, TlsAcceptorExt, TlsStream};
use server::{AcceptorService, IoStream};
#[derive(Clone)]
/// Support `SSL` connections via native-tls package
///
/// `tls` feature enables `NativeTlsAcceptor` type
pub struct NativeTlsAcceptor {
acceptor: TlsAcceptor,
}
impl NativeTlsAcceptor {
/// Create `NativeTlsAcceptor` instance
pub fn new(acceptor: TlsAcceptor) -> Self {
NativeTlsAcceptor { acceptor }
}
}
pub struct AcceptorFut<Io>(AcceptAsync<Io>);
impl<Io: IoStream> Future for AcceptorFut<Io> {
type Item = TlsStream<Io>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0
.poll()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
}
impl<Io: IoStream> AcceptorService<Io> for NativeTlsAcceptor {
type Accepted = TlsStream<Io>;
type Future = AcceptorFut<Io>;
fn scheme(&self) -> &'static str {
"https"
}
fn accept(&self, io: Io) -> Self::Future {
AcceptorFut(TlsAcceptorExt::accept_async(&self.acceptor, io))
}
}
impl<Io: IoStream> IoStream for TlsStream<Io> {
#[inline]
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
let _ = self.get_mut().shutdown();
Ok(())
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.get_mut().get_mut().set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<time::Duration>) -> io::Result<()> {
self.get_mut().get_mut().set_linger(dur)
}
}

96
src/server/ssl/openssl.rs Normal file
View file

@ -0,0 +1,96 @@
use std::net::Shutdown;
use std::{io, time};
use futures::{Future, Poll};
use openssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
use tokio_openssl::{AcceptAsync, SslAcceptorExt, SslStream};
use server::{AcceptorService, IoStream, ServerFlags};
#[derive(Clone)]
/// Support `SSL` connections via openssl package
///
/// `alpn` feature enables `OpensslAcceptor` type
pub struct OpensslAcceptor {
acceptor: SslAcceptor,
}
impl OpensslAcceptor {
/// Create `OpensslAcceptor` with enabled `HTTP/2` and `HTTP1.1` support.
pub fn new(builder: SslAcceptorBuilder) -> io::Result<Self> {
OpensslAcceptor::with_flags(builder, ServerFlags::HTTP1 | ServerFlags::HTTP2)
}
/// Create `OpensslAcceptor` with custom server flags.
pub fn with_flags(
mut builder: SslAcceptorBuilder, flags: ServerFlags,
) -> io::Result<Self> {
let mut protos = Vec::new();
if flags.contains(ServerFlags::HTTP1) {
protos.extend(b"\x08http/1.1");
}
if flags.contains(ServerFlags::HTTP2) {
protos.extend(b"\x02h2");
builder.set_alpn_select_callback(|_, protos| {
const H2: &[u8] = b"\x02h2";
if protos.windows(3).any(|window| window == H2) {
Ok(b"h2")
} else {
Err(AlpnError::NOACK)
}
});
}
if !protos.is_empty() {
builder.set_alpn_protos(&protos)?;
}
Ok(OpensslAcceptor {
acceptor: builder.build(),
})
}
}
pub struct AcceptorFut<Io>(AcceptAsync<Io>);
impl<Io: IoStream> Future for AcceptorFut<Io> {
type Item = SslStream<Io>;
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.0
.poll()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
}
impl<Io: IoStream> AcceptorService<Io> for OpensslAcceptor {
type Accepted = SslStream<Io>;
type Future = AcceptorFut<Io>;
fn scheme(&self) -> &'static str {
"https"
}
fn accept(&self, io: Io) -> Self::Future {
AcceptorFut(SslAcceptorExt::accept_async(&self.acceptor, io))
}
}
impl<T: IoStream> IoStream for SslStream<T> {
#[inline]
fn shutdown(&mut self, _how: Shutdown) -> io::Result<()> {
let _ = self.get_mut().shutdown();
Ok(())
}
#[inline]
fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> {
self.get_mut().get_mut().set_nodelay(nodelay)
}
#[inline]
fn set_linger(&mut self, dur: Option<time::Duration>) -> io::Result<()> {
self.get_mut().get_mut().set_linger(dur)
}
}

92
src/server/ssl/rustls.rs Normal file
View file

@ -0,0 +1,92 @@
use std::net::Shutdown;
use std::sync::Arc;
use std::{io, time};
use rustls::{ClientSession, ServerConfig, ServerSession};
use tokio_io::AsyncWrite;
use tokio_rustls::{AcceptAsync, ServerConfigExt, TlsStream};
use server::{AcceptorService, IoStream, ServerFlags};
#[derive(Clone)]
/// Support `SSL` connections via rustls package
///
/// `rust-tls` feature enables `RustlsAcceptor` type
pub struct RustlsAcceptor {
config: Arc<ServerConfig>,
}
impl RustlsAcceptor {
/// Create `OpensslAcceptor` with enabled `HTTP/2` and `HTTP1.1` support.
pub fn new(config: ServerConfig) -> Self {
RustlsAcceptor::with_flags(config, ServerFlags::HTTP1 | ServerFlags::HTTP2)
}
/// Create `OpensslAcceptor` with custom server flags.
pub fn with_flags(mut config: ServerConfig, flags: ServerFlags) -> Self {
let mut protos = Vec::new();
if flags.contains(ServerFlags::HTTP1) {
protos.push("http/1.1".to_string());
}
if flags.contains(ServerFlags::HTTP2) {
protos.push("h2".to_string());
}
if !protos.is_empty() {
config.set_protocols(&protos);
}
RustlsAcceptor {
config: Arc::new(config),
}
}
}
impl<Io: IoStream> AcceptorService<Io> for RustlsAcceptor {
type Accepted = TlsStream<Io, ServerSession>;
type Future = AcceptAsync<Io>;
fn scheme(&self) -> &'static str {
"https"
}
fn accept(&self, io: Io) -> Self::Future {
ServerConfigExt::accept_async(&self.config, io)
}
}
impl<Io: IoStream> IoStream for TlsStream<Io, 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)
}
}
impl<Io: IoStream> IoStream for TlsStream<Io, 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)
}
}

View file

@ -1,102 +1,195 @@
use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc}; use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc};
use std::{net, time}; use std::{io, mem, net, time};
use futures::sync::mpsc::{unbounded, SendError, UnboundedSender}; use futures::sync::mpsc::{unbounded, SendError, UnboundedSender};
use futures::sync::oneshot; use futures::sync::oneshot;
use futures::Future; use futures::Future;
use net2::TcpStreamExt; use net2::{TcpBuilder, TcpStreamExt};
use slab::Slab;
use tokio::executor::current_thread; use tokio::executor::current_thread;
use tokio_reactor::Handle;
use tokio_tcp::TcpStream; use tokio_tcp::TcpStream;
#[cfg(any(feature = "tls", feature = "alpn", feature = "rust-tls"))]
use futures::future;
#[cfg(feature = "tls")]
use native_tls::TlsAcceptor;
#[cfg(feature = "tls")]
use tokio_tls::TlsAcceptorExt;
#[cfg(feature = "alpn")]
use openssl::ssl::SslAcceptor;
#[cfg(feature = "alpn")]
use tokio_openssl::SslAcceptorExt;
#[cfg(feature = "rust-tls")]
use rustls::{ServerConfig, Session};
#[cfg(feature = "rust-tls")]
use tokio_rustls::ServerConfigExt;
use actix::msgs::StopArbiter; use actix::msgs::StopArbiter;
use actix::{Actor, Addr, Arbiter, AsyncContext, Context, Handler, Message, Response}; use actix::{Actor, Addr, Arbiter, AsyncContext, Context, Handler, Message, Response};
use super::accept::AcceptNotify; use super::accept::AcceptNotify;
use super::channel::HttpChannel; use super::channel::HttpChannel;
use super::settings::{ServerSettings, WorkerSettings}; use super::settings::{ServerSettings, WorkerSettings};
use super::{HttpHandler, IntoHttpHandler, KeepAlive}; use super::{
AcceptorService, HttpHandler, IntoAsyncIo, IntoHttpHandler, IoStream, KeepAlive,
};
#[derive(Message)] #[derive(Message)]
pub(crate) struct Conn<T> { pub(crate) struct Conn<T> {
pub io: T, pub io: T,
pub token: usize, pub token: Token,
pub peer: Option<net::SocketAddr>, pub peer: Option<net::SocketAddr>,
pub http2: bool, pub http2: bool,
} }
#[derive(Clone)] #[derive(Clone, Copy)]
pub(crate) struct SocketInfo { pub struct Token(usize);
pub addr: net::SocketAddr,
pub htype: StreamHandlerType, impl Token {
pub(crate) fn new(val: usize) -> Token {
Token(val)
}
} }
pub(crate) struct WorkersPool<H: IntoHttpHandler + 'static> { pub(crate) struct Socket {
sockets: Slab<SocketInfo>, pub lst: net::TcpListener,
pub addr: net::SocketAddr,
pub token: Token,
}
pub(crate) struct WorkerFactory<H: IntoHttpHandler + 'static> {
pub factory: Arc<Fn() -> Vec<H> + Send + Sync>, pub factory: Arc<Fn() -> Vec<H> + Send + Sync>,
pub host: Option<String>, pub host: Option<String>,
pub keep_alive: KeepAlive, pub keep_alive: KeepAlive,
pub backlog: i32,
sockets: Vec<Socket>,
handlers: Vec<Box<IoStreamHandler<H::Handler, net::TcpStream>>>,
} }
impl<H: IntoHttpHandler + 'static> WorkersPool<H> { impl<H: IntoHttpHandler + 'static> WorkerFactory<H> {
pub fn new<F>(factory: F) -> Self pub fn new<F>(factory: F) -> Self
where where
F: Fn() -> Vec<H> + Send + Sync + 'static, F: Fn() -> Vec<H> + Send + Sync + 'static,
{ {
WorkersPool { WorkerFactory {
factory: Arc::new(factory), factory: Arc::new(factory),
host: None, host: None,
backlog: 2048,
keep_alive: KeepAlive::Os, keep_alive: KeepAlive::Os,
sockets: Slab::new(), sockets: Vec::new(),
handlers: Vec::new(),
} }
} }
pub fn insert(&mut self, addr: net::SocketAddr, htype: StreamHandlerType) -> usize { pub fn addrs(&self) -> Vec<net::SocketAddr> {
let entry = self.sockets.vacant_entry(); self.sockets.iter().map(|s| s.addr).collect()
let token = entry.key(); }
entry.insert(SocketInfo { addr, htype });
token pub fn addrs_with_scheme(&self) -> Vec<(net::SocketAddr, &str)> {
self.handlers
.iter()
.map(|s| (s.addr(), s.scheme()))
.collect()
}
pub fn take_sockets(&mut self) -> Vec<Socket> {
mem::replace(&mut self.sockets, Vec::new())
}
pub fn listen(&mut self, lst: net::TcpListener) {
let token = Token(self.handlers.len());
let addr = lst.local_addr().unwrap();
self.handlers
.push(Box::new(SimpleHandler::new(lst.local_addr().unwrap())));
self.sockets.push(Socket { lst, addr, token })
}
pub fn listen_with<A>(&mut self, lst: net::TcpListener, acceptor: A)
where
A: AcceptorService<TcpStream> + Send + 'static,
{
let token = Token(self.handlers.len());
let addr = lst.local_addr().unwrap();
self.handlers.push(Box::new(StreamHandler::new(
lst.local_addr().unwrap(),
acceptor,
)));
self.sockets.push(Socket { lst, addr, token })
}
pub fn bind<S>(&mut self, addr: S) -> io::Result<()>
where
S: net::ToSocketAddrs,
{
let sockets = self.bind2(addr)?;
for lst in sockets {
let token = Token(self.handlers.len());
let addr = lst.local_addr().unwrap();
self.handlers
.push(Box::new(SimpleHandler::new(lst.local_addr().unwrap())));
self.sockets.push(Socket { lst, addr, token })
}
Ok(())
}
pub fn bind_with<S, A>(&mut self, addr: S, acceptor: &A) -> io::Result<()>
where
S: net::ToSocketAddrs,
A: AcceptorService<TcpStream> + Send + 'static,
{
let sockets = self.bind2(addr)?;
for lst in sockets {
let token = Token(self.handlers.len());
let addr = lst.local_addr().unwrap();
self.handlers.push(Box::new(StreamHandler::new(
lst.local_addr().unwrap(),
acceptor.clone(),
)));
self.sockets.push(Socket { lst, addr, token })
}
Ok(())
}
fn bind2<S: net::ToSocketAddrs>(
&self, addr: S,
) -> io::Result<Vec<net::TcpListener>> {
let mut err = None;
let mut succ = false;
let mut sockets = Vec::new();
for addr in addr.to_socket_addrs()? {
match create_tcp_listener(addr, self.backlog) {
Ok(lst) => {
succ = true;
sockets.push(lst);
}
Err(e) => err = Some(e),
}
}
if !succ {
if let Some(e) = err.take() {
Err(e)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Can not bind to address.",
))
}
} else {
Ok(sockets)
}
} }
pub fn start( pub fn start(
&mut self, idx: usize, notify: AcceptNotify, &mut self, idx: usize, notify: AcceptNotify,
) -> (WorkerClient, Addr<Worker<H::Handler>>) { ) -> (WorkerClient, Addr<Worker>) {
let host = self.host.clone(); let host = self.host.clone();
let addr = self.sockets[0].addr; let addr = self.handlers[0].addr();
let factory = Arc::clone(&self.factory); let factory = Arc::clone(&self.factory);
let socks = self.sockets.clone();
let ka = self.keep_alive; let ka = self.keep_alive;
let (tx, rx) = unbounded::<Conn<net::TcpStream>>(); let (tx, rx) = unbounded::<Conn<net::TcpStream>>();
let client = WorkerClient::new(idx, tx, self.sockets.clone()); let client = WorkerClient::new(idx, tx);
let conn = client.conn.clone(); let conn = client.conn.clone();
let sslrate = client.sslrate.clone(); let sslrate = client.sslrate.clone();
let handlers: Vec<_> = self.handlers.iter().map(|v| v.clone()).collect();
let addr = Arbiter::start(move |ctx: &mut Context<_>| { let addr = Arbiter::start(move |ctx: &mut Context<_>| {
let s = ServerSettings::new(Some(addr), &host, false); let s = ServerSettings::new(Some(addr), &host, false);
let apps: Vec<_> = let apps: Vec<_> =
(*factory)().into_iter().map(|h| h.into_handler()).collect(); (*factory)().into_iter().map(|h| h.into_handler()).collect();
ctx.add_message_stream(rx); ctx.add_message_stream(rx);
Worker::new(apps, socks, ka, s, conn, sslrate, notify) let inner = WorkerInner::new(apps, handlers, ka, s, conn, sslrate, notify);
Worker {
inner: Box::new(inner),
}
}); });
(client, addr) (client, addr)
@ -107,19 +200,15 @@ impl<H: IntoHttpHandler + 'static> WorkersPool<H> {
pub(crate) struct WorkerClient { pub(crate) struct WorkerClient {
pub idx: usize, pub idx: usize,
tx: UnboundedSender<Conn<net::TcpStream>>, tx: UnboundedSender<Conn<net::TcpStream>>,
info: Slab<SocketInfo>,
pub conn: Arc<AtomicUsize>, pub conn: Arc<AtomicUsize>,
pub sslrate: Arc<AtomicUsize>, pub sslrate: Arc<AtomicUsize>,
} }
impl WorkerClient { impl WorkerClient {
fn new( fn new(idx: usize, tx: UnboundedSender<Conn<net::TcpStream>>) -> Self {
idx: usize, tx: UnboundedSender<Conn<net::TcpStream>>, info: Slab<SocketInfo>,
) -> Self {
WorkerClient { WorkerClient {
idx, idx,
tx, tx,
info,
conn: Arc::new(AtomicUsize::new(0)), conn: Arc::new(AtomicUsize::new(0)),
sslrate: Arc::new(AtomicUsize::new(0)), sslrate: Arc::new(AtomicUsize::new(0)),
} }
@ -154,47 +243,30 @@ impl Message for StopWorker {
/// ///
/// Worker accepts Socket objects via unbounded channel and start requests /// Worker accepts Socket objects via unbounded channel and start requests
/// processing. /// processing.
pub(crate) struct Worker<H> pub(crate) struct Worker {
where inner: Box<WorkerHandler>,
H: HttpHandler + 'static,
{
settings: Rc<WorkerSettings<H>>,
socks: Slab<SocketInfo>,
tcp_ka: Option<time::Duration>,
} }
impl<H: HttpHandler + 'static> Worker<H> { impl Actor for Worker {
pub(crate) fn new( type Context = Context<Self>;
h: Vec<H>, socks: Slab<SocketInfo>, keep_alive: KeepAlive,
settings: ServerSettings, conn: Arc<AtomicUsize>, sslrate: Arc<AtomicUsize>,
notify: AcceptNotify,
) -> Worker<H> {
let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive {
Some(time::Duration::new(val as u64, 0))
} else {
None
};
Worker { fn started(&mut self, ctx: &mut Self::Context) {
settings: Rc::new(WorkerSettings::new( self.update_date(ctx);
h, keep_alive, settings, notify, conn, sslrate,
)),
socks,
tcp_ka,
}
} }
}
fn update_time(&self, ctx: &mut Context<Self>) { impl Worker {
self.settings.update_date(); fn update_date(&self, ctx: &mut Context<Self>) {
ctx.run_later(time::Duration::new(1, 0), |slf, ctx| slf.update_time(ctx)); self.inner.update_date();
ctx.run_later(time::Duration::new(1, 0), |slf, ctx| slf.update_date(ctx));
} }
fn shutdown_timeout( fn shutdown_timeout(
&self, ctx: &mut Context<Self>, tx: oneshot::Sender<bool>, dur: time::Duration, &self, ctx: &mut Context<Worker>, tx: oneshot::Sender<bool>, dur: time::Duration,
) { ) {
// sleep for 1 second and then check again // sleep for 1 second and then check again
ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| { ctx.run_later(time::Duration::new(1, 0), move |slf, ctx| {
let num = slf.settings.num_channels(); let num = slf.inner.num_channels();
if num == 0 { if num == 0 {
let _ = tx.send(true); let _ = tx.send(true);
Arbiter::current().do_send(StopArbiter(0)); Arbiter::current().do_send(StopArbiter(0));
@ -202,7 +274,7 @@ impl<H: HttpHandler + 'static> Worker<H> {
slf.shutdown_timeout(ctx, tx, d); slf.shutdown_timeout(ctx, tx, d);
} else { } else {
info!("Force shutdown http worker, {} connections", num); info!("Force shutdown http worker, {} connections", num);
slf.settings.head().traverse::<TcpStream, H>(); slf.inner.force_shutdown();
let _ = tx.send(false); let _ = tx.send(false);
Arbiter::current().do_send(StopArbiter(0)); Arbiter::current().do_send(StopArbiter(0));
} }
@ -210,44 +282,20 @@ impl<H: HttpHandler + 'static> Worker<H> {
} }
} }
impl<H: 'static> Actor for Worker<H> impl Handler<Conn<net::TcpStream>> for Worker {
where
H: HttpHandler + 'static,
{
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
self.update_time(ctx);
}
}
impl<H> Handler<Conn<net::TcpStream>> for Worker<H>
where
H: HttpHandler + 'static,
{
type Result = (); type Result = ();
fn handle(&mut self, msg: Conn<net::TcpStream>, _: &mut Context<Self>) { fn handle(&mut self, msg: Conn<net::TcpStream>, _: &mut Context<Self>) {
if self.tcp_ka.is_some() && msg.io.set_keepalive(self.tcp_ka).is_err() { self.inner.handle_connect(msg)
error!("Can not set socket keep-alive option");
}
self.socks
.get_mut(msg.token)
.unwrap()
.htype
.handle(Rc::clone(&self.settings), msg);
} }
} }
/// `StopWorker` message handler /// `StopWorker` message handler
impl<H> Handler<StopWorker> for Worker<H> impl Handler<StopWorker> for Worker {
where
H: HttpHandler + 'static,
{
type Result = Response<bool, ()>; type Result = Response<bool, ()>;
fn handle(&mut self, msg: StopWorker, ctx: &mut Context<Self>) -> Self::Result { fn handle(&mut self, msg: StopWorker, ctx: &mut Context<Self>) -> Self::Result {
let num = self.settings.num_channels(); let num = self.inner.num_channels();
if num == 0 { if num == 0 {
info!("Shutting down http worker, 0 connections"); info!("Shutting down http worker, 0 connections");
Response::reply(Ok(true)) Response::reply(Ok(true))
@ -258,148 +306,242 @@ where
Response::async(rx.map_err(|_| ())) Response::async(rx.map_err(|_| ()))
} else { } else {
info!("Force shutdown http worker, {} connections", num); info!("Force shutdown http worker, {} connections", num);
self.settings.head().traverse::<TcpStream, H>(); self.inner.force_shutdown();
Response::reply(Ok(false)) Response::reply(Ok(false))
} }
} }
} }
#[derive(Clone)] trait WorkerHandler {
pub(crate) enum StreamHandlerType { fn update_date(&self);
Normal,
#[cfg(feature = "tls")] fn handle_connect(&mut self, Conn<net::TcpStream>);
Tls(TlsAcceptor),
#[cfg(feature = "alpn")] fn force_shutdown(&self);
Alpn(SslAcceptor),
#[cfg(feature = "rust-tls")] fn num_channels(&self) -> usize;
Rustls(Arc<ServerConfig>),
} }
impl StreamHandlerType { struct WorkerInner<H>
pub fn is_ssl(&self) -> bool { where
match *self { H: HttpHandler + 'static,
StreamHandlerType::Normal => false, {
#[cfg(feature = "tls")] settings: Rc<WorkerSettings<H>>,
StreamHandlerType::Tls(_) => true, socks: Vec<Box<IoStreamHandler<H, net::TcpStream>>>,
#[cfg(feature = "alpn")] tcp_ka: Option<time::Duration>,
StreamHandlerType::Alpn(_) => true, }
#[cfg(feature = "rust-tls")]
StreamHandlerType::Rustls(_) => true,
}
}
fn handle<H: HttpHandler>( impl<H: HttpHandler + 'static> WorkerInner<H> {
&mut self, h: Rc<WorkerSettings<H>>, msg: Conn<net::TcpStream>, pub(crate) fn new(
) { h: Vec<H>, socks: Vec<Box<IoStreamHandler<H, net::TcpStream>>>,
match *self { keep_alive: KeepAlive, settings: ServerSettings, conn: Arc<AtomicUsize>,
StreamHandlerType::Normal => { sslrate: Arc<AtomicUsize>, notify: AcceptNotify,
let _ = msg.io.set_nodelay(true); ) -> WorkerInner<H> {
let io = TcpStream::from_std(msg.io, &Handle::default()) let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive {
.expect("failed to associate TCP stream"); Some(time::Duration::new(val as u64, 0))
} else {
None
};
current_thread::spawn(HttpChannel::new(h, io, msg.peer, msg.http2)); WorkerInner {
} settings: Rc::new(WorkerSettings::new(
#[cfg(feature = "tls")] h, keep_alive, settings, notify, conn, sslrate,
StreamHandlerType::Tls(ref acceptor) => { )),
let Conn { socks,
io, peer, http2, .. tcp_ka,
} = msg;
let _ = io.set_nodelay(true);
let io = TcpStream::from_std(io, &Handle::default())
.expect("failed to associate TCP stream");
h.ssl_conn_add();
current_thread::spawn(TlsAcceptorExt::accept_async(acceptor, io).then(
move |res| {
h.ssl_conn_del();
match res {
Ok(io) => current_thread::spawn(HttpChannel::new(
h, io, peer, http2,
)),
Err(err) => {
trace!("Error during handling tls connection: {}", err)
}
};
future::result(Ok(()))
},
));
}
#[cfg(feature = "alpn")]
StreamHandlerType::Alpn(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");
h.ssl_conn_add();
current_thread::spawn(SslAcceptorExt::accept_async(acceptor, io).then(
move |res| {
h.ssl_conn_del();
match res {
Ok(io) => {
let http2 = if let Some(p) =
io.get_ref().ssl().selected_alpn_protocol()
{
p.len() == 2 && &p == b"h2"
} else {
false
};
current_thread::spawn(HttpChannel::new(
h, io, peer, http2,
));
}
Err(err) => {
trace!("Error during handling tls connection: {}", err)
}
};
future::result(Ok(()))
},
));
}
#[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");
h.ssl_conn_add();
current_thread::spawn(ServerConfigExt::accept_async(acceptor, io).then(
move |res| {
h.ssl_conn_del();
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(()))
},
));
}
}
}
pub(crate) fn scheme(&self) -> &'static str {
match *self {
StreamHandlerType::Normal => "http",
#[cfg(feature = "tls")]
StreamHandlerType::Tls(_) => "https",
#[cfg(feature = "alpn")]
StreamHandlerType::Alpn(_) => "https",
#[cfg(feature = "rust-tls")]
StreamHandlerType::Rustls(_) => "https",
} }
} }
} }
impl<H> WorkerHandler for WorkerInner<H>
where
H: HttpHandler + 'static,
{
fn update_date(&self) {
self.settings.update_date();
}
fn handle_connect(&mut self, msg: Conn<net::TcpStream>) {
if self.tcp_ka.is_some() && msg.io.set_keepalive(self.tcp_ka).is_err() {
error!("Can not set socket keep-alive option");
}
self.socks[msg.token.0].handle(Rc::clone(&self.settings), msg.io, msg.peer);
}
fn num_channels(&self) -> usize {
self.settings.num_channels()
}
fn force_shutdown(&self) {
self.settings.head().traverse::<TcpStream, H>();
}
}
struct SimpleHandler<Io> {
addr: net::SocketAddr,
io: PhantomData<Io>,
}
impl<Io: IntoAsyncIo> Clone for SimpleHandler<Io> {
fn clone(&self) -> Self {
SimpleHandler {
addr: self.addr,
io: PhantomData,
}
}
}
impl<Io: IntoAsyncIo> SimpleHandler<Io> {
fn new(addr: net::SocketAddr) -> Self {
SimpleHandler {
addr,
io: PhantomData,
}
}
}
impl<H, Io> IoStreamHandler<H, Io> for SimpleHandler<Io>
where
H: HttpHandler,
Io: IntoAsyncIo + Send + 'static,
Io::Io: IoStream,
{
fn addr(&self) -> net::SocketAddr {
self.addr
}
fn clone(&self) -> Box<IoStreamHandler<H, Io>> {
Box::new(Clone::clone(self))
}
fn scheme(&self) -> &'static str {
"http"
}
fn handle(&self, h: Rc<WorkerSettings<H>>, io: Io, peer: Option<net::SocketAddr>) {
let mut io = match io.into_async_io() {
Ok(io) => io,
Err(err) => {
trace!("Failed to create async io: {}", err);
return;
}
};
let _ = io.set_nodelay(true);
current_thread::spawn(HttpChannel::new(h, io, peer, false));
}
}
struct StreamHandler<A, Io> {
acceptor: A,
addr: net::SocketAddr,
io: PhantomData<Io>,
}
impl<Io: IntoAsyncIo, A: AcceptorService<Io::Io>> StreamHandler<A, Io> {
fn new(addr: net::SocketAddr, acceptor: A) -> Self {
StreamHandler {
addr,
acceptor,
io: PhantomData,
}
}
}
impl<Io: IntoAsyncIo, A: AcceptorService<Io::Io>> Clone for StreamHandler<A, Io> {
fn clone(&self) -> Self {
StreamHandler {
addr: self.addr,
acceptor: self.acceptor.clone(),
io: PhantomData,
}
}
}
impl<H, Io, A> IoStreamHandler<H, Io> for StreamHandler<A, Io>
where
H: HttpHandler,
Io: IntoAsyncIo + Send + 'static,
Io::Io: IoStream,
A: AcceptorService<Io::Io> + Send + 'static,
{
fn addr(&self) -> net::SocketAddr {
self.addr
}
fn clone(&self) -> Box<IoStreamHandler<H, Io>> {
Box::new(Clone::clone(self))
}
fn scheme(&self) -> &'static str {
self.acceptor.scheme()
}
fn handle(&self, h: Rc<WorkerSettings<H>>, io: Io, peer: Option<net::SocketAddr>) {
let mut io = match io.into_async_io() {
Ok(io) => io,
Err(err) => {
trace!("Failed to create async io: {}", err);
return;
}
};
let _ = io.set_nodelay(true);
h.conn_rate_add();
current_thread::spawn(self.acceptor.accept(io).then(move |res| {
h.conn_rate_del();
match res {
Ok(io) => current_thread::spawn(HttpChannel::new(h, io, peer, false)),
Err(err) => trace!("Can not establish connection: {}", err),
}
Ok(())
}))
}
}
impl<H, Io: 'static> IoStreamHandler<H, Io> for Box<IoStreamHandler<H, Io>>
where
H: HttpHandler,
Io: IntoAsyncIo,
{
fn addr(&self) -> net::SocketAddr {
self.as_ref().addr()
}
fn clone(&self) -> Box<IoStreamHandler<H, Io>> {
self.as_ref().clone()
}
fn scheme(&self) -> &'static str {
self.as_ref().scheme()
}
fn handle(&self, h: Rc<WorkerSettings<H>>, io: Io, peer: Option<net::SocketAddr>) {
self.as_ref().handle(h, io, peer)
}
}
pub(crate) trait IoStreamHandler<H, Io>: Send
where
H: HttpHandler,
{
fn clone(&self) -> Box<IoStreamHandler<H, Io>>;
fn addr(&self) -> net::SocketAddr;
fn scheme(&self) -> &'static str;
fn handle(&self, h: Rc<WorkerSettings<H>>, io: Io, peer: Option<net::SocketAddr>);
}
fn create_tcp_listener(
addr: net::SocketAddr, backlog: i32,
) -> io::Result<net::TcpListener> {
let builder = match addr {
net::SocketAddr::V4(_) => TcpBuilder::new_v4()?,
net::SocketAddr::V6(_) => TcpBuilder::new_v6()?,
};
builder.reuse_address(true)?;
builder.bind(addr)?;
Ok(builder.listen(backlog)?)
}