1
0
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:
Nikolay Kim
2019-11-14 18:38:24 +06:00
committed by GitHub
parent 9fa2a36b4e
commit 13049b80ca
92 changed files with 4273 additions and 4165 deletions

View File

@ -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

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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)
}
}
*/
}

View File

@ -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),

View File

@ -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.

View File

@ -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()
}
}

View File

@ -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)))
}
}

View File

@ -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)),
)
}
}

View File

@ -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(()),
}
}
}

View File

@ -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"[..]);