mirror of
https://github.com/fafhrd91/actix-net
synced 2025-06-26 19:47:43 +02:00
Migrate actix-net to std::future (#64)
* Migrate actix-codec, actix-rt, and actix-threadpool to std::future * update to latest tokio alpha and futures-rs * Migrate actix-service to std::future, This is a squash of ~8 commits, since it included a lot of experimentation. To see the commits, look into the semtexzv/std-future-service-tmp branch. * update futures-rs and tokio * Migrate actix-threadpool to std::future (#59) * Migrate actix-threadpool to std::future * Cosmetic refactor - turn log::error! into log::warn! as it doesn't throw any error - add Clone and Copy impls for Cancelled making it cheap to operate with - apply rustfmt * Bump up crate version to 0.2.0 and pre-fill its changelog * Disable patching 'actix-threadpool' crate in global workspace as unnecessary * Revert patching and fix 'actix-rt' * Migrate actix-rt to std::future (#47) * remove Pin from Service::poll_ready(); simplify combinators api; make code compile * disable tests * update travis config * refactor naming * drop IntoFuture trait * Migrate actix-server to std::future (#50) Still not finished, this is more WIP, this is an aggregation of several commits, which can be found in semtexzv/std-future-server-tmp branch * update actix-server * rename Factor to ServiceFactory * start server worker in start mehtod * update actix-utils * remove IntoTransform trait * Migrate actix-server::ssl::nativetls to std futures (#61) * Refactor 'nativetls' module * Migrate 'actix-server-config' to std futures - remove "uds" feature - disable features by default * Switch NativeTlsAcceptor to use 'tokio-tls' crate * Bikeshed features names and remove unnecessary dependencies for 'actix-server-config' crate * update openssl impl * migrate actix-connect to std::future * migrate actix-ioframe to std::future * update version to alpha.1 * fix boxed service * migrate server rustls support * migratte openssl and rustls connecttors * store the thread's handle with arbiter (#62) * update ssl connect tests * restore service tests * update readme
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
* Update `rustls` to 0.16
|
||||
* Minimum required Rust version upped to 1.37.0
|
||||
|
||||
|
||||
## [0.6.1] - 2019-09-25
|
||||
|
||||
### Added
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-server"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0-alpha.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix server - General purpose tcp server"
|
||||
keywords = ["network", "framework", "async", "futures"]
|
||||
@ -14,7 +14,7 @@ edition = "2018"
|
||||
workspace = ".."
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["ssl", "tls", "rust-tls", "uds"]
|
||||
features = ["nativetls", "openssl", "rustls", "uds"]
|
||||
|
||||
[lib]
|
||||
name = "actix_server"
|
||||
@ -22,55 +22,47 @@ path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
# tls
|
||||
tls = ["native-tls"]
|
||||
|
||||
# openssl
|
||||
ssl = ["openssl", "tokio-openssl", "actix-server-config/ssl"]
|
||||
|
||||
# rustls
|
||||
rust-tls = ["rustls", "tokio-rustls", "webpki", "webpki-roots", "actix-server-config/rust-tls"]
|
||||
|
||||
# uds
|
||||
uds = ["mio-uds", "tokio-uds", "actix-server-config/uds"]
|
||||
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-rt = "0.2.2"
|
||||
actix-service = "0.4.1"
|
||||
actix-server-config = "0.2.0"
|
||||
actix-rt = "1.0.0-alpha.1"
|
||||
actix-service = "1.0.0-alpha.1"
|
||||
actix-server-config = "0.3.0-alpha.1"
|
||||
|
||||
log = "0.4"
|
||||
num_cpus = "1.0"
|
||||
|
||||
pin-project = "0.4.5"
|
||||
mio = "0.6.19"
|
||||
net2 = "0.2"
|
||||
futures = "0.1"
|
||||
futures = "0.3.1"
|
||||
slab = "0.4"
|
||||
tokio-io = "0.1"
|
||||
tokio-tcp = "0.1"
|
||||
tokio-timer = "0.2.8"
|
||||
tokio-reactor = "0.1"
|
||||
tokio-signal = "0.2"
|
||||
|
||||
tokio = "0.2.0-alpha.6"
|
||||
tokio-io = "0.2.0-alpha.6"
|
||||
tokio-net = { version = "0.2.0-alpha.6", features = ["signal"] }
|
||||
tokio-timer = "0.3.0-alpha.6"
|
||||
|
||||
# unix domain sockets
|
||||
mio-uds = { version="0.6.7", optional = true }
|
||||
tokio-uds = { version="0.2.5", optional = true }
|
||||
mio-uds = { version = "0.6.7", optional = true }
|
||||
|
||||
# native-tls
|
||||
native-tls = { version="0.2", optional = true }
|
||||
# nativetls
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
tokio-tls = { version = "0.3.0-alpha.6", optional = true }
|
||||
|
||||
# openssl
|
||||
openssl = { version="0.10", optional = true }
|
||||
tokio-openssl = { version="0.3", optional = true }
|
||||
open-ssl = { version = "0.10", package = "openssl", optional = true }
|
||||
tokio-openssl = { version = "0.4.0-alpha.6", optional = true }
|
||||
|
||||
# rustls
|
||||
rustls = { version = "0.16.0", optional = true }
|
||||
tokio-rustls = { version = "0.10.0", optional = true }
|
||||
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.1.2"
|
||||
actix-codec = "0.2.0-alpha.1"
|
||||
env_logger = "0.6"
|
||||
|
@ -3,10 +3,10 @@ use std::time::{Duration, Instant};
|
||||
use std::{io, thread};
|
||||
|
||||
use actix_rt::System;
|
||||
use futures::future::{lazy, Future};
|
||||
use futures::FutureExt;
|
||||
use log::{error, info};
|
||||
use slab::Slab;
|
||||
use tokio_timer::Delay;
|
||||
use tokio_timer::delay;
|
||||
|
||||
use crate::server::Server;
|
||||
use crate::socket::{SocketAddr, SocketListener, StdListener};
|
||||
@ -370,7 +370,7 @@ impl Accept {
|
||||
match self.workers[self.next].send(msg) {
|
||||
Ok(_) => (),
|
||||
Err(tmp) => {
|
||||
self.srv.worker_died(self.workers[self.next].idx);
|
||||
self.srv.worker_faulted(self.workers[self.next].idx);
|
||||
msg = tmp;
|
||||
self.workers.swap_remove(self.next);
|
||||
if self.workers.is_empty() {
|
||||
@ -396,7 +396,7 @@ impl Accept {
|
||||
return;
|
||||
}
|
||||
Err(tmp) => {
|
||||
self.srv.worker_died(self.workers[self.next].idx);
|
||||
self.srv.worker_faulted(self.workers[self.next].idx);
|
||||
msg = tmp;
|
||||
self.workers.swap_remove(self.next);
|
||||
if self.workers.is_empty() {
|
||||
@ -440,14 +440,13 @@ impl Accept {
|
||||
info.timeout = Some(Instant::now() + Duration::from_millis(500));
|
||||
|
||||
let r = self.timer.1.clone();
|
||||
System::current().arbiter().send(lazy(move || {
|
||||
Delay::new(Instant::now() + Duration::from_millis(510))
|
||||
.map_err(|_| ())
|
||||
.and_then(move |_| {
|
||||
let _ = r.set_readiness(mio::Ready::readable());
|
||||
Ok(())
|
||||
})
|
||||
}));
|
||||
System::current().arbiter().send(
|
||||
async move {
|
||||
delay(Instant::now() + Duration::from_millis(510)).await;
|
||||
let _ = r.set_readiness(mio::Ready::readable());
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,24 @@
|
||||
use std::time::Duration;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{io, mem, net};
|
||||
|
||||
use actix_rt::{spawn, Arbiter, System};
|
||||
use futures::future::{lazy, ok};
|
||||
use futures::stream::futures_unordered;
|
||||
use futures::sync::mpsc::{unbounded, UnboundedReceiver};
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::channel::mpsc::{unbounded, UnboundedReceiver};
|
||||
use futures::future::ready;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::{ready, Future, FutureExt, Stream, StreamExt};
|
||||
use log::{error, info};
|
||||
use net2::TcpBuilder;
|
||||
use num_cpus;
|
||||
use tokio_tcp::TcpStream;
|
||||
use tokio_timer::sleep;
|
||||
use tokio_net::tcp::TcpStream;
|
||||
use tokio_timer::delay;
|
||||
|
||||
use crate::accept::{AcceptLoop, AcceptNotify, Command};
|
||||
use crate::config::{ConfiguredService, ServiceConfig};
|
||||
use crate::server::{Server, ServerCommand};
|
||||
use crate::services::{InternalServiceFactory, ServiceFactory, StreamNewService};
|
||||
use crate::signals::{Signal, Signals};
|
||||
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};
|
||||
@ -301,7 +303,7 @@ impl ServerBuilder {
|
||||
|
||||
// handle signals
|
||||
if !self.no_signals {
|
||||
Signals::start(self.server.clone());
|
||||
// Signals::start(self.server.clone());
|
||||
}
|
||||
|
||||
// start http server actor
|
||||
@ -320,10 +322,12 @@ impl ServerBuilder {
|
||||
let services: Vec<Box<dyn InternalServiceFactory>> =
|
||||
self.services.iter().map(|v| v.clone_factory()).collect();
|
||||
|
||||
Arbiter::new().send(lazy(move || {
|
||||
Worker::start(rx1, rx2, services, avail, timeout);
|
||||
Ok::<_, ()>(())
|
||||
}));
|
||||
Arbiter::new().send(
|
||||
async move {
|
||||
Worker::start(rx1, rx2, services, avail, timeout);
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
|
||||
worker
|
||||
}
|
||||
@ -338,37 +342,37 @@ impl ServerBuilder {
|
||||
self.accept.send(Command::Resume);
|
||||
let _ = tx.send(());
|
||||
}
|
||||
ServerCommand::Signal(sig) => {
|
||||
// Signals support
|
||||
// Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system
|
||||
match sig {
|
||||
Signal::Int => {
|
||||
info!("SIGINT received, exiting");
|
||||
self.exit = true;
|
||||
self.handle_cmd(ServerCommand::Stop {
|
||||
graceful: false,
|
||||
completion: None,
|
||||
})
|
||||
}
|
||||
Signal::Term => {
|
||||
info!("SIGTERM received, stopping");
|
||||
self.exit = true;
|
||||
self.handle_cmd(ServerCommand::Stop {
|
||||
graceful: true,
|
||||
completion: None,
|
||||
})
|
||||
}
|
||||
Signal::Quit => {
|
||||
info!("SIGQUIT received, exiting");
|
||||
self.exit = true;
|
||||
self.handle_cmd(ServerCommand::Stop {
|
||||
graceful: false,
|
||||
completion: None,
|
||||
})
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// ServerCommand::Signal(sig) => {
|
||||
// Signals support
|
||||
// Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system
|
||||
// match sig {
|
||||
// Signal::Int => {
|
||||
// info!("SIGINT received, exiting");
|
||||
// self.exit = true;
|
||||
// self.handle_cmd(ServerCommand::Stop {
|
||||
// graceful: false,
|
||||
// completion: None,
|
||||
// })
|
||||
// }
|
||||
// Signal::Term => {
|
||||
// info!("SIGTERM received, stopping");
|
||||
// self.exit = true;
|
||||
// self.handle_cmd(ServerCommand::Stop {
|
||||
// graceful: true,
|
||||
// completion: None,
|
||||
// })
|
||||
// }
|
||||
// Signal::Quit => {
|
||||
// info!("SIGQUIT received, exiting");
|
||||
// self.exit = true;
|
||||
// self.handle_cmd(ServerCommand::Stop {
|
||||
// graceful: false,
|
||||
// completion: None,
|
||||
// })
|
||||
// }
|
||||
// _ => (),
|
||||
// }
|
||||
// }
|
||||
ServerCommand::Stop {
|
||||
graceful,
|
||||
completion,
|
||||
@ -381,39 +385,44 @@ impl ServerBuilder {
|
||||
// stop workers
|
||||
if !self.workers.is_empty() && graceful {
|
||||
spawn(
|
||||
futures_unordered(
|
||||
self.workers
|
||||
.iter()
|
||||
.map(move |worker| worker.1.stop(graceful)),
|
||||
)
|
||||
.collect()
|
||||
.then(move |_| {
|
||||
if let Some(tx) = completion {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
if exit {
|
||||
spawn(sleep(Duration::from_millis(300)).then(|_| {
|
||||
System::current().stop();
|
||||
ok(())
|
||||
}));
|
||||
}
|
||||
ok(())
|
||||
}),
|
||||
self.workers
|
||||
.iter()
|
||||
.map(move |worker| worker.1.stop(graceful))
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.collect::<Vec<_>>()
|
||||
.then(move |_| {
|
||||
if let Some(tx) = completion {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
if exit {
|
||||
spawn(
|
||||
async {
|
||||
delay(Instant::now() + Duration::from_millis(300))
|
||||
.await;
|
||||
System::current().stop();
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
ready(())
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
// we need to stop system if server was spawned
|
||||
if self.exit {
|
||||
spawn(sleep(Duration::from_millis(300)).then(|_| {
|
||||
System::current().stop();
|
||||
ok(())
|
||||
}));
|
||||
spawn(
|
||||
delay(Instant::now() + Duration::from_millis(300)).then(|_| {
|
||||
System::current().stop();
|
||||
ready(())
|
||||
}),
|
||||
);
|
||||
}
|
||||
if let Some(tx) = completion {
|
||||
let _ = tx.send(());
|
||||
}
|
||||
}
|
||||
}
|
||||
ServerCommand::WorkerDied(idx) => {
|
||||
ServerCommand::WorkerFaulted(idx) => {
|
||||
let mut found = false;
|
||||
for i in 0..self.workers.len() {
|
||||
if self.workers[i].0 == idx {
|
||||
@ -447,15 +456,15 @@ impl ServerBuilder {
|
||||
}
|
||||
|
||||
impl Future for ServerBuilder {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
type Output = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
loop {
|
||||
match self.cmd.poll() {
|
||||
Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())),
|
||||
Ok(Async::NotReady) => return Ok(Async::NotReady),
|
||||
Ok(Async::Ready(Some(item))) => self.handle_cmd(item),
|
||||
match ready!(Pin::new(&mut self.cmd).poll_next(cx)) {
|
||||
Some(it) => self.as_mut().get_mut().handle_cmd(it),
|
||||
None => {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,17 @@ use std::collections::HashMap;
|
||||
use std::{fmt, io, net};
|
||||
|
||||
use actix_server_config::{Io, ServerConfig};
|
||||
use actix_service::{IntoNewService, NewService};
|
||||
use futures::future::{join_all, Future};
|
||||
use actix_service as actix;
|
||||
use futures::future::{Future, FutureExt, LocalBoxFuture};
|
||||
use log::error;
|
||||
use tokio_tcp::TcpStream;
|
||||
|
||||
use crate::counter::CounterGuard;
|
||||
use tokio_net::tcp::TcpStream;
|
||||
|
||||
use super::builder::bind_addr;
|
||||
use super::services::{
|
||||
use super::service::{
|
||||
BoxedServerService, InternalServiceFactory, ServerMessage, StreamService,
|
||||
};
|
||||
use super::Token;
|
||||
use crate::counter::CounterGuard;
|
||||
|
||||
pub struct ServiceConfig {
|
||||
pub(crate) services: Vec<(String, net::TcpListener)>,
|
||||
@ -108,50 +107,39 @@ impl InternalServiceFactory for ConfiguredService {
|
||||
})
|
||||
}
|
||||
|
||||
fn create(&self) -> Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>> {
|
||||
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>> {
|
||||
// configure services
|
||||
let mut rt = ServiceRuntime::new(self.services.clone());
|
||||
self.rt.configure(&mut rt);
|
||||
rt.validate();
|
||||
|
||||
let services = rt.services;
|
||||
let names = self.names.clone();
|
||||
|
||||
// on start futures
|
||||
if rt.onstart.is_empty() {
|
||||
// construct services
|
||||
let mut fut = Vec::new();
|
||||
for (token, ns) in services {
|
||||
let config = ServerConfig::new(self.names[&token].1);
|
||||
fut.push(ns.new_service(&config).map(move |service| (token, service)));
|
||||
// construct services
|
||||
async move {
|
||||
let services = rt.services;
|
||||
// TODO: Proper error handling here
|
||||
for f in rt.onstart.into_iter() {
|
||||
f.await;
|
||||
}
|
||||
let mut res = vec![];
|
||||
for (token, ns) in services.into_iter() {
|
||||
let config = ServerConfig::new(names[&token].1);
|
||||
|
||||
Box::new(join_all(fut).map_err(|e| {
|
||||
error!("Can not construct service: {:?}", e);
|
||||
}))
|
||||
} else {
|
||||
let names = self.names.clone();
|
||||
|
||||
// run onstart future and then construct services
|
||||
Box::new(
|
||||
join_all(rt.onstart)
|
||||
.map_err(|e| {
|
||||
error!("Can not construct service: {:?}", e);
|
||||
})
|
||||
.and_then(move |_| {
|
||||
// construct services
|
||||
let mut fut = Vec::new();
|
||||
for (token, ns) in services {
|
||||
let config = ServerConfig::new(names[&token].1);
|
||||
fut.push(
|
||||
ns.new_service(&config).map(move |service| (token, service)),
|
||||
);
|
||||
}
|
||||
join_all(fut).map_err(|e| {
|
||||
error!("Can not construct service: {:?}", e);
|
||||
})
|
||||
}),
|
||||
)
|
||||
let newserv = ns.new_service(&config);
|
||||
match newserv.await {
|
||||
Ok(serv) => {
|
||||
res.push((token, serv));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Can not construct service {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
return Ok(res);
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +169,7 @@ fn not_configured(_: &mut ServiceRuntime) {
|
||||
pub struct ServiceRuntime {
|
||||
names: HashMap<String, Token>,
|
||||
services: HashMap<Token, BoxedNewService>,
|
||||
onstart: Vec<Box<dyn Future<Item = (), Error = ()>>>,
|
||||
onstart: Vec<LocalBoxFuture<'static, ()>>,
|
||||
}
|
||||
|
||||
impl ServiceRuntime {
|
||||
@ -207,8 +195,8 @@ impl ServiceRuntime {
|
||||
/// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods.
|
||||
pub fn service<T, F>(&mut self, name: &str, service: F)
|
||||
where
|
||||
F: IntoNewService<T>,
|
||||
T: NewService<Config = ServerConfig, Request = Io<TcpStream>> + 'static,
|
||||
F: actix::IntoServiceFactory<T>,
|
||||
T: actix::ServiceFactory<Config = ServerConfig, Request = Io<TcpStream>> + 'static,
|
||||
T::Future: 'static,
|
||||
T::Service: 'static,
|
||||
T::InitError: fmt::Debug,
|
||||
@ -218,7 +206,7 @@ impl ServiceRuntime {
|
||||
self.services.insert(
|
||||
token.clone(),
|
||||
Box::new(ServiceFactory {
|
||||
inner: service.into_new_service(),
|
||||
inner: service.into_factory(),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
@ -229,21 +217,21 @@ impl ServiceRuntime {
|
||||
/// Execute future before services initialization.
|
||||
pub fn on_start<F>(&mut self, fut: F)
|
||||
where
|
||||
F: Future<Item = (), Error = ()> + 'static,
|
||||
F: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.onstart.push(Box::new(fut))
|
||||
self.onstart.push(fut.boxed_local())
|
||||
}
|
||||
}
|
||||
|
||||
type BoxedNewService = Box<
|
||||
dyn NewService<
|
||||
dyn actix::ServiceFactory<
|
||||
Request = (Option<CounterGuard>, ServerMessage),
|
||||
Response = (),
|
||||
Error = (),
|
||||
InitError = (),
|
||||
Config = ServerConfig,
|
||||
Service = BoxedServerService,
|
||||
Future = Box<dyn Future<Item = BoxedServerService, Error = ()>>,
|
||||
Future = LocalBoxFuture<'static, Result<BoxedServerService, ()>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
@ -251,9 +239,9 @@ struct ServiceFactory<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> NewService for ServiceFactory<T>
|
||||
impl<T> actix::ServiceFactory for ServiceFactory<T>
|
||||
where
|
||||
T: NewService<Config = ServerConfig, Request = Io<TcpStream>>,
|
||||
T: actix::ServiceFactory<Config = ServerConfig, Request = Io<TcpStream>>,
|
||||
T::Future: 'static,
|
||||
T::Service: 'static,
|
||||
T::Error: 'static,
|
||||
@ -265,12 +253,19 @@ where
|
||||
type InitError = ();
|
||||
type Config = ServerConfig;
|
||||
type Service = BoxedServerService;
|
||||
type Future = Box<dyn Future<Item = BoxedServerService, Error = ()>>;
|
||||
type Future = LocalBoxFuture<'static, Result<BoxedServerService, ()>>;
|
||||
|
||||
fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
|
||||
Box::new(self.inner.new_service(cfg).map_err(|_| ()).map(|s| {
|
||||
let service: BoxedServerService = Box::new(StreamService::new(s));
|
||||
service
|
||||
}))
|
||||
let fut = self.inner.new_service(cfg);
|
||||
async move {
|
||||
return match fut.await {
|
||||
Ok(s) => Ok(Box::new(StreamService::new(s)) as BoxedServerService),
|
||||
Err(e) => {
|
||||
error!("Can not construct service: {:?}", e);
|
||||
Err(())
|
||||
}
|
||||
};
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::task::AtomicTask;
|
||||
use futures::task::AtomicWaker;
|
||||
use std::task;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Simple counter with ability to notify task on reaching specific number
|
||||
@ -13,7 +14,7 @@ pub struct Counter(Rc<CounterInner>);
|
||||
struct CounterInner {
|
||||
count: Cell<usize>,
|
||||
capacity: usize,
|
||||
task: AtomicTask,
|
||||
task: AtomicWaker,
|
||||
}
|
||||
|
||||
impl Counter {
|
||||
@ -22,7 +23,7 @@ impl Counter {
|
||||
Counter(Rc::new(CounterInner {
|
||||
capacity,
|
||||
count: Cell::new(0),
|
||||
task: AtomicTask::new(),
|
||||
task: AtomicWaker::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
@ -31,8 +32,8 @@ impl Counter {
|
||||
}
|
||||
|
||||
/// Check if counter is not at capacity
|
||||
pub fn available(&self) -> bool {
|
||||
self.0.available()
|
||||
pub fn available(&self, cx: &mut task::Context) -> bool {
|
||||
self.0.available(cx)
|
||||
}
|
||||
|
||||
/// Get total number of acquired counts
|
||||
@ -66,14 +67,14 @@ impl CounterInner {
|
||||
let num = self.count.get();
|
||||
self.count.set(num - 1);
|
||||
if num == self.capacity {
|
||||
self.task.notify();
|
||||
self.task.wake();
|
||||
}
|
||||
}
|
||||
|
||||
fn available(&self) -> bool {
|
||||
fn available(&self, cx: &mut task::Context) -> bool {
|
||||
let avail = self.count.get() < self.capacity;
|
||||
if !avail {
|
||||
self.task.register();
|
||||
self.task.register(cx.waker());
|
||||
}
|
||||
avail
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ mod builder;
|
||||
mod config;
|
||||
mod counter;
|
||||
mod server;
|
||||
mod services;
|
||||
mod signals;
|
||||
mod service;
|
||||
// mod signals;
|
||||
mod socket;
|
||||
pub mod ssl;
|
||||
mod worker;
|
||||
@ -16,14 +16,11 @@ 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;
|
||||
pub use self::services::ServiceFactory;
|
||||
pub use self::service::ServiceFactory;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use self::socket::FromStream;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use self::services::ServiceFactory as StreamServiceFactory;
|
||||
|
||||
/// Socket id token
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct Token(usize);
|
||||
|
@ -1,16 +1,16 @@
|
||||
use futures::sync::mpsc::UnboundedSender;
|
||||
use futures::sync::oneshot;
|
||||
use futures::Future;
|
||||
use futures::channel::mpsc::UnboundedSender;
|
||||
use futures::channel::oneshot;
|
||||
use futures::{Future, TryFutureExt};
|
||||
|
||||
use crate::builder::ServerBuilder;
|
||||
use crate::signals::Signal;
|
||||
// use crate::signals::Signal;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ServerCommand {
|
||||
WorkerDied(usize),
|
||||
WorkerFaulted(usize),
|
||||
Pause(oneshot::Sender<()>),
|
||||
Resume(oneshot::Sender<()>),
|
||||
Signal(Signal),
|
||||
// Signal(Signal),
|
||||
/// Whether to try and shut down gracefully
|
||||
Stop {
|
||||
graceful: bool,
|
||||
@ -31,26 +31,26 @@ impl Server {
|
||||
ServerBuilder::default()
|
||||
}
|
||||
|
||||
pub(crate) fn signal(&self, sig: Signal) {
|
||||
let _ = self.0.unbounded_send(ServerCommand::Signal(sig));
|
||||
}
|
||||
// pub(crate) fn signal(&self, sig: Signal) {
|
||||
// let _ = self.0.unbounded_send(ServerCommand::Signal(sig));
|
||||
// }
|
||||
|
||||
pub(crate) fn worker_died(&self, idx: usize) {
|
||||
let _ = self.0.unbounded_send(ServerCommand::WorkerDied(idx));
|
||||
pub(crate) fn worker_faulted(&self, idx: usize) {
|
||||
let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx));
|
||||
}
|
||||
|
||||
/// Pause accepting incoming connections
|
||||
///
|
||||
/// If socket contains some pending connection, they might be dropped.
|
||||
/// All opened connection remains active.
|
||||
pub fn pause(&self) -> impl Future<Item = (), Error = ()> {
|
||||
pub fn pause(&self) -> impl Future<Output = Result<(), ()>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.0.unbounded_send(ServerCommand::Pause(tx));
|
||||
rx.map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Resume accepting incoming connections
|
||||
pub fn resume(&self) -> impl Future<Item = (), Error = ()> {
|
||||
pub fn resume(&self) -> impl Future<Output = Result<(), ()>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.0.unbounded_send(ServerCommand::Resume(tx));
|
||||
rx.map_err(|_| ())
|
||||
@ -59,7 +59,7 @@ impl Server {
|
||||
/// Stop incoming connection processing, stop all workers and exit.
|
||||
///
|
||||
/// If server starts with `spawn()` method, then spawned thread get terminated.
|
||||
pub fn stop(&self, graceful: bool) -> impl Future<Item = (), Error = ()> {
|
||||
pub fn stop(&self, graceful: bool) -> impl Future<Output = Result<(), ()>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.0.unbounded_send(ServerCommand::Stop {
|
||||
graceful,
|
||||
|
@ -1,12 +1,13 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
|
||||
use actix_rt::spawn;
|
||||
use actix_server_config::{Io, ServerConfig};
|
||||
use actix_service::{NewService, Service};
|
||||
use futures::future::{err, ok, FutureResult};
|
||||
use futures::{Future, Poll};
|
||||
use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory};
|
||||
use futures::future::{err, ok, LocalBoxFuture, Ready};
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use log::error;
|
||||
|
||||
use super::Token;
|
||||
@ -24,7 +25,7 @@ pub(crate) enum ServerMessage {
|
||||
}
|
||||
|
||||
pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static {
|
||||
type NewService: NewService<Config = ServerConfig, Request = Io<Stream>>;
|
||||
type NewService: actix::ServiceFactory<Config = ServerConfig, Request = Io<Stream>>;
|
||||
|
||||
fn create(&self) -> Self::NewService;
|
||||
}
|
||||
@ -34,7 +35,7 @@ pub(crate) trait InternalServiceFactory: Send {
|
||||
|
||||
fn clone_factory(&self) -> Box<dyn InternalServiceFactory>;
|
||||
|
||||
fn create(&self) -> Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>>;
|
||||
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>>;
|
||||
}
|
||||
|
||||
pub(crate) type BoxedServerService = Box<
|
||||
@ -42,7 +43,7 @@ pub(crate) type BoxedServerService = Box<
|
||||
Request = (Option<CounterGuard>, ServerMessage),
|
||||
Response = (),
|
||||
Error = (),
|
||||
Future = FutureResult<(), ()>,
|
||||
Future = Ready<Result<(), ()>>,
|
||||
>,
|
||||
>;
|
||||
|
||||
@ -66,10 +67,10 @@ where
|
||||
type Request = (Option<CounterGuard>, ServerMessage);
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = FutureResult<(), ()>;
|
||||
type Future = Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.service.poll_ready().map_err(|_| ())
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(ctx).map_err(|_| ())
|
||||
}
|
||||
|
||||
fn call(&mut self, (guard, req): (Option<CounterGuard>, ServerMessage)) -> Self::Future {
|
||||
@ -80,10 +81,14 @@ where
|
||||
});
|
||||
|
||||
if let Ok(stream) = stream {
|
||||
spawn(self.service.call(Io::new(stream)).then(move |res| {
|
||||
drop(guard);
|
||||
res.map_err(|_| ()).map(|_| ())
|
||||
}));
|
||||
let f = self.service.call(Io::new(stream));
|
||||
spawn(
|
||||
async move {
|
||||
let _ = f.await;
|
||||
drop(guard);
|
||||
}
|
||||
.boxed_local(),
|
||||
);
|
||||
ok(())
|
||||
} else {
|
||||
err(())
|
||||
@ -142,19 +147,19 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn create(&self) -> Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>> {
|
||||
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>> {
|
||||
let token = self.token;
|
||||
let config = ServerConfig::new(self.addr);
|
||||
Box::new(
|
||||
self.inner
|
||||
.create()
|
||||
.new_service(&config)
|
||||
.map_err(|_| ())
|
||||
.map(move |inner| {
|
||||
let service: BoxedServerService = Box::new(StreamService::new(inner));
|
||||
vec![(token, service)]
|
||||
}),
|
||||
)
|
||||
|
||||
self.inner
|
||||
.create()
|
||||
.new_service(&config)
|
||||
.map_err(|_| ())
|
||||
.map_ok(move |inner| {
|
||||
let service: BoxedServerService = Box::new(StreamService::new(inner));
|
||||
vec![(token, service)]
|
||||
})
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +172,7 @@ impl InternalServiceFactory for Box<dyn InternalServiceFactory> {
|
||||
self.as_ref().clone_factory()
|
||||
}
|
||||
|
||||
fn create(&self) -> Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>> {
|
||||
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>> {
|
||||
self.as_ref().create()
|
||||
}
|
||||
}
|
||||
@ -175,7 +180,7 @@ impl InternalServiceFactory for Box<dyn InternalServiceFactory> {
|
||||
impl<F, T, I> ServiceFactory<I> for F
|
||||
where
|
||||
F: Fn() -> T + Send + Clone + 'static,
|
||||
T: NewService<Config = ServerConfig, Request = Io<I>>,
|
||||
T: actix::ServiceFactory<Config = ServerConfig, Request = Io<I>>,
|
||||
I: FromStream,
|
||||
{
|
||||
type NewService = T;
|
@ -1,8 +1,13 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_rt::spawn;
|
||||
use futures::stream::futures_unordered;
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::stream::{futures_unordered, FuturesUnordered, LocalBoxStream};
|
||||
use futures::{FutureExt, Stream, StreamExt, TryFutureExt, TryStream, TryStreamExt};
|
||||
use tokio_net::signal::unix::signal;
|
||||
|
||||
use crate::server::Server;
|
||||
|
||||
@ -27,14 +32,14 @@ pub(crate) struct Signals {
|
||||
streams: Vec<SigStream>,
|
||||
}
|
||||
|
||||
type SigStream = Box<dyn Stream<Item = Signal, Error = io::Error>>;
|
||||
type SigStream = LocalBoxStream<'static, Result<Signal, io::Error>>;
|
||||
|
||||
impl Signals {
|
||||
pub(crate) fn start(srv: Server) {
|
||||
let fut = {
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
tokio_signal::ctrl_c()
|
||||
tokio_net::signal::ctrl_c()
|
||||
.map_err(|_| ())
|
||||
.and_then(move |stream| Signals {
|
||||
srv,
|
||||
@ -44,51 +49,79 @@ impl Signals {
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use tokio_signal::unix;
|
||||
use tokio_net::signal::unix;
|
||||
|
||||
let mut sigs: Vec<Box<dyn Future<Item = SigStream, Error = io::Error>>> =
|
||||
Vec::new();
|
||||
sigs.push(Box::new(
|
||||
tokio_signal::unix::Signal::new(tokio_signal::unix::SIGINT).map(|stream| {
|
||||
let s: SigStream = Box::new(stream.map(|_| Signal::Int));
|
||||
s
|
||||
}),
|
||||
));
|
||||
sigs.push(Box::new(
|
||||
tokio_signal::unix::Signal::new(tokio_signal::unix::SIGHUP).map(
|
||||
|stream: unix::Signal| {
|
||||
let mut sigs: Vec<_> = Vec::new();
|
||||
|
||||
let mut SIG_MAP = [
|
||||
(
|
||||
tokio_net::signal::unix::SignalKind::interrupt(),
|
||||
Signal::Int,
|
||||
),
|
||||
(tokio_net::signal::unix::SignalKind::hangup(), Signal::Hup),
|
||||
(
|
||||
tokio_net::signal::unix::SignalKind::terminate(),
|
||||
Signal::Term,
|
||||
),
|
||||
(tokio_net::signal::unix::SignalKind::quit(), Signal::Quit),
|
||||
];
|
||||
|
||||
for (kind, sig) in SIG_MAP.into_iter() {
|
||||
let sig = sig.clone();
|
||||
let fut = signal(*kind).unwrap();
|
||||
sigs.push(fut.map(move |_| Ok(sig)).boxed_local());
|
||||
}
|
||||
/* TODO: Finish rewriting this
|
||||
sigs.push(
|
||||
tokio_net::signal::unix::signal(tokio_net::signal::si).unwrap()
|
||||
.map(|stream| {
|
||||
let s: SigStream = Box::new(stream.map(|_| Signal::Int));
|
||||
s
|
||||
}).boxed()
|
||||
);
|
||||
sigs.push(
|
||||
|
||||
tokio_net::signal::unix::signal(tokio_net::signal::unix::SignalKind::hangup()).unwrap()
|
||||
.map(|stream: unix::Signal| {
|
||||
let s: SigStream = Box::new(stream.map(|_| Signal::Hup));
|
||||
s
|
||||
},
|
||||
),
|
||||
));
|
||||
sigs.push(Box::new(
|
||||
tokio_signal::unix::Signal::new(tokio_signal::unix::SIGTERM).map(
|
||||
|stream| {
|
||||
}).boxed()
|
||||
);
|
||||
sigs.push(
|
||||
tokio_net::signal::unix::signal(
|
||||
tokio_net::signal::unix::SignalKind::terminate()
|
||||
).unwrap()
|
||||
.map(|stream| {
|
||||
let s: SigStream = Box::new(stream.map(|_| Signal::Term));
|
||||
s
|
||||
},
|
||||
),
|
||||
));
|
||||
sigs.push(Box::new(
|
||||
tokio_signal::unix::Signal::new(tokio_signal::unix::SIGQUIT).map(
|
||||
|stream| {
|
||||
}).boxed(),
|
||||
);
|
||||
sigs.push(
|
||||
tokio_net::signal::unix::signal(
|
||||
tokio_net::signal::unix::SignalKind::quit()
|
||||
).unwrap()
|
||||
.map(|stream| {
|
||||
let s: SigStream = Box::new(stream.map(|_| Signal::Quit));
|
||||
s
|
||||
},
|
||||
),
|
||||
));
|
||||
futures_unordered(sigs)
|
||||
.collect()
|
||||
.map_err(|_| ())
|
||||
.and_then(move |streams| Signals { srv, streams })
|
||||
}).boxed()
|
||||
);
|
||||
*/
|
||||
|
||||
Signals { srv, streams: sigs }
|
||||
}
|
||||
};
|
||||
spawn(fut);
|
||||
spawn(async {});
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Signals {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/*
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
@ -115,4 +148,5 @@ impl Future for Signals {
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::{fmt, io, net};
|
||||
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_reactor::Handle;
|
||||
use tokio_tcp::TcpStream;
|
||||
use tokio_net::driver::Handle;
|
||||
use tokio_net::tcp::TcpStream;
|
||||
|
||||
pub(crate) enum StdListener {
|
||||
Tcp(net::TcpListener),
|
||||
|
@ -3,19 +3,19 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::counter::Counter;
|
||||
|
||||
#[cfg(feature = "ssl")]
|
||||
#[cfg(feature = "openssl")]
|
||||
mod openssl;
|
||||
#[cfg(feature = "ssl")]
|
||||
#[cfg(feature = "openssl")]
|
||||
pub use self::openssl::OpensslAcceptor;
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
#[cfg(feature = "nativetls")]
|
||||
mod nativetls;
|
||||
#[cfg(feature = "tls")]
|
||||
pub use self::nativetls::{NativeTlsAcceptor, TlsStream};
|
||||
#[cfg(feature = "nativetls")]
|
||||
pub use self::nativetls::NativeTlsAcceptor;
|
||||
|
||||
#[cfg(feature = "rust-tls")]
|
||||
#[cfg(feature = "rustls")]
|
||||
mod rustls;
|
||||
#[cfg(feature = "rust-tls")]
|
||||
#[cfg(feature = "rustls")]
|
||||
pub use self::rustls::RustlsAcceptor;
|
||||
|
||||
/// Sets the maximum per-worker concurrent ssl connection establish process.
|
||||
|
@ -1,14 +1,16 @@
|
||||
use std::io;
|
||||
use std::convert::Infallible;
|
||||
use std::marker::PhantomData;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{NewService, Service};
|
||||
use futures::{future::ok, future::FutureResult, Async, Future, Poll};
|
||||
use native_tls::{self, Error, HandshakeError, TlsAcceptor};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures::future::{self, FutureExt as _, LocalBoxFuture, TryFutureExt as _};
|
||||
use native_tls::Error;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tls::{TlsAcceptor, TlsStream};
|
||||
|
||||
use crate::counter::{Counter, CounterGuard};
|
||||
use crate::counter::Counter;
|
||||
use crate::ssl::MAX_CONN_COUNTER;
|
||||
use crate::{Io, Protocol, ServerConfig};
|
||||
use crate::{Io, ServerConfig};
|
||||
|
||||
/// Support `SSL` connections via native-tls package
|
||||
///
|
||||
@ -18,8 +20,12 @@ pub struct NativeTlsAcceptor<T, P = ()> {
|
||||
io: PhantomData<(T, P)>,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> NativeTlsAcceptor<T, P> {
|
||||
impl<T, P> NativeTlsAcceptor<T, P>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
/// Create `NativeTlsAcceptor` instance
|
||||
#[inline]
|
||||
pub fn new(acceptor: TlsAcceptor) -> Self {
|
||||
NativeTlsAcceptor {
|
||||
acceptor,
|
||||
@ -28,7 +34,8 @@ impl<T: AsyncRead + AsyncWrite, P> NativeTlsAcceptor<T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Clone for NativeTlsAcceptor<T, P> {
|
||||
impl<T, P> Clone for NativeTlsAcceptor<T, P> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
acceptor: self.acceptor.clone(),
|
||||
@ -37,21 +44,25 @@ impl<T: AsyncRead + AsyncWrite, P> Clone for NativeTlsAcceptor<T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> NewService for NativeTlsAcceptor<T, P> {
|
||||
impl<T, P> ServiceFactory for NativeTlsAcceptor<T, P>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||
P: 'static,
|
||||
{
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<TlsStream<T>, P>;
|
||||
type Error = Error;
|
||||
|
||||
type Config = ServerConfig;
|
||||
type Service = NativeTlsAcceptorService<T, P>;
|
||||
type InitError = ();
|
||||
type Future = FutureResult<Self::Service, Self::InitError>;
|
||||
type InitError = Infallible;
|
||||
type Future = future::Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
|
||||
cfg.set_secure();
|
||||
|
||||
MAX_CONN_COUNTER.with(|conns| {
|
||||
ok(NativeTlsAcceptorService {
|
||||
future::ok(NativeTlsAcceptorService {
|
||||
acceptor: self.acceptor.clone(),
|
||||
conns: conns.clone(),
|
||||
io: PhantomData,
|
||||
@ -66,117 +77,46 @@ pub struct NativeTlsAcceptorService<T, P> {
|
||||
conns: Counter,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Service for NativeTlsAcceptorService<T, P> {
|
||||
impl<T, P> Clone for NativeTlsAcceptorService<T, P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
acceptor: self.acceptor.clone(),
|
||||
io: PhantomData,
|
||||
conns: self.conns.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P> Service for NativeTlsAcceptorService<T, P>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||
P: 'static,
|
||||
{
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<TlsStream<T>, P>;
|
||||
type Error = Error;
|
||||
type Future = Accept<T, P>;
|
||||
type Future = LocalBoxFuture<'static, Result<Io<TlsStream<T>, P>, Error>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
if self.conns.available() {
|
||||
Ok(Async::Ready(()))
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if self.conns.available(cx) {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
let (io, params, _) = req.into_parts();
|
||||
Accept {
|
||||
_guard: self.conns.get(),
|
||||
inner: Some(self.acceptor.accept(io)),
|
||||
params: Some(params),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
||||
/// protocol.
|
||||
///
|
||||
/// A `TlsStream<S>` represents a handshake that has been completed successfully
|
||||
/// and both the server and the client are ready for receiving and sending
|
||||
/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written
|
||||
/// to a `TlsStream` are encrypted when passing through to `S`.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsStream<S> {
|
||||
inner: native_tls::TlsStream<S>,
|
||||
}
|
||||
|
||||
/// Future returned from `NativeTlsAcceptor::accept` which will resolve
|
||||
/// once the accept handshake has finished.
|
||||
pub struct Accept<S, P> {
|
||||
inner: Option<Result<native_tls::TlsStream<S>, HandshakeError<S>>>,
|
||||
params: Option<P>,
|
||||
_guard: CounterGuard,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Future for Accept<T, P> {
|
||||
type Item = Io<TlsStream<T>, P>;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match self.inner.take().expect("cannot poll MidHandshake twice") {
|
||||
Ok(stream) => Ok(Async::Ready(Io::from_parts(
|
||||
TlsStream { inner: stream },
|
||||
self.params.take().unwrap(),
|
||||
Protocol::Unknown,
|
||||
))),
|
||||
Err(HandshakeError::Failure(e)) => Err(e),
|
||||
Err(HandshakeError::WouldBlock(s)) => match s.handshake() {
|
||||
Ok(stream) => Ok(Async::Ready(Io::from_parts(
|
||||
TlsStream { inner: stream },
|
||||
self.params.take().unwrap(),
|
||||
Protocol::Unknown,
|
||||
))),
|
||||
Err(HandshakeError::Failure(e)) => Err(e),
|
||||
Err(HandshakeError::WouldBlock(s)) => {
|
||||
self.inner = Some(Err(HandshakeError::WouldBlock(s)));
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> TlsStream<S> {
|
||||
/// Get access to the internal `native_tls::TlsStream` stream which also
|
||||
/// transitively allows access to `S`.
|
||||
pub fn get_ref(&self) -> &native_tls::TlsStream<S> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Get mutable access to the internal `native_tls::TlsStream` stream which
|
||||
/// also transitively allows mutable access to `S`.
|
||||
pub fn get_mut(&mut self) -> &mut native_tls::TlsStream<S> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsyncRead + AsyncWrite> AsyncRead for TlsStream<S> {}
|
||||
|
||||
impl<S: AsyncRead + AsyncWrite> AsyncWrite for TlsStream<S> {
|
||||
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||
match self.inner.shutdown() {
|
||||
Ok(_) => (),
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
self.inner.get_mut().shutdown()
|
||||
let guard = self.conns.get();
|
||||
let this = self.clone();
|
||||
let (io, params, proto) = req.into_parts();
|
||||
async move { this.acceptor.accept(io).await }
|
||||
.map_ok(move |stream| Io::from_parts(stream, params, proto))
|
||||
.map_ok(move |io| {
|
||||
// Required to preserve `CounterGuard` until `Self::Future`
|
||||
// is completely resolved.
|
||||
let _ = guard;
|
||||
io
|
||||
})
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{NewService, Service};
|
||||
use futures::{future::ok, future::FutureResult, Async, Future, Poll};
|
||||
use openssl::ssl::{HandshakeError, SslAcceptor};
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures::future::{ok, FutureExt, LocalBoxFuture, Ready};
|
||||
use open_ssl::ssl::SslAcceptor;
|
||||
use pin_project::pin_project;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_openssl::{AcceptAsync, SslAcceptorExt, SslStream};
|
||||
use tokio_openssl::{HandshakeError, SslStream};
|
||||
|
||||
use crate::counter::{Counter, CounterGuard};
|
||||
use crate::ssl::MAX_CONN_COUNTER;
|
||||
@ -37,14 +41,14 @@ impl<T: AsyncRead + AsyncWrite, P> Clone for OpensslAcceptor<T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> NewService for OpensslAcceptor<T, P> {
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin + 'static, P> ServiceFactory for OpensslAcceptor<T, P> {
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<SslStream<T>, P>;
|
||||
type Error = HandshakeError<T>;
|
||||
type Config = ServerConfig;
|
||||
type Service = OpensslAcceptorService<T, P>;
|
||||
type InitError = ();
|
||||
type Future = FutureResult<Self::Service, Self::InitError>;
|
||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, cfg: &ServerConfig) -> Self::Future {
|
||||
cfg.set_secure();
|
||||
@ -65,46 +69,54 @@ pub struct OpensslAcceptorService<T, P> {
|
||||
io: PhantomData<(T, P)>,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Service for OpensslAcceptorService<T, P> {
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin + 'static, P> Service for OpensslAcceptorService<T, P> {
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<SslStream<T>, P>;
|
||||
type Error = HandshakeError<T>;
|
||||
type Future = OpensslAcceptorServiceFut<T, P>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
if self.conns.available() {
|
||||
Ok(Async::Ready(()))
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if self.conns.available(ctx) {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
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: SslAcceptorExt::accept_async(&self.acceptor, io),
|
||||
fut: async move {
|
||||
let acc = acc;
|
||||
tokio_openssl::accept(&acc, io).await
|
||||
}
|
||||
.boxed_local::<'static>(),
|
||||
params: Some(params),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct OpensslAcceptorServiceFut<T, P>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
{
|
||||
fut: AcceptAsync<T>,
|
||||
#[pin]
|
||||
fut: LocalBoxFuture<'static, Result<SslStream<T>, HandshakeError<T>>>,
|
||||
params: Option<P>,
|
||||
_guard: CounterGuard,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Future for OpensslAcceptorServiceFut<T, P> {
|
||||
type Item = Io<SslStream<T>, P>;
|
||||
type Error = HandshakeError<T>;
|
||||
type Output = Result<Io<SslStream<T>, P>, HandshakeError<T>>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let io = futures::try_ready!(self.fut.poll());
|
||||
let proto = if let Some(protos) = io.get_ref().ssl().selected_alpn_protocol() {
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
|
||||
let io = futures::ready!(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";
|
||||
@ -121,10 +133,7 @@ impl<T: AsyncRead + AsyncWrite, P> Future for OpensslAcceptorServiceFut<T, P> {
|
||||
} else {
|
||||
Protocol::Unknown
|
||||
};
|
||||
Ok(Async::Ready(Io::from_parts(
|
||||
io,
|
||||
self.params.take().unwrap(),
|
||||
proto,
|
||||
)))
|
||||
|
||||
Poll::Ready(Ok(Io::from_parts(io, this.params.take().unwrap(), proto)))
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use actix_service::{NewService, Service};
|
||||
use futures::{future::ok, future::FutureResult, Async, Future, Poll};
|
||||
use rustls::ServerConfig;
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use futures::future::{ok, Ready};
|
||||
use pin_project::pin_project;
|
||||
use rust_tls::ServerConfig;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio_rustls::{server::TlsStream, Accept, TlsAcceptor};
|
||||
|
||||
@ -39,7 +43,7 @@ impl<T, P> Clone for RustlsAcceptor<T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> NewService for RustlsAcceptor<T, P> {
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin, P> ServiceFactory for RustlsAcceptor<T, P> {
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<TlsStream<T>, P>;
|
||||
type Error = io::Error;
|
||||
@ -47,7 +51,7 @@ impl<T: AsyncRead + AsyncWrite, P> NewService for RustlsAcceptor<T, P> {
|
||||
type Config = SrvConfig;
|
||||
type Service = RustlsAcceptorService<T, P>;
|
||||
type InitError = ();
|
||||
type Future = FutureResult<Self::Service, Self::InitError>;
|
||||
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||
|
||||
fn new_service(&self, cfg: &SrvConfig) -> Self::Future {
|
||||
cfg.set_secure();
|
||||
@ -68,17 +72,17 @@ pub struct RustlsAcceptorService<T, P> {
|
||||
conns: Counter,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Service for RustlsAcceptorService<T, P> {
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin, P> Service for RustlsAcceptorService<T, P> {
|
||||
type Request = Io<T, P>;
|
||||
type Response = Io<TlsStream<T>, P>;
|
||||
type Error = io::Error;
|
||||
type Future = RustlsAcceptorServiceFut<T, P>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
if self.conns.available() {
|
||||
Ok(Async::Ready(()))
|
||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
if self.conns.available(cx) {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Ok(Async::NotReady)
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,25 +96,26 @@ impl<T: AsyncRead + AsyncWrite, P> Service for RustlsAcceptorService<T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct RustlsAcceptorServiceFut<T, P>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
#[pin]
|
||||
fut: Accept<T>,
|
||||
params: Option<P>,
|
||||
_guard: CounterGuard,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + AsyncWrite, P> Future for RustlsAcceptorServiceFut<T, P> {
|
||||
type Item = Io<TlsStream<T>, P>;
|
||||
type Error = io::Error;
|
||||
impl<T: AsyncRead + AsyncWrite + Unpin, P> Future for RustlsAcceptorServiceFut<T, P> {
|
||||
type Output = Result<Io<TlsStream<T>, P>, io::Error>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let io = futures::try_ready!(self.fut.poll());
|
||||
Ok(Async::Ready(Io::from_parts(
|
||||
io,
|
||||
self.params.take().unwrap(),
|
||||
Protocol::Unknown,
|
||||
)))
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
let params = this.params.take().unwrap();
|
||||
Poll::Ready(
|
||||
futures::ready!(this.fut.poll(cx))
|
||||
.map(move |io| Io::from_parts(io, params, Protocol::Unknown)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,20 @@
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{mem, time};
|
||||
|
||||
use actix_rt::{spawn, Arbiter};
|
||||
use futures::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures::sync::oneshot;
|
||||
use futures::{future, Async, Future, Poll, Stream};
|
||||
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::{join_all, LocalBoxFuture, MapOk};
|
||||
use futures::{Future, FutureExt, Stream, TryFutureExt};
|
||||
use log::{error, info, trace};
|
||||
use tokio_timer::{sleep, Delay};
|
||||
use tokio_timer::{delay, Delay};
|
||||
|
||||
use crate::accept::AcceptNotify;
|
||||
use crate::counter::Counter;
|
||||
use crate::services::{BoxedServerService, InternalServiceFactory, ServerMessage};
|
||||
use crate::service::{BoxedServerService, InternalServiceFactory, ServerMessage};
|
||||
use crate::socket::{SocketAddr, StdStream};
|
||||
use crate::Token;
|
||||
|
||||
@ -153,31 +156,38 @@ impl Worker {
|
||||
state: WorkerState::Unavailable(Vec::new()),
|
||||
});
|
||||
|
||||
let mut fut = Vec::new();
|
||||
let mut fut: Vec<MapOk<LocalBoxFuture<'static, _>, _>> = Vec::new();
|
||||
for (idx, factory) in wrk.factories.iter().enumerate() {
|
||||
fut.push(factory.create().map(move |res| {
|
||||
res.into_iter()
|
||||
.map(|(t, s)| (idx, t, s))
|
||||
fut.push(factory.create().map_ok(move |r| {
|
||||
r.into_iter()
|
||||
.map(|(t, s): (Token, _)| (idx, t, s))
|
||||
.collect::<Vec<_>>()
|
||||
}));
|
||||
}
|
||||
|
||||
spawn(
|
||||
future::join_all(fut)
|
||||
.map_err(|e| {
|
||||
error!("Can not start worker: {:?}", e);
|
||||
Arbiter::current().stop();
|
||||
})
|
||||
.and_then(move |services| {
|
||||
for item in services {
|
||||
for (idx, token, service) in item {
|
||||
while token.0 >= wrk.services.len() {
|
||||
wrk.services.push(None);
|
||||
async move {
|
||||
let res = join_all(fut).await;
|
||||
let res: Result<Vec<_>, _> = res.into_iter().collect();
|
||||
match res {
|
||||
Ok(services) => {
|
||||
for item in services {
|
||||
for (idx, token, service) in item {
|
||||
while token.0 >= wrk.services.len() {
|
||||
wrk.services.push(None);
|
||||
}
|
||||
wrk.services[token.0] = Some((idx, service));
|
||||
}
|
||||
wrk.services[token.0] = Some((idx, service));
|
||||
}
|
||||
}
|
||||
wrk
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Can not start worker: {:?}", e);
|
||||
Arbiter::current().stop();
|
||||
}
|
||||
}
|
||||
wrk.await
|
||||
}
|
||||
.boxed_local(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -198,13 +208,17 @@ impl Worker {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_readiness(&mut self, trace: bool) -> Result<bool, (Token, usize)> {
|
||||
let mut ready = self.conns.available();
|
||||
fn check_readiness(
|
||||
&mut self,
|
||||
trace: bool,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Result<bool, (Token, usize)> {
|
||||
let mut ready = self.conns.available(cx);
|
||||
let mut failed = None;
|
||||
for (token, service) in &mut self.services.iter_mut().enumerate() {
|
||||
if let Some(service) = service {
|
||||
match service.1.poll_ready() {
|
||||
Ok(Async::Ready(_)) => {
|
||||
match service.1.poll_ready(cx) {
|
||||
Poll::Ready(Ok(_)) => {
|
||||
if trace {
|
||||
trace!(
|
||||
"Service {:?} is available",
|
||||
@ -212,8 +226,8 @@ impl Worker {
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(Async::NotReady) => ready = false,
|
||||
Err(_) => {
|
||||
Poll::Pending => ready = false,
|
||||
Poll::Ready(Err(_)) => {
|
||||
error!(
|
||||
"Service {:?} readiness check returned error, restarting",
|
||||
self.factories[service.0].name(Token(token))
|
||||
@ -238,43 +252,44 @@ enum WorkerState {
|
||||
Restarting(
|
||||
usize,
|
||||
Token,
|
||||
Box<dyn Future<Item = Vec<(Token, BoxedServerService)>, Error = ()>>,
|
||||
Pin<Box<dyn Future<Output = Result<Vec<(Token, BoxedServerService)>, ()>>>>,
|
||||
),
|
||||
Shutdown(Delay, Delay, oneshot::Sender<bool>),
|
||||
}
|
||||
|
||||
impl Future for Worker {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
type Output = ();
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// `StopWorker` message handler
|
||||
if let Ok(Async::Ready(Some(StopCommand { graceful, result }))) = self.rx2.poll() {
|
||||
if let Poll::Ready(Some(StopCommand { graceful, result })) =
|
||||
Pin::new(&mut self.rx2).poll_next(cx)
|
||||
{
|
||||
self.availability.set(false);
|
||||
let num = num_connections();
|
||||
if num == 0 {
|
||||
info!("Shutting down worker, 0 connections");
|
||||
let _ = result.send(true);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
} else if graceful {
|
||||
self.shutdown(false);
|
||||
let num = num_connections();
|
||||
if num != 0 {
|
||||
info!("Graceful worker shutdown, {} connections", num);
|
||||
self.state = WorkerState::Shutdown(
|
||||
sleep(time::Duration::from_secs(1)),
|
||||
sleep(self.shutdown_timeout),
|
||||
delay(time::Instant::now() + time::Duration::from_secs(1)),
|
||||
delay(time::Instant::now() + self.shutdown_timeout),
|
||||
result,
|
||||
);
|
||||
} else {
|
||||
let _ = result.send(true);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
} else {
|
||||
info!("Force shutdown worker, {} connections", num);
|
||||
self.shutdown(true);
|
||||
let _ = result.send(false);
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,13 +297,13 @@ impl Future for Worker {
|
||||
|
||||
match state {
|
||||
WorkerState::Unavailable(mut conns) => {
|
||||
match self.check_readiness(true) {
|
||||
match self.check_readiness(true, cx) {
|
||||
Ok(true) => {
|
||||
self.state = WorkerState::Available;
|
||||
|
||||
// process requests from wait queue
|
||||
while let Some(msg) = conns.pop() {
|
||||
match self.check_readiness(false) {
|
||||
match self.check_readiness(false, cx) {
|
||||
Ok(true) => {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[msg.token.0]
|
||||
@ -300,7 +315,7 @@ impl Future for Worker {
|
||||
Ok(false) => {
|
||||
trace!("Worker is unavailable");
|
||||
self.state = WorkerState::Unavailable(conns);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Err((token, idx)) => {
|
||||
trace!(
|
||||
@ -312,16 +327,16 @@ impl Future for Worker {
|
||||
token,
|
||||
self.factories[idx].create(),
|
||||
);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.availability.set(true);
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Ok(false) => {
|
||||
self.state = WorkerState::Unavailable(conns);
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Err((token, idx)) => {
|
||||
trace!(
|
||||
@ -330,13 +345,13 @@ impl Future for Worker {
|
||||
);
|
||||
self.state =
|
||||
WorkerState::Restarting(idx, token, self.factories[idx].create());
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
WorkerState::Restarting(idx, token, mut fut) => {
|
||||
match fut.poll() {
|
||||
Ok(Async::Ready(item)) => {
|
||||
match Pin::new(&mut fut).poll(cx) {
|
||||
Poll::Ready(Ok(item)) => {
|
||||
for (token, service) in item {
|
||||
trace!(
|
||||
"Service {:?} has been restarted",
|
||||
@ -346,55 +361,55 @@ impl Future for Worker {
|
||||
self.state = WorkerState::Unavailable(Vec::new());
|
||||
}
|
||||
}
|
||||
Ok(Async::NotReady) => {
|
||||
self.state = WorkerState::Restarting(idx, token, fut);
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
Err(_) => {
|
||||
Poll::Ready(Err(_)) => {
|
||||
panic!(
|
||||
"Can not restart {:?} service",
|
||||
self.factories[idx].name(token)
|
||||
);
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.state = WorkerState::Restarting(idx, token, fut);
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
WorkerState::Shutdown(mut t1, mut t2, tx) => {
|
||||
let num = num_connections();
|
||||
if num == 0 {
|
||||
let _ = tx.send(true);
|
||||
Arbiter::current().stop();
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
// check graceful timeout
|
||||
match t2.poll().unwrap() {
|
||||
Async::NotReady => (),
|
||||
Async::Ready(_) => {
|
||||
match Pin::new(&mut t2).poll(cx) {
|
||||
Poll::Pending => (),
|
||||
Poll::Ready(_) => {
|
||||
self.shutdown(true);
|
||||
let _ = tx.send(false);
|
||||
Arbiter::current().stop();
|
||||
return Ok(Async::Ready(()));
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
|
||||
// sleep for 1 second and then check again
|
||||
match t1.poll().unwrap() {
|
||||
Async::NotReady => (),
|
||||
Async::Ready(_) => {
|
||||
t1 = sleep(time::Duration::from_secs(1));
|
||||
let _ = t1.poll();
|
||||
match Pin::new(&mut t1).poll(cx) {
|
||||
Poll::Pending => (),
|
||||
Poll::Ready(_) => {
|
||||
t1 = delay(time::Instant::now() + time::Duration::from_secs(1));
|
||||
let _ = Pin::new(&mut t1).poll(cx);
|
||||
}
|
||||
}
|
||||
self.state = WorkerState::Shutdown(t1, t2, tx);
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
WorkerState::Available => {
|
||||
loop {
|
||||
match self.rx.poll() {
|
||||
match Pin::new(&mut self.rx).poll_next(cx) {
|
||||
// handle incoming tcp stream
|
||||
Ok(Async::Ready(Some(WorkerCommand(msg)))) => {
|
||||
match self.check_readiness(false) {
|
||||
Poll::Ready(Some(WorkerCommand(msg))) => {
|
||||
match self.check_readiness(false, cx) {
|
||||
Ok(true) => {
|
||||
let guard = self.conns.get();
|
||||
let _ = self.services[msg.token.0]
|
||||
@ -422,13 +437,13 @@ impl Future for Worker {
|
||||
);
|
||||
}
|
||||
}
|
||||
return self.poll();
|
||||
return self.poll(cx);
|
||||
}
|
||||
Ok(Async::NotReady) => {
|
||||
Poll::Pending => {
|
||||
self.state = WorkerState::Available;
|
||||
return Ok(Async::NotReady);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())),
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ use std::{net, thread, time};
|
||||
|
||||
use actix_codec::{BytesCodec, Framed};
|
||||
use actix_server::{Io, Server, ServerConfig};
|
||||
use actix_service::{new_service_cfg, service_fn, IntoService};
|
||||
use actix_service::{factory_fn_cfg, service_fn, service_fn2};
|
||||
use bytes::Bytes;
|
||||
use futures::{Future, Sink};
|
||||
use futures::{future::ok, SinkExt};
|
||||
use net2::TcpBuilder;
|
||||
use tokio_tcp::TcpStream;
|
||||
use tokio_net::tcp::TcpStream;
|
||||
|
||||
fn unused_addr() -> net::SocketAddr {
|
||||
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||
@ -28,9 +28,9 @@ fn test_bind() {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv = Server::build()
|
||||
.bind("test", addr, move || {
|
||||
new_service_cfg(move |cfg: &ServerConfig| {
|
||||
factory_fn_cfg(move |cfg: &ServerConfig| {
|
||||
assert_eq!(cfg.local_addr(), addr);
|
||||
Ok::<_, ()>((|_| Ok::<_, ()>(())).into_service())
|
||||
ok::<_, ()>(service_fn2(|_| ok::<_, ()>(())))
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
@ -54,7 +54,7 @@ fn test_bind_no_config() {
|
||||
let h = thread::spawn(move || {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv = Server::build()
|
||||
.bind("test", addr, move || service_fn(|_| Ok::<_, ()>(())))
|
||||
.bind("test", addr, move || service_fn(|_| ok::<_, ()>(())))
|
||||
.unwrap()
|
||||
.start();
|
||||
let _ = tx.send((srv, actix_rt::System::current()));
|
||||
@ -76,9 +76,9 @@ fn test_listen() {
|
||||
let lst = net::TcpListener::bind(addr).unwrap();
|
||||
let srv = Server::build()
|
||||
.listen("test", lst, move || {
|
||||
new_service_cfg(move |cfg: &ServerConfig| {
|
||||
factory_fn_cfg(move |cfg: &ServerConfig| {
|
||||
assert_eq!(cfg.local_addr(), addr);
|
||||
Ok::<_, ()>((|_| Ok::<_, ()>(())).into_service())
|
||||
ok::<_, ()>(service_fn2(|_| ok::<_, ()>(())))
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
@ -102,19 +102,21 @@ fn test_start() {
|
||||
|
||||
let h = thread::spawn(move || {
|
||||
let sys = actix_rt::System::new("test");
|
||||
let srv = Server::build()
|
||||
let srv: Server = Server::build()
|
||||
.backlog(100)
|
||||
.bind("test", addr, move || {
|
||||
new_service_cfg(move |cfg: &ServerConfig| {
|
||||
factory_fn_cfg(move |cfg: &ServerConfig| {
|
||||
assert_eq!(cfg.local_addr(), addr);
|
||||
Ok::<_, ()>(
|
||||
(|io: Io<TcpStream>| {
|
||||
Framed::new(io.into_parts().0, BytesCodec)
|
||||
.send(Bytes::from_static(b"test"))
|
||||
.then(|_| Ok::<_, ()>(()))
|
||||
})
|
||||
.into_service(),
|
||||
)
|
||||
|
||||
let srv = service_fn2(|io: Io<TcpStream>| {
|
||||
async {
|
||||
let mut f = Framed::new(io.into_parts().0, BytesCodec);
|
||||
f.send(Bytes::from_static(b"test")).await.unwrap();
|
||||
Ok::<_, ()>(())
|
||||
}
|
||||
});
|
||||
|
||||
ok::<_, ()>(srv)
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
@ -125,7 +127,7 @@ fn test_start() {
|
||||
});
|
||||
let (srv, sys) = rx.recv().unwrap();
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
let mut buf = [1u8; 4];
|
||||
let mut conn = net::TcpStream::connect(addr).unwrap();
|
||||
let _ = conn.read_exact(&mut buf);
|
||||
assert_eq!(buf, b"test"[..]);
|
||||
|
Reference in New Issue
Block a user